summaryrefslogtreecommitdiff
path: root/sql/sql_trigger.cc
diff options
context:
space:
mode:
authorDmitry Shulga <Dmitry.Shulga@oracle.com>2011-06-10 14:20:15 +0700
committerDmitry Shulga <Dmitry.Shulga@oracle.com>2011-06-10 14:20:15 +0700
commit2d715ba6042cc8e1ad5114c25070aa8d18095246 (patch)
tree1baeecb0b58bae9ae8a41c5f81245e2b8037b411 /sql/sql_trigger.cc
parent155df6c6574bbc23f8458ea958b838c02cd263d7 (diff)
parent1fea8c1b9070018be12f9c748fec781322944d7c (diff)
downloadmariadb-git-2d715ba6042cc8e1ad5114c25070aa8d18095246.tar.gz
Manual-merge of patch for bug#11753738 from mysql-5.1 tree.
Diffstat (limited to 'sql/sql_trigger.cc')
-rw-r--r--sql/sql_trigger.cc149
1 files changed, 139 insertions, 10 deletions
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index d026714c007..6e256d12275 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -31,6 +31,7 @@
#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>
/*************************************************************************/
@@ -305,6 +306,55 @@ 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];
+ 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,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* message,
+ MYSQL_ERROR ** 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.
@param thd current thread context (including trigger definition in LEX)
@@ -591,6 +641,8 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
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,
@@ -864,7 +916,7 @@ static bool rm_trigger_file(char *path, const char *db,
@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
@@ -1329,12 +1381,11 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
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
@@ -1345,6 +1396,54 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
*/
lex.set_trg_event_type_for_tables();
+ if (parse_error)
+ {
+ if (!triggers->m_has_unparseable_trigger)
+ triggers->set_parse_error_message(error_handler.get_error_message());
+ /* Currently sphead is always set to NULL in case of a parse error */
+ DBUG_ASSERT(lex.sphead == 0);
+ if (error_handler.get_trigger_name())
+ {
+ LEX_STRING *trigger_name;
+ const LEX_STRING *orig_trigger_name= error_handler.get_trigger_name();
+
+ if (!(trigger_name= alloc_lex_string(&table->mem_root)) ||
+ !(trigger_name->str= strmake_root(&table->mem_root,
+ orig_trigger_name->str,
+ orig_trigger_name->length)))
+ goto err_with_lex_cleanup;
+
+ trigger_name->length= orig_trigger_name->length;
+
+ if (triggers->names_list.push_back(trigger_name,
+ &table->mem_root))
+ goto err_with_lex_cleanup;
+ }
+ else
+ {
+ /*
+ The Table_triggers_list is not constructed as a list of
+ trigger objects as one would expect, but rather of lists of
+ properties of equal length. Thus, even if we don't get the
+ trigger name, we still fill all in all the lists with
+ placeholders as we might otherwise create a skew in the
+ lists. Obviously, this has to be refactored.
+ */
+ LEX_STRING *empty= alloc_lex_string(&table->mem_root);
+ if (!empty)
+ goto err_with_lex_cleanup;
+
+ empty->str= const_cast<char*>("");
+ empty->length= 0;
+ if (triggers->names_list.push_back(empty, &table->mem_root))
+ goto err_with_lex_cleanup;
+ }
+ lex_end(&lex);
+ continue;
+ }
+
+ lex.sphead->set_info(0, 0, &lex.sp_chistics, (ulong) *trg_sql_mode);
+
int event= lex.trg_chistics.event;
int action_time= lex.trg_chistics.action_time;
@@ -1411,9 +1510,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
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
@@ -1695,6 +1793,13 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, char *db, char *name)
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))
{
/*
@@ -1913,6 +2018,11 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
}
if (table.triggers)
{
+ if (table.triggers->check_for_broken_triggers())
+ {
+ result= 1;
+ goto end;
+ }
LEX_STRING old_table_name= { (char *) old_alias, strlen(old_alias) };
LEX_STRING new_table_name= { (char *) new_table, strlen(new_table) };
/*
@@ -2001,6 +2111,9 @@ bool Table_triggers_list::process_triggers(THD *thd,
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;
@@ -2135,6 +2248,22 @@ void Table_triggers_list::mark_fields_used(trg_event_type event)
/**
+ 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