From: Dmitry Shulga Date: May 23 2011 10:55am Subject: bzr commit into mysql-5.1 branch (Dmitry.Shulga:3625) Bug#45235 Bug#11753738 List-Archive: http://lists.mysql.com/commits/137850 X-Bug: 45235,11753738 Message-Id: <201105231055.p4NAttmR028811@acsmt358.oracle.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============4813455589773227492==" --===============4813455589773227492== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///Users/shulga/projects/mysql/mysql-5.1-bug11753738/ based on revid:luis.soares@stripped 3625 Dmitry Shulga 2011-05-23 Fixed bug#11753738 (formely known as bug#45235) - 5.1 DOES NOT SUPPORT 5.0-ONLY SYNTAX TRIGGERS IN ANY WAY Table with triggers which were using deprecated (5.0-only) syntax became unavailable for any DML and DDL after upgrade to 5.1 version of server. Attempt to execute any statement on such a table resulted in parsing error reported. Since this included DROP TRIGGER and DROP TABLES statements (actually, the latter was allowed but was not functioning properly for such tables) it was impossible to fix the problem without manual operations on .TRG and .TRN files in data directory. The problem was that failure to parse trigger body (due to 5.0-only syntax) when opening trigger file for a table prevented the table from being open. This made all operations on the table impossible (except DROP TABLE which due to peculiarity in its implementation dropped the table but left trigger files around). This patch solves this problem by silencing error which occurs when we parse trigger body during table open. Error message is preserved for the future use and table is marked as having a broken trigger. We also try to analyze parse tree to recover trigger name, which will be needed in order to drop the broken trigger. DML statements which invoke triggers on the table marked as having broken trigger are prohibited and emit saved error message. The same happens for DDL which change triggers except DROP TRIGGER and DROP TABLE which try their best to do what was requested. Table becomes no longer marked as having broken trigger when last such trigger is dropped. @ mysql-test/r/trigger-compat.result Add results for test case for bug#45235 @ mysql-test/t/trigger-compat.test Add test case for bug#45235. @ sql/sp_head.cc Added protection against MEM_ROOT double restoring to sp_head::restore_thd_mem_root() method. Since this method can be sometimes called twice during parsing of stored routine (the first time during normal flow of parsing, and the second time when a syntax error is detected) we need to shortcut execution of the method to avoid damaging MEM_ROOT by the second consecutive call to this method. @ sql/sql_trigger.cc Bug#45235: - New class to implement error suppression and recovery of trigger name. - Comment correction - Errors for trigger manipulation statements - More comment correction - Fix exploiting new class. - indentation correction. - Split up a conjunction in an assert. - Added code for handling completely broken triggers - More errors for trigger manipulation statements. - Method for setting broken triggers flag & error message. @ sql/sql_trigger.h Bug#45235: New members to handle broken triggers and error messages. modified: mysql-test/r/trigger-compat.result mysql-test/r/trigger.result mysql-test/t/trigger-compat.test sql/sp_head.cc sql/sql_parse.cc sql/sql_trigger.cc sql/sql_trigger.h === modified file 'mysql-test/r/trigger-compat.result' --- a/mysql-test/r/trigger-compat.result 2009-02-19 23:24:25 +0000 +++ b/mysql-test/r/trigger-compat.result 2011-05-23 10:55:40 +0000 @@ -43,3 +43,98 @@ DROP TABLE t2; DROP USER mysqltest_dfn@localhost; DROP USER mysqltest_inv@localhost; DROP DATABASE mysqltest_db1; +USE test; +# +# Bug#45235: 5.1 does not support 5.0-only syntax triggers in any way +# +DROP TABLE IF EXISTS t1, t2, t3; +CREATE TABLE t1 ( a INT ); +CREATE TABLE t2 ( a INT ); +CREATE TABLE t3 ( a INT ); +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (1), (2), (3); +INSERT INTO t3 VALUES (1), (2), (3); +# We simulate importing a trigger from 5.0 by writing a .TRN file for +# each trigger plus a .TRG file the way MySQL 5.0 would have done it, +# with syntax allowed in 5.0 only. +# +# 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' +# We will get parse errors for most DDL and DML statements when the table +# has broken triggers. The parse error refers to the first broken +# trigger. +CREATE TRIGGER tr16 AFTER UPDATE ON t1 FOR EACH ROW INSERT INTO t1 VALUES (1); +ERROR 42000: Trigger 'tr13' has an error in its body: 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'a USING t1 a' at line 1' +CREATE TRIGGER tr22 BEFORE INSERT ON t2 FOR EACH ROW DELETE FROM non_existing_table; +ERROR 42000: Unknown trigger has an error in its body: 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Not allowed syntax here, and trigger name cant be extracted either.' at line 1' +SHOW TRIGGERS; +Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation +tr11 INSERT t1 DELETE FROM t3 BEFORE NULL root@localhost latin1 latin1_swedish_ci latin1_swedish_ci +tr12 INSERT t1 DELETE FROM t3 AFTER NULL root@localhost latin1 latin1_swedish_ci latin1_swedish_ci +tr14 DELETE t1 DELETE FROM non_existing_table AFTER NULL root@localhost latin1 latin1_swedish_ci latin1_swedish_ci +Warnings: +Warning 1603 Triggers for table `test`.`t1` have no creation context +Warning 1603 Triggers for table `test`.`t2` have no creation context +INSERT INTO t1 VALUES (1); +ERROR 42000: Trigger 'tr13' has an error in its body: 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'a USING t1 a' at line 1' +INSERT INTO t2 VALUES (1); +ERROR 42000: Unknown trigger has an error in its body: 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Not allowed syntax here, and trigger name cant be extracted either.' at line 1' +DELETE FROM t1; +ERROR 42000: Trigger 'tr13' has an error in its body: 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'a USING t1 a' at line 1' +UPDATE t1 SET a = 1 WHERE a = 1; +ERROR 42000: Trigger 'tr13' has an error in its body: 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'a USING t1 a' at line 1' +SELECT * FROM t1; +a +1 +2 +3 +RENAME TABLE t1 TO t1_2; +ERROR 42000: Trigger 'tr13' has an error in its body: 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'a USING t1 a' at line 1' +SHOW TRIGGERS; +Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation +tr11 INSERT t1 DELETE FROM t3 BEFORE NULL root@localhost latin1 latin1_swedish_ci latin1_swedish_ci +tr12 INSERT t1 DELETE FROM t3 AFTER NULL root@localhost latin1 latin1_swedish_ci latin1_swedish_ci +tr14 DELETE t1 DELETE FROM non_existing_table AFTER NULL root@localhost latin1 latin1_swedish_ci latin1_swedish_ci +Warnings: +Warning 1603 Triggers for table `test`.`t1` have no creation context +DROP TRIGGER tr11; +Warnings: +Warning 1603 Triggers for table `test`.`t1` have no creation context +DROP TRIGGER tr12; +DROP TRIGGER tr13; +DROP TRIGGER tr14; +DROP TRIGGER tr15; +SHOW TRIGGERS; +Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation +# Make sure there is no trigger file left. +# We write the same trigger files one more time to test DROP TABLE. +DROP TABLE t1; +Warnings: +Warning 1603 Triggers for table `test`.`t1` have no creation context +DROP TABLE t2; +Warnings: +Warning 1603 Triggers for table `test`.`t2` have no creation context +DROP TABLE t3; +# Make sure there is no trigger file left. +CREATE TABLE t1 ( a INT ); +CREATE TABLE t2 ( a INT ); +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (1), (2), (3); +# We write three trigger files. First trigger is syntaxically incorrect, next trigger is correct +# and last trigger is broken. +# Next we try to execute SHOW CREATE TRGGIR command for broken trigger and then try to drop one. +FLUSH TABLE t1; +SHOW CREATE TRIGGER tr12; +Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation +tr12 CREATE DEFINER=`root`@`localhost` TRIGGER tr12 BEFORE INSERT ON t1 FOR EACH ROW DELETE FROM t2 latin1 latin1_swedish_ci latin1_swedish_ci +Warnings: +Warning 1603 Triggers for table `test`.`t1` have no creation context +DROP TRIGGER tr12; +Warnings: +Warning 1603 Triggers for table `test`.`t1` have no creation context +DROP TRIGGER tr11; +DROP TABLE t1; +DROP TABLE t2; === modified file 'mysql-test/r/trigger.result' --- a/mysql-test/r/trigger.result 2010-08-18 04:56:06 +0000 +++ b/mysql-test/r/trigger.result 2011-05-23 10:55:40 +0000 @@ -2134,10 +2134,8 @@ CREATE TRIGGER trg1 BEFORE INSERT ON t2 # Used to crash SHOW TRIGGERS IN db1; Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation -Warnings: -Warning 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'VALUES (1)' at line 1 INSERT INTO t2 VALUES (1); -ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'VALUES (1)' at line 1 +ERROR 42000: Trigger 'trg1' has an error in its body: 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'VALUES (1)' at line 1' SELECT * FROM t1; b # Work around Bug#45235 === modified file 'mysql-test/t/trigger-compat.test' --- a/mysql-test/t/trigger-compat.test 2009-02-19 23:24:25 +0000 +++ b/mysql-test/t/trigger-compat.test 2011-05-23 10:55:40 +0000 @@ -106,4 +106,178 @@ DROP TABLE t2; DROP USER mysqltest_dfn@localhost; DROP USER mysqltest_inv@localhost; DROP DATABASE mysqltest_db1; +USE test; + +--echo # +--echo # Bug#45235: 5.1 does not support 5.0-only syntax triggers in any way +--echo # +let $MYSQLD_DATADIR=`SELECT @@datadir`; + +--disable_warnings +DROP TABLE IF EXISTS t1, t2, t3; +--enable_warnings + +CREATE TABLE t1 ( a INT ); +CREATE TABLE t2 ( a INT ); +CREATE TABLE t3 ( a INT ); +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (1), (2), (3); +INSERT INTO t3 VALUES (1), (2), (3); + +--echo # We simulate importing a trigger from 5.0 by writing a .TRN file for +--echo # each trigger plus a .TRG file the way MySQL 5.0 would have done it, +--echo # with syntax allowed in 5.0 only. +--echo # +--echo # Note that in 5.0 the following lines are missing from t1.TRG: +--echo # +--echo # client_cs_names='latin1' +--echo # connection_cl_names='latin1_swedish_ci' +--echo # db_cl_names='latin1_swedish_ci' + +--write_file $MYSQLD_DATADIR/test/tr11.TRN +TYPE=TRIGGERNAME +trigger_table=t1 +EOF + +--write_file $MYSQLD_DATADIR/test/tr12.TRN +TYPE=TRIGGERNAME +trigger_table=t1 +EOF + +--write_file $MYSQLD_DATADIR/test/tr13.TRN +TYPE=TRIGGERNAME +trigger_table=t1 +EOF + +--write_file $MYSQLD_DATADIR/test/tr14.TRN +TYPE=TRIGGERNAME +trigger_table=t1 +EOF + +--write_file $MYSQLD_DATADIR/test/tr15.TRN +TYPE=TRIGGERNAME +trigger_table=t1 +EOF + +--write_file $MYSQLD_DATADIR/test/t1.TRG +TYPE=TRIGGERS +triggers='CREATE DEFINER=`root`@`localhost` TRIGGER tr11 BEFORE INSERT ON t1 FOR EACH ROW DELETE FROM t3' 'CREATE DEFINER=`root`@`localhost` TRIGGER tr12 AFTER INSERT ON t1 FOR EACH ROW DELETE FROM t3' 'CREATE DEFINER=`root`@`localhost` TRIGGER tr13 BEFORE DELETE ON t1 FOR EACH ROW DELETE FROM t1 a USING t1 a' 'CREATE DEFINER=`root`@`localhost` TRIGGER tr14 AFTER DELETE ON t1 FOR EACH ROW DELETE FROM non_existing_table' 'CREATE DEFINER=`root`@`localhost` TRIGGER tr15 BEFORE UPDATE ON t1 FOR EACH ROW DELETE FROM non_existing_table a USING non_existing_table a' +sql_modes=0 0 0 0 0 +definers='root@localhost' 'root@localhost' 'root@localhost' 'root@localhost' 'root@localhost' +EOF + +--write_file $MYSQLD_DATADIR/test/t2.TRG +TYPE=TRIGGERS +triggers='Not allowed syntax here, and trigger name cant be extracted either.' +sql_modes=0 +definers='root@localhost' +EOF + +--echo # We will get parse errors for most DDL and DML statements when the table +--echo # has broken triggers. The parse error refers to the first broken +--echo # trigger. +--error ER_PARSE_ERROR +CREATE TRIGGER tr16 AFTER UPDATE ON t1 FOR EACH ROW INSERT INTO t1 VALUES (1); +--error ER_PARSE_ERROR +CREATE TRIGGER tr22 BEFORE INSERT ON t2 FOR EACH ROW DELETE FROM non_existing_table; +SHOW TRIGGERS; +--error ER_PARSE_ERROR +INSERT INTO t1 VALUES (1); +--error ER_PARSE_ERROR +INSERT INTO t2 VALUES (1); +--error ER_PARSE_ERROR +DELETE FROM t1; +--error ER_PARSE_ERROR +UPDATE t1 SET a = 1 WHERE a = 1; +SELECT * FROM t1; +--error ER_PARSE_ERROR +RENAME TABLE t1 TO t1_2; +SHOW TRIGGERS; + +DROP TRIGGER tr11; +DROP TRIGGER tr12; +DROP TRIGGER tr13; +DROP TRIGGER tr14; +DROP TRIGGER tr15; + +SHOW TRIGGERS; + +--echo # Make sure there is no trigger file left. +--list_files $MYSQLD_DATADIR/test/ tr* + +--echo # We write the same trigger files one more time to test DROP TABLE. +--write_file $MYSQLD_DATADIR/test/tr11.TRN +TYPE=TRIGGERNAME +trigger_table=t1 +EOF + +--write_file $MYSQLD_DATADIR/test/tr12.TRN +TYPE=TRIGGERNAME +trigger_table=t1 +EOF + +--write_file $MYSQLD_DATADIR/test/tr13.TRN +TYPE=TRIGGERNAME +trigger_table=t1 +EOF + +--write_file $MYSQLD_DATADIR/test/tr14.TRN +TYPE=TRIGGERNAME +trigger_table=t1 +EOF + +--write_file $MYSQLD_DATADIR/test/tr15.TRN +TYPE=TRIGGERNAME +trigger_table=t1 +EOF + +--write_file $MYSQLD_DATADIR/test/t1.TRG +TYPE=TRIGGERS +triggers='CREATE DEFINER=`root`@`localhost` TRIGGER tr11 BEFORE INSERT ON t1 FOR EACH ROW DELETE FROM t3' 'CREATE DEFINER=`root`@`localhost` TRIGGER tr12 AFTER INSERT ON t1 FOR EACH ROW DELETE FROM t3' 'CREATE DEFINER=`root`@`localhost` TRIGGER tr13 BEFORE DELETE ON t1 FOR EACH ROW DELETE FROM t1 a USING t1 a' 'CREATE DEFINER=`root`@`localhost` TRIGGER tr14 AFTER DELETE ON t1 FOR EACH ROW DELETE FROM non_existing_table' 'CREATE DEFINER=`root`@`localhost` TRIGGER tr15 BEFORE UPDATE ON t1 FOR EACH ROW DELETE FROM non_existing_table a USING non_existing_table a' +sql_modes=0 0 0 0 0 +definers='root@localhost' 'root@localhost' 'root@localhost' 'root@localhost' 'root@localhost' +EOF + +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; + +--echo # Make sure there is no trigger file left. + +--list_files $MYSQLD_DATADIR/test/ tr* + +CREATE TABLE t1 ( a INT ); +CREATE TABLE t2 ( a INT ); +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (1), (2), (3); + +--echo # We write three trigger files. First trigger is syntaxically incorrect, next trigger is correct +--echo # and last trigger is broken. +--echo # Next we try to execute SHOW CREATE TRGGIR command for broken trigger and then try to drop one. +--write_file $MYSQLD_DATADIR/test/tr11.TRN +TYPE=TRIGGERNAME +trigger_table=t1 +EOF + +--write_file $MYSQLD_DATADIR/test/tr12.TRN +TYPE=TRIGGERNAME +trigger_table=t1 +EOF + +--write_file $MYSQLD_DATADIR/test/t1.TRG +TYPE=TRIGGERS +triggers='CREATE the wrongest trigger_in_the_world' 'CREATE DEFINER=`root`@`localhost` TRIGGER tr11 BEFORE DELETE ON t1 FOR EACH ROW DELETE FROM t1 a USING t1 a' 'CREATE DEFINER=`root`@`localhost` TRIGGER tr12 BEFORE INSERT ON t1 FOR EACH ROW DELETE FROM t2' +sql_modes=0 0 0 +definers='root@localhost' 'root@localhost' 'root@localhost' +EOF + +FLUSH TABLE t1; + +SHOW CREATE TRIGGER tr12; +SHOW CREATE TRIGGER tr11; +DROP TRIGGER tr12; +DROP TRIGGER tr11; + +DROP TABLE t1; +DROP TABLE t2; === modified file 'sql/sp_head.cc' --- a/sql/sp_head.cc 2010-11-11 04:52:51 +0000 +++ b/sql/sp_head.cc 2011-05-23 10:55:40 +0000 @@ -2354,6 +2354,21 @@ void sp_head::restore_thd_mem_root(THD *thd) { DBUG_ENTER("sp_head::restore_thd_mem_root"); + + /* + In some cases our parser detects a syntax error and calls + LEX::cleanup_lex_after_parse_error() method only after + finishing parsing the whole routine. In such a situation + sp_head::restore_thd_mem_root() will be called twice - the + first time as part of normal parsing process and the second + time by cleanup_lex_after_parse_error(). + To avoid ruining active arena/mem_root state in this case we + skip restoration of old arena/mem_root if this method has been + already called for this routine. + */ + if (!m_thd) + DBUG_VOID_RETURN; + Item *flist= free_list; // The old list set_query_arena(thd); // Get new free_list and mem_root state= INITIALIZED_FOR_SP; === modified file 'sql/sql_parse.cc' --- a/sql/sql_parse.cc 2011-03-03 08:25:37 +0000 +++ b/sql/sql_parse.cc 2011-05-23 10:55:40 +0000 @@ -7973,10 +7973,14 @@ bool parse_sql(THD *thd, bool mysql_parse_status= MYSQLparse(thd) != 0; - /* Check that if MYSQLparse() failed, thd->is_error() is set. */ + /* + Check that if MYSQLparse() failed, thd->is_error() is set (unless + we have an error handler installed, which might have silenced error). + */ DBUG_ASSERT(!mysql_parse_status || - (mysql_parse_status && thd->is_error())); + (mysql_parse_status && + (thd->is_error() || thd->get_internal_handler()))); /* Reset parser state. */ === modified file 'sql/sql_trigger.cc' --- a/sql/sql_trigger.cc 2010-09-07 08:53:46 +0000 +++ b/sql/sql_trigger.cc 2011-05-23 10:55:40 +0000 @@ -19,6 +19,7 @@ #include "sp_head.h" #include "sql_trigger.h" #include "parse_file.h" +#include /*************************************************************************/ @@ -293,6 +294,53 @@ private: /** + 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]; + +public: + + LEX_STRING *m_trigger_name; + + Deprecated_trigger_syntax_handler() : m_trigger_name(NULL) {} + + bool handle_error(uint sql_errno, const char *message, + MYSQL_ERROR::enum_warning_level level, THD *thd) + { + 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), + "Trigger '%s' has an error in its body: '%s'", + m_trigger_name->str, message); + else + my_snprintf(m_message, sizeof(m_message), + "Unknown trigger has an error in its body: '%s'", + message); + return TRUE; + } + return FALSE; + } + + LEX_STRING *get_trigger_name() { return m_trigger_name; } + char *get_error_text() { return m_message; } + +}; + +/** Create or drop trigger for table. @param thd current thread context (including trigger definition in LEX) @@ -552,9 +600,9 @@ end: (SUID/new trigger). @retval - False success + false success @retval - True error + true error */ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, String *stmt_query) @@ -575,6 +623,8 @@ bool Table_triggers_list::create_trigger 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, @@ -848,7 +898,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 @@ -1312,12 +1362,11 @@ bool Table_triggers_list::check_n_load(T lex_start(thd); thd->spcont= NULL; - if (parse_sql(thd, & parser_state, creation_ctx)) - { - /* Currently sphead is always deleted in case of a parse error */ - DBUG_ASSERT(lex.sphead == 0); - goto err_with_lex_cleanup; - } + Deprecated_trigger_syntax_handler error_handler; + thd->push_internal_handler(&error_handler); + bool parse_error= parse_sql(thd, & parser_state, creation_ctx); + thd->pop_internal_handler(); + /* Not strictly necessary to invoke this method here, since we know that we've parsed CREATE TRIGGER and not an @@ -1328,6 +1377,52 @@ bool Table_triggers_list::check_n_load(T */ lex.set_trg_event_type_for_tables(); + if (parse_error) + { + if (!triggers->m_has_unparseable_trigger) + triggers->set_parse_error(error_handler.get_error_text()); + /* Currently sphead is always set to NULL in case of a parse error */ + DBUG_ASSERT(lex.sphead == 0); + if (error_handler.get_trigger_name()) + { + LEX_STRING *trigger_name; + const LEX_STRING *copied_trg_name= error_handler.get_trigger_name(); + + if (!(trigger_name= alloc_lex_string(&table->mem_root)) || + !(trigger_name->str= strmake_root(&table->mem_root, + copied_trg_name->str, + copied_trg_name->length))) + goto err_with_lex_cleanup; + + trigger_name->length= copied_trg_name->length; + + if (triggers->names_list.push_back(trigger_name, + &table->mem_root)) + goto err_with_lex_cleanup; + } + else + { + /* + The Table_triggers_list is not constructed as a list of + trigger objects as one would expect, but rather of lists of + properties of equal length. Thus, even if we don't get the + trigger name, we still fill all in all the lists with + placeholders as we might otherwise create a skew in the + lists. Obviously, this has to be refactored. + */ + LEX_STRING *empty= alloc_lex_string(&table->mem_root); + if (!empty) + goto err_with_lex_cleanup; + + empty->str= const_cast(""); + empty->length= 0; + if (triggers->names_list.push_back(empty, &table->mem_root)) + goto err_with_lex_cleanup; + } + lex_end(&lex); + continue; + } + lex.sphead->set_info(0, 0, &lex.sp_chistics, (ulong) *trg_sql_mode); int event= lex.trg_chistics.event; @@ -1368,8 +1463,8 @@ bool Table_triggers_list::check_n_load(T if (triggers->names_list.push_back(&lex.sphead->m_name, &table->mem_root)) - goto err_with_lex_cleanup; - + goto err_with_lex_cleanup; + if (!(on_table_name= alloc_lex_string(&table->mem_root))) goto err_with_lex_cleanup; @@ -1394,9 +1489,8 @@ bool Table_triggers_list::check_n_load(T 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))) && - (!my_strcasecmp(table_alias_charset, lex.query_tables->table_name, - table_name) || + !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 @@ -1680,6 +1774,13 @@ bool Table_triggers_list::drop_all_trigg while ((trigger= it_name++)) { + /* + Trigger, which body we failed to parse during call + Table_triggers_list::check_n_load(), might be missing name. + Such triggers have zero-length name and are skipped here. + */ + if (trigger->length == 0) + continue; if (rm_trigname_file(path, db, trigger->str)) { /* @@ -1903,6 +2004,11 @@ bool Table_triggers_list::change_table_n } if (table.triggers) { + if (table.triggers->check_for_broken_triggers()) + { + result= 1; + goto end; + } LEX_STRING old_table_name= { (char *) old_table, strlen(old_table) }; LEX_STRING new_table_name= { (char *) new_table, strlen(new_table) }; /* @@ -1991,6 +2097,9 @@ bool Table_triggers_list::process_trigge sp_head *sp_trigger= bodies[event][time_type]; SELECT_LEX *save_current_select; + if (check_for_broken_triggers()) + return TRUE; + if (sp_trigger == NULL) return FALSE; @@ -2070,6 +2179,22 @@ void Table_triggers_list::mark_fields_us /** + 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(char *error_message) +{ + m_has_unparseable_trigger= TRUE; + strcpy(m_parse_error_text, error_message); +} + + +/** Trigger BUG#14090 compatibility hook. @param[in,out] unknown_key reference on the line with unknown === modified file 'sql/sql_trigger.h' --- a/sql/sql_trigger.h 2009-01-14 14:50:51 +0000 +++ b/sql/sql_trigger.h 2011-05-23 10:55:40 +0000 @@ -62,6 +62,27 @@ class Table_triggers_list: public Sql_al */ GRANT_INFO subject_table_grants[TRG_EVENT_MAX][TRG_ACTION_MAX]; + /** + 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_text[MYSQL_ERRMSG_SIZE]; + public: /** Field responsible for storing triggers definitions in file. @@ -84,7 +105,7 @@ public: /* End of character ser context. */ Table_triggers_list(TABLE *table_arg): - record1_field(0), trigger_table(table_arg) + record1_field(0), trigger_table(table_arg), m_has_unparseable_trigger(FALSE) { bzero((char *)bodies, sizeof(bodies)); bzero((char *)trigger_fields, sizeof(trigger_fields)); @@ -140,6 +161,8 @@ public: void mark_fields_used(trg_event_type event); + void set_parse_error(char *error_message); + friend class Item_trigger_field; friend int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, TABLE_LIST *table); @@ -155,6 +178,16 @@ private: const char *new_db_name, LEX_STRING *old_table_name, LEX_STRING *new_table_name); + + bool check_for_broken_triggers() + { + if (m_has_unparseable_trigger) + { + my_message(ER_PARSE_ERROR, m_parse_error_text, MYF(0)); + return TRUE; + } + return FALSE; + } }; extern const LEX_STRING trg_action_time_type_names[]; --===============4813455589773227492== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/dmitry.shulga@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: dmitry.shulga@stripped\ # tc09zrywz22v47ly # target_branch: file:///Users/shulga/projects/mysql/mysql-5.1-\ # bug11753738/ # testament_sha1: b8e263e6d954be77a61ea60d7907513b3b06f48d # timestamp: 2011-05-23 17:55:49 +0700 # base_revision_id: luis.soares@stripped\ # 4g8gpmg0ebg4jitd # # Begin bundle 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 --===============4813455589773227492==--