summaryrefslogtreecommitdiff
path: root/sql/sql_trigger.h
diff options
context:
space:
mode:
authorMonty <monty@mariadb.org>2016-10-02 15:35:08 +0300
committerMonty <monty@mariadb.org>2016-10-05 01:11:07 +0300
commit8be53a389c8eebed337057fa366b7c4506ba16b1 (patch)
treeabd36c95a598f1d6e5537e94fd7de034715ce299 /sql/sql_trigger.h
parent0bae1957dd124f8382ae6af1de0e2168fc200bfb (diff)
downloadmariadb-git-8be53a389c8eebed337057fa366b7c4506ba16b1.tar.gz
MDEV-6112 multiple triggers per table
This is similar to MysQL Worklog 3253, but with a different implementation. The disk format and SQL syntax is identical with MySQL 5.7. Fetures supported: - "Any" ammount of any trigger - Supports FOLLOWS and PRECEDES to be able to put triggers in a certain execution order. Implementation details: - Class Trigger added to hold information about a trigger. Before this trigger information was stored in a set of lists in Table_triggers_list and in Table_triggers_list::bodies - Each Trigger has a next field that poinst to the next Trigger with the same action and time. - When accessing a trigger, we now always access all linked triggers - The list are now only used to load and save trigger files. - MySQL trigger test case (trigger_wl3253) added and we execute these identically. - Even more gracefully handling of wrong trigger files than before. This is useful if a trigger file uses functions or syntax not provided by the server. - Each trigger now has a "Created" field that shows when the trigger was created, with 2 decimals. Other comments: - Many of the changes in test files was done because of the new "Created" field in the trigger file. This shows up in SHOW ... TRIGGER and when using information_schema.trigger. - Don't check if all memory is released if on uses --gdb; This is needed to be able to get a list from safemalloc of not freed memory while debugging. - Added option to trim_whitespace() to know how many prefix characters was skipped. - Changed a few ulonglong sql_mode to sql_mode_t, to find some wrong usage of sql_mode.
Diffstat (limited to 'sql/sql_trigger.h')
-rw-r--r--sql/sql_trigger.h174
1 files changed, 113 insertions, 61 deletions
diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h
index fa858a0582b..f1161cdd967 100644
--- a/sql/sql_trigger.h
+++ b/sql/sql_trigger.h
@@ -51,22 +51,92 @@ enum trg_action_time_type
TRG_ACTION_BEFORE= 0, TRG_ACTION_AFTER= 1, TRG_ACTION_MAX
};
+enum trigger_order_type
+{
+ TRG_ORDER_NONE= 0,
+ TRG_ORDER_FOLLOWS= 1,
+ TRG_ORDER_PRECEDES= 2
+};
+
+
+struct st_trg_execution_order
+{
+ /**
+ FOLLOWS or PRECEDES as specified in the CREATE TRIGGER statement.
+ */
+ enum trigger_order_type ordering_clause;
+
+ /**
+ Trigger name referenced in the FOLLOWS/PRECEDES clause of the
+ CREATE TRIGGER statement.
+ */
+ LEX_STRING anchor_trigger_name;
+};
-/**
- This class holds all information about triggers of table.
- TODO: Will it be merged into TABLE in the future ?
+class Table_triggers_list;
+
+/**
+ The trigger object
*/
-class Table_triggers_list: public Sql_alloc
+class Trigger :public Sql_alloc
{
- /** Triggers as SPs grouped by event, action_time */
- sp_head *bodies[TRG_EVENT_MAX][TRG_ACTION_MAX];
+public:
+ Trigger(Table_triggers_list *base_arg, sp_head *code):
+ base(base_arg), body(code), next(0), trigger_fields(0), action_order(0)
+ {
+ bzero((char *)&subject_table_grants, sizeof(subject_table_grants));
+ }
+ ~Trigger();
+ Table_triggers_list *base;
+ sp_head *body;
+ Trigger *next; /* Next trigger of same type */
+
/**
Heads of the lists linking items for all fields used in triggers
grouped by event and action_time.
*/
- Item_trigger_field *trigger_fields[TRG_EVENT_MAX][TRG_ACTION_MAX];
+ Item_trigger_field *trigger_fields;
+ LEX_STRING name;
+ LEX_STRING on_table_name; /* Raw table name */
+ LEX_STRING definition;
+ LEX_STRING definer;
+
+ /* Character sets used */
+ LEX_STRING client_cs_name;
+ LEX_STRING connection_cl_name;
+ LEX_STRING db_cl_name;
+
+ GRANT_INFO subject_table_grants;
+ ulonglong sql_mode;
+ /* Store create time. Can't be mysql_time_t as this holds also sub seconds */
+ ulonglong create_time;
+ trg_event_type event;
+ trg_action_time_type action_time;
+ uint action_order;
+
+ bool is_fields_updated_in_trigger(MY_BITMAP *used_fields);
+ void get_trigger_info(LEX_STRING *stmt, LEX_STRING *body,
+ LEX_STRING *definer);
+ /* Functions executed over each active trigger */
+ bool change_on_table_name(void* param_arg);
+ bool change_table_name(void* param_arg);
+ bool add_to_file_list(void* param_arg);
+};
+
+typedef bool (Trigger::*Triggers_processor)(void *arg);
+
+/**
+ This class holds all information about triggers of table.
+*/
+
+class Table_triggers_list: public Sql_alloc
+{
+ friend class Trigger;
+
+ /* Points to first trigger for a certain type */
+ Trigger *triggers[TRG_EVENT_MAX][TRG_ACTION_MAX];
/**
Copy of TABLE::Field array which all fields made nullable
(using extra_null_bitmap, if needed). Used for NEW values in
@@ -90,22 +160,6 @@ class Table_triggers_list: public Sql_alloc
/* TABLE instance for which this triggers list object was created */
TABLE *trigger_table;
- /**
- Names of triggers.
- Should correspond to order of triggers on definitions_list,
- used in CREATE/DROP TRIGGER for looking up trigger by name.
- */
- List<LEX_STRING> names_list;
- /**
- List of "ON table_name" parts in trigger definitions, used for
- updating trigger definitions during RENAME TABLE.
- */
- List<LEX_STRING> on_table_names_list;
-
- /**
- Grant information for each trigger (pair: subject table, trigger definer).
- */
- GRANT_INFO subject_table_grants[TRG_EVENT_MAX][TRG_ACTION_MAX];
/**
This flag indicates that one of the triggers was not parsed successfully,
@@ -127,6 +181,7 @@ class Table_triggers_list: public Sql_alloc
the trigger file.
*/
char m_parse_error_message[MYSQL_ERRMSG_SIZE];
+ uint count; /* Number of triggers */
public:
/**
@@ -138,6 +193,8 @@ public:
List of sql modes for triggers
*/
List<ulonglong> definition_modes_list;
+ /** Create times for triggers */
+ List<ulonglong> create_times;
List<LEX_STRING> definers_list;
@@ -152,11 +209,9 @@ public:
Table_triggers_list(TABLE *table_arg)
:record0_field(0), extra_null_bitmap(0), record1_field(0),
trigger_table(table_arg),
- m_has_unparseable_trigger(false)
+ m_has_unparseable_trigger(false), count(0)
{
- bzero((char *)bodies, sizeof(bodies));
- bzero((char *)trigger_fields, sizeof(trigger_fields));
- bzero((char *)&subject_table_grants, sizeof(subject_table_grants));
+ bzero((char *) triggers, sizeof(triggers));
}
~Table_triggers_list();
@@ -165,26 +220,9 @@ public:
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,
- ulong *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,
- ulonglong *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);
+ void empty_lists();
+ bool create_lists_needed_for_files(MEM_ROOT *root);
+ bool save_trigger_file(THD *thd, const char *db, const char *table_name);
static bool check_n_load(THD *thd, const char *db, const char *table_name,
TABLE *table, bool names_only);
@@ -194,15 +232,32 @@ public:
const char *old_table,
const char *new_db,
const char *new_table);
+ void add_trigger(trg_event_type event_type,
+ trg_action_time_type action_time,
+ trigger_order_type ordering_clause,
+ LEX_STRING *anchor_trigger_name,
+ Trigger *trigger);
+ Trigger *get_trigger(trg_event_type event_type,
+ trg_action_time_type action_time)
+ {
+ return triggers[event_type][action_time];
+ }
+ /* Simpler version of the above, to avoid casts in the code */
+ Trigger *get_trigger(uint event_type, uint action_time)
+ {
+ return get_trigger((trg_event_type) event_type,
+ (trg_action_time_type) action_time);
+ }
+
bool has_triggers(trg_event_type event_type,
trg_action_time_type action_time)
{
- return (bodies[event_type][action_time] != NULL);
+ return get_trigger(event_type,action_time) != 0;
}
bool has_delete_triggers()
{
- return (bodies[TRG_EVENT_DELETE][TRG_ACTION_BEFORE] ||
- bodies[TRG_EVENT_DELETE][TRG_ACTION_AFTER]);
+ return (has_triggers(TRG_EVENT_DELETE,TRG_ACTION_BEFORE) ||
+ has_triggers(TRG_EVENT_DELETE,TRG_ACTION_AFTER));
}
void mark_fields_used(trg_event_type event);
@@ -215,10 +270,6 @@ public:
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);
-
Field **nullable_fields() { return record0_field; }
void reset_extra_null_bitmap()
{
@@ -227,12 +278,16 @@ public:
bzero(extra_null_bitmap, null_bytes);
}
+ Trigger *find_trigger(const LEX_STRING *name, bool remove_from_list);
+
+ Trigger* for_all_triggers(Triggers_processor func, void *arg);
+
private:
bool prepare_record_accessors(TABLE *table);
- LEX_STRING* change_table_name_in_trignames(const char *old_db_name,
- const char *new_db_name,
- LEX_STRING *new_table_name,
- LEX_STRING *stopper);
+ Trigger *change_table_name_in_trignames(const char *old_db_name,
+ const char *new_db_name,
+ LEX_STRING *new_table_name,
+ Trigger *trigger);
bool change_table_name_in_triggers(THD *thd,
const char *old_db_name,
const char *new_db_name,
@@ -257,9 +312,6 @@ inline Field **TABLE::field_to_fill()
}
-extern const LEX_STRING trg_action_time_type_names[];
-extern const LEX_STRING trg_event_type_names[];
-
bool add_table_for_trigger(THD *thd,
const sp_name *trg_name,
bool continue_if_not_exist,