List:Commits« Previous MessageNext Message »
From:Dmitry Shulga Date:January 25 2013 11:55am
Subject:bzr push into mysql-trunk branch (Dmitry.Shulga:5356 to 5376)
View as plain text  
 5376 Dmitry Shulga	2013-01-25
      Separate trigger loading and parsing. Added DOXYGEN-style comments
      for new methods. 

    added:
      sql/table_trigger_dispatcher.cc
      sql/table_trigger_dispatcher.h
    modified:
      sql/trigger.cc
      sql/trigger.h
      sql/trigger_loader.cc
      sql/trigger_loader.h
 5375 Dmitry Shulga	2013-01-23
      Table_trigger_list was renamed to Table_trigger_dispatcher

    removed:
      sql/table_trigger_list.cc
      sql/table_trigger_list.h
    modified:
      sql/CMakeLists.txt
      sql/item.cc
      sql/item.h
      sql/ndb_local_schema.cc
      sql/sp_head.h
      sql/sp_instr.cc
      sql/sql_base.cc
      sql/sql_base.h
      sql/sql_rename.cc
      sql/sql_show.cc
      sql/sql_table.cc
      sql/sql_trigger.cc
      sql/table.cc
      sql/table.h
      sql/trigger.cc
      sql/trigger.h
      sql/trigger_loader.cc
      sql/trigger_loader.h
 5374 Alexander Nozdrin	2013-01-23
      Remove xxx.empty() as Trigger_loader does not have state.

    modified:
      sql/table_trigger_list.cc
      sql/trigger_loader.cc
 5373 Alexander Nozdrin	2013-01-23
      Reformat Trigger_loader::parse_triggers() (II).

    modified:
      sql/trigger_loader.cc
 5372 Alexander Nozdrin	2013-01-23
      Reformat Trigger_loader::parse_triggers() (I).

    modified:
      sql/trigger_loader.cc
 5371 Alexander Nozdrin	2013-01-23
      Reformat Trigger_loader::load_triggers() (II).

    modified:
      sql/trigger_loader.cc
      sql/trigger_loader.h
 5370 Alexander Nozdrin	2013-01-23
      Reformat Trigger_loader::load_triggers().

    modified:
      sql/trigger_loader.cc
 5369 Alexander Nozdrin	2013-01-23
      Join Trigger_loader::load_triggers() and Trigger_loader::parse_triggers().

    modified:
      sql/table_trigger_list.cc
      sql/trigger_loader.cc
      sql/trigger_loader.h
 5368 Alexander Nozdrin	2013-01-23
      A step to join Trigger_loader::load_triggers() and Trigger_loader::parse_triggers().

    modified:
      sql/table_trigger_list.cc
 5367 Alexander Nozdrin	2013-01-23
      Move parse_triggers() to Trigger_loader.

    added:
      sql/trigger_creation_ctx.cc
      sql/trigger_creation_ctx.h
    modified:
      sql/CMakeLists.txt
      sql/sql_trigger.cc
      sql/sql_trigger.h
      sql/table_trigger_list.cc
      sql/table_trigger_list.h
      sql/trigger.cc
      sql/trigger_loader.cc
      sql/trigger_loader.h
 5366 Alexander Nozdrin	2013-01-23
      Getting rid of names_only parameter (Final step).

    modified:
      sql/table_trigger_list.cc
      sql/table_trigger_list.h
 5365 Alexander Nozdrin	2013-01-22
      Getting rid of names_only parameter (Step 2).

    modified:
      sql/table_trigger_list.cc
      sql/trigger.cc
      sql/trigger.h
 5364 Alexander Nozdrin	2013-01-22
      Getting rid of names_only parameter (Step 1); Polishing.

    modified:
      sql/table_trigger_list.cc
      sql/table_trigger_list.h
 5363 Alexander Nozdrin	2013-01-22
      Make Table_loader::check_uniqueness() static.

    modified:
      sql/table_trigger_list.cc
      sql/trigger_loader.cc
      sql/trigger_loader.h
 5362 Alexander Nozdrin	2013-01-22
      Rename Table_triggers_list to Table_trigger_list.

    modified:
      sql/item.cc
      sql/item.h
      sql/ndb_local_schema.cc
      sql/sp_head.h
      sql/sp_instr.cc
      sql/sql_base.cc
      sql/sql_base.h
      sql/sql_rename.cc
      sql/sql_show.cc
      sql/sql_table.cc
      sql/sql_trigger.cc
      sql/table.cc
      sql/table.h
      sql/table_trigger_list.cc
      sql/table_trigger_list.h
      sql/trigger.cc
      sql/trigger.h
      sql/trigger_loader.cc
 5361 Alexander Nozdrin	2013-01-22
      Extract Table_triggers_list into a separate file (table_trigger_list.h/.cc).

    added:
      sql/table_trigger_list.cc
      sql/table_trigger_list.h
    modified:
      sql/CMakeLists.txt
      sql/item.h
      sql/sql_trigger.cc
      sql/sql_trigger.h
 5360 Alexander Nozdrin	2013-01-22
      Get rid of Table_triggers_list::trigger_loader

    modified:
      sql/sql_trigger.cc
      sql/sql_trigger.h
 5359 Alexander Nozdrin	2013-01-22
      Extract Trigger_loader into trigger_loader.h/.cc. Tests pass.

    added:
      sql/trigger_loader.cc
      sql/trigger_loader.h
    modified:
      sql/CMakeLists.txt
      sql/sql_show.cc
      sql/sql_trigger.cc
      sql/sql_trigger.h
      sql/trigger.h
 5358 Alexander Nozdrin	2013-01-22
      Extract Trigger into trigger.h/.cc

    added:
      sql/trigger.cc
      sql/trigger.h
    modified:
      sql/CMakeLists.txt
      sql/sql_class.h
      sql/sql_trigger.cc
      sql/sql_trigger.h
      sql/table.h
 5357 Alexander Nozdrin	2013-01-22
      Fix build failure

    modified:
      sql/sql_trigger.cc
 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
=== modified file 'sql/CMakeLists.txt'
--- a/sql/CMakeLists.txt	2012-11-21 12:44:48 +0000
+++ b/sql/CMakeLists.txt	2013-01-23 15:11:25 +0000
@@ -160,10 +160,14 @@ SET(SQL_SHARED_SOURCES
   sql_view.cc
   strfunc.cc
   sys_vars.cc
-  table.cc
   table_cache.cc
+  table.cc
+  table_trigger_dispatcher.cc
   thr_malloc.cc 
   transaction.cc
+  trigger.cc
+  trigger_creation_ctx.cc
+  trigger_loader.cc
   tztime.cc
   uniques.cc
   unireg.cc

=== modified file 'sql/item.cc'
--- a/sql/item.cc	2012-12-13 11:16:18 +0000
+++ b/sql/item.cc	2013-01-23 15:11:25 +0000
@@ -8132,9 +8132,10 @@ void Item_insert_value::print(String *st
     this stage we can't say exactly what Field object (corresponding
     to TABLE::record[0] or TABLE::record[1]) should be bound to this
     Item, we only find out index of the Field and then select concrete
-    Field object in fix_fields() (by that time Table_trigger_list::old_field/
-    new_field should point to proper array of Fields).
-    It also binds Item_trigger_field to Table_triggers_list object for
+    Field object in fix_fields() (by that time
+    Table_trigger_dispatcher::old_field/new_field should point to proper
+    array of Fields).
+    It also binds Item_trigger_field to Table_trigger_dispatcher object for
     table of trigger which uses this item.
 */
 
@@ -8145,7 +8146,7 @@ void Item_trigger_field::setup_field(THD
     It is too early to mark fields used here, because before execution
     of statement that will invoke trigger other statements may use same
     TABLE object, so all such mark-up will be wiped out.
-    So instead we do it in Table_triggers_list::mark_fields_used()
+    So instead we do it in Table_trigger_dispatcher::mark_fields_used()
     method which is called during execution of these statements.
   */
   enum_mark_columns save_mark_used_columns= thd->mark_used_columns;

=== modified file 'sql/item.h'
--- a/sql/item.h	2012-12-11 11:58:31 +0000
+++ b/sql/item.h	2013-01-23 15:11:25 +0000
@@ -24,7 +24,7 @@
 #include "thr_malloc.h"                         /* sql_calloc */
 #include "field.h"                              /* Derivation */
 #include "sql_array.h"
-#include "sql_trigger.h"
+#include "table_trigger_dispatcher.h"
 
 class Protocol;
 struct TABLE_LIST;
@@ -3990,8 +3990,6 @@ public:
 };
 
 
-class Table_triggers_list;
-
 /*
   Represents NEW/OLD version of field of row which is
   changed/read in trigger.
@@ -4014,8 +4012,8 @@ public:
   Item_trigger_field *next_trg_field;
   /* Index of the field in the TABLE::field array */
   uint field_idx;
-  /* Pointer to Table_trigger_list object for table of this trigger */
-  Table_triggers_list *triggers;
+  /* Pointer to Table_trigger_dispatcher object for table of this trigger */
+  Table_trigger_dispatcher *triggers;
 
   Item_trigger_field(Name_resolution_context *context_arg,
                      row_version_type row_ver_arg,

=== modified file 'sql/ndb_local_schema.cc'
--- a/sql/ndb_local_schema.cc	2012-11-23 10:59:29 +0000
+++ b/sql/ndb_local_schema.cc	2013-01-23 15:11:25 +0000
@@ -246,7 +246,7 @@ Ndb_local_schema::Table::remove_table(vo
     strmov(db_name_buf, m_db);
     strmov(table_name_buf, m_name);
 
-    if (Table_triggers_list::drop_all_triggers(m_thd,
+    if (Table_trigger_dispatcher::drop_all_triggers(m_thd,
                                                db_name_buf,
                                                table_name_buf))
     {
@@ -273,9 +273,9 @@ Ndb_local_schema::Table::rename_table(co
     }
     else
     {
-      if (Table_triggers_list::change_table_name(m_thd,
-                                                 m_db, m_name, m_name,
-                                                 new_db, new_name))
+      if (Table_trigger_dispatcher::change_table_name(m_thd,
+                                                      m_db, m_name, m_name,
+                                                      new_db, new_name))
       {
         log_warning("Failed to rename all triggers");
       }

=== modified file 'sql/sp_head.h'
--- a/sql/sp_head.h	2012-11-06 14:16:49 +0000
+++ b/sql/sp_head.h	2013-01-23 15:11:25 +0000
@@ -578,8 +578,8 @@ public:
   /// Trigger characteristics.
   st_trg_chistics m_trg_chistics;
 
-  /// The Table_triggers_list instance, where this trigger belongs to.
-  class Table_triggers_list *m_trg_list;
+  /// The Table_trigger_dispatcher instance, where this trigger belongs to.
+  class Table_trigger_dispatcher *m_trg_list;
 
 public:
   static void *operator new(size_t size) throw ();

=== modified file 'sql/sp_instr.cc'
--- a/sql/sp_instr.cc	2012-11-09 09:18:49 +0000
+++ b/sql/sp_instr.cc	2013-01-23 15:11:25 +0000
@@ -515,7 +515,7 @@ LEX *sp_lex_instr::parse_expr(THD *thd, 
         execution.
       */
 
-      Table_triggers_list *ttl= sp->m_trg_list;
+      Table_trigger_dispatcher *ttl= sp->m_trg_list;
       int event= sp->m_trg_chistics.event;
       int action_time= sp->m_trg_chistics.action_time;
       GRANT_INFO *grant_table= &ttl->subject_table_grants[event][action_time];

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2012-12-21 09:22:13 +0000
+++ b/sql/sql_base.cc	2013-01-23 15:11:25 +0000
@@ -3767,8 +3767,8 @@ err:
 
 static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry)
 {
-  if (Table_triggers_list::check_n_load(thd, share->db.str,
-                                        share->table_name.str, entry, 0))
+  if (Table_trigger_dispatcher::check_n_load(thd, share->db.str,
+                                             share->table_name.str, entry, 0))
     return TRUE;
 
   /*
@@ -8940,7 +8940,7 @@ inline bool command_invokes_insert_trigg
     @retval true    Error occurred
 */
 inline bool call_before_insert_triggers(THD *thd,
-                                        Table_triggers_list *triggers,
+                                        Table_trigger_dispatcher *triggers,
                                         enum trg_event_type event,
                                         MY_BITMAP *insert_into_fields_bitmap)
 {
@@ -8983,7 +8983,7 @@ inline bool call_before_insert_triggers(
 bool
 fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields,
                                      List<Item> &values, bool ignore_errors,
-                                     Table_triggers_list *triggers,
+                                     Table_trigger_dispatcher *triggers,
                                      enum trg_event_type event,
                                      int num_fields)
 {
@@ -9138,7 +9138,7 @@ err:
 bool
 fill_record_n_invoke_before_triggers(THD *thd, Field **ptr,
                                      List<Item> &values, bool ignore_errors,
-                                     Table_triggers_list *triggers,
+                                     Table_trigger_dispatcher *triggers,
                                      enum trg_event_type event,
                                      int num_fields)
 {

=== modified file 'sql/sql_base.h'
--- a/sql/sql_base.h	2012-12-11 18:12:13 +0000
+++ b/sql/sql_base.h	2013-01-23 15:11:25 +0000
@@ -175,13 +175,13 @@ void close_thread_tables(THD *thd);
 bool fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields,
                                           List<Item> &values,
                                           bool ignore_errors,
-                                          Table_triggers_list *triggers,
+                                          Table_trigger_dispatcher *triggers,
                                           enum trg_event_type event,
                                           int num_fields);
 bool fill_record_n_invoke_before_triggers(THD *thd, Field **field,
                                           List<Item> &values,
                                           bool ignore_errors,
-                                          Table_triggers_list *triggers,
+                                          Table_trigger_dispatcher *triggers,
                                           enum trg_event_type event,
                                           int num_fields);
 bool insert_fields(THD *thd, Name_resolution_context *context,

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2013-01-22 09:24:23 +0000
+++ b/sql/sql_class.h	2013-01-22 10:39:43 +0000
@@ -5138,6 +5138,21 @@ inline bool add_group_to_list(THD *thd, 
   return thd->lex->current_select->add_group_to_list(thd, item, asc);
 }
 
+/*************************************************************************/
+
+template <class T>
+inline T *alloc_type(MEM_ROOT *m)
+{
+  return (T *) alloc_root(m, sizeof (T));
+}
+
+inline LEX_STRING *alloc_lex_string(MEM_ROOT *m)
+{
+  return alloc_type<LEX_STRING>(m);
+}
+
+/*************************************************************************/
+
 #endif /* MYSQL_SERVER */
 
 #endif /* SQL_CLASS_INCLUDED */

=== modified file 'sql/sql_rename.cc'
--- a/sql/sql_rename.cc	2012-11-21 15:32:54 +0000
+++ b/sql/sql_rename.cc	2013-01-23 15:11:25 +0000
@@ -281,11 +281,13 @@ do_rename(THD *thd, TABLE_LIST *ren_tabl
         if (!(rc= mysql_rename_table(hton, ren_table->db, old_alias,
                                      new_db, new_alias, 0)))
         {
-          if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db,
-                                                          old_alias,
-                                                          ren_table->table_name,
-                                                          new_db,
-                                                          new_alias)))
+          if ((rc= Table_trigger_dispatcher::change_table_name(
+                                                         thd,
+                                                         ren_table->db,
+                                                         old_alias,
+                                                         ren_table->table_name,
+                                                         new_db,
+                                                         new_alias)))
           {
             /*
               We've succeeded in renaming table's .frm and in updating

=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc	2012-11-21 15:32:54 +0000
+++ b/sql/sql_show.cc	2013-01-23 15:11:25 +0000
@@ -94,6 +94,20 @@ enum enum_i_s_events_fields
   ISE_DB_CL
 };
 
+
+static const LEX_STRING trg_action_time_type_names[]=
+{
+  { C_STRING_WITH_LEN("BEFORE") },
+  { C_STRING_WITH_LEN("AFTER") }
+};
+
+static const LEX_STRING trg_event_type_names[]=
+{
+  { C_STRING_WITH_LEN("INSERT") },
+  { C_STRING_WITH_LEN("UPDATE") },
+  { C_STRING_WITH_LEN("DELETE") }
+};
+
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
 static const char *grant_names[]={
   "select","insert","update","delete","create","drop","reload","shutdown",
@@ -3716,8 +3730,8 @@ static int fill_schema_table_from_frm(TH
   if (schema_table->i_s_requested_object & OPEN_TRIGGER_ONLY)
   {
     init_sql_alloc(&tbl.mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
-    if (!Table_triggers_list::check_n_load(thd, db_name->str,
-                                           table_name->str, &tbl, 1))
+    if (!Table_trigger_dispatcher::check_n_load(thd, db_name->str,
+                                                table_name->str, &tbl, 1))
     {
       table_list.table= &tbl;
       res= schema_table->process_table(thd, &table_list, table,
@@ -5655,7 +5669,7 @@ static int get_schema_triggers_record(TH
   }
   if (!tables->view && tables->table->triggers)
   {
-    Table_triggers_list *triggers= tables->table->triggers;
+    Table_trigger_dispatcher *triggers= tables->table->triggers;
     int event, timing;
 
     if (check_table_access(thd, TRIGGER_ACL, tables, FALSE, 1, TRUE))
@@ -8140,7 +8154,7 @@ int finalize_schema_table(st_plugin_int 
 */
 
 static bool show_create_trigger_impl(THD *thd,
-                                     Table_triggers_list *triggers,
+                                     Table_trigger_dispatcher *triggers,
                                      int trigger_idx)
 {
   int ret_code;
@@ -8333,7 +8347,7 @@ bool show_create_trigger(THD *thd, const
 {
   TABLE_LIST *lst= get_trigger_table(thd, trg_name);
   uint num_tables; /* NOTE: unused, only to pass to open_tables(). */
-  Table_triggers_list *triggers;
+  Table_trigger_dispatcher *triggers;
   int trigger_idx;
   bool error= TRUE;
 
@@ -8353,7 +8367,7 @@ bool show_create_trigger(THD *thd, const
   MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
 
   /*
-    Open the table by name in order to load Table_triggers_list object.
+    Open the table by name in order to load Table_trigger_dispatcher object.
   */
   if (open_tables(thd, &lst, &num_tables,
                   MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL))

=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc	2012-12-27 02:11:06 +0000
+++ b/sql/sql_table.cc	2013-01-23 15:11:25 +0000
@@ -2467,8 +2467,9 @@ int mysql_rm_table_no_locks(THD *thd, TA
         if (!(new_error= mysql_file_delete(key_file_frm, path, MYF(MY_WME))))
         {
           non_tmp_table_deleted= TRUE;
-          new_error= Table_triggers_list::drop_all_triggers(thd, db,
-                                                            table->table_name);
+          new_error=
+              Table_trigger_dispatcher::drop_all_triggers(thd, db,
+                                                          table->table_name);
         }
         error|= new_error;
       }
@@ -6524,12 +6525,12 @@ static bool mysql_inplace_alter_table(TH
       */
       DBUG_RETURN(true);
     }
-    if (Table_triggers_list::change_table_name(thd,
-                                               alter_ctx->db,
-                                               alter_ctx->alias,
-                                               alter_ctx->table_name,
-                                               alter_ctx->new_db,
-                                               alter_ctx->new_alias))
+    if (Table_trigger_dispatcher::change_table_name(thd,
+                                                    alter_ctx->db,
+                                                    alter_ctx->alias,
+                                                    alter_ctx->table_name,
+                                                    alter_ctx->new_db,
+                                                    alter_ctx->new_alias))
     {
       /*
         If the rename of trigger files fails, try to rename the table
@@ -7468,12 +7469,12 @@ simple_rename_or_index_change(THD *thd, 
     if (mysql_rename_table(old_db_type, alter_ctx->db, alter_ctx->table_name,
                            alter_ctx->new_db, alter_ctx->new_alias, 0))
       error= -1;
-    else if (Table_triggers_list::change_table_name(thd,
-                                                    alter_ctx->db,
-                                                    alter_ctx->alias,
-                                                    alter_ctx->table_name,
-                                                    alter_ctx->new_db,
-                                                    alter_ctx->new_alias))
+    else if (Table_trigger_dispatcher::change_table_name(thd,
+                                                         alter_ctx->db,
+                                                         alter_ctx->alias,
+                                                         alter_ctx->table_name,
+                                                         alter_ctx->new_db,
+                                                         alter_ctx->new_alias))
     {
       (void) mysql_rename_table(old_db_type,
                                 alter_ctx->new_db, alter_ctx->new_alias,
@@ -8358,12 +8359,12 @@ bool mysql_alter_table(THD *thd,char *ne
 
   // Check if we renamed the table and if so update trigger files.
   if (alter_ctx.is_table_renamed() &&
-      Table_triggers_list::change_table_name(thd,
-                                             alter_ctx.db,
-                                             alter_ctx.alias,
-                                             alter_ctx.table_name,
-                                             alter_ctx.new_db,
-                                             alter_ctx.new_alias))
+      Table_trigger_dispatcher::change_table_name(thd,
+                                                  alter_ctx.db,
+                                                  alter_ctx.alias,
+                                                  alter_ctx.table_name,
+                                                  alter_ctx.new_db,
+                                                  alter_ctx.new_alias))
   {
     // Rename succeeded, delete the new table.
     (void) quick_rm_table(thd, new_db_type,

=== modified file 'sql/sql_trigger.cc'
--- a/sql/sql_trigger.cc	2013-01-22 09:24:23 +0000
+++ b/sql/sql_trigger.cc	2013-01-23 15:11:25 +0000
@@ -15,279 +15,23 @@
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
 
 
-#define MYSQL_LEX 1
-#include "my_global.h"                          /* NO_EMBEDDED_ACCESS_CHECKS */
-#include "sql_priv.h"
-#include "unireg.h"
-#include "sp_head.h"
+#include "my_global.h"      // NO_EMBEDDED_ACCESS_CHECKS
 #include "sql_trigger.h"
-#include "sql_parse.h"                          // parse_sql
-#include "parse_file.h"
-#include "sp.h"
-#include "sql_base.h"                          // find_temporary_table
-#include "sql_show.h"                // append_definer, append_identifier
-#include "sql_table.h"                        // build_table_filename,
-                                              // check_n_cut_mysql50_prefix
-#include "sql_db.h"                        // get_default_db_collation
-#include "sql_acl.h"                       // *_ACL, is_acl_user
-#include "sql_handler.h"                        // mysql_ha_rm_tables
-#include "sp_cache.h"                     // sp_invalidate_cache
-#include <mysys_err.h>
+#include "sp.h"             // sp_add_to_query_tables()
+#include "sql_base.h"       // find_temporary_table()
+#include "sql_table.h"      // build_table_filename()
+                            // write_bin_log()
+#include "sql_handler.h"    // mysql_ha_rm_tables()
+#include "sp_cache.h"       // sp_invalidate_cache()
+#include "sql_parse.h"      // check_table_access()
+#include "trigger_loader.h" // Trigger_loader
 
-/*************************************************************************/
-
-template <class T>
-inline T *alloc_type(MEM_ROOT *m)
-{
-  return (T *) alloc_root(m, sizeof (T));
-}
-
-/*
-  NOTE: Since alloc_type() is declared as inline, alloc_root() calls should
-  be inlined by the compiler. So, implementation of alloc_root() is not
-  needed. However, let's put the implementation in object file just in case
-  of stupid MS or other old compilers.
-*/
-
-template LEX_STRING *alloc_type<LEX_STRING>(MEM_ROOT *m);
-template ulonglong *alloc_type<ulonglong>(MEM_ROOT *m);
-
-inline LEX_STRING *alloc_lex_string(MEM_ROOT *m)
-{
-  return alloc_type<LEX_STRING>(m);
-}
-
-/*************************************************************************/
-/**
-  Trigger_creation_ctx -- creation context of triggers.
-*/
-
-class Trigger_creation_ctx : public Stored_program_creation_ctx,
-                             public Sql_alloc
-{
-public:
-  static Trigger_creation_ctx *create(THD *thd,
-                                      const char *db_name,
-                                      const char *table_name,
-                                      const LEX_STRING *client_cs_name,
-                                      const LEX_STRING *connection_cl_name,
-                                      const LEX_STRING *db_cl_name);
-
-public:
-  virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root)
-  {
-    return new (mem_root) Trigger_creation_ctx(m_client_cs,
-                                               m_connection_cl,
-                                               m_db_cl);
-  }
-
-protected:
-  virtual Object_creation_ctx *create_backup_ctx(THD *thd) const
-  {
-    return new Trigger_creation_ctx(thd);
-  }
-
-private:
-  Trigger_creation_ctx(THD *thd)
-    :Stored_program_creation_ctx(thd)
-  { }
-
-  Trigger_creation_ctx(const CHARSET_INFO *client_cs,
-                       const CHARSET_INFO *connection_cl,
-                       const CHARSET_INFO *db_cl)
-    :Stored_program_creation_ctx(client_cs, connection_cl, db_cl)
-  { }
-};
-
-/**************************************************************************
-  Trigger_creation_ctx implementation.
-**************************************************************************/
-
-Trigger_creation_ctx *
-Trigger_creation_ctx::create(THD *thd,
-                             const char *db_name,
-                             const char *table_name,
-                             const LEX_STRING *client_cs_name,
-                             const LEX_STRING *connection_cl_name,
-                             const LEX_STRING *db_cl_name)
-{
-  const CHARSET_INFO *client_cs;
-  const CHARSET_INFO *connection_cl;
-  const CHARSET_INFO *db_cl;
-
-  bool invalid_creation_ctx= FALSE;
-
-  if (resolve_charset(client_cs_name->str,
-                      thd->variables.character_set_client,
-                      &client_cs))
-  {
-    sql_print_warning("Trigger for table '%s'.'%s': "
-                      "invalid character_set_client value (%s).",
-                      (const char *) db_name,
-                      (const char *) table_name,
-                      (const char *) client_cs_name->str);
-
-    invalid_creation_ctx= TRUE;
-  }
-
-  if (resolve_collation(connection_cl_name->str,
-                        thd->variables.collation_connection,
-                        &connection_cl))
-  {
-    sql_print_warning("Trigger for table '%s'.'%s': "
-                      "invalid collation_connection value (%s).",
-                      (const char *) db_name,
-                      (const char *) table_name,
-                      (const char *) connection_cl_name->str);
-
-    invalid_creation_ctx= TRUE;
-  }
-
-  if (resolve_collation(db_cl_name->str, NULL, &db_cl))
-  {
-    sql_print_warning("Trigger for table '%s'.'%s': "
-                      "invalid database_collation value (%s).",
-                      (const char *) db_name,
-                      (const char *) table_name,
-                      (const char *) db_cl_name->str);
-
-    invalid_creation_ctx= TRUE;
-  }
-
-  if (invalid_creation_ctx)
-  {
-    push_warning_printf(thd,
-                        Sql_condition::SL_WARNING,
-                        ER_TRG_INVALID_CREATION_CTX,
-                        ER(ER_TRG_INVALID_CREATION_CTX),
-                        (const char *) db_name,
-                        (const char *) table_name);
-  }
-
-  /*
-    If we failed to resolve the database collation, load the default one
-    from the disk.
-  */
-
-  if (!db_cl)
-    db_cl= get_default_db_collation(thd, db_name);
-
-  return new Trigger_creation_ctx(client_cs, connection_cl, db_cl);
-}
-
-/*************************************************************************/
-
-const char * const TRG_EXT= ".TRG";
-
-File_option sql_modes_parameters=
-{
-  { C_STRING_WITH_LEN("sql_modes") },
-  my_offsetof(class Triggers_definition_loader, definition_modes_list),
-  FILE_OPTIONS_ULLLIST
-};
-
-
-/*
-  Structure representing contents of .TRN file which are used to support
-  database wide trigger namespace.
-*/
-
-struct st_trigname
-{
-  LEX_STRING trigger_table;
-};
+///////////////////////////////////////////////////////////////////////////
 
 const char * const TRN_EXT= ".TRN";
+const char * const TRG_EXT= ".TRG";
 
-const LEX_STRING trg_action_time_type_names[]=
-{
-  { C_STRING_WITH_LEN("BEFORE") },
-  { C_STRING_WITH_LEN("AFTER") }
-};
-
-const LEX_STRING trg_event_type_names[]=
-{
-  { C_STRING_WITH_LEN("INSERT") },
-  { C_STRING_WITH_LEN("UPDATE") },
-  { C_STRING_WITH_LEN("DELETE") }
-};
-
-
-class Handle_old_incorrect_sql_modes_hook: public Unknown_key_hook
-{
-private:
-  char *path;
-public:
-  Handle_old_incorrect_sql_modes_hook(char *file_path)
-    :path(file_path)
-  {};
-  virtual bool process_unknown_string(const char *&unknown_key, uchar* base,
-                                      MEM_ROOT *mem_root, const char *end);
-};
-
-class Handle_old_incorrect_trigger_table_hook: public Unknown_key_hook
-{
-public:
-  Handle_old_incorrect_trigger_table_hook(char *file_path,
-                                          LEX_STRING *trigger_table_arg)
-    :path(file_path), trigger_table_value(trigger_table_arg)
-  {};
-  virtual bool process_unknown_string(const char *&unknown_key, uchar* base,
-                                      MEM_ROOT *mem_root, const char *end);
-private:
-  char *path;
-  LEX_STRING *trigger_table_value;
-};
-
-
-/**
-  An error handler that catches all non-OOM errors which can occur during
-  parsing of trigger body. Such errors are ignored and corresponding error
-  message is used to construct a more verbose error message which contains
-  name of problematic trigger. This error message is later emitted when
-  one tries to perform DML or some of DDL on this table.
-  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
-{
-private:
-
-  char m_message[MYSQL_ERRMSG_SIZE];
-  LEX_STRING *m_trigger_name;
-
-public:
-
-  Deprecated_trigger_syntax_handler() : m_trigger_name(NULL) {}
-
-  virtual bool handle_condition(THD *thd,
-                                uint sql_errno,
-                                const char* sqlstate,
-                                Sql_condition::enum_severity_level level,
-                                const char* message,
-                                Sql_condition ** cond_hdl)
-  {
-    if (sql_errno != EE_OUTOFMEMORY &&
-        sql_errno != ER_OUT_OF_RESOURCES)
-    {
-      if(thd->lex->spname)
-        m_trigger_name= &thd->lex->spname->m_name;
-      if (m_trigger_name)
-        my_snprintf(m_message, sizeof(m_message),
-                    ER(ER_ERROR_IN_TRIGGER_BODY),
-                    m_trigger_name->str, message);
-      else
-        my_snprintf(m_message, sizeof(m_message),
-                    ER(ER_ERROR_IN_UNKNOWN_TRIGGER_BODY), message);
-      return true;
-    }
-    return false;
-  }
-
-  LEX_STRING *get_trigger_name() { return m_trigger_name; }
-  char *get_error_message() { return m_message; }
-};
-
+///////////////////////////////////////////////////////////////////////////
 
 /**
   Create or drop trigger for table.
@@ -299,8 +43,8 @@ public:
   @note
     This function is mainly responsible for opening and locking of table and
     invalidation of all its instances in table cache after trigger creation.
-    Real work on trigger creation/dropping is done inside Table_triggers_list
-    methods.
+    Real work on trigger creation/dropping is done inside
+    Table_trigger_dispatcher methods.
 
   @todo
     TODO: We should check if user has TRIGGER privilege for table here.
@@ -484,7 +228,8 @@ bool mysql_create_or_drop_trigger(THD *t
       goto end;
     }
 
-    if (!(table->triggers= new (&table->mem_root) Table_triggers_list(table)))
+    if (!(table->triggers=
+        new (&table->mem_root) Table_trigger_dispatcher(table)))
       goto end;
   }
 
@@ -536,2108 +281,68 @@ 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.
-
-  @param thd           current thread context (including trigger definition in
-                       LEX)
-  @param tables        table list containing one open table for which the
-                       trigger is created.
-  @param[out] stmt_query    after successful return, this string contains
-                            well-formed statement for creation this trigger.
+  Find trigger's table from trigger identifier and add it to
+  the statement table list.
 
-  @note
-    - Assumes that trigger name is fully qualified.
-    - NULL-string means the following LEX_STRING instance:
-    { str = 0; length = 0 }.
-    - In other words, definer_user and definer_host should contain
-    simultaneously NULL-strings (non-SUID/old trigger) or valid strings
-    (SUID/new trigger).
+  @param[in] thd       Thread context.
+  @param[in] trg_name  Trigger name.
+  @param[in] if_exists TRUE if SQL statement contains "IF EXISTS" clause.
+                       That means a warning instead of error should be
+                       thrown if trigger with given name does not exist.
+  @param[out] table    Pointer to TABLE_LIST object for the
+                       table trigger.
 
-  @retval
-    False   success
-  @retval
-    True    error
+  @return Operation status
+    @retval FALSE On success.
+    @retval TRUE  Otherwise.
 */
-bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
-                                         String *stmt_query)
+
+bool add_table_for_trigger(THD *thd,
+                           const sp_name *trg_name,
+                           bool if_exists,
+                           TABLE_LIST **table)
 {
   LEX *lex= thd->lex;
-  TABLE *table= tables->table;
-  LEX_STRING *trg_def;
-  LEX_STRING definer_user;
-  LEX_STRING definer_host;
-  sql_mode_t trg_sql_mode;
-  LEX_STRING *trg_definer;
-  LEX_STRING *trg_client_cs_name;
-  LEX_STRING *trg_connection_cl_name;
-  LEX_STRING *trg_db_cl_name;
-
-  if (check_for_broken_triggers())
-    return true;
-
-  /* Trigger must be in the same schema as target table. */
-  if (my_strcasecmp(table_alias_charset, table->s->db.str,
-                    lex->spname->m_db.str))
-  {
-    my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0));
-    return true;
-  }
+  char trn_path_buff[FN_REFLEN];
+  LEX_STRING trn_path= { trn_path_buff, 0 };
+  LEX_STRING tbl_name= { NULL, 0 };
 
-  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;
-  int trg_action_time= trg->m_trg_chistics.action_time;
+  DBUG_ENTER("add_table_for_trigger");
 
-  /* We don't allow creation of several triggers of the same type yet */
-  if (bodies[trg_event][trg_action_time] != NULL)
-  {
-    my_error(ER_NOT_SUPPORTED_YET, MYF(0),
-             "multiple triggers with the same action time"
-             " and event for one table");
-    return true;
-  }
+  build_trn_path(thd, trg_name, &trn_path);
 
-  if (!lex->definer)
+  if (check_trn_exists(&trn_path))
   {
-    /*
-      DEFINER-clause is missing.
-
-      If we are in slave thread, this means that we received CREATE TRIGGER
-      from the master, that does not support definer in triggers. So, we
-      should mark this trigger as non-SUID. Note that this does not happen
-      when we parse triggers' definitions during opening .TRG file.
-      LEX::definer is ignored in that case.
-
-      Otherwise, we should use CURRENT_USER() as definer.
-
-      NOTE: when CREATE TRIGGER statement is allowed to be executed in PS/SP,
-      it will be required to create the definer below in persistent MEM_ROOT
-      of PS/SP.
-    */
-
-    if (!thd->slave_thread)
+    if (if_exists)
     {
-      if (!(lex->definer= create_default_definer(thd)))
-        return true;
-    }
-  }
+      push_warning_printf(thd,
+                          Sql_condition::SL_NOTE,
+                          ER_TRG_DOES_NOT_EXIST,
+                          ER(ER_TRG_DOES_NOT_EXIST));
 
-  /*
-    If the specified definer differs from the current user, we should check
-    that the current user has SUPER privilege (in order to create trigger
-    under another user one must have SUPER privilege).
-  */
+      *table= NULL;
 
-  if (lex->definer &&
-      (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
-       my_strcasecmp(system_charset_info,
-                     lex->definer->host.str,
-                     thd->security_ctx->priv_host)))
-  {
-    if (check_global_access(thd, SUPER_ACL))
-    {
-      my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
-      return true;
+      DBUG_RETURN(FALSE);
     }
-  }
-
-  /*
-    Let us check if all references to fields in old/new versions of row in
-    this trigger are ok.
-
-    NOTE: We do it here more from ease of use standpoint. We still have to
-    do some checks on each execution. E.g. we can catch privilege changes
-    only during execution. Also in near future, when we will allow access
-    to other tables from trigger we won't be able to catch changes in other
-    tables...
-
-    Since we don't plan to access to contents of the fields it does not
-    matter that we choose for both OLD and NEW values the same versions
-    of Field objects here.
-  */
-  old_field= new_field= table->field;
-
-  for (Item_trigger_field *trg_field= lex->sphead->m_trg_table_fields.first;
-       trg_field; trg_field= trg_field->next_trg_field)
-  {
-    /*
-      NOTE: now we do not check privileges at CREATE TRIGGER time. This will
-      be changed in the future.
-    */
-    trg_field->setup_field(thd, table, NULL);
-
-    if (!trg_field->fixed &&
-        trg_field->fix_fields(thd, (Item **)0))
-      return true;
-  }
-
-
-  /*
-    Soon we will invalidate table object and thus Table_triggers_list object
-    so don't care about place to which trg_def->ptr points and other
-    invariants (e.g. we don't bother to update names_list)
-
-    QQ: Hmm... probably we should not care about setting up active thread
-        mem_root too.
-  */
-  if (!(trg_def= alloc_lex_string(&table->mem_root)) ||
-      !(trg_definer= alloc_lex_string(&table->mem_root)) ||
-      !(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)))
-  {
-    return true;
-  }
-
-  trg_sql_mode= thd->variables.sql_mode;
-
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-  if (lex->definer && !is_acl_user(lex->definer->host.str,
-                                   lex->definer->user.str))
-  {
-    push_warning_printf(thd,
-                        Sql_condition::SL_NOTE,
-                        ER_NO_SUCH_USER,
-                        ER(ER_NO_SUCH_USER),
-                        lex->definer->user.str,
-                        lex->definer->host.str);
-  }
-#endif /* NO_EMBEDDED_ACCESS_CHECKS */
-
-  if (lex->definer)
-  {
-    char trg_definer_buf[USER_HOST_BUFF_SIZE];
-    /* SUID trigger. */
-
-    definer_user= lex->definer->user;
-    definer_host= lex->definer->host;
-
-    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
-  {
-    /* non-SUID trigger. */
-
-    definer_user.str= 0;
-    definer_user.length= 0;
-
-    definer_host.str= 0;
-    definer_host.length= 0;
-
-    trg_definer->str= (char*) "";
-    trg_definer->length= 0;
-  }
-
-  /*
-    Fill character set information:
-      - client character set contains charset info only;
-      - connection collation contains pair {character set, collation};
-      - database collation contains pair {character set, collation};
-  */
-
-  lex_string_set(trg_client_cs_name, thd->charset()->csname);
-
-  lex_string_set(trg_connection_cl_name,
-                 thd->variables.collation_connection->name);
-
-  lex_string_set(trg_db_cl_name,
-                 get_default_db_collation(thd, tables->db)->name);
-
-  /*
-    Create well-formed trigger definition query. Original query is not
-    appropriated, because definer-clause can be not truncated.
-  */
-
-  stmt_query->append(STRING_WITH_LEN("CREATE "));
-
-  /*
-    Append definer-clause if the trigger is SUID (a usual trigger in
-    new MySQL versions).
-  */
-
-  append_definer(thd, stmt_query, &definer_user, &definer_host);
-
-  LEX_STRING stmt_definition;
-  stmt_definition.str= (char*) thd->lex->stmt_definition_begin;
-  stmt_definition.length= thd->lex->stmt_definition_end
-    - thd->lex->stmt_definition_begin;
-  trim_whitespace(thd->charset(), & stmt_definition);
-
-  stmt_query->append(stmt_definition.str, stmt_definition.length);
-
-  lex_string_copy(&table->mem_root, trg_def, stmt_query->c_ptr(),
-                  stmt_query->length());
-
-  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);
-
-  return trigger_loader.store_trigger(tables, new_trigger,
-                                      &table_triggers);
-}
-
-
-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)
-{
-  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;
-
-  lex->sphead= NULL; /* Prevent double cleanup. */
-
-  sp->set_creation_ctx(trg_creation_ctx);
-
-  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.
-    */
-
-    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.
-    */
-
-    sp->set_definer((char*) "", 0);
-
-    /*
-      Triggers without definer information are executed under the
-      authorization of the invoker.
-    */
-
-    sp->m_chistics->suid= SP_IS_NOT_SUID;
-  }
-  else
-    sp->set_definer(definer->str, definer->length);
-
-  if (!(on_table_name= alloc_lex_string(&table->mem_root)))
-    return true;
 
-  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];
-
-  if (!names_only)
-  {
-    /*
-      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, subject_table_grant);
-    }
+    my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
+    DBUG_RETURN(TRUE);
   }
 
-  return false;
-}
-
-bool Trigger::execute(THD *thd)
-{
-  if (has_parse_error)
-    return true;
-
-  bool err_status;
-  Sub_statement_state statement_state;
-  SELECT_LEX *save_current_select;
-
-  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;
-
-  thd->restore_sub_statement_state(&statement_state);
-
-  return err_status;
-}
-
+  if (load_table_name_for_trigger(thd, trg_name, &trn_path, &tbl_name))
+    DBUG_RETURN(TRUE);
 
-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 (trg_definer)
-    *trg_definer= *definer;
-
-  if (trg_definition)
-    *trg_definition= *definition;
-
-  *client_cs_nam= *client_cs_name;
-  *connection_cl_nam= *connection_cl_name;
-  *db_cl_nam= *db_cl_name;
-}
-
-/**
-  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).
-*/
-File_option Triggers_definition_loader::triggers_file_parameters[]=
-{
-  {
-    { 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 }
-};
-
-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 }
-};
-
-const LEX_STRING Triggers_definition_loader::triggers_file_type=
-  { C_STRING_WITH_LEN("TRIGGERS") };
-
-const LEX_STRING Triggers_definition_loader::trigname_file_type=
-  { C_STRING_WITH_LEN("TRIGGERNAME") };
-
-
-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;
-
-  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;
-
-  *triggers_not_found= false;
-
-  if (access(path_buff, F_OK))
-  {
-    if (errno == ENOENT)
-      *triggers_not_found= true;
-    DBUG_RETURN(true);
-  }
-
-  /*
-    File exists so we got to load triggers.
-    FIXME: A lot of things to do here e.g. how about other funcs and being
-    more paranoical ?
-  */
-
-  if ((parser= sql_parse_prepare(&path, &table->mem_root, 1)))
-  {
-    if (is_equal(&triggers_file_type, parser->type()))
-    {
-      Handle_old_incorrect_sql_modes_hook sql_modes_hook(path.str);
-
-      /*
-        We don't have the following attributes in old versions of .TRG file, so
-        we should initialize the list for safety:
-          - sql_modes;
-          - definers;
-          - character sets (client, connection, database);
-      */
-      definition_modes_list.empty();
-      definers_list.empty();
-      client_cs_names.empty();
-      connection_cl_names.empty();
-      db_cl_names.empty();
-
-      if (parser->parse((uchar*)this, &table->mem_root,
-                        triggers_file_parameters,
-                        TRG_NUM_REQUIRED_PARAMETERS,
-                        &sql_modes_hook))
-        DBUG_RETURN(true);
-
-      List_iterator_fast<LEX_STRING> it(definitions_list);
-
-      if (!definitions_list.is_empty())
-      {
-        if (definition_modes_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.
-          */
-          sql_mode_t *trg_sql_mode;
-          if (!(trg_sql_mode= alloc_type<sql_mode_t>(&table->mem_root)))
-          {
-            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();
-        }
-
-        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".
-          */
-
-          LEX_STRING *trg_definer;
-
-          if (!(trg_definer= alloc_lex_string(&table->mem_root)))
-            DBUG_RETURN(true); // EOM
-
-          trg_definer->str= (char*) "";
-          trg_definer->length= 0;
-
-          while (it++)
-          {
-            if (definers_list.push_back(trg_definer,
-                                        &table->mem_root))
-            {
-              DBUG_RETURN(true); // EOM
-            }
-          }
-          it.rewind();
-        }
-
-        if (client_cs_names.is_empty() ||
-            connection_cl_names.is_empty() ||
-            db_cl_names.is_empty())
-        {
-          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);
-
-            DBUG_RETURN(true); // EOM
-          }
-
-          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(true); // EOM
-          }
-
-          /*
-            Backward compatibility: assume that the query is in the current
-            character set.
-          */
-
-          lex_string_set(trg_client_cs_name,
-                         thd->variables.character_set_client->csname);
-
-          lex_string_set(trg_connection_cl_name,
-                         thd->variables.collation_connection->name);
-
-          lex_string_set(trg_db_cl_name,
-                         thd->variables.collation_database->name);
-
-          while (it++)
-          {
-            if (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));
-}
-
-
-/**
-  Drop trigger for table.
-
-  @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
-    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).
-
-  @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());
-
-  return trigger_loader.drop_trigger(tables, sp_name, &table_triggers);
-}
-
-
-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 (record1_field)
-    for (Field **fld_ptr= record1_field; *fld_ptr; fld_ptr++)
-      delete *fld_ptr;
-}
-
-
-/**
-  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;
-
-  if (!(record1_field= (Field **)alloc_root(&trigger_table->mem_root,
-                                            (trigger_table->s->fields + 1) *
-                                            sizeof(Field*))))
-    return true;
-
-  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;
-
-  return false;
-}
-
-
-/**
-  Adjust Table_triggers_list with new TABLE pointer.
-
-  @param new_table   new pointer to TABLE instance
-*/
-
-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;
-  }
-}
-
-
-/**
-  Check whenever .TRG file for table exist and load all triggers it contains.
-
-  @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
-
-  @todo
-    A lot of things to do here e.g. how about other funcs and being
-    more paranoical ?
-
-  @todo
-    This could be avoided if there is no triggers for UPDATE and DELETE.
-
-  @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)
-{
-  DBUG_ENTER("Table_triggers_list::check_n_load");
-
-  //if (Table_triggers_list::)
-  Table_triggers_list *triggers=
-    new (&table->mem_root) Table_triggers_list(table);
-
-  if (!triggers)
-    DBUG_RETURN(true);
-
-  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);
-  }
-
-  table->triggers= triggers;
-
-  return triggers->parse_trigger_definitions(thd, table, db,
-                                             table_name, names_only);
-}
-
-
-/**
-  Obtains and returns trigger metadata.
-
-  @param thd           current thread context
-  @param event         trigger event type
-  @param time_type     trigger action time
-  @param trigger_name  returns name of trigger
-  @param trigger_stmt  returns statement of trigger
-  @param sql_mode      returns sql_mode of trigger
-  @param definer       returns definer/creator of trigger. The caller is
-                       responsible to allocate enough space for storing
-                       definer information.
-
-  @retval
-    False   success
-  @retval
-    True    error
-*/
-
-bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event,
-                                           trg_action_time_type time_type,
-                                           LEX_STRING *trigger_name,
-                                           LEX_STRING *trigger_stmt,
-                                           sql_mode_t *sql_mode,
-                                           LEX_STRING *definer,
-                                           LEX_STRING *client_cs_name,
-                                           LEX_STRING *connection_cl_name,
-                                           LEX_STRING *db_cl_name)
-{
-  Trigger *trigger;
-  DBUG_ENTER("get_trigger_info");
-
-  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(true);
-}
-
-
-void Table_triggers_list::get_trigger_info(THD *thd,
-                                           int trigger_idx,
-                                           LEX_STRING *trigger_name,
-                                           sql_mode_t *sql_mode,
-                                           LEX_STRING *sql_original_stmt,
-                                           LEX_STRING *client_cs_name,
-                                           LEX_STRING *connection_cl_name,
-                                           LEX_STRING *db_cl_name)
-{
-  List_iterator_fast<Trigger> it_table_triggers(table_triggers);
-  for (int i = 0; i < trigger_idx; ++i)
-  {
-    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<Trigger> it_table_triggers(table_triggers);
-  for (int i = 0; ; ++i)
-  {
-    Trigger *trigger= it_table_triggers++;
-
-    if (!trigger)
-      return -1;
-
-    if (strcmp(trigger->trigger_name->str, trg_name->str) == 0)
-      return i;
-  }
-}
-
-/**
-  Find trigger's table from trigger identifier and add it to
-  the statement table list.
-
-  @param[in] thd       Thread context.
-  @param[in] trg_name  Trigger name.
-  @param[in] if_exists TRUE if SQL statement contains "IF EXISTS" clause.
-                       That means a warning instead of error should be
-                       thrown if trigger with given name does not exist.
-  @param[out] table    Pointer to TABLE_LIST object for the
-                       table trigger.
-
-  @return Operation status
-    @retval FALSE On success.
-    @retval TRUE  Otherwise.
-*/
-
-bool add_table_for_trigger(THD *thd,
-                           const sp_name *trg_name,
-                           bool if_exists,
-                           TABLE_LIST **table)
-{
-  LEX *lex= thd->lex;
-  char trn_path_buff[FN_REFLEN];
-  LEX_STRING trn_path= { trn_path_buff, 0 };
-  LEX_STRING tbl_name= { NULL, 0 };
-
-  DBUG_ENTER("add_table_for_trigger");
-
-  build_trn_path(thd, trg_name, &trn_path);
-
-  if (check_trn_exists(&trn_path))
-  {
-    if (if_exists)
-    {
-      push_warning_printf(thd,
-                          Sql_condition::SL_NOTE,
-                          ER_TRG_DOES_NOT_EXIST,
-                          ER(ER_TRG_DOES_NOT_EXIST));
-
-      *table= NULL;
-
-      DBUG_RETURN(FALSE);
-    }
-
-    my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
-    DBUG_RETURN(TRUE);
-  }
-
-  if (load_table_name_for_trigger(thd, trg_name, &trn_path, &tbl_name))
-    DBUG_RETURN(TRUE);
-
-  *table= sp_add_to_query_tables(thd, lex, trg_name->m_db.str,
-                                 tbl_name.str, TL_IGNORE,
-                                 MDL_SHARED_NO_WRITE);
+  *table= sp_add_to_query_tables(thd, lex, trg_name->m_db.str,
+                                 tbl_name.str, TL_IGNORE,
+                                 MDL_SHARED_NO_WRITE);
 
   DBUG_RETURN(*table ? FALSE : TRUE);
 }
 
 
 /**
-  Drop all triggers for table.
-
-  @param thd      current thread context
-  @param db       schema for table
-  @param name     name for table
-
-  @retval
-    False   success
-  @retval
-    True    error
-*/
-
-bool Table_triggers_list::drop_all_triggers(THD *thd, char *db, char *name)
-{
-  TABLE table;
-  bool result= 0;
-  DBUG_ENTER("drop_all_triggers");
-
-  memset(&table, 0, sizeof(table));
-  init_sql_alloc(&table.mem_root, 8192, 0);
-
-  if (Table_triggers_list::check_n_load(thd, db, name, &table, 1))
-  {
-    result= 1;
-    goto end;
-  }
-  if (table.triggers)
-  {
-    Triggers_definition_loader triggers_definition_loader;
-    result= triggers_definition_loader.drop_all_triggers(
-        db, name,
-        table.triggers->table_triggers);
-
-  }
-end:
-  if (table.triggers)
-    delete table.triggers;
-  table.triggers= NULL;
-  free_root(&table.mem_root, MYF(0));
-  DBUG_RETURN(result);
-}
-
-
-/**
-  Update .TRG file after renaming triggers' subject table
-  (change name of table in triggers' definitions).
-
-  @param thd                 Thread context
-  @param old_db_name         Old database of subject table
-  @param new_db_name         New database of subject table
-  @param old_table_name      Old subject table's name
-  @param new_table_name      New subject table's name
-
-  @retval
-    FALSE  Success
-  @retval
-    TRUE   Failure
-*/
-
-bool
-Table_triggers_list::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,
-                                                   bool upgrading50to51)
-{
-  LEX_STRING *def, *on_table_name, new_def;
-  sql_mode_t save_sql_mode= thd->variables.sql_mode;
-  List_iterator_fast<Trigger> it_table_triggers(table_triggers);
-  size_t on_q_table_name_len, before_on_len;
-  String buff;
-  Trigger *trigger;
-
-  while ((trigger= it_table_triggers++))
-  {
-    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);
-
-    /* WARNING: 'on_table_name' is supposed to point inside 'def' */
-    DBUG_ASSERT(on_table_name->str > def->str);
-    DBUG_ASSERT(on_table_name->str < (def->str + def->length));
-    before_on_len= on_table_name->str - def->str;
-
-    buff.append(def->str, before_on_len);
-    buff.append(STRING_WITH_LEN("ON "));
-    append_identifier(thd, &buff, new_table_name->str, new_table_name->length);
-    buff.append(STRING_WITH_LEN(" "));
-    on_q_table_name_len= buff.length() - before_on_len;
-    buff.append(on_table_name->str + on_table_name->length,
-                def->length - (before_on_len + on_table_name->length));
-    /*
-      It is OK to allocate some memory on table's MEM_ROOT since this
-      table instance will be thrown out at the end of rename anyway.
-    */
-    new_def.str= (char*) memdup_root(&trigger_table->mem_root, buff.ptr(),
-                                     buff.length());
-    new_def.length= buff.length();
-    on_table_name->str= new_def.str + before_on_len;
-    on_table_name->length= on_q_table_name_len;
-    *def= new_def;
-  }
-
-  thd->variables.sql_mode= save_sql_mode;
-
-  if (thd->is_fatal_error)
-    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(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 false;
-}
-
-/**
-  Iterate though Table_triggers_list::names_list list and update
-  .TRN files after renaming triggers' subject table.
-
-  @param old_db_name         Old database of subject table
-  @param new_db_name         New database of subject table
-  @param new_table_name      New subject table's name
-  @param stopper             Pointer to Table_triggers_list::names_list at
-                             which we should stop updating.
-
-  @retval
-    0      Success
-  @retval
-    non-0  Failure, pointer to Table_triggers_list::names_list element
-    for which update failed.
-*/
-
-LEX_STRING*
-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;
-  Trigger *trigger;
-  List_iterator_fast<Trigger> it_table_triggers(table_triggers);
-
-  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->trigger_name->str,
-                                               TRN_EXT, 0);
-    trigname_file.str= trigname_buff;
-
-    trigname.trigger_table= *new_table_name;
-
-    if (sql_create_definition_file(NULL, &trigname_file, &trigname_file_type,
-                                   (uchar*)&trigname, trigname_file_parameters))
-      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->trigger_name->str))
-      {
-        (void) rm_trigname_file(trigname_buff, new_db_name,
-                                trigger->trigger_name->str);
-        return trigger->trigger_name;
-      }
-    }
-  }
-
-  return NULL;
-}
-
-
-/**
-  Update .TRG and .TRN files after renaming triggers' subject table.
-
-  @param[in,out] thd Thread context
-  @param[in] db Old database of subject table
-  @param[in] old_alias Old alias of subject table
-  @param[in] old_table Old name of subject table
-  @param[in] new_db New database for subject table
-  @param[in] new_table New name of subject table
-
-  @note
-    This method tries to leave trigger related files in consistent state,
-    i.e. it either will complete successfully, or will fail leaving files
-    in their initial state.
-    Also this method assumes that subject table is not renamed to itself.
-    This method needs to be called under an exclusive table metadata lock.
-
-  @retval FALSE Success
-  @retval TRUE  Error
-*/
-
-bool Table_triggers_list::change_table_name(THD *thd, const char *db,
-                                            const char *old_alias,
-                                            const char *old_table,
-                                            const char *new_db,
-                                            const char *new_table)
-{
-  TABLE table;
-  bool result= false;
-  bool upgrading50to51= false;
-  DBUG_ENTER("change_table_name");
-
-  memset(&table, 0, sizeof(table));
-  init_sql_alloc(&table.mem_root, 8192, 0);
-
-  /*
-    This method interfaces the mysql server code protected by
-    an exclusive metadata lock.
-  */
-  DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, old_table,
-                                             MDL_EXCLUSIVE));
-
-  DBUG_ASSERT(my_strcasecmp(table_alias_charset, db, new_db) ||
-              my_strcasecmp(table_alias_charset, old_alias, new_table));
-
-  if (Table_triggers_list::check_n_load(thd, db, old_table, &table, TRUE))
-  {
-    result= true;
-    goto end;
-  }
-  if (table.triggers)
-  {
-    if (table.triggers->check_for_broken_triggers())
-    {
-      result= true;
-      goto end;
-    }
-    LEX_STRING old_table_name= { (char *) old_alias, strlen(old_alias) };
-    LEX_STRING new_table_name= { (char *) new_table, strlen(new_table) };
-    /*
-      Since triggers should be in the same schema as their subject tables
-      moving table with them between two schemas raises too many questions.
-      (E.g. what should happen if in new schema we already have trigger
-       with same name ?).
-       
-      In case of "ALTER DATABASE `#mysql50#db1` UPGRADE DATA DIRECTORY NAME"
-      we will be given table name with "#mysql50#" prefix
-      To remove this prefix we use check_n_cut_mysql50_prefix().
-    */
-    if (my_strcasecmp(table_alias_charset, db, new_db))
-    {
-      char dbname[NAME_LEN + 1];
-      if (check_n_cut_mysql50_prefix(db, dbname, sizeof(dbname)) && 
-          !my_strcasecmp(table_alias_charset, dbname, new_db))
-      {
-        upgrading50to51= true;
-      }
-      else
-      {
-        my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0));
-        result= true;
-        goto end;
-      }
-    }
-
-    if (table.triggers->change_table_name_in_triggers(thd, db, new_db,
-                                                      &old_table_name,
-                                                      &new_table_name,
-                                                      upgrading50to51))
-    {
-      result= true;
-      goto end;
-    }
-  }
-  
-end:
-  delete table.triggers;
-  table.triggers= NULL;
-  free_root(&table.mem_root, MYF(0));
-  DBUG_RETURN(result);
-}
-
-
-/**
-  Execute trigger for given (event, time) pair.
-
-  The operation executes trigger for the specified event (insert, update,
-  delete) and time (after, before) if it is set.
-
-  @param thd
-  @param event
-  @param time_type
-  @param old_row_is_record1
-
-  @return Error status.
-    @retval FALSE on success.
-    @retval TRUE  on error.
-*/
-
-bool Table_triggers_list::process_triggers(THD *thd,
-                                           trg_event_type event,
-                                           trg_action_time_type time_type,
-                                           bool old_row_is_record1)
-{
-  Trigger *trigger= bodies[event][time_type];
-
-  if (check_for_broken_triggers())
-    return true;
-
-  if (trigger == NULL)
-    return false;
-
-  if (old_row_is_record1)
-  {
-    old_field= record1_field;
-    new_field= trigger_table->field;
-  }
-  else
-  {
-    new_field= record1_field;
-    old_field= trigger_table->field;
-  }
-  /*
-    This trigger must have been processed by the pre-locking
-    algorithm.
-  */
-  DBUG_ASSERT(trigger_table->pos_in_table_list->trg_event_map &
-              static_cast<uint>(1 << static_cast<int>(event)));
-
-  return trigger->execute(thd);
-}
-
-
-/**
-  Add triggers for table to the set of routines used by statement.
-  Add tables used by them to statement table list. Do the same for
-  routines used by triggers.
-
-  @param thd             Thread context.
-  @param prelocking_ctx  Prelocking context of the statement.
-  @param table_list      Table list element for table with trigger.
-
-  @retval FALSE  Success.
-  @retval TRUE   Failure.
-*/
-
-bool
-Table_triggers_list::
-add_tables_and_routines_for_triggers(THD *thd,
-                                     Query_tables_list *prelocking_ctx,
-                                     TABLE_LIST *table_list)
-{
-  DBUG_ASSERT(static_cast<int>(table_list->lock_type) >=
-              static_cast<int>(TL_WRITE_ALLOW_WRITE));
-
-  for (int i= 0; i < (int)TRG_EVENT_MAX; i++)
-  {
-    if (table_list->trg_event_map &
-        static_cast<uint8>(1 << static_cast<int>(i)))
-    {
-      for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
-      {
-        /* We can have only one trigger per action type currently */
-        Trigger *trigger= table_list->table->triggers->bodies[i][j];
-
-        if (trigger)
-        {
-          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->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->sp->m_sroutines,
-                                         table_list->belong_to_view);
-            trigger->sp->propagate_attributes(prelocking_ctx);
-          }
-        }
-      }
-    }
-  }
-  return FALSE;
-}
-
-
-/**
-  Check if any of the marked fields are used in the trigger.
-
-  @param used_fields  Bitmap over fields to check
-  @param event_type   Type of event triggers for which we are going to inspect
-  @param action_time  Type of trigger action time we are going to inspect
-*/
-
-bool Table_triggers_list::is_fields_updated_in_trigger(MY_BITMAP *used_fields,
-                                                       trg_event_type event_type,
-                                                       trg_action_time_type action_time)
-{
-  Item_trigger_field *trg_field;
-  Trigger *trigger= bodies[event_type][action_time];
-  DBUG_ASSERT(used_fields->n_bits == trigger_table->s->fields);
-
-  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. */
-    if (trg_field->field_idx != (uint)-1)
-    {
-      if (bitmap_is_set(used_fields, trg_field->field_idx) &&
-          trg_field->get_settable_routine_parameter())
-        return true;
-    }
-  }
-  return false;
-}
-
-
-/**
-  Mark all trigger fields as "temporary nullable" and remember the current
-  THD::count_cuted_fields value.
-
-  @param thd Thread context.
-*/
-void Table_triggers_list::enable_fields_temporary_nullability(THD* thd)
-{
-  for (Field** next_field= trigger_table->field; *next_field; ++next_field)
-  {
-    (*next_field)->set_tmp_nullable();
-    (*next_field)->set_count_cuted_fields(thd->count_cuted_fields);
-
-    /*
-      For statement LOAD INFILE we set field values during parsing of data file
-      and later run fill_record_n_invoke_before_triggers() to invoke table's
-      triggers. fill_record_n_invoke_before_triggers() calls this method
-      to enable temporary nullability before running trigger's instructions
-      Since for the case of handling statement LOAD INFILE the null value of
-      fields have been already set we don't have to reset these ones here.
-      In case of handling statements INSERT/REPLACE/INSERT SELECT/
-      REPLACE SELECT we set field's values inside method fill_record
-      that is called from fill_record_n_invoke_before_triggers()
-      after the method enable_fields_temporary_nullability has been executed.
-    */
-    if (thd->lex->sql_command != SQLCOM_LOAD)
-      (*next_field)->reset_tmp_null();
-  }
-}
-
-
-/**
-  Reset "temporary nullable" flag from trigger fields.
-*/
-void Table_triggers_list::disable_fields_temporary_nullability()
-{
-  for (Field** next_field= trigger_table->field; *next_field; ++next_field)
-    (*next_field)->reset_tmp_nullable();
-}
-
-
-/**
-  Mark fields of subject table which we read/set in its triggers
-  as such.
-
-  This method marks fields of subject table which are read/set in its
-  triggers as such (by properly updating TABLE::read_set/write_set)
-  and thus informs handler that values for these fields should be
-  retrieved/stored during execution of statement.
-
-  @param event  Type of event triggers for which we are going to inspect
-*/
-
-void Table_triggers_list::mark_fields_used(trg_event_type event)
-{
-  int action_time;
-  Item_trigger_field *trg_field;
-
-  for (action_time= 0; action_time < (int)TRG_ACTION_MAX; action_time++)
-  {
-    Trigger *trigger= bodies[event][action_time];
-
-    if (!trigger)
-      continue;
-
-    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. */
-      if (trg_field->field_idx != (uint)-1)
-      {
-        bitmap_set_bit(trigger_table->read_set, trg_field->field_idx);
-        if (trg_field->get_settable_routine_parameter())
-          bitmap_set_bit(trigger_table->write_set, trg_field->field_idx);
-      }
-    }
-  }
-  trigger_table->file->column_bitmaps_signal();
-}
-
-
-/**
-   Signals to the Table_triggers_list that a parse error has occured when
-   reading a trigger from file. This makes the Table_triggers_list enter an
-   error state flagged by m_has_unparseable_trigger == true. The error message
-   will be used whenever a statement invoking or manipulating triggers is
-   issued against the Table_triggers_list's table.
-
-   @param error_message The error message thrown by the parser.
- */
-void Table_triggers_list::set_parse_error_message(char *error_message)
-{
-  m_has_unparseable_trigger= true;
-  strcpy(m_parse_error_message, error_message);
-}
-
-
-/**
-  Trigger BUG#14090 compatibility hook.
-
-  @param[in,out] unknown_key       reference on the line with unknown
-    parameter and the parsing point
-  @param[in]     base              base address for parameter writing
-    (structure like TABLE)
-  @param[in]     mem_root          MEM_ROOT for parameters allocation
-  @param[in]     end               the end of the configuration
-
-  @note
-    NOTE: this hook process back compatibility for incorrectly written
-    sql_modes parameter (see BUG#14090).
-
-  @retval
-    FALSE OK
-  @retval
-    TRUE  Error
-*/
-
-#define INVALID_SQL_MODES_LENGTH 13
-
-bool
-Handle_old_incorrect_sql_modes_hook::
-process_unknown_string(const char *&unknown_key, uchar* base,
-                       MEM_ROOT *mem_root, const char *end)
-{
-  DBUG_ENTER("Handle_old_incorrect_sql_modes_hook::process_unknown_string");
-  DBUG_PRINT("info", ("unknown key: %60s", unknown_key));
-
-  if (unknown_key + INVALID_SQL_MODES_LENGTH + 1 < end &&
-      unknown_key[INVALID_SQL_MODES_LENGTH] == '=' &&
-      !memcmp(unknown_key, STRING_WITH_LEN("sql_modes")))
-  {
-    const char *ptr= unknown_key + INVALID_SQL_MODES_LENGTH + 1;
-
-    DBUG_PRINT("info", ("sql_modes affected by BUG#14090 detected"));
-    push_warning_printf(current_thd,
-                        Sql_condition::SL_NOTE,
-                        ER_OLD_FILE_FORMAT,
-                        ER(ER_OLD_FILE_FORMAT),
-                        (char *)path, "TRIGGER");
-    if (get_file_options_ulllist(ptr, end, unknown_key, base,
-                                 &sql_modes_parameters, mem_root))
-    {
-      DBUG_RETURN(TRUE);
-    }
-    /*
-      Set parsing pointer to the last symbol of string (\n)
-      1) to avoid problem with \0 in the junk after sql_modes
-      2) to speed up skipping this line by parser.
-    */
-    unknown_key= ptr-1;
-  }
-  DBUG_RETURN(FALSE);
-}
-
-#define INVALID_TRIGGER_TABLE_LENGTH 15
-
-/**
-  Trigger BUG#15921 compatibility hook. For details see
-  Handle_old_incorrect_sql_modes_hook::process_unknown_string().
-*/
-bool
-Handle_old_incorrect_trigger_table_hook::
-process_unknown_string(const char *&unknown_key, uchar* base,
-                       MEM_ROOT *mem_root, const char *end)
-{
-  DBUG_ENTER("Handle_old_incorrect_trigger_table_hook::process_unknown_string");
-  DBUG_PRINT("info", ("unknown key: %60s", unknown_key));
-
-  if (unknown_key + INVALID_TRIGGER_TABLE_LENGTH + 1 < end &&
-      unknown_key[INVALID_TRIGGER_TABLE_LENGTH] == '=' &&
-      !memcmp(unknown_key, STRING_WITH_LEN("trigger_table")))
-  {
-    const char *ptr= unknown_key + INVALID_TRIGGER_TABLE_LENGTH + 1;
-
-    DBUG_PRINT("info", ("trigger_table affected by BUG#15921 detected"));
-    push_warning_printf(current_thd,
-                        Sql_condition::SL_NOTE,
-                        ER_OLD_FILE_FORMAT,
-                        ER(ER_OLD_FILE_FORMAT),
-                        (char *)path, "TRIGGER");
-
-    if (!(ptr= parse_escaped_string(ptr, end, mem_root, trigger_table_value)))
-    {
-      my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0), "trigger_table",
-               unknown_key);
-      DBUG_RETURN(TRUE);
-    }
-
-    /* Set parsing pointer to the last symbol of string (\n). */
-    unknown_key= ptr-1;
-  }
-  DBUG_RETURN(FALSE);
-}
-
-
-/**
   Contruct path to TRN-file.
 
   @param thd[in]        Thread context.
@@ -2690,7 +395,7 @@ bool load_table_name_for_trigger(THD *th
                                  const LEX_STRING *trn_path,
                                  LEX_STRING *tbl_name)
 {
-  Triggers_definition_loader trigger_loader;
+  Trigger_loader trigger_loader;
   return trigger_loader.get_table_name_for_trigger(thd, trg_name,
                                                    trn_path, tbl_name);
 

=== modified file 'sql/sql_trigger.h'
--- a/sql/sql_trigger.h	2013-01-22 09:24:23 +0000
+++ b/sql/sql_trigger.h	2013-01-22 20:38:40 +0000
@@ -17,396 +17,23 @@
    along with this program; if not, write to the Free Software Foundation,
    51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
 
-/* Forward declarations */
+///////////////////////////////////////////////////////////////////////////
+
+#include "m_string.h"
 
-class Item_trigger_field;
-class sp_head;
 class sp_name;
-class Query_tables_list;
+class THD;
+
 struct TABLE_LIST;
-class Query_tables_list;
-class Stored_program_creation_ctx;
 
-/** Event on which trigger is invoked. */
-enum trg_event_type
-{
-  TRG_EVENT_INSERT= 0,
-  TRG_EVENT_UPDATE= 1,
-  TRG_EVENT_DELETE= 2,
-  TRG_EVENT_MAX
-};
-
-#include "table.h"                              /* GRANT_INFO */
-
-/*
-  We need this two enums here instead of sql_lex.h because
-  at least one of them is used by Item_trigger_field interface.
-
-  Time when trigger is invoked (i.e. before or after row actually
-  inserted/updated/deleted).
-*/
-enum trg_action_time_type
-{
-  TRG_ACTION_BEFORE= 0, TRG_ACTION_AFTER= 1, TRG_ACTION_MAX
-};
-
-
-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.
-
-  QQ: Will it be merged into TABLE in the future ?
-*/
-
-class Table_triggers_list: public Sql_alloc
-{
-  /** 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]
-    buffer instead of TABLE::record[0] (used for OLD values in on UPDATE
-    trigger and DELETE trigger when it is called for REPLACE).
-  */
-  Field             **record1_field;
-
-  /**
-    During execution of trigger new_field and old_field should point to the
-    array of fields representing new or old version of row correspondingly
-    (so it can point to TABLE::field or to Tale_triggers_list::record1_field)
-  */
-  Field             **new_field;
-  Field             **old_field;
-
-public:
-  /** TABLE instance for which this triggers list object was created. */
-  TABLE *trigger_table;
-
-private:
-  /*
-    List of triggers assigned to this table object.
-  */
-  List<Trigger> table_triggers;
-
-public:
-  /**
-    Grant information for each trigger (pair: subject table, trigger definer).
-  */
-  GRANT_INFO        subject_table_grants[TRG_EVENT_MAX][TRG_ACTION_MAX];
-
-private:
-  /**
-     This flag indicates that one of the triggers was not parsed successfully,
-     and as a precaution the object has entered a state where all trigger
-     access results in errors until all such triggers are dropped. It is not
-     safe to add triggers since we don't know if the broken trigger has the
-     same name or event type. Nor is it safe to invoke any trigger for the
-     aforementioned reasons. The only safe operations are drop_trigger and
-     drop_all_triggers.
-
-     @see Table_triggers_list::set_parse_error
-   */
-  bool m_has_unparseable_trigger;
-
-  /**
-    This error will be displayed when the user tries to manipulate or invoke
-    triggers on a table that has broken triggers. It will get set only once
-    per statement and thus will contain the first parse error encountered in
-    the trigger file.
-   */
-  char m_parse_error_message[MYSQL_ERRMSG_SIZE];
-
-  Triggers_definition_loader trigger_loader;
-
-public:
-  Table_triggers_list(TABLE *table_arg)
-    :record1_field(0), trigger_table(table_arg),
-    m_has_unparseable_trigger(false)
-  {
-    memset(bodies, 0, sizeof(bodies));
-    memset(&subject_table_grants, 0, sizeof(subject_table_grants));
-  }
-
-  ~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,
-                        trg_action_time_type time_type,
-                        bool old_row_is_record1);
-
-  bool get_trigger_info(THD *thd, trg_event_type event,
-                        trg_action_time_type time_type,
-                        LEX_STRING *trigger_name, LEX_STRING *trigger_stmt,
-                        sql_mode_t *sql_mode,
-                        LEX_STRING *definer,
-                        LEX_STRING *client_cs_name,
-                        LEX_STRING *connection_cl_name,
-                        LEX_STRING *db_cl_name);
-
-  void get_trigger_info(THD *thd,
-                        int trigger_idx,
-                        LEX_STRING *trigger_name,
-                        sql_mode_t *sql_mode,
-                        LEX_STRING *sql_original_stmt,
-                        LEX_STRING *client_cs_name,
-                        LEX_STRING *connection_cl_name,
-                        LEX_STRING *db_cl_name);
-
-  int find_trigger_by_name(const LEX_STRING *trigger_name);
-
-  static bool check_n_load(THD *thd, const char *db, const char *table_name,
-                           TABLE *table, bool names_only);
-  static bool drop_all_triggers(THD *thd, char *db, char *table_name);
-  static bool change_table_name(THD *thd, const char *db,
-                                const char *old_alias,
-                                const char *old_table,
-                                const char *new_db,
-                                const char *new_table);
-  bool has_triggers(trg_event_type event_type, 
-                    trg_action_time_type action_time)
-  {
-    return (bodies[event_type][action_time] != NULL);
-  }
-  bool has_delete_triggers()
-  {
-    return (bodies[TRG_EVENT_DELETE][TRG_ACTION_BEFORE] ||
-            bodies[TRG_EVENT_DELETE][TRG_ACTION_AFTER]);
-  }
-
-  void set_table(TABLE *new_table);
-
-  void mark_fields_used(trg_event_type event);
-
-  void set_parse_error_message(char *error_message);
-
-  friend class Item_trigger_field;
-
-  bool add_tables_and_routines_for_triggers(THD *thd,
-                                            Query_tables_list *prelocking_ctx,
-                                            TABLE_LIST *table_list);
-  bool is_fields_updated_in_trigger(MY_BITMAP *used_fields,
-                                    trg_event_type event_type,
-                                    trg_action_time_type action_time);
-
-  void enable_fields_temporary_nullability(THD* thd);
-  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();
-  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,
-                                     bool upgrading50to51);
-
-  bool check_for_broken_triggers() 
-  {
-    if (m_has_unparseable_trigger)
-    {
-      my_message(ER_PARSE_ERROR, m_parse_error_message, MYF(0));
-      return true;
-    }
-    return false;
-  }
-};
+///////////////////////////////////////////////////////////////////////////
+
+extern const char * const TRG_EXT;
+extern const char * const TRN_EXT;
+
+///////////////////////////////////////////////////////////////////////////
 
-extern const LEX_STRING trg_action_time_type_names[];
-extern const LEX_STRING trg_event_type_names[];
+bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create);
 
 bool add_table_for_trigger(THD *thd,
                            const sp_name *trg_name,
@@ -421,9 +48,7 @@ bool load_table_name_for_trigger(THD *th
                                  const sp_name *trg_name,
                                  const LEX_STRING *trn_path,
                                  LEX_STRING *tbl_name);
-bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create);
 
-extern const char * const TRG_EXT;
-extern const char * const TRN_EXT;
+///////////////////////////////////////////////////////////////////////////
 
 #endif /* SQL_TRIGGER_INCLUDED */

=== modified file 'sql/table.cc'
--- a/sql/table.cc	2012-12-13 11:16:18 +0000
+++ b/sql/table.cc	2013-01-23 15:11:25 +0000
@@ -5288,7 +5288,7 @@ void TABLE::mark_columns_needed_for_dele
     
     Unlike other similar methods, it doesn't mark fields used by triggers,
     that is the responsibility of the caller to do, by using
-    Table_triggers_list::mark_used_fields(TRG_EVENT_UPDATE)!
+    Table_trigger_dispatcher::mark_used_fields(TRG_EVENT_UPDATE)!
 */
 
 void TABLE::mark_columns_needed_for_update()

=== modified file 'sql/table.h'
--- a/sql/table.h	2012-12-11 18:12:13 +0000
+++ b/sql/table.h	2013-01-23 15:11:25 +0000
@@ -261,7 +261,7 @@ typedef struct st_grant_internal_info GR
    A GRANT_INFO also serves as a cache of the privilege hash tables. Relevant
    members are grant_table and version.
  */
-typedef struct st_grant_info
+struct GRANT_INFO
 {
   /**
      @brief A copy of the privilege information regarding the current host,
@@ -310,7 +310,7 @@ typedef struct st_grant_info
   ulong orig_want_privilege;
   /** The grant state for internal tables. */
   GRANT_INTERNAL_INFO m_internal;
-} GRANT_INFO;
+};
 
 enum tmp_table_type
 {
@@ -367,7 +367,7 @@ public:
 };
 
 class Field_blob;
-class Table_triggers_list;
+class Table_trigger_dispatcher;
 
 /**
   Category of table found in the table share.
@@ -1038,7 +1038,7 @@ public:
   Field *found_next_number_field;	/* Set on open */
 
   /* Table's triggers, 0 if there are no of them */
-  Table_triggers_list *triggers;
+  Table_trigger_dispatcher *triggers;
   TABLE_LIST *pos_in_table_list;/* Element referring to this table */
   /* Position in thd->locked_table_list under LOCK TABLES */
   TABLE_LIST *pos_in_locked_tables;

=== added file 'sql/table_trigger_dispatcher.cc'
--- a/sql/table_trigger_dispatcher.cc	1970-01-01 00:00:00 +0000
+++ b/sql/table_trigger_dispatcher.cc	2013-01-25 11:48:04 +0000
@@ -0,0 +1,1071 @@
+/*
+   Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#define MYSQL_LEX 1
+
+#include "my_global.h"
+#include "table_trigger_dispatcher.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sp_head.h"
+#include "sql_trigger.h"
+#include "sql_parse.h"                          // parse_sql
+#include "parse_file.h"
+#include "sp.h"
+#include "sql_base.h"                          // find_temporary_table
+#include "sql_show.h"                // append_definer, append_identifier
+#include "sql_table.h"                        // build_table_filename,
+                                              // check_n_cut_mysql50_prefix
+#include "sql_db.h"                        // get_default_db_collation
+#include "sql_acl.h"                       // *_ACL, is_acl_user
+#include "sql_handler.h"                        // mysql_ha_rm_tables
+#include "sp_cache.h"                     // sp_invalidate_cache
+
+#include "trigger_loader.h"
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Create trigger for table.
+
+  @param thd           current thread context (including trigger definition in
+                       LEX)
+  @param tables        table list containing one open table for which the
+                       trigger is created.
+  @param[out] stmt_query    after successful return, this string contains
+                            well-formed statement for creation this trigger.
+
+  @note
+    - Assumes that trigger name is fully qualified.
+    - NULL-string means the following LEX_STRING instance:
+    { str = 0; length = 0 }.
+    - In other words, definer_user and definer_host should contain
+    simultaneously NULL-strings (non-SUID/old trigger) or valid strings
+    (SUID/new trigger).
+
+  @retval
+    false   success
+  @retval
+    true    error
+*/
+
+bool Table_trigger_dispatcher::create_trigger(THD *thd, TABLE_LIST *tables,
+                                              String *stmt_query)
+{
+  LEX *lex= thd->lex;
+  TABLE *table= tables->table;
+  LEX_STRING *trg_def;
+  LEX_STRING definer_user;
+  LEX_STRING definer_host;
+  sql_mode_t trg_sql_mode;
+  LEX_STRING *trg_definer;
+  LEX_STRING *trg_client_cs_name;
+  LEX_STRING *trg_connection_cl_name;
+  LEX_STRING *trg_db_cl_name;
+
+  if (check_for_broken_triggers())
+    return true;
+
+  /* Trigger must be in the same schema as target table. */
+  if (my_strcasecmp(table_alias_charset, table->s->db.str,
+                    lex->spname->m_db.str))
+  {
+    my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0));
+    return true;
+  }
+
+  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;
+  int trg_action_time= trg->m_trg_chistics.action_time;
+
+  /* We don't allow creation of several triggers of the same type yet */
+  if (bodies[trg_event][trg_action_time] != NULL)
+  {
+    my_error(ER_NOT_SUPPORTED_YET, MYF(0),
+             "multiple triggers with the same action time"
+             " and event for one table");
+    return true;
+  }
+
+  if (!lex->definer)
+  {
+    /*
+      DEFINER-clause is missing.
+
+      If we are in slave thread, this means that we received CREATE TRIGGER
+      from the master, that does not support definer in triggers. So, we
+      should mark this trigger as non-SUID. Note that this does not happen
+      when we parse triggers' definitions during opening .TRG file.
+      LEX::definer is ignored in that case.
+
+      Otherwise, we should use CURRENT_USER() as definer.
+
+      NOTE: when CREATE TRIGGER statement is allowed to be executed in PS/SP,
+      it will be required to create the definer below in persistent MEM_ROOT
+      of PS/SP.
+    */
+
+    if (!thd->slave_thread)
+    {
+      if (!(lex->definer= create_default_definer(thd)))
+        return true;
+    }
+  }
+
+  /*
+    If the specified definer differs from the current user, we should check
+    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,
+                     lex->definer->host.str,
+                     thd->security_ctx->priv_host)))
+  {
+    if (check_global_access(thd, SUPER_ACL))
+    {
+      my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
+      return true;
+    }
+  }
+
+  /*
+    Let us check if all references to fields in old/new versions of row in
+    this trigger are ok.
+
+    NOTE: We do it here more from ease of use standpoint. We still have to
+    do some checks on each execution. E.g. we can catch privilege changes
+    only during execution. Also in near future, when we will allow access
+    to other tables from trigger we won't be able to catch changes in other
+    tables...
+
+    Since we don't plan to access to contents of the fields it does not
+    matter that we choose for both OLD and NEW values the same versions
+    of Field objects here.
+  */
+  old_field= new_field= table->field;
+
+  for (Item_trigger_field *trg_field= lex->sphead->m_trg_table_fields.first;
+       trg_field; trg_field= trg_field->next_trg_field)
+  {
+    /*
+      NOTE: now we do not check privileges at CREATE TRIGGER time. This will
+      be changed in the future.
+    */
+    trg_field->setup_field(thd, table, NULL);
+
+    if (!trg_field->fixed &&
+        trg_field->fix_fields(thd, (Item **)0))
+      return true;
+  }
+
+
+  /*
+    Soon we will invalidate table object and thus Table_trigger_dispatcher
+    object so don't care about place to which trg_def->ptr points and other
+    invariants (e.g. we don't bother to update names_list)
+
+    QQ: Hmm... probably we should not care about setting up active thread
+        mem_root too.
+  */
+  if (!(trg_def= alloc_lex_string(&table->mem_root)) ||
+      !(trg_definer= alloc_lex_string(&table->mem_root)) ||
+      !(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)))
+  {
+    return true;
+  }
+
+  trg_sql_mode= thd->variables.sql_mode;
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+  if (lex->definer && !is_acl_user(lex->definer->host.str,
+                                   lex->definer->user.str))
+  {
+    push_warning_printf(thd,
+                        Sql_condition::SL_NOTE,
+                        ER_NO_SUCH_USER,
+                        ER(ER_NO_SUCH_USER),
+                        lex->definer->user.str,
+                        lex->definer->host.str);
+  }
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+
+  if (lex->definer)
+  {
+    char trg_definer_buf[USER_HOST_BUFF_SIZE];
+    /* SUID trigger. */
+
+    definer_user= lex->definer->user;
+    definer_host= lex->definer->host;
+
+    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
+  {
+    /* non-SUID trigger. */
+
+    definer_user.str= 0;
+    definer_user.length= 0;
+
+    definer_host.str= 0;
+    definer_host.length= 0;
+
+    trg_definer->str= (char*) "";
+    trg_definer->length= 0;
+  }
+
+  /*
+    Fill character set information:
+      - client character set contains charset info only;
+      - connection collation contains pair {character set, collation};
+      - database collation contains pair {character set, collation};
+  */
+
+  lex_string_set(trg_client_cs_name, thd->charset()->csname);
+
+  lex_string_set(trg_connection_cl_name,
+                 thd->variables.collation_connection->name);
+
+  lex_string_set(trg_db_cl_name,
+                 get_default_db_collation(thd, tables->db)->name);
+
+  /*
+    Create well-formed trigger definition query. Original query is not
+    appropriated, because definer-clause can be not truncated.
+  */
+
+  stmt_query->append(STRING_WITH_LEN("CREATE "));
+
+  /*
+    Append definer-clause if the trigger is SUID (a usual trigger in
+    new MySQL versions).
+  */
+
+  append_definer(thd, stmt_query, &definer_user, &definer_host);
+
+  LEX_STRING stmt_definition;
+  stmt_definition.str= (char*) thd->lex->stmt_definition_begin;
+  stmt_definition.length= thd->lex->stmt_definition_end
+    - thd->lex->stmt_definition_begin;
+  trim_whitespace(thd->charset(), & stmt_definition);
+
+  stmt_query->append(stmt_definition.str, stmt_definition.length);
+
+  lex_string_copy(&table->mem_root, trg_def, stmt_query->c_ptr(),
+                  stmt_query->length());
+
+  Trigger *new_trigger=
+      new (&table->mem_root) Trigger(&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);
+
+  new_trigger->set_trigger_name(&lex->spname->m_name);
+
+  {
+    Trigger_loader trigger_loader;
+    return trigger_loader.store_trigger(tables, new_trigger,
+                                        &table_triggers);
+  }
+}
+
+
+/**
+  Drop trigger for table.
+
+  @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
+    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).
+
+  @retval
+    False   success
+  @retval
+    True    error
+*/
+bool Table_trigger_dispatcher::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());
+
+  Trigger_loader trigger_loader;
+  return trigger_loader.drop_trigger(tables, sp_name, &table_triggers);
+}
+
+
+Table_trigger_dispatcher::~Table_trigger_dispatcher()
+{
+  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 (record1_field)
+    for (Field **fld_ptr= record1_field; *fld_ptr; fld_ptr++)
+      delete *fld_ptr;
+}
+
+
+/**
+  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_trigger_dispatcher::prepare_record1_accessors()
+{
+  Field **fld, **old_fld;
+
+  if (!(record1_field= (Field **)alloc_root(&trigger_table->mem_root,
+                                            (trigger_table->s->fields + 1) *
+                                            sizeof(Field*))))
+    return true;
+
+  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;
+
+  return false;
+}
+
+
+/**
+  Adjust Table_trigger_dispatcher with new TABLE pointer.
+
+  @param new_table   new pointer to TABLE instance
+*/
+
+void Table_trigger_dispatcher::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;
+  }
+}
+
+
+/**
+  Check whenever .TRG file for table exist and load all triggers it contains.
+
+  @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
+
+  @todo
+    A lot of things to do here e.g. how about other funcs and being
+    more paranoical ?
+
+  @todo
+    This could be avoided if there is no triggers for UPDATE and DELETE.
+
+  @retval
+    False   success
+  @retval
+    True    error
+*/
+
+bool Table_trigger_dispatcher::check_n_load(THD *thd, const char *db,
+                                            const char *table_name,
+                                            TABLE *table,
+                                            bool names_only)
+{
+  DBUG_ENTER("Table_trigger_dispatcher::check_n_load");
+
+  Table_trigger_dispatcher *table_trigger_dispatch=
+    new (&table->mem_root) Table_trigger_dispatcher(table);
+
+  if (!table_trigger_dispatch)
+    DBUG_RETURN(true);
+
+  bool triggers_not_found;
+
+  if (Trigger_loader::load_triggers(thd, db, table_name, table,
+                                    &table_trigger_dispatch->table_triggers,
+                                    &triggers_not_found))
+  {
+    delete table_trigger_dispatch;
+    DBUG_RETURN(!triggers_not_found);
+  }
+
+  parse_triggers(thd, table, table_trigger_dispatch,
+                 table_trigger_dispatch->table_triggers);
+  table->triggers= table_trigger_dispatch;
+
+  /*
+    TODO: This could be avoided if there is no triggers
+          for UPDATE and DELETE.
+  */
+  if (!names_only && table_trigger_dispatch->prepare_record1_accessors())
+  {
+    delete table_trigger_dispatch;
+    DBUG_RETURN(true);
+  }
+
+  {
+    List_iterator_fast<Trigger> it(table_trigger_dispatch->table_triggers);
+    while (true)
+    {
+      Trigger *t= it++;
+
+      if (!t)
+        break;
+
+      table_trigger_dispatch->bodies[t->get_trg_event()]
+                                    [t->get_trg_action_time()]= t;
+
+      if (!names_only)
+        t->setup_fields(thd, table);
+    }
+  }
+
+  DBUG_RETURN(false);
+}
+
+
+/**
+  Obtains and returns trigger metadata.
+
+  @param thd           current thread context
+  @param event         trigger event type
+  @param time_type     trigger action time
+  @param trigger_name  returns name of trigger
+  @param trigger_stmt  returns statement of trigger
+  @param sql_mode      returns sql_mode of trigger
+  @param definer       returns definer/creator of trigger. The caller is
+                       responsible to allocate enough space for storing
+                       definer information.
+
+  @retval
+    false   success
+  @retval
+    true    error
+*/
+
+bool Table_trigger_dispatcher::get_trigger_info(THD *thd, trg_event_type event,
+                                                trg_action_time_type time_type,
+                                                LEX_STRING *trigger_name,
+                                                LEX_STRING *trigger_stmt,
+                                                sql_mode_t *sql_mode,
+                                                LEX_STRING *definer,
+                                                LEX_STRING *client_cs_name,
+                                                LEX_STRING *connection_cl_name,
+                                                LEX_STRING *db_cl_name)
+{
+  Trigger *trigger;
+  DBUG_ENTER("get_trigger_info");
+
+  if ((trigger= bodies[event][time_type]))
+  {
+    trigger->get_info(trigger_name, trigger_stmt, sql_mode, definer, NULL,
+                      client_cs_name, connection_cl_name, db_cl_name);
+    DBUG_RETURN(false);
+  }
+
+  DBUG_RETURN(true);
+}
+
+
+/**
+  Get information about trigger by its id.
+
+  @param [in] thd                  thread handle
+  @param [in] trigger_idx          ordinal number of trigger in the list
+  @param [out] trigger_name        pointer to variable where to store
+                                   the trigger name
+  @param [out] sql_mode            pointer to variable where to store
+                                   the sql mode
+  @param [out] sql_original_stmt   pointer to variable where to store
+                                   the trigger definition
+  @param [out] client_cs_name      client character set
+  @param [out] connection_cl_name  connection collation
+  @param [out] db_cl_name          database collation
+*/
+
+void Table_trigger_dispatcher::get_trigger_info(THD *thd,
+                                                int trigger_idx,
+                                                LEX_STRING *trigger_name,
+                                                sql_mode_t *sql_mode,
+                                                LEX_STRING *sql_original_stmt,
+                                                LEX_STRING *client_cs_name,
+                                                LEX_STRING *connection_cl_name,
+                                                LEX_STRING *db_cl_name)
+{
+  List_iterator_fast<Trigger> it_table_triggers(table_triggers);
+  for (int i = 0; i < trigger_idx; ++i)
+  {
+    it_table_triggers.next_fast();
+  }
+
+  Trigger *found_trigger= it_table_triggers++;
+  found_trigger->get_info(trigger_name, NULL, sql_mode, NULL,
+                          sql_original_stmt, client_cs_name,
+                          connection_cl_name, db_cl_name);
+}
+
+
+/**
+  Get trigger ordinal number by trigger name.
+
+  @param [in] trigger_name        trigger name
+
+  @return  ordinal number of trigger in the list
+    @retval -1  if trigger not found
+    @retval >= 0  trigger index
+*/
+
+int Table_trigger_dispatcher::find_trigger_by_name(const LEX_STRING *trg_name)
+{
+  List_iterator_fast<Trigger> it_table_triggers(table_triggers);
+  for (int i = 0; ; ++i)
+  {
+    Trigger *trigger= it_table_triggers++;
+
+    if (!trigger)
+      return -1;
+
+    if (strcmp(trigger->m_trigger_name->str, trg_name->str) == 0)
+      return i;
+  }
+}
+
+
+/**
+  Drop all triggers for table.
+
+  @param thd      current thread context
+  @param db       schema for table
+  @param name     name for table
+
+  @retval
+    false   success
+  @retval
+    true    error
+*/
+
+bool Table_trigger_dispatcher::drop_all_triggers(THD *thd, char *db,
+                                                 char *name)
+{
+  TABLE table;
+  bool result= 0;
+  DBUG_ENTER("drop_all_triggers");
+
+  memset(&table, 0, sizeof(table));
+  init_sql_alloc(&table.mem_root, 8192, 0);
+
+  if (Table_trigger_dispatcher::check_n_load(thd, db, name, &table, 1))
+  {
+    result= 1;
+    goto end;
+  }
+  if (table.triggers)
+  {
+    Trigger_loader trigger_loader;
+    result= trigger_loader.drop_all_triggers(
+        db, name,
+        table.triggers->table_triggers);
+
+  }
+end:
+  if (table.triggers)
+    delete table.triggers;
+  table.triggers= NULL;
+  free_root(&table.mem_root, MYF(0));
+  DBUG_RETURN(result);
+}
+
+
+/**
+  Update .TRG file after renaming triggers' subject table
+  (change name of table in triggers' definitions).
+
+  @param thd                 Thread context
+  @param old_db_name         Old database of subject table
+  @param new_db_name         New database of subject table
+  @param old_table_name      Old subject table's name
+  @param new_table_name      New subject table's name
+
+  @retval
+    false  Success
+  @retval
+    true   Failure
+*/
+
+bool
+Table_trigger_dispatcher::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,
+    bool upgrading50to51)
+{
+  LEX_STRING *def, *on_table_name, new_def;
+  sql_mode_t save_sql_mode= thd->variables.sql_mode;
+  List_iterator_fast<Trigger> it_table_triggers(table_triggers);
+  size_t on_q_table_name_len, before_on_len;
+  String buff;
+  Trigger *trigger;
+
+  while ((trigger= it_table_triggers++))
+  {
+    def= trigger->m_definition;
+    on_table_name= trigger->m_on_table_name;
+    thd->variables.sql_mode= trigger->m_definition_mode;
+
+    /* Construct CREATE TRIGGER statement with new table name. */
+    buff.length(0);
+
+    /* WARNING: 'on_table_name' is supposed to point inside 'def' */
+    DBUG_ASSERT(on_table_name->str > def->str);
+    DBUG_ASSERT(on_table_name->str < (def->str + def->length));
+    before_on_len= on_table_name->str - def->str;
+
+    buff.append(def->str, before_on_len);
+    buff.append(STRING_WITH_LEN("ON "));
+    append_identifier(thd, &buff, new_table_name->str, new_table_name->length);
+    buff.append(STRING_WITH_LEN(" "));
+    on_q_table_name_len= buff.length() - before_on_len;
+    buff.append(on_table_name->str + on_table_name->length,
+                def->length - (before_on_len + on_table_name->length));
+    /*
+      It is OK to allocate some memory on table's MEM_ROOT since this
+      table instance will be thrown out at the end of rename anyway.
+    */
+    new_def.str= (char*) memdup_root(&trigger_table->mem_root, buff.ptr(),
+                                     buff.length());
+    new_def.length= buff.length();
+    on_table_name->str= new_def.str + before_on_len;
+    on_table_name->length= on_q_table_name_len;
+    *def= new_def;
+  }
+
+  thd->variables.sql_mode= save_sql_mode;
+
+  if (thd->is_fatal_error)
+    return true; /* OOM */
+
+  Trigger_loader trigger_loader;
+  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;
+}
+
+
+/**
+  Update .TRG and .TRN files after renaming triggers' subject table.
+
+  @param[in,out] thd Thread context
+  @param[in] db Old database of subject table
+  @param[in] old_alias Old alias of subject table
+  @param[in] old_table Old name of subject table
+  @param[in] new_db New database for subject table
+  @param[in] new_table New name of subject table
+
+  @note
+    This method tries to leave trigger related files in consistent state,
+    i.e. it either will complete successfully, or will fail leaving files
+    in their initial state.
+    Also this method assumes that subject table is not renamed to itself.
+    This method needs to be called under an exclusive table metadata lock.
+
+  @retval false Success
+  @retval true  Error
+*/
+
+bool Table_trigger_dispatcher::change_table_name(THD *thd, const char *db,
+                                                 const char *old_alias,
+                                                 const char *old_table,
+                                                 const char *new_db,
+                                                 const char *new_table)
+{
+  TABLE table;
+  bool result= false;
+  bool upgrading50to51= false;
+  DBUG_ENTER("change_table_name");
+
+  memset(&table, 0, sizeof(table));
+  init_sql_alloc(&table.mem_root, 8192, 0);
+
+  /*
+    This method interfaces the mysql server code protected by
+    an exclusive metadata lock.
+  */
+  DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, old_table,
+                                             MDL_EXCLUSIVE));
+
+  DBUG_ASSERT(my_strcasecmp(table_alias_charset, db, new_db) ||
+              my_strcasecmp(table_alias_charset, old_alias, new_table));
+
+  if (Table_trigger_dispatcher::check_n_load(thd, db, old_table, &table, true))
+  {
+    result= true;
+    goto end;
+  }
+  if (table.triggers)
+  {
+    if (table.triggers->check_for_broken_triggers())
+    {
+      result= true;
+      goto end;
+    }
+    LEX_STRING old_table_name= { (char *) old_alias, strlen(old_alias) };
+    LEX_STRING new_table_name= { (char *) new_table, strlen(new_table) };
+    /*
+      Since triggers should be in the same schema as their subject tables
+      moving table with them between two schemas raises too many questions.
+      (E.g. what should happen if in new schema we already have trigger
+       with same name ?).
+       
+      In case of "ALTER DATABASE `#mysql50#db1` UPGRADE DATA DIRECTORY NAME"
+      we will be given table name with "#mysql50#" prefix
+      To remove this prefix we use check_n_cut_mysql50_prefix().
+    */
+    if (my_strcasecmp(table_alias_charset, db, new_db))
+    {
+      char dbname[NAME_LEN + 1];
+      if (check_n_cut_mysql50_prefix(db, dbname, sizeof(dbname)) && 
+          !my_strcasecmp(table_alias_charset, dbname, new_db))
+      {
+        upgrading50to51= true;
+      }
+      else
+      {
+        my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0));
+        result= true;
+        goto end;
+      }
+    }
+
+    if (table.triggers->change_table_name_in_triggers(thd, db, new_db,
+                                                      &old_table_name,
+                                                      &new_table_name,
+                                                      upgrading50to51))
+    {
+      result= true;
+      goto end;
+    }
+  }
+  
+end:
+  delete table.triggers;
+  table.triggers= NULL;
+  free_root(&table.mem_root, MYF(0));
+  DBUG_RETURN(result);
+}
+
+
+/**
+  Load table triggers from the data dictionary.
+
+  @param [in] thd                  thread handle
+  @param [in] table                pointer to the trigger's table
+  @param [in] dsptch               pointer to the Table_trigger_dispatcher
+  @param [in] table_trigger_lst    reference to the list of triggers to parse
+
+  @return Operation status
+    @retval true   Failure
+    @retval false  Success
+*/
+
+bool Table_trigger_dispatcher::parse_triggers(THD *thd, TABLE *table,
+                                              Table_trigger_dispatcher *dsptch,
+                                              List<Trigger> &table_trigger_lst)
+{
+  List_iterator<Trigger> it(table_trigger_lst);
+  while (true)
+  {
+    Trigger *t= it++;
+
+    if (!t)
+      break;
+
+    bool result= t->parse_trigger_body(thd, table);
+    if (result || t->has_parse_error())
+    {
+      dsptch->set_parse_error_message(t->get_parse_error_message());
+      if (result)
+      {
+        /*
+          The method Trigger::parse_trigger_body() returns true when the
+          trigger definition is unparseable, e.g. instead of SQL-statement
+          'CREATE TRIGGER trg1 ...'  the trigger definition contains
+          the string 'bla-bla-bla'. In this case we remove the trigger object
+          from the list.
+        */
+        delete t;
+        it.remove();
+      }
+      continue;
+    }
+
+    GRANT_INFO *table_grant=
+        &dsptch->subject_table_grants[t->get_trg_event()]
+                                     [t->get_trg_action_time()];
+    t->set_subject_table_grant(table_grant);
+
+    if (t->m_sp)
+      t->m_sp->m_trg_list= dsptch;
+  }
+
+  return false;
+}
+
+
+/**
+  Execute trigger for given (event, time) pair.
+
+  The operation executes trigger for the specified event (insert, update,
+  delete) and time (after, before) if it is set.
+
+  @param thd
+  @param event
+  @param time_type
+  @param old_row_is_record1
+
+  @return Error status.
+    @retval false  on success.
+    @retval true   on error.
+*/
+
+bool Table_trigger_dispatcher::process_triggers(THD *thd,
+                                                trg_event_type event,
+                                                trg_action_time_type time_type,
+                                                bool old_row_is_record1)
+{
+  Trigger *trigger= bodies[event][time_type];
+
+  if (check_for_broken_triggers())
+    return true;
+
+  if (trigger == NULL)
+    return false;
+
+  if (old_row_is_record1)
+  {
+    old_field= record1_field;
+    new_field= trigger_table->field;
+  }
+  else
+  {
+    new_field= record1_field;
+    old_field= trigger_table->field;
+  }
+  /*
+    This trigger must have been processed by the pre-locking
+    algorithm.
+  */
+  DBUG_ASSERT(trigger_table->pos_in_table_list->trg_event_map &
+              static_cast<uint>(1 << static_cast<int>(event)));
+
+  return trigger->execute(thd);
+}
+
+
+/**
+  Add triggers for table to the set of routines used by statement.
+  Add tables used by them to statement table list. Do the same for
+  routines used by triggers.
+
+  @param thd             Thread context.
+  @param prelocking_ctx  Prelocking context of the statement.
+  @param table_list      Table list element for table with trigger.
+
+  @retval false  Success.
+  @retval true   Failure.
+*/
+
+bool
+Table_trigger_dispatcher::
+add_tables_and_routines_for_triggers(THD *thd,
+                                     Query_tables_list *prelocking_ctx,
+                                     TABLE_LIST *table_list)
+{
+  DBUG_ASSERT(static_cast<int>(table_list->lock_type) >=
+              static_cast<int>(TL_WRITE_ALLOW_WRITE));
+
+  for (int i= 0; i < (int)TRG_EVENT_MAX; i++)
+  {
+    if (table_list->trg_event_map &
+        static_cast<uint8>(1 << static_cast<int>(i)))
+    {
+      for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
+      {
+        /* We can have only one trigger per action type currently */
+        Trigger *trigger= table_list->table->triggers->bodies[i][j];
+
+        if (trigger)
+          trigger->add_tables_and_routines(thd, prelocking_ctx, table_list);
+      }
+    }
+  }
+  return FALSE;
+}
+
+
+/**
+  Check if any of the marked fields are used in the trigger.
+
+  @param used_fields  Bitmap over fields to check
+  @param event_type   Type of event triggers for which we are going to inspect
+  @param action_time  Type of trigger action time we are going to inspect
+*/
+
+bool Table_trigger_dispatcher::is_fields_updated_in_trigger(
+    MY_BITMAP *used_fields,
+    trg_event_type event_type,
+    trg_action_time_type action_time)
+{
+  Trigger *trigger= bodies[event_type][action_time];
+  DBUG_ASSERT(used_fields->n_bits == trigger_table->s->fields);
+
+  return trigger->is_fields_updated_in_trigger(used_fields);
+}
+
+
+/**
+  Mark all trigger fields as "temporary nullable" and remember the current
+  THD::count_cuted_fields value.
+
+  @param thd Thread context.
+*/
+void Table_trigger_dispatcher::enable_fields_temporary_nullability(THD* thd)
+{
+  for (Field** next_field= trigger_table->field; *next_field; ++next_field)
+  {
+    (*next_field)->set_tmp_nullable();
+    (*next_field)->set_count_cuted_fields(thd->count_cuted_fields);
+
+    /*
+      For statement LOAD INFILE we set field values during parsing of data file
+      and later run fill_record_n_invoke_before_triggers() to invoke table's
+      triggers. fill_record_n_invoke_before_triggers() calls this method
+      to enable temporary nullability before running trigger's instructions
+      Since for the case of handling statement LOAD INFILE the null value of
+      fields have been already set we don't have to reset these ones here.
+      In case of handling statements INSERT/REPLACE/INSERT SELECT/
+      REPLACE SELECT we set field's values inside method fill_record
+      that is called from fill_record_n_invoke_before_triggers()
+      after the method enable_fields_temporary_nullability has been executed.
+    */
+    if (thd->lex->sql_command != SQLCOM_LOAD)
+      (*next_field)->reset_tmp_null();
+  }
+}
+
+
+/**
+  Reset "temporary nullable" flag from trigger fields.
+*/
+
+void Table_trigger_dispatcher::disable_fields_temporary_nullability()
+{
+  for (Field** next_field= trigger_table->field; *next_field; ++next_field)
+    (*next_field)->reset_tmp_nullable();
+}
+
+
+/**
+  Mark fields of subject table which we read/set in its triggers
+  as such.
+
+  This method marks fields of subject table which are read/set in its
+  triggers as such (by properly updating TABLE::read_set/write_set)
+  and thus informs handler that values for these fields should be
+  retrieved/stored during execution of statement.
+
+  @param event  Type of event triggers for which we are going to inspect
+*/
+
+void Table_trigger_dispatcher::mark_fields_used(trg_event_type event)
+{
+  int action_time;
+
+  for (action_time= 0; action_time < (int)TRG_ACTION_MAX; action_time++)
+  {
+    Trigger *trigger= bodies[event][action_time];
+
+    if (!trigger)
+      continue;
+
+    trigger->mark_field_used(trigger_table);
+  }
+  trigger_table->file->column_bitmaps_signal();
+}
+
+
+/**
+   Signals to the Table_trigger_dispatcher that a parse error has occurred when
+   reading a trigger from file. This makes the Table_trigger_dispatcher enter
+   an error state flagged by m_has_unparseable_trigger == true. The error
+   message will be used whenever a statement invoking or manipulating triggers
+   is issued against the Table_trigger_dispatcher's table.
+
+   @param error_message The error message thrown by the parser.
+*/
+
+void Table_trigger_dispatcher::set_parse_error_message(const char *error_message)
+{
+  if (!m_has_unparseable_trigger)
+  {
+    m_has_unparseable_trigger= true;
+    strlcpy(m_parse_error_message, error_message, sizeof(m_parse_error_message));
+  }
+}
+

=== added file 'sql/table_trigger_dispatcher.h'
--- a/sql/table_trigger_dispatcher.h	1970-01-01 00:00:00 +0000
+++ b/sql/table_trigger_dispatcher.h	2013-01-25 11:48:04 +0000
@@ -0,0 +1,199 @@
+/*
+   Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#ifndef TABLE_TRIGGER_DISPATCHER_H_INCLUDED
+#define TABLE_TRIGGER_DISPATCHER_H_INCLUDED
+
+///////////////////////////////////////////////////////////////////////////
+
+#include "table.h"                              /* GRANT_INFO */
+#include "trigger.h"
+
+#include <mysqld_error.h>
+
+///////////////////////////////////////////////////////////////////////////
+
+struct TABLE_LIST;
+
+class sp_name;
+class Query_tables_list;
+class Trigger;
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  This class holds all information about triggers of table.
+*/
+
+class Table_trigger_dispatcher: public Sql_alloc
+{
+  /** 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]
+    buffer instead of TABLE::record[0] (used for OLD values in on UPDATE
+    trigger and DELETE trigger when it is called for REPLACE).
+  */
+  Field             **record1_field;
+
+  /**
+    During execution of trigger new_field and old_field should point to the
+    array of fields representing new or old version of row correspondingly
+    (so it can point to TABLE::field or to Tale_triggers_list::record1_field)
+  */
+  Field             **new_field;
+  Field             **old_field;
+
+public:
+  /** TABLE instance for which this triggers list object was created. */
+  TABLE *trigger_table;
+
+private:
+  /*
+    List of triggers assigned to this table object.
+  */
+  List<Trigger> table_triggers;
+
+public:
+  /**
+    Grant information for each trigger (pair: subject table, trigger definer).
+  */
+  GRANT_INFO        subject_table_grants[TRG_EVENT_MAX][TRG_ACTION_MAX];
+
+private:
+  /**
+     This flag indicates that one of the triggers was not parsed successfully,
+     and as a precaution the object has entered a state where all trigger
+     access results in errors until all such triggers are dropped. It is not
+     safe to add triggers since we don't know if the broken trigger has the
+     same name or event type. Nor is it safe to invoke any trigger for the
+     aforementioned reasons. The only safe operations are drop_trigger and
+     drop_all_triggers.
+
+     @see Table_trigger_dispatcher::set_parse_error
+   */
+  bool m_has_unparseable_trigger;
+
+  /**
+    This error will be displayed when the user tries to manipulate or invoke
+    triggers on a table that has broken triggers. It will get set only once
+    per statement and thus will contain the first parse error encountered in
+    the trigger file.
+   */
+  char m_parse_error_message[MYSQL_ERRMSG_SIZE];
+
+public:
+  Table_trigger_dispatcher(TABLE *table_arg)
+    :record1_field(0), trigger_table(table_arg),
+     m_has_unparseable_trigger(false)
+  {
+    memset(bodies, 0, sizeof(bodies));
+    memset(&subject_table_grants, 0, sizeof(subject_table_grants));
+  }
+
+  ~Table_trigger_dispatcher();
+
+  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,
+                        trg_action_time_type time_type,
+                        bool old_row_is_record1);
+
+  bool get_trigger_info(THD *thd, trg_event_type event,
+                        trg_action_time_type time_type,
+                        LEX_STRING *trigger_name, LEX_STRING *trigger_stmt,
+                        sql_mode_t *sql_mode,
+                        LEX_STRING *definer,
+                        LEX_STRING *client_cs_name,
+                        LEX_STRING *connection_cl_name,
+                        LEX_STRING *db_cl_name);
+
+  void get_trigger_info(THD *thd,
+                        int trigger_idx,
+                        LEX_STRING *trigger_name,
+                        sql_mode_t *sql_mode,
+                        LEX_STRING *sql_original_stmt,
+                        LEX_STRING *client_cs_name,
+                        LEX_STRING *connection_cl_name,
+                        LEX_STRING *db_cl_name);
+
+  int find_trigger_by_name(const LEX_STRING *trigger_name);
+
+  static bool check_n_load(THD *thd, const char *db, const char *table_name,
+                           TABLE *table, bool names_only);
+  static bool drop_all_triggers(THD *thd, char *db, char *table_name);
+  static bool change_table_name(THD *thd, const char *db,
+                                const char *old_alias,
+                                const char *old_table,
+                                const char *new_db,
+                                const char *new_table);
+  bool has_triggers(trg_event_type event_type, 
+                    trg_action_time_type action_time)
+  {
+    return (bodies[event_type][action_time] != NULL);
+  }
+  bool has_delete_triggers()
+  {
+    return (bodies[TRG_EVENT_DELETE][TRG_ACTION_BEFORE] ||
+            bodies[TRG_EVENT_DELETE][TRG_ACTION_AFTER]);
+  }
+
+  void set_table(TABLE *new_table);
+
+  void mark_fields_used(trg_event_type event);
+
+  void set_parse_error_message(const char *error_message);
+
+  friend class Item_trigger_field;
+
+  bool add_tables_and_routines_for_triggers(THD *thd,
+                                            Query_tables_list *prelocking_ctx,
+                                            TABLE_LIST *table_list);
+  bool is_fields_updated_in_trigger(MY_BITMAP *used_fields,
+                                    trg_event_type event_type,
+                                    trg_action_time_type action_time);
+
+  void enable_fields_temporary_nullability(THD* thd);
+  void disable_fields_temporary_nullability();
+
+private:
+  bool prepare_record1_accessors();
+  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,
+                                     bool upgrading50to51);
+
+  static bool parse_triggers(THD *thd, TABLE *table,
+                             Table_trigger_dispatcher *dsptch,
+                             List<Trigger> &table_trigger_list);
+
+  bool check_for_broken_triggers()
+  {
+    if (m_has_unparseable_trigger)
+    {
+      my_message(ER_PARSE_ERROR, m_parse_error_message, MYF(0));
+      return true;
+    }
+    return false;
+  }
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+#endif // TABLE_TRIGGER_LIST_H_INCLUDED

=== added file 'sql/trigger.cc'
--- a/sql/trigger.cc	1970-01-01 00:00:00 +0000
+++ b/sql/trigger.cc	2013-01-25 11:48:04 +0000
@@ -0,0 +1,476 @@
+/*
+   Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#include "my_global.h"
+#include "sql_class.h"
+#include "trigger.h"
+#include "sp_head.h"
+#include "mysys_err.h"    // EE_OUTOFMEMORY
+#include "trigger_creation_ctx.h"
+#include "sql_parse.h"    // parse_sql()
+#include "sql_lex.h" //Query_tables_list
+#include "table.h" // TABLE_LIST
+#include "sp.h" // sp_update_stmt_used_routines, sp_add_used_routine
+
+
+/**
+  An error handler that catches all non-OOM errors which can occur during
+  parsing of trigger body. Such errors are ignored and corresponding error
+  message is used to construct a more verbose error message which contains
+  name of problematic trigger. This error message is later emitted when
+  one tries to perform DML or some of DDL on this table.
+  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
+{
+private:
+
+  char m_message[MYSQL_ERRMSG_SIZE];
+  LEX_STRING *m_trigger_name;
+
+public:
+
+  Deprecated_trigger_syntax_handler() : m_trigger_name(NULL) {}
+
+  virtual bool handle_condition(THD *thd,
+                                uint sql_errno,
+                                const char* sqlstate,
+                                Sql_condition::enum_severity_level level,
+                                const char* message,
+                                Sql_condition ** cond_hdl)
+  {
+    if (sql_errno != EE_OUTOFMEMORY &&
+        sql_errno != ER_OUT_OF_RESOURCES)
+    {
+      if(thd->lex->spname)
+        m_trigger_name= &thd->lex->spname->m_name;
+      if (m_trigger_name)
+        my_snprintf(m_message, sizeof(m_message),
+                    ER(ER_ERROR_IN_TRIGGER_BODY),
+                    m_trigger_name->str, message);
+      else
+        my_snprintf(m_message, sizeof(m_message),
+                    ER(ER_ERROR_IN_UNKNOWN_TRIGGER_BODY), message);
+      return true;
+    }
+    return false;
+  }
+
+  LEX_STRING *get_trigger_name() { return m_trigger_name; }
+  const char *get_error_message() { return m_message; }
+};
+
+
+/**
+  Initialize an instance of Trigger.
+
+  @param [in] thd               thread handle
+  @param [in] lex               LEX for parsing of trigger
+  @param [in] trigger_name      trigger name
+  @param [in] trg_creation_ctx  creation context of trigger
+  @param [in] db_name           name of schema
+  @param [in] table             pointer to the trigger's table
+
+  @return Operation status
+    @retval true   Failure
+    @retval false  Success
+*/
+
+bool Trigger::init(THD *thd, LEX *lex, LEX_STRING *trigger_name,
+                   Stored_program_creation_ctx *trg_creation_ctx,
+                   const LEX_STRING *db_name, TABLE *table)
+{
+  m_sp= lex->sphead;
+  m_sp->set_info(0, 0, &lex->sp_chistics, m_definition_mode);
+
+  m_trg_event= m_sp->m_trg_chistics.event;
+  m_trg_action_time= m_sp->m_trg_chistics.action_time;
+
+  lex->sphead= NULL; /* Prevent double cleanup. */
+
+  m_sp->set_creation_ctx(trg_creation_ctx);
+  set_trigger_name(trigger_name);
+
+  if (!m_definer->length)
+  {
+    /*
+      This trigger was created/imported from the previous version of
+      MySQL, which does not support triggers definers. We should emit
+      warning here.
+    */
+
+    push_warning_printf(thd, Sql_condition::SL_WARNING,
+                        ER_TRG_NO_DEFINER, ER(ER_TRG_NO_DEFINER),
+                        db_name->str,
+                        m_sp->m_name.str);
+
+    /*
+      Set definer to the '' to correct displaying in the information
+      schema.
+    */
+
+    m_sp->set_definer((char*) "", 0);
+
+    /*
+      Triggers without definer information are executed under the
+      authorization of the invoker.
+    */
+
+    m_sp->m_chistics->suid= SP_IS_NOT_SUID;
+  }
+  else
+    m_sp->set_definer(m_definer->str, m_definer->length);
+
+  if (!(m_on_table_name= alloc_lex_string(&table->mem_root)))
+    return true;
+
+  m_on_table_name->str= (char*) lex->raw_trg_on_table_name_begin;
+  m_on_table_name->length= lex->raw_trg_on_table_name_end -
+                           lex->raw_trg_on_table_name_begin;
+
+  return false;
+}
+
+
+/**
+  Execute trigger's body.
+
+  @param [in] thd               Thread handle
+
+  @return Operation status
+    @retval true   Trigger execution failed or trigger has compilation errors
+    @retval false  Success
+*/
+
+bool Trigger::execute(THD *thd)
+{
+  if (m_has_parse_error)
+    return true;
+
+  bool err_status;
+  Sub_statement_state statement_state;
+  SELECT_LEX *save_current_select;
+
+  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=
+    m_sp->execute_trigger(thd,
+                          m_db_name,
+                          m_table_name,
+                          m_subject_table_grant);
+  thd->lex->current_select= save_current_select;
+
+  thd->restore_sub_statement_state(&statement_state);
+
+  return err_status;
+}
+
+
+/**
+  Get information about trigger.
+
+  @param[out] trg_name            trigger name
+  @param[out] trigger_stmt        returns statement of trigger
+  @param[out] sql_mode            returns sql_mode of trigger
+  @param[out] trg_definer         returns definer/creator of trigger
+  @param[out] trg_definition      returns definition of trigger
+  @param[out] client_cs_nam       returns client character set
+  @param[out] connection_cl_nam   returns connection collation
+  @param[out] db_cl_nam           returns database collation
+*/
+
+void Trigger::get_info(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_name,
+                       LEX_STRING *connection_cl_name,
+                       LEX_STRING *db_cl_name)
+{
+  *trg_name= *m_trigger_name;
+  if (trigger_stmt)
+    *trigger_stmt= m_sp->m_body_utf8;
+  *sql_mode= m_definition_mode;
+
+  if (trg_definer)
+    *trg_definer= *m_definer;
+
+  if (trg_definition)
+    *trg_definition= *m_definition;
+
+  *client_cs_name= *m_client_cs_name;
+  *connection_cl_name= *m_connection_cl_name;
+  *db_cl_name= *m_db_cl_name;
+}
+
+
+/**
+  Parse CREATE TRIGGER statement.
+
+  @param [in] thd               thread handle
+  @param [in] table             pointer to the trigger's table
+
+  @return Operation status
+    @retval true   Failure
+    @retval false  Success
+*/
+
+bool Trigger::parse_trigger_body(THD *thd, TABLE *table)
+{
+  LEX *old_lex= thd->lex, lex;
+  sp_rcontext *sp_runtime_ctx_saved= thd->sp_runtime_ctx;
+  sql_mode_t sql_mode_saved= thd->variables.sql_mode;
+  PSI_statement_locker *parent_locker= thd->m_statement_psi;
+  LEX_STRING *trigger_name= NULL;
+  bool result= false;
+
+  thd->variables.sql_mode= m_definition_mode;
+
+  Parser_state parser_state;
+  if (parser_state.init(thd, m_definition->str, m_definition->length))
+  {
+    thd->variables.sql_mode= sql_mode_saved;
+    return true;
+  }
+
+  thd->lex= &lex;
+
+  LEX_STRING current_db_name_saved= {thd->db, thd->db_length};
+  thd->reset_db(m_db_name->str, m_db_name->length);
+
+  Trigger_creation_ctx *creation_ctx=
+      Trigger_creation_ctx::create(thd,
+                                   m_db_name->str,
+                                   m_table_name->str,
+                                   m_client_cs_name,
+                                   m_connection_cl_name,
+                                   m_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();
+
+  const LEX_STRING *trigger_name_ptr= NULL;
+
+  if (parse_error)
+  {
+    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())
+    {
+      result= true;
+      goto cleanup;
+    }
+
+    trigger_name_ptr= error_handler.get_trigger_name();
+  }
+  else
+  {
+    trigger_name_ptr= &lex.spname->m_name;
+  }
+
+  // Make a copy of trigger name.
+
+  trigger_name= lex_string_dup(&table->mem_root, trigger_name_ptr);
+
+  if (!trigger_name)
+  {
+    result= true;
+    goto cleanup;
+  }
+
+  if (!parse_error)
+  {
+    if (init(thd, &lex, trigger_name, creation_ctx, m_db_name, table))
+    {
+      result= true;
+      goto cleanup;
+    }
+  }
+  else
+    set_trigger_name(trigger_name);
+
+#ifndef DBUG_OFF
+  if (!parse_error)
+  {
+    /*
+      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, m_db_name) ||
+                 (check_n_cut_mysql50_prefix(m_db_name, 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, m_table_name) ||
+                 (check_n_cut_mysql50_prefix(m_table_name, fname, sizeof(fname)) &&
+                  !my_strcasecmp(table_alias_charset, lex.query_tables->table_name, fname))));
+  }
+#endif
+
+cleanup:
+  lex_end(&lex);
+  thd->reset_db(current_db_name_saved.str, current_db_name_saved.length);
+  thd->lex= old_lex;
+  thd->sp_runtime_ctx= sp_runtime_ctx_saved;
+  thd->variables.sql_mode= sql_mode_saved;
+
+  DBUG_RETURN(result);
+}
+
+
+/**
+  Setup table fields referenced from trigger.
+
+  @param [in] thd               thread handle
+  @param [in] table             pointer to the trigger's table
+*/
+
+void Trigger::setup_fields(THD *thd, TABLE *table)
+{
+  if (!m_sp)
+    return;
+
+  /*
+    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= m_sp->m_trg_table_fields.first;
+       trg_field;
+       trg_field= trg_field->next_trg_field)
+  {
+    trg_field->setup_field(thd, table, m_subject_table_grant);
+  }
+}
+
+
+/**
+  Add tables and routines used by trigger to the set of elements
+  used by statement.
+
+  @param [in]     thd               thread handle
+  @param [in out] prelocking_ctx    prelocking context of the statement
+  @param [in]     table_list        TABLE_LIST for the table
+*/
+
+void Trigger::add_tables_and_routines(THD *thd,
+                                      Query_tables_list *prelocking_ctx,
+                                      TABLE_LIST *table_list)
+{
+  MDL_key key(MDL_key::TRIGGER, m_sp->m_db.str, m_sp->m_name.str);
+
+  if (sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
+                          &key, table_list->belong_to_view))
+  {
+    m_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,
+                                  &m_sp->m_sroutines,
+                                  table_list->belong_to_view);
+    m_sp->propagate_attributes(prelocking_ctx);
+  }
+}
+
+
+/**
+  Check whether any table's fields are used in trigger.
+
+  @param [in] used_fields       bitmap of fields to check
+
+  @return Check result
+    @retval true   Some table fields are used in trigger
+    @retval false  None of table fields are used in trigger
+*/
+
+bool Trigger::is_fields_updated_in_trigger(const MY_BITMAP *used_fields) const
+{
+  Item_trigger_field *trg_field;
+  for (trg_field= m_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. */
+    if (trg_field->field_idx != (uint)-1)
+    {
+      if (bitmap_is_set(used_fields, trg_field->field_idx) &&
+          trg_field->get_settable_routine_parameter())
+        return true;
+    }
+  }
+  return false;
+}
+
+/**
+  Mark fields of subject table which we read/set in the trigger
+
+  @param [in] trigger_table    pointer to the trigger's table
+*/
+
+void Trigger::mark_field_used(TABLE *trigger_table)
+{
+  Item_trigger_field *trg_field;
+
+  for (trg_field= m_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. */
+    if (trg_field->field_idx != (uint)-1)
+    {
+      bitmap_set_bit(trigger_table->read_set, trg_field->field_idx);
+      if (trg_field->get_settable_routine_parameter())
+        bitmap_set_bit(trigger_table->write_set, trg_field->field_idx);
+    }
+  }
+}

=== added file 'sql/trigger.h'
--- a/sql/trigger.h	1970-01-01 00:00:00 +0000
+++ b/sql/trigger.h	2013-01-25 11:48:04 +0000
@@ -0,0 +1,211 @@
+/*
+   Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#ifndef TRIGGER_H_INCLUDED
+#define TRIGGER_H_INCLUDED
+
+///////////////////////////////////////////////////////////////////////////
+
+struct GRANT_INFO;
+
+class sp_head;
+class Stored_program_creation_ctx;
+
+/** Event on which trigger is invoked. */
+enum trg_event_type
+{
+  TRG_EVENT_INSERT= 0,
+  TRG_EVENT_UPDATE= 1,
+  TRG_EVENT_DELETE= 2,
+  TRG_EVENT_MAX
+};
+
+/*
+  We need this two enums here instead of sql_lex.h because
+  at least one of them is used by Item_trigger_field interface.
+
+  Time when trigger is invoked (i.e. before or after row actually
+  inserted/updated/deleted).
+*/
+enum trg_action_time_type
+{
+  TRG_ACTION_BEFORE= 0,
+  TRG_ACTION_AFTER= 1,
+  TRG_ACTION_MAX
+};
+
+#include "my_global.h"
+#include "sql_alloc.h"
+
+class Query_tables_list;
+
+// FIXME: remove it.
+typedef ulonglong sql_mode_t;
+
+
+/**
+  This is a class that represents a trigger entity.
+  Trigger can be created, initialized, parsed and executed.
+*/
+class Trigger : public Sql_alloc
+{
+public:
+  Trigger(LEX_STRING *db_name, LEX_STRING *table_name,
+          LEX_STRING *trg_create_str, sql_mode_t trg_sql_mode,
+          LEX_STRING *trg_definer, LEX_STRING *client_cs_name,
+          LEX_STRING *connection_cl_name, LEX_STRING *db_cl_name)
+  : m_has_parse_error(false),
+    m_trigger_name(NULL), m_db_name(db_name), m_table_name(table_name),
+    m_on_table_name(NULL), m_subject_table_grant(NULL),
+    m_definition(trg_create_str), m_definition_mode(trg_sql_mode),
+    m_definer(trg_definer), m_client_cs_name(client_cs_name),
+    m_connection_cl_name(connection_cl_name), m_db_cl_name(db_cl_name),
+    m_sp(NULL), m_trg_action_time(TRG_ACTION_MAX), m_trg_event(TRG_EVENT_MAX)
+  {}
+
+  void set_has_parse_error()
+  {
+    m_has_parse_error= true;
+  }
+
+  bool init(THD *thd, LEX *lex, LEX_STRING *trg_name,
+            Stored_program_creation_ctx *trg_creation_ctx,
+            const LEX_STRING *db_name, TABLE *table);
+
+  bool execute(THD *thd);
+
+  void get_info(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 parse_trigger_body(THD *thd, TABLE *table);
+
+  void set_parse_error_message(const char *error_message)
+  {
+    m_has_parse_error= true;
+    strlcpy(m_parse_error_message, error_message,
+            sizeof(m_parse_error_message));
+  }
+
+  void set_trigger_name(LEX_STRING *trigger_name)
+  {
+    m_trigger_name= trigger_name;
+  }
+
+  bool has_parse_error() const
+  {
+    return m_has_parse_error;
+  }
+
+  const char* get_parse_error_message() const
+  {
+    return m_parse_error_message;
+  }
+
+  void setup_fields(THD *thd, TABLE *table);
+
+  void add_tables_and_routines(THD *thd,
+                               Query_tables_list *prelocking_ctx,
+                               TABLE_LIST *table_list);
+
+  bool is_fields_updated_in_trigger(const MY_BITMAP *used_fields) const;
+
+  void mark_field_used(TABLE *trigger_table);
+
+  void set_subject_table_grant(GRANT_INFO *table_grant)
+  {
+    m_subject_table_grant= table_grant;
+  }
+
+  trg_action_time_type get_trg_action_time() const
+  {
+    return m_trg_action_time;
+  }
+
+  trg_event_type get_trg_event() const
+  {
+    return m_trg_event;
+  }
+
+private:
+  bool m_has_parse_error;
+
+  /*
+    This error will be displayed when the user tries to manipulate or invoke
+    triggers on a table that has broken triggers. It will get set only once
+    per statement and thus will contain the first parse error encountered in
+    the trigger file.
+   */
+  char m_parse_error_message[MYSQL_ERRMSG_SIZE];
+
+public:
+  /*
+    Names of trigger.
+  */
+  LEX_STRING *m_trigger_name;
+
+private:
+  LEX_STRING *m_db_name;
+  LEX_STRING *m_table_name;
+
+public:
+  /**
+    "ON table_name" part in trigger definition, used for
+    updating trigger definition during RENAME TABLE.
+  */
+  LEX_STRING *m_on_table_name;
+
+private:
+  /**
+    Grant information for the trigger.
+  */
+  GRANT_INFO *m_subject_table_grant;
+
+public:
+  /*
+    Trigger definition to save it in file.
+  */
+  LEX_STRING *m_definition;
+
+  /*
+    sql mode for trigger
+  */
+  sql_mode_t m_definition_mode;
+
+  LEX_STRING *m_definer;
+
+  /*
+    Character set context, used for parsing and executing trigger.
+  */
+  LEX_STRING *m_client_cs_name;
+  LEX_STRING *m_connection_cl_name;
+  LEX_STRING *m_db_cl_name;
+  sp_head *m_sp;
+
+private:
+  trg_action_time_type m_trg_action_time;
+  trg_event_type m_trg_event;
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+#endif // TRIGGER_H_INCLUDED
+

=== added file 'sql/trigger_creation_ctx.cc'
--- a/sql/trigger_creation_ctx.cc	1970-01-01 00:00:00 +0000
+++ b/sql/trigger_creation_ctx.cc	2013-01-22 20:38:40 +0000
@@ -0,0 +1,75 @@
+#include "my_global.h"
+#include "trigger_creation_ctx.h"
+#include "sql_db.h" // get_default_db_collation()
+
+Trigger_creation_ctx *
+Trigger_creation_ctx::create(THD *thd,
+                             const char *db_name,
+                             const char *table_name,
+                             const LEX_STRING *client_cs_name,
+                             const LEX_STRING *connection_cl_name,
+                             const LEX_STRING *db_cl_name)
+{
+  const CHARSET_INFO *client_cs;
+  const CHARSET_INFO *connection_cl;
+  const CHARSET_INFO *db_cl;
+
+  bool invalid_creation_ctx= FALSE;
+
+  if (resolve_charset(client_cs_name->str,
+                      thd->variables.character_set_client,
+                      &client_cs))
+  {
+    sql_print_warning("Trigger for table '%s'.'%s': "
+                      "invalid character_set_client value (%s).",
+                      (const char *) db_name,
+                      (const char *) table_name,
+                      (const char *) client_cs_name->str);
+
+    invalid_creation_ctx= TRUE;
+  }
+
+  if (resolve_collation(connection_cl_name->str,
+                        thd->variables.collation_connection,
+                        &connection_cl))
+  {
+    sql_print_warning("Trigger for table '%s'.'%s': "
+                      "invalid collation_connection value (%s).",
+                      (const char *) db_name,
+                      (const char *) table_name,
+                      (const char *) connection_cl_name->str);
+
+    invalid_creation_ctx= TRUE;
+  }
+
+  if (resolve_collation(db_cl_name->str, NULL, &db_cl))
+  {
+    sql_print_warning("Trigger for table '%s'.'%s': "
+                      "invalid database_collation value (%s).",
+                      (const char *) db_name,
+                      (const char *) table_name,
+                      (const char *) db_cl_name->str);
+
+    invalid_creation_ctx= TRUE;
+  }
+
+  if (invalid_creation_ctx)
+  {
+    push_warning_printf(thd,
+                        Sql_condition::SL_WARNING,
+                        ER_TRG_INVALID_CREATION_CTX,
+                        ER(ER_TRG_INVALID_CREATION_CTX),
+                        (const char *) db_name,
+                        (const char *) table_name);
+  }
+
+  /*
+    If we failed to resolve the database collation, load the default one
+    from the disk.
+  */
+
+  if (!db_cl)
+    db_cl= get_default_db_collation(thd, db_name);
+
+  return new Trigger_creation_ctx(client_cs, connection_cl, db_cl);
+}

=== added file 'sql/trigger_creation_ctx.h'
--- a/sql/trigger_creation_ctx.h	1970-01-01 00:00:00 +0000
+++ b/sql/trigger_creation_ctx.h	2013-01-22 20:38:40 +0000
@@ -0,0 +1,67 @@
+/*
+   Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#ifndef TRIGGER_CREATION_CTX_H_INCLUDED
+#define TRIGGER_CREATION_CTX_H_INCLUDED
+
+///////////////////////////////////////////////////////////////////////////
+
+#include "sp_head.h" // Stored_program_creation_ctx
+
+/**
+  Trigger_creation_ctx -- creation context of triggers.
+*/
+
+class Trigger_creation_ctx : public Stored_program_creation_ctx,
+                             public Sql_alloc
+{
+public:
+  static Trigger_creation_ctx *create(THD *thd,
+                                      const char *db_name,
+                                      const char *table_name,
+                                      const LEX_STRING *client_cs_name,
+                                      const LEX_STRING *connection_cl_name,
+                                      const LEX_STRING *db_cl_name);
+
+public:
+  virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root)
+  {
+    return new (mem_root) Trigger_creation_ctx(m_client_cs,
+                                               m_connection_cl,
+                                               m_db_cl);
+  }
+
+protected:
+  virtual Object_creation_ctx *create_backup_ctx(THD *thd) const
+  {
+    return new Trigger_creation_ctx(thd);
+  }
+
+private:
+  Trigger_creation_ctx(THD *thd)
+    :Stored_program_creation_ctx(thd)
+  { }
+
+  Trigger_creation_ctx(const CHARSET_INFO *client_cs,
+                       const CHARSET_INFO *connection_cl,
+                       const CHARSET_INFO *db_cl)
+    :Stored_program_creation_ctx(client_cs, connection_cl, db_cl)
+  { }
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+#endif // TRIGGER_CREATION_CTX_H_INCLUDED

=== added file 'sql/trigger_loader.cc'
--- a/sql/trigger_loader.cc	1970-01-01 00:00:00 +0000
+++ b/sql/trigger_loader.cc	2013-01-25 11:48:04 +0000
@@ -0,0 +1,1078 @@
+/*
+   Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#include "my_global.h"
+#include "trigger_loader.h"
+#include "sql_class.h"
+#include "sp_head.h"      // sp_name
+#include "sql_base.h"     // is_equal(LEX_STRING, LEX_STRING)
+#include "sql_table.h"    // build_table_filename()
+#include <mysys_err.h>    // EE_OUTOFMEMORY
+
+///////////////////////////////////////////////////////////////////////////
+
+/*
+  Structure representing contents of .TRN file which are used to support
+  database wide trigger namespace.
+*/
+
+struct Trigger_name
+{
+  LEX_STRING trigger_table;
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  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).
+*/
+
+File_option Trigger_loader::trg_file_parameters[]=
+{
+  {
+    { C_STRING_WITH_LEN("triggers") },
+    my_offsetof(class Trigger_loader, definitions_list),
+    FILE_OPTIONS_STRLIST
+  },
+  {
+    { C_STRING_WITH_LEN("sql_modes") },
+    my_offsetof(class Trigger_loader, definition_modes_list),
+    FILE_OPTIONS_ULLLIST
+  },
+  {
+    { C_STRING_WITH_LEN("definers") },
+    my_offsetof(class Trigger_loader, definers_list),
+    FILE_OPTIONS_STRLIST
+  },
+  {
+    { C_STRING_WITH_LEN("client_cs_names") },
+    my_offsetof(class Trigger_loader, client_cs_names),
+    FILE_OPTIONS_STRLIST
+  },
+  {
+    { C_STRING_WITH_LEN("connection_cl_names") },
+    my_offsetof(class Trigger_loader, connection_cl_names),
+    FILE_OPTIONS_STRLIST
+  },
+  {
+    { C_STRING_WITH_LEN("db_cl_names") },
+    my_offsetof(class Trigger_loader, db_cl_names),
+    FILE_OPTIONS_STRLIST
+  },
+  { { 0, 0 }, 0, FILE_OPTIONS_STRING }
+};
+
+File_option Trigger_loader::trn_file_parameters[]=
+{
+  {
+    { C_STRING_WITH_LEN("trigger_table")},
+    offsetof(struct Trigger_name, trigger_table),
+    FILE_OPTIONS_ESTRING
+  },
+  { { 0, 0 }, 0, FILE_OPTIONS_STRING }
+};
+
+File_option sql_modes_parameters=
+{
+  { C_STRING_WITH_LEN("sql_modes") },
+  my_offsetof(class Trigger_loader, definition_modes_list),
+  FILE_OPTIONS_ULLLIST
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+const LEX_STRING Trigger_loader::trg_file_type=
+  { C_STRING_WITH_LEN("TRIGGERS") };
+
+const LEX_STRING Trigger_loader::trn_file_type=
+  { C_STRING_WITH_LEN("TRIGGERNAME") };
+
+///////////////////////////////////////////////////////////////////////////
+
+class Handle_old_incorrect_sql_modes_hook: public Unknown_key_hook
+{
+private:
+  char *path;
+public:
+  Handle_old_incorrect_sql_modes_hook(char *file_path)
+    :path(file_path)
+  {};
+  virtual bool process_unknown_string(const char *&unknown_key, uchar* base,
+                                      MEM_ROOT *mem_root, const char *end);
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+class Handle_old_incorrect_trigger_table_hook: public Unknown_key_hook
+{
+public:
+  Handle_old_incorrect_trigger_table_hook(char *file_path,
+                                          LEX_STRING *trigger_table_arg)
+    :path(file_path), trigger_table_value(trigger_table_arg)
+  {};
+  virtual bool process_unknown_string(const char *&unknown_key, uchar* base,
+                                      MEM_ROOT *mem_root, const char *end);
+private:
+  char *path;
+  LEX_STRING *trigger_table_value;
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Trigger BUG#14090 compatibility hook.
+
+  @param[in,out] unknown_key       reference on the line with unknown
+    parameter and the parsing point
+  @param[in]     base              base address for parameter writing
+    (structure like TABLE)
+  @param[in]     mem_root          MEM_ROOT for parameters allocation
+  @param[in]     end               the end of the configuration
+
+  @note
+    NOTE: this hook process back compatibility for incorrectly written
+    sql_modes parameter (see BUG#14090).
+
+  @retval
+    false OK
+  @retval
+    true  Error
+*/
+
+#define INVALID_SQL_MODES_LENGTH 13
+
+bool
+Handle_old_incorrect_sql_modes_hook::
+process_unknown_string(const char *&unknown_key, uchar* base,
+                       MEM_ROOT *mem_root, const char *end)
+{
+  DBUG_ENTER("Handle_old_incorrect_sql_modes_hook::process_unknown_string");
+  DBUG_PRINT("info", ("unknown key: %60s", unknown_key));
+
+  if (unknown_key + INVALID_SQL_MODES_LENGTH + 1 < end &&
+      unknown_key[INVALID_SQL_MODES_LENGTH] == '=' &&
+      !memcmp(unknown_key, STRING_WITH_LEN("sql_modes")))
+  {
+    const char *ptr= unknown_key + INVALID_SQL_MODES_LENGTH + 1;
+
+    DBUG_PRINT("info", ("sql_modes affected by BUG#14090 detected"));
+    push_warning_printf(current_thd,
+                        Sql_condition::SL_NOTE,
+                        ER_OLD_FILE_FORMAT,
+                        ER(ER_OLD_FILE_FORMAT),
+                        (char *)path, "TRIGGER");
+    if (get_file_options_ulllist(ptr, end, unknown_key, base,
+                                 &sql_modes_parameters, mem_root))
+    {
+      DBUG_RETURN(true);
+    }
+    /*
+      Set parsing pointer to the last symbol of string (\n)
+      1) to avoid problem with \0 in the junk after sql_modes
+      2) to speed up skipping this line by parser.
+    */
+    unknown_key= ptr-1;
+  }
+  DBUG_RETURN(false);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+#define INVALID_TRIGGER_TABLE_LENGTH 15
+
+/**
+  Trigger BUG#15921 compatibility hook. For details see
+  Handle_old_incorrect_sql_modes_hook::process_unknown_string().
+*/
+
+bool
+Handle_old_incorrect_trigger_table_hook::
+process_unknown_string(const char *&unknown_key, uchar* base,
+                       MEM_ROOT *mem_root, const char *end)
+{
+  DBUG_ENTER("Handle_old_incorrect_trigger_table_hook::process_unknown_string");
+  DBUG_PRINT("info", ("unknown key: %60s", unknown_key));
+
+  if (unknown_key + INVALID_TRIGGER_TABLE_LENGTH + 1 < end &&
+      unknown_key[INVALID_TRIGGER_TABLE_LENGTH] == '=' &&
+      !memcmp(unknown_key, STRING_WITH_LEN("trigger_table")))
+  {
+    const char *ptr= unknown_key + INVALID_TRIGGER_TABLE_LENGTH + 1;
+
+    DBUG_PRINT("info", ("trigger_table affected by BUG#15921 detected"));
+    push_warning_printf(current_thd,
+                        Sql_condition::SL_NOTE,
+                        ER_OLD_FILE_FORMAT,
+                        ER(ER_OLD_FILE_FORMAT),
+                        (char *)path, "TRIGGER");
+
+    if (!(ptr= parse_escaped_string(ptr, end, mem_root, trigger_table_value)))
+    {
+      my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0), "trigger_table",
+               unknown_key);
+      DBUG_RETURN(true);
+    }
+
+    /* Set parsing pointer to the last symbol of string (\n). */
+    unknown_key= ptr-1;
+  }
+  DBUG_RETURN(false);
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  An error handler that catches all non-OOM errors which can occur during
+  parsing of trigger body. Such errors are ignored and corresponding error
+  message is used to construct a more verbose error message which contains
+  name of problematic trigger. This error message is later emitted when
+  one tries to perform DML or some of DDL on this table.
+  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
+{
+private:
+
+  char m_message[MYSQL_ERRMSG_SIZE];
+  LEX_STRING *m_trigger_name;
+
+public:
+
+  Deprecated_trigger_syntax_handler() : m_trigger_name(NULL) {}
+
+  virtual bool handle_condition(THD *thd,
+                                uint sql_errno,
+                                const char* sqlstate,
+                                Sql_condition::enum_severity_level level,
+                                const char* message,
+                                Sql_condition ** cond_hdl)
+  {
+    if (sql_errno != EE_OUTOFMEMORY &&
+        sql_errno != ER_OUT_OF_RESOURCES)
+    {
+      if(thd->lex->spname)
+        m_trigger_name= &thd->lex->spname->m_name;
+      if (m_trigger_name)
+        my_snprintf(m_message, sizeof(m_message),
+                    ER(ER_ERROR_IN_TRIGGER_BODY),
+                    m_trigger_name->str, message);
+      else
+        my_snprintf(m_message, sizeof(m_message),
+                    ER(ER_ERROR_IN_UNKNOWN_TRIGGER_BODY), message);
+      return true;
+    }
+    return false;
+  }
+
+  LEX_STRING *get_trigger_name() { return m_trigger_name; }
+  char *get_error_message() { return m_message; }
+};
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+
+/**
+  Load table triggers from the data dictionary.
+
+  @param [in]  thd                  thread handle
+  @param [in]  db_name              name of schema
+  @param [in]  db_name              name of table
+  @param [in]  table                pointer to the trigger's table
+  @param [out] table_trigger_list   pointer to the list where new Trigger
+                                    objects will be inserted
+  @param [out] trigger_not_found    true if trigger was not found by name,
+                                    else false
+
+  @return Operation status
+    @retval true   Failure
+    @retval false  Success
+*/
+
+bool Trigger_loader::load_triggers(THD *thd,
+                                   const char *db_name,
+                                   const char *table_name,
+                                   TABLE *table,
+                                   List<Trigger> *table_trigger_lst,
+                                   bool *trigger_not_found)
+{
+  Trigger_loader l;
+
+  return
+    l.load_triggers_impl(thd, db_name, table_name, table, trigger_not_found,
+                         table_trigger_lst);
+}
+
+
+/**
+  Implementation for loading table triggers from the data dictionary.
+
+  @param [in]  thd                  thread handle
+  @param [in]  db_name              name of schema
+  @param [in]  table_name           name of table
+  @param [in]  table                pointer to the trigger's table
+  @param [out] trigger_not_found    true if trigger was not found by name,
+                                    else false
+  @param [out] table_trigger_list   pointer to the list where new Trigger
+                                    objects will be inserted
+
+  @return Operation status
+    @retval true   Failure
+    @retval false  Success
+*/
+
+bool Trigger_loader::load_triggers_impl(THD *thd,
+                                        const char *db_name,
+                                        const char *table_name,
+                                        TABLE *table,
+                                        bool *triggers_not_found,
+                                        List<Trigger> *table_trigger_list)
+{
+  DBUG_ENTER("Trigger_loader::load_triggers_impl");
+
+  // Check that TRG-file exists.
+
+  char trg_file_path_buffer[FN_REFLEN];
+  LEX_STRING trg_file_path;
+
+  trg_file_path.length= build_table_filename(trg_file_path_buffer,
+                                             FN_REFLEN - 1,
+                                             db_name, table_name, TRG_EXT, 0);
+  trg_file_path.str= trg_file_path_buffer;
+
+  *triggers_not_found= false;
+
+  if (access(trg_file_path_buffer, F_OK))
+  {
+    if (errno == ENOENT)
+      *triggers_not_found= true;
+
+    DBUG_RETURN(true);
+  }
+
+  // The TRG-file exists so we got to load triggers.
+
+  File_parser *parser=
+    sql_parse_prepare(&trg_file_path, &table->mem_root, true);
+
+  if (!parser)
+    DBUG_RETURN(true);
+
+  if (!is_equal(&trg_file_type, parser->type()))
+  {
+    my_error(ER_WRONG_OBJECT, MYF(0), table_name, TRG_EXT + 1, "TRIGGER");
+    DBUG_RETURN(true);
+  }
+
+  Handle_old_incorrect_sql_modes_hook sql_modes_hook(trg_file_path.str);
+
+  if (parser->parse((uchar*)this, &table->mem_root,
+                    trg_file_parameters,
+                    TRG_NUM_REQUIRED_PARAMETERS,
+                    &sql_modes_hook))
+    DBUG_RETURN(true);
+
+  if (definitions_list.is_empty())
+  {
+    DBUG_ASSERT(definition_modes_list.is_empty());
+    DBUG_ASSERT(definers_list.is_empty());
+    DBUG_ASSERT(client_cs_names.is_empty());
+    DBUG_ASSERT(connection_cl_names.is_empty());
+    DBUG_ASSERT(db_cl_names.is_empty());
+    DBUG_RETURN(false);
+  }
+
+  List_iterator_fast<LEX_STRING> it(definitions_list);
+
+  // Make sure sql_mode list is filled.
+
+  if (definition_modes_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.
+    */
+    sql_mode_t *sql_mode= alloc_type<sql_mode_t>(&table->mem_root);
+
+    if (!sql_mode)
+      DBUG_RETURN(true); // EOM
+
+    *sql_mode= global_system_variables.sql_mode;
+
+    while (it++)
+    {
+      if (definition_modes_list.push_back(sql_mode, &table->mem_root))
+        DBUG_RETURN(true); // EOM
+    }
+
+    it.rewind();
+  }
+
+  // Make sure definer list is filled.
+
+  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".
+    */
+
+    LEX_STRING *definer= alloc_lex_string(&table->mem_root);
+
+    if (!definer)
+      DBUG_RETURN(true); // EOM
+
+    definer->str= (char*) "";
+    definer->length= 0;
+
+    while (it++)
+    {
+      if (definers_list.push_back(definer, &table->mem_root))
+        DBUG_RETURN(true); // EOM
+    }
+
+    it.rewind();
+  }
+
+  // Make sure character set properties are filled.
+
+  if (client_cs_names.is_empty() ||
+      connection_cl_names.is_empty() ||
+      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_name,
+               (const char *) table_name);
+
+      DBUG_RETURN(true);
+    }
+
+    push_warning_printf(thd, Sql_condition::SL_WARNING,
+                        ER_TRG_NO_CREATION_CTX,
+                        ER(ER_TRG_NO_CREATION_CTX),
+                        (const char*) db_name,
+                        (const char*) table_name);
+
+    LEX_STRING *trg_client_cs_name= alloc_lex_string(&table->mem_root);
+    LEX_STRING *trg_connection_cl_name= alloc_lex_string(&table->mem_root);
+    LEX_STRING *trg_db_cl_name= alloc_lex_string(&table->mem_root);
+
+    if (!trg_client_cs_name || !trg_connection_cl_name || !trg_db_cl_name)
+      DBUG_RETURN(true); // EOM
+
+    /*
+      Backward compatibility: assume that the query is in the current
+      character set.
+    */
+
+    lex_string_set(trg_client_cs_name,
+                   thd->variables.character_set_client->csname);
+
+    lex_string_set(trg_connection_cl_name,
+                   thd->variables.collation_connection->name);
+
+    lex_string_set(trg_db_cl_name,
+                   thd->variables.collation_database->name);
+
+    while (it++)
+    {
+      if (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);
+
+  LEX_STRING *current_db_name= alloc_lex_string(&table->mem_root);
+  lex_string_set(current_db_name, db_name);
+
+  LEX_STRING *table_name_str= alloc_lex_string(&table->mem_root);
+  lex_string_set(table_name_str, table_name);
+
+  LEX_STRING *trg_create_str;
+
+  List_iterator_fast<sql_mode_t> itm(definition_modes_list);
+  List_iterator_fast<LEX_STRING> it_definer(definers_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);
+
+  while ((trg_create_str= it++))
+  {
+    sql_mode_t *sql_mode= itm++;
+    LEX_STRING *definer= it_definer++;
+    LEX_STRING *client_cs_name= it_client_cs_name++;
+    LEX_STRING *connection_cl_name= it_connection_cl_name++;
+    LEX_STRING *db_cl_name= it_db_cl_name++;
+
+    // Create an new trigger instance
+
+    Trigger* trigger=
+        new (&table->mem_root) Trigger(current_db_name,
+                                       table_name_str, trg_create_str,
+                                       *sql_mode, definer,
+                                       client_cs_name, connection_cl_name,
+                                       db_cl_name);
+
+    if (table_trigger_list->push_back(trigger, &table->mem_root))
+    {
+      delete trigger;
+      DBUG_RETURN(true);
+    }
+  }
+
+  DBUG_RETURN(false);
+}
+
+
+/**
+  Store a table trigger into the data dictionary.
+
+  @param [in]  tables             pointer to trigger's table
+  @param [in]  new_trigger        trigger to save
+  @param [in]  table_triggers     pointer to the list where new trigger object
+                                  has to be added
+
+  @return Operation status
+    @retval true   Failure
+    @retval false  Success
+*/
+
+bool Trigger_loader::store_trigger(TABLE_LIST *tables,
+                                   Trigger *new_trigger,
+                                   List<Trigger> *table_triggers)
+{
+  LEX_STRING trn_file_name;
+  TABLE *table= tables->table;
+  char trigger_name_buffer[FN_REFLEN];
+  Trigger_name trigger_name;
+  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
+  */
+  trigger_name.trigger_table.str= tables->table_name;
+  trigger_name.trigger_table.length= tables->table_name_length;
+
+  /* Building .TRN trigger filenames */
+  trn_file_name.length= build_table_filename(trigger_name_buffer, FN_REFLEN-1,
+                                             tables->db,
+                                             new_trigger->m_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(trigger_name_buffer)-1,
+             trigger_name_buffer);
+    return true;
+  }
+  trn_file_name.str= trigger_name_buffer;
+
+  if (sql_create_definition_file(NULL, &trn_file_name, &trn_file_type,
+                                 (uchar*)&trigger_name, trn_file_parameters))
+    return true;
+
+  /* Create trigger definition file. */
+  if (save_trigger_file(tables->db, tables->table_name))
+  {
+    mysql_file_delete(key_file_trn, trigger_name_buffer, MYF(MY_WME));
+    return true;
+  }
+
+  return false;
+}
+
+
+/**
+  Store a table trigger into the data dictionary.
+
+  @param [in]  tables             pointer to trigger's table
+  @param [in]  new_trigger        trigger to save
+  @param [in]  table_triggers     pointer to the list where new trigger object
+                                  has to be added
+
+  @return Operation status
+    @retval true   Failure
+    @retval false  Success
+*/
+
+bool Trigger_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 db          name of database for subject table
+  @param table_name  name of subject table
+
+  @retval
+    false  Success
+  @retval
+    true   Error
+*/
+
+bool Trigger_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, &trg_file_type,
+                                    (uchar*)this, trg_file_parameters);
+}
+
+
+/**
+  Update trigger informations in internal structures.
+  This internal structure is used later to save trigger definitions
+  in the data dictionary.
+
+  @param [in]     table        pointer to the TABLE that triggers belong
+  @param [in out] trgs         list of triggers to prepare for storing
+                               in the data dictionary. On return those triggers
+                               that have unparseable SQL-definition will be
+                               removed from the list
+  @param [in]     name         name of trigger whose definition has to be
+                               removed
+  @param [out]    found        pointer to the variable where the result of
+                               trigger removing will be stored. found has to be
+                               set to the false before passing to this method
+
+
+  @return Operation status
+    @retval false On success.
+    @retval true  Otherwise.
+*/
+
+bool Trigger_loader::update_triggers_definition(TABLE *table,
+                                                List<Trigger> *trgs,
+                                                const char *name,
+                                                bool *found)
+{
+  List_iterator<Trigger> it_table_triggers(*trgs);
+
+  Trigger *next_trigger;
+  while ((next_trigger= it_table_triggers++))
+  {
+    if (name &&
+        my_strcasecmp(table_alias_charset, next_trigger->m_trigger_name->str,
+                      name) == 0)
+    {
+      it_table_triggers.remove();
+      *found= true;
+      continue;
+    }
+    if (definitions_list.push_back(next_trigger->m_definition,
+                                   &table->mem_root) ||
+        definition_modes_list.push_back(&next_trigger->m_definition_mode,
+                                    &table->mem_root) ||
+        definers_list.push_back(next_trigger->m_definer,
+                                &table->mem_root) ||
+        client_cs_names.push_back(next_trigger->m_client_cs_name,
+                                  &table->mem_root) ||
+        connection_cl_names.push_back(next_trigger->m_connection_cl_name,
+                                      &table->mem_root) ||
+        db_cl_names.push_back(next_trigger->m_db_cl_name, &table->mem_root))
+      return true;
+  }
+
+  return false;
+}
+
+
+/**
+  Check whether the trigger specified by schema and name does exist
+  in the data dictionary.
+
+  @param [in]  db_name              name of schema
+  @param [in]  trigger_name         name of trigger
+
+  @return Operation status
+    @retval true   Trigger exists. Set error in Diagnostic_area as
+                   a side effect
+    @retval false  Trigger doesn't exist
+*/
+
+bool Trigger_loader::check_for_uniqueness(const char *db_name,
+                                          const char *trigger_name)
+{
+  char trigger_name_buffer[FN_REFLEN];
+  bool was_truncated;
+
+  /* Building .TRN trigger filenames */
+  build_table_filename(trigger_name_buffer, 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(trigger_name_buffer)-1,
+             trigger_name_buffer);
+    return true;
+  }
+
+  /* Use the filesystem to enforce trigger namespace constraints. */
+  if (!access(trigger_name_buffer, F_OK))
+  {
+    my_error(ER_TRG_ALREADY_EXISTS, MYF(0));
+    return true;
+  }
+
+  return false;
+}
+
+
+/**
+  Get name of table by trigger name.
+
+  @param [in]  thd              thread handle
+  @param [in]  trg_name         name of trigger
+  @param [in]  trn_path         path to the corresponding TRN-file
+  @param [out] tbl_name         variable to store retrieved table name
+
+  @return Operation status
+    @retval true   Failure.
+    @retval false  Success.
+*/
+
+bool Trigger_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 Trigger_name trn_data;
+
+  Handle_old_incorrect_trigger_table_hook trigger_table_hook(
+                                          trn_path->str,
+                                          &trn_data.trigger_table);
+
+  DBUG_ENTER("Trigger_loader::get_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(&trn_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,
+                    trn_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);
+}
+
+
+/**
+  Get name of table by trigger name.
+
+  @param [in]  thd              Thread handle
+  @param [in]  trg_name         Name of trigger
+  @param [in]  trn_path         Path to the corresponding TRN-file
+  @param [out] tbl_name         Variable to store retrieved table name
+
+  @return Operation status
+    @retval true   Failure.
+    @retval false  Success.
+*/
+
+bool Trigger_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->m_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 [in] path           char buffer of size FN_REFLEN to be used
+                             for constructing path to .TRN file
+  @param [in] db             trigger's database name
+  @param [in] trigger_name   trigger's name
+
+  @return Operation status
+    @retval true   Failure
+    @retval false  Success
+*/
+
+bool Trigger_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
+
+  @return Operation status
+    @retval true   Failure.
+    @retval false  Success.
+*/
+
+bool Trigger_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));
+}
+
+
+/**
+  Get name of table by trigger name.
+
+  @param [in]  thd              thread handle
+  @param [in]  trg_name         name of trigger
+  @param [in]  trn_path         path to the corresponding TRN-file
+  @param [out] tbl_name         variable to store retrieved table name
+
+  @return Operation status
+    @retval true   Failure.
+    @retval false  Success.
+*/
+
+bool Trigger_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(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 false;
+}
+
+/**
+  Iterate though list of triggers and update
+  .TRN files after renaming triggers' subject table.
+
+  @param old_db_name         Old database of subject table
+  @param new_db_name         New database of subject table
+  @param new_table_name      New subject table's name
+  @param stopper             Pointer to a trigger_name for
+                             which we should stop updating.
+
+  @retval
+    0      Success
+  @retval
+    non-0  Failure, pointer to trigger_name
+    for which update failed.
+*/
+
+LEX_STRING*
+Trigger_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 trigger_name_buffer[FN_REFLEN];
+  Trigger_name trigger_name;
+  LEX_STRING trn_file_name;
+  Trigger *trigger;
+  List_iterator_fast<Trigger> it_table_triggers(table_triggers);
+
+  while ((trigger= it_table_triggers++))
+  {
+    if (trigger->m_trigger_name ==  stopper)
+      break;
+    trn_file_name.length= build_table_filename(trigger_name_buffer, FN_REFLEN-1,
+                                               new_db_name,
+                                               trigger->m_trigger_name->str,
+                                               TRN_EXT, 0);
+    trn_file_name.str= trigger_name_buffer;
+
+    trigger_name.trigger_table= *new_table_name;
+
+    if (sql_create_definition_file(NULL, &trn_file_name, &trn_file_type,
+                                   (uchar*)&trigger_name, trn_file_parameters))
+      return trigger->m_trigger_name;
+
+    /* Remove stale .TRN file in case of database upgrade */
+    if (old_db_name)
+    {
+      if (rm_trigname_file(trigger_name_buffer, old_db_name,
+                           trigger->m_trigger_name->str))
+      {
+        (void) rm_trigname_file(trigger_name_buffer, new_db_name,
+                                trigger->m_trigger_name->str);
+        return trigger->m_trigger_name;
+      }
+    }
+  }
+
+  return NULL;
+}

=== added file 'sql/trigger_loader.h'
--- a/sql/trigger_loader.h	1970-01-01 00:00:00 +0000
+++ b/sql/trigger_loader.h	2013-01-25 11:48:04 +0000
@@ -0,0 +1,147 @@
+/*
+   Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#ifndef TRIGGER_LOADER_H_INCLUDED
+#define TRIGGER_LOADER_H_INCLUDED
+
+///////////////////////////////////////////////////////////////////////////
+
+#include "sql_list.h"
+
+#include "parse_file.h" // File_option FIXME: remove dependency.
+
+struct TABLE;
+struct TABLE_LIST;
+
+class THD;
+class Trigger;
+class sp_name;
+
+class Trigger_loader
+{
+public:
+  static bool load_triggers(THD *thd,
+                            const char *db_name,
+                            const char *table_name,
+                            TABLE *table,
+                            List<Trigger> *table_trigger_lst,
+                            bool *trigger_not_found);
+
+private:
+  bool load_triggers_impl(THD *thd,
+                          const char *db_name,
+                          const char *table_name,
+                          TABLE *table,
+                          bool *trigger_not_found,
+                          List<Trigger> *table_trigger_lst);
+
+public:
+  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);
+
+  static 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 trg_file_parameters[];
+
+  static File_option trn_file_parameters[];
+
+  static const LEX_STRING trg_file_type;
+
+  static const LEX_STRING trn_file_type;
+
+  /**
+    Field responsible for storing triggers definitions in file.
+  */
+  List<LEX_STRING>  definitions_list;
+
+public:
+  /**
+    List of sql modes for triggers.
+    It has to be public because we are using it directly from parser.
+  */
+  List<ulonglong> definition_modes_list;
+
+private:
+  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;
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+#endif // TRIGGER_LOADER_H_INCLUDED

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-trunk branch (Dmitry.Shulga:5356 to 5376) Dmitry Shulga11 Mar