List:Commits« Previous MessageNext Message »
From:Martin Hansson Date:August 18 2009 9:18am
Subject:bzr commit into mysql-5.1-bugteam branch (martin.hansson:2936) Bug#45235
View as plain text  
#At file:///data0/martin/bzr/bug45235/5.1bt-gca-commit/ based on revid:davi.arnaut@stripped

 2936 Martin Hansson	2009-08-18
      Bug#45235: 5.1 does not support 5.0-only syntax triggers in any way
      
      Execution of DROP TRIGGER expected the .TRG files to be fullly parsed before
      deleting them. This is not always possible, however, as the trigger files may
      have been created with an older verion of MySQL and contain deprecated syntax.
      Fixed by making DROP TRIGGER silently delete .TRN and .TRG files if .TRG file
      is not parseable.
     @ mysql-test/r/trigger.result
        Bug#45235: Test result.
     @ mysql-test/t/trigger.test
        Bug#45235: Test case.
     @ sql/sql_base.cc
        Bug#45235: Setting the trigger list member to NULL in order to avoid double-deletes
        in case of a parse failure in trigger file.
     @ sql/sql_class.h
        Bug#45235: Addded utility class for suppression of parse errors in trigger file read.
     @ sql/sql_parse.cc
        Bug#45235: Removed assertion which can no longer be guaranteed to be true.
     @ sql/sql_trigger.cc
        Bug#45235: 
        - The fix: Added code for silently deleting trigger files in case of
        parse errors in .TRG files during DROP TRIGGER.
        - Corrected a doxgen comment.
     @ sql/sql_trigger.h
        Bug#45235: Declaration of new method for deleting trigger files.

    modified:
      mysql-test/r/trigger.result
      mysql-test/t/trigger.test
      sql/sql_base.cc
      sql/sql_class.h
      sql/sql_parse.cc
      sql/sql_trigger.cc
      sql/sql_trigger.h
=== modified file 'mysql-test/r/trigger.result'
--- a/mysql-test/r/trigger.result	2009-03-27 12:55:14 +0000
+++ b/mysql-test/r/trigger.result	2009-08-18 09:18:26 +0000
@@ -2073,4 +2073,29 @@ select @a, @b;
 drop trigger trg1;
 drop trigger trg2;
 drop table t1, t2;
+#
+# Bug#45235: 5.1 does not support 5.0-only syntax triggers in any way
+#
+CREATE TABLE t1 ( a INT );
+INSERT INTO t1 VALUES (1), (2), (3);
+SHOW TRIGGERS;
+Trigger	Event	Table	Statement	Timing	Created	sql_mode	Definer	character_set_client	collation_connection	Database Collation
+DROP TRIGGER tx;
+Warnings:
+Warning	1603	Triggers for table `test`.`t1` have no creation context
+SHOW TRIGGERS;
+Trigger	Event	Table	Statement	Timing	Created	sql_mode	Definer	character_set_client	collation_connection	Database Collation
+t1.MYD
+t1.MYI
+t1.frm
+SELECT * FROM t1;
+a
+1
+2
+3
+DROP TRIGGER tx;
+ERROR HY000: Trigger does not exist
+DROP TRIGGER tx;
+ERROR HY000: Trigger does not exist
+DROP TABLE t1;
 End of 5.1 tests.

=== modified file 'mysql-test/t/trigger.test'
--- a/mysql-test/t/trigger.test	2009-03-27 12:55:14 +0000
+++ b/mysql-test/t/trigger.test	2009-08-18 09:18:26 +0000
@@ -2370,4 +2370,64 @@ drop trigger trg1;
 drop trigger trg2;
 drop table t1, t2;
 
+--echo #
+--echo # Bug#45235: 5.1 does not support 5.0-only syntax triggers in any way
+--echo #
+let $MYSQLD_DATADIR=`SELECT @@datadir`;
+
+CREATE TABLE t1 ( a INT );
+INSERT INTO t1 VALUES (1), (2), (3);
+
+# We simulate importing a trigger from 5.0 by writing a .TRN and .TRG file as 
+# MySQL 5.0 would have done it, with syntax allowed in 5.0.
+#
+# Note that in 5.0 the following lines are missing from t1.TRG:
+#
+# client_cs_names='latin1'
+# connection_cl_names='latin1_swedish_ci'
+# db_cl_names='latin1_swedish_ci'
+--write_file $MYSQLD_DATADIR/test/tx.TRN
+TYPE=TRIGGERNAME
+trigger_table=t1
+EOF
+
+--write_file $MYSQLD_DATADIR/test/t1.TRG
+TYPE=TRIGGERS
+triggers='CREATE DEFINER=`root`@`localhost` TRIGGER tx BEFORE INSERT ON t1 FOR EACH ROW DELETE FROM t1 a USING t1 a'
+sql_modes=0
+definers='root@localhost'
+EOF
+
+SHOW TRIGGERS;
+DROP TRIGGER tx;
+SHOW TRIGGERS;
+
+# Make sure there is no trigger file left.
+--exec ls $MYSQLD_DATADIR/test
+
+SELECT * FROM t1;
+
+# Try and drop trigger when there is only a .TRG file, just to make sure
+# server does not crash.
+--write_file $MYSQLD_DATADIR/test/t1.TRG
+TYPE=TRIGGERS
+triggers='CREATE DEFINER=`root`@`localhost` TRIGGER tx BEFORE INSERT ON t1 FOR EACH ROW DELETE FROM t1 a USING t1 a'
+sql_modes=0
+definers='root@localhost'
+EOF
+--error ER_TRG_DOES_NOT_EXIST
+DROP TRIGGER tx;
+--exec rm $MYSQLD_DATADIR/test/t1.TRG
+
+# Try and drop trigger when there is only a .TRN file, just to make sure
+# server does not crash.
+--write_file $MYSQLD_DATADIR/test/tx.TRN
+TYPE=TRIGGERNAME
+trigger_table=t1
+EOF
+--error ER_TRG_DOES_NOT_EXIST
+DROP TRIGGER tx;
+--exec rm $MYSQLD_DATADIR/test/tx.TRN
+
+DROP TABLE t1;
 --echo End of 5.1 tests.

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2009-05-30 13:32:28 +0000
+++ b/sql/sql_base.cc	2009-08-18 09:18:26 +0000
@@ -3996,6 +3996,7 @@ retry:
                                         share->table_name.str, entry, 0))
   {
     closefrm(entry, 0);
+    entry->triggers= NULL;
     goto err;
   }
 

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2009-06-08 22:05:24 +0000
+++ b/sql/sql_class.h	2009-08-18 09:18:26 +0000
@@ -1098,6 +1098,51 @@ public:
 
 
 /**
+   Utility error handler for suppressing a particular error. After execution of
+   code where the error might have occured, the object can be queried whether
+   the error was raised or not.   
+ */
+class Error_absorber : public Internal_error_handler
+{
+private:
+  uint m_errno_to_absorb;
+  bool m_was_absorbed;
+  
+public:
+  
+  /**
+     Creates a new Error_absorber.
+     
+     @param errno_to_absorb The number of the SQL error to suppress.
+   */
+  Error_absorber(uint errno_to_absorb) : 
+  m_errno_to_absorb(errno_to_absorb), m_was_absorbed(FALSE) 
+  {}
+  
+  /**
+     Will return true for the error number given at construction.
+   */
+  bool handle_error(uint sql_errno,
+                    const char *message,
+                    MYSQL_ERROR::enum_warning_level level,
+                    THD *thd)
+  {
+    if (sql_errno == m_errno_to_absorb)
+    {
+      m_was_absorbed= TRUE;
+      return TRUE;
+    }
+    return FALSE;
+  }
+  
+  /**
+     True if the error number given at construction was ever raised.
+   */
+  bool was_absorbed() { return m_was_absorbed; }
+};
+
+
+/**
   Stores status of the currently executed statement.
   Cleared at the beginning of the statement, and then
   can hold either OK, ERROR, or EOF status.

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2009-06-05 11:23:58 +0000
+++ b/sql/sql_parse.cc	2009-08-18 09:18:26 +0000
@@ -7800,11 +7800,6 @@ bool parse_sql(THD *thd,
 
   bool mysql_parse_status= MYSQLparse(thd) != 0;
 
-  /* Check that if MYSQLparse() failed, thd->is_error() is set. */
-
-  DBUG_ASSERT(!mysql_parse_status ||
-              mysql_parse_status && thd->is_error());
-
   /* Reset parser state. */
 
   thd->m_parser_state= NULL;

=== modified file 'sql/sql_trigger.cc'
--- a/sql/sql_trigger.cc	2009-05-15 12:57:51 +0000
+++ b/sql/sql_trigger.cc	2009-08-18 09:18:26 +0000
@@ -328,6 +328,8 @@ bool mysql_create_or_drop_trigger(THD *t
   bool result= TRUE;
   String stmt_query;
   bool need_start_waiting= FALSE;
+  /* Used only when create == FALSE */
+  Error_absorber parse_error_silencer(ER_PARSE_ERROR);
 
   DBUG_ENTER("mysql_create_or_drop_trigger");
 
@@ -458,16 +460,22 @@ bool mysql_create_or_drop_trigger(THD *t
     if (lock_table_names(thd, tables))
       goto end;
 
+    if (!create)
+      thd->push_internal_handler(&parse_error_silencer);
+
     /* Convert the placeholder to a real table */
     if (reopen_name_locked_table(thd, tables, TRUE))
     {
       unlock_table_name(thd, tables);
-      goto end;
+      if (!parse_error_silencer.was_absorbed())
+        goto end;
     }
+    if (!create)
+      thd->pop_internal_handler();
   }
   table= tables->table;
 
-  if (!table->triggers)
+  if (!table->triggers && !parse_error_silencer.was_absorbed())
   {
     if (!create)
     {
@@ -479,9 +487,15 @@ bool mysql_create_or_drop_trigger(THD *t
       goto end;
   }
 
-  result= (create ?
-           table->triggers->create_trigger(thd, tables, &stmt_query):
-           table->triggers->drop_trigger(thd, tables, &stmt_query));
+  if (parse_error_silencer.was_absorbed())
+  {
+    Table_triggers_list::purge_trigger(thd, tables);
+    result= 0;
+  }
+  else
+    result= (create ?
+             table->triggers->create_trigger(thd, tables, &stmt_query):
+             table->triggers->drop_trigger(thd, tables, &stmt_query));
 
   /* Under LOCK TABLES we must reopen the table to activate the trigger. */
   if (!result && thd->locked_tables)
@@ -837,7 +851,7 @@ static bool rm_trigger_file(char *path, 
   @param path         char buffer of size FN_REFLEN to be used
                       for constructing path to .TRN file.
   @param db           trigger's database name
-  @param table_name   trigger's name
+  @param trigger_name trigger's name
 
   @retval
     False   success
@@ -969,6 +983,27 @@ bool Table_triggers_list::drop_trigger(T
   return 1;
 }
 
+/**
+   Remove trigger files for a table. This method will remove the trigger files
+   only, no cleanup of in-memory structures is carried out.
+
+   @param thd The thread context.
+
+   @param table The table ref object.
+
+   @remark The parser state is queried for the name of the trigger, hence this 
+   method is dependent on being called after the trigger name file was parsed,
+   before any other file was parsed.
+ */
+bool Table_triggers_list::purge_trigger(THD *thd, TABLE_LIST *table)
+{
+  const char *trigger_name= thd->lex->spname->m_name.str;
+  char path[FN_REFLEN];
+  rm_trigger_file(path, table->db, table->table_name);
+  rm_trigname_file(path, table->db, trigger_name);
+  return 0;
+}
+
 
 Table_triggers_list::~Table_triggers_list()
 {

=== modified file 'sql/sql_trigger.h'
--- a/sql/sql_trigger.h	2009-01-14 14:50:51 +0000
+++ b/sql/sql_trigger.h	2009-08-18 09:18:26 +0000
@@ -118,6 +118,7 @@ public:
 
   int find_trigger_by_name(const LEX_STRING *trigger_name);
 
+  static bool purge_trigger(THD *thd, TABLE_LIST *tables);
   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);


Attachment: [text/bzr-bundle] bzr/martin.hansson@sun.com-20090818091826-lxx5okctzzfy1xoi.bundle
Thread
bzr commit into mysql-5.1-bugteam branch (martin.hansson:2936) Bug#45235Martin Hansson18 Aug