diff options
author | Monty <monty@mariadb.org> | 2016-10-02 15:35:08 +0300 |
---|---|---|
committer | Monty <monty@mariadb.org> | 2016-10-05 01:11:07 +0300 |
commit | 8be53a389c8eebed337057fa366b7c4506ba16b1 (patch) | |
tree | abd36c95a598f1d6e5537e94fd7de034715ce299 /sql/sql_trigger.h | |
parent | 0bae1957dd124f8382ae6af1de0e2168fc200bfb (diff) | |
download | mariadb-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.h | 174 |
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, |