#ifndef SQL_TRIGGER_INCLUDED
#define SQL_TRIGGER_INCLUDED
/*
Copyright ( c ) 2004 , 2011 , Oracle and / or its affiliates .
Copyright ( c ) 2017 , MariaDB Corporation .
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; version 2 of the License .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
#include <mysqld_error.h>
/* Forward declarations */
class Item_trigger_field;
class sp_head;
class sp_name;
class Query_tables_list;
struct TABLE_LIST;
class Query_tables_list;
typedef struct st_ddl_log_state DDL_LOG_STATE;
#include <sql_list.h>
static const uint8 TRG_EVENT_UNKNOWN= 0 ;
/** Event on which trigger is invoked. */
enum trg_event_type
{
TRG_EVENT_INSERT= 0 ,
TRG_EVENT_UPDATE= 1 ,
TRG_EVENT_DELETE= 2 ,
TRG_EVENT_MAX
};
typedef uint8 trg_event_set;
static inline uint8 trg2bit(enum trg_event_type trg)
{ return static_cast <uint8>(1 << static_cast <int >(trg)); }
/**
Check whether the specified trigger event type is set in
the trigger ' s event mask
*/
static inline bool is_trg_event_on(uint8 trg_event_mask,
enum trg_event_type event_type)
{
return (trg_event_mask & trg2bit(event_type)) != 0 ;
}
/**
Check whether the specified trigger event type is the right most event bit
that is set in the trigger ' s event mask
*/
static inline bool is_the_right_most_event_bit(trg_event_set events,
int event_type)
{
return (1 << event_type) == (events & ~((events - 1 ) & events));
}
/**
Check whether trg_events_mask includes all bits of trg_events
*/
static inline bool is_subset_of_trg_events(trg_event_set trg_events_mask,
trg_event_set trg_events)
{
return (trg_events_mask & trg_events) == trg_events;
}
#include "table.h" /* GRANT_INFO */
/*
We need this two enums here instead of sql_lex . h because
at least one of them is used by Item_trigger_field interface .
Time when trigger is invoked ( i . e . before or after row actually
inserted / updated / deleted ) .
*/
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 .
Cannot be Lex_ident_trigger ,
as this structure is used in % union in sql_yacc . yy
*/
LEX_CSTRING anchor_trigger_name; // Used in sql_yacc %union
};
/*
Parameter to change_table_name_in_triggers ( )
*/
class TRIGGER_RENAME_PARAM
{
public :
TABLE table;
bool upgrading50to51;
bool got_error;
TRIGGER_RENAME_PARAM()
{
upgrading50to51= got_error= 0 ;
table.reset();
}
~TRIGGER_RENAME_PARAM()
{
reset();
}
void reset();
};
class Table_triggers_list;
/**
The trigger object
One instance of the Trigger class can handle several trigger events .
E . g . , one object of the Trigger class can be instantiated to handle
every of events INSERT , UPDATE , DELETE .
Since one instance of the Trigger class can be associated with several
trigger events , the data member ` action_order ` and ` next ` are represented
as an array of TRG_EVENT_MAX elements .
*/
class Trigger :public Sql_alloc
{
public :
Trigger(Table_triggers_list *base_arg, sp_head *code):
base(base_arg), body(code),
sql_mode{0 },
hr_create_time{(unsigned long long )-1 },
sql_path{STRING_WITH_LEN("" )},
events{0 },
action_time{TRG_ACTION_MAX},
updatable_columns{nullptr}
{
bzero((char *)&subject_table_grants, sizeof (subject_table_grants));
bzero(next, sizeof (next));
bzero(action_order, sizeof (action_order));
}
~Trigger();
Table_triggers_list *base;
sp_head *body;
/**
Next trigger of the same type in every of the event groups
*/
Trigger *next[TRG_EVENT_MAX];
Lex_ident_trigger name;
LEX_CSTRING on_table_name; /* Raw table name */
LEX_CSTRING definition;
LEX_CSTRING definer;
/* Character sets used */
LEX_CSTRING client_cs_name;
LEX_CSTRING connection_cl_name;
LEX_CSTRING db_cl_name;
GRANT_INFO subject_table_grants;
sql_mode_t sql_mode;
/* Store create time. Can't be mysql_time_t as this holds also sub seconds */
my_hrtime_t hr_create_time; // Create time timestamp in microseconds
LEX_CSTRING sql_path;
/* Set of events this trigger object assigned to */
trg_event_set events;
trg_action_time_type action_time;
/**
action order of the trigger for every of supplied event type
*/
uint action_order[TRG_EVENT_MAX];
List<LEX_CSTRING> *updatable_columns;
void get_trigger_info(LEX_CSTRING *stmt, LEX_CSTRING *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);
bool match_updatable_columns(List<Item> &fields);
};
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
BEFORE INSERT / UPDATE triggers .
*/
Field **record0_field;
uchar *extra_null_bitmap, *extra_null_bitmap_init;
/**
Copy of TABLE : : Field array with field pointers set to TABLE : : record [ 1 ]
buffer instead of TABLE : : record [ 0 ] ( used for OLD values in on UPDATE
trigger and DELETE trigger when it is called for REPLACE ) .
*/
Field **record1_field;
/**
During execution of trigger new_field and old_field should point to the
array of fields representing new or old version of row correspondingly
( so it can point to TABLE : : field or to Tale_triggers_list : : record1_field )
*/
Field **new_field;
Field **old_field;
/* TABLE instance for which this triggers list object was created */
TABLE *trigger_table;
/**
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_message[MYSQL_ERRMSG_SIZE];
uint count; /* Number of triggers */
public :
/**
Field responsible for storing triggers definitions in file .
It have to be public because we are using it directly from parser .
*/
List<LEX_CSTRING> definitions_list;
/**
List of sql modes for triggers
*/
List<ulonglong> definition_modes_list;
/** Create times for triggers */
List<ulonglong> hr_create_times;
List<LEX_CSTRING> definers_list;
/* Character set context, used for parsing and executing triggers. */
List<LEX_CSTRING> client_cs_names;
List<LEX_CSTRING> connection_cl_names;
List<LEX_CSTRING> db_cl_names;
/* End of character ser context. */
/**
List of paths for triggers
*/
List<LEX_CSTRING> sql_paths;
Table_triggers_list(TABLE *table_arg)
:record0_field(0 ), extra_null_bitmap(0 ), extra_null_bitmap_init(0 ),
record1_field(0 ), trigger_table(table_arg),
m_has_unparseable_trigger(false ), count(0 )
{
bzero((char *) triggers, sizeof (triggers));
}
~Table_triggers_list();
bool create_trigger(THD *thd, TABLE_LIST *table, String *stmt_query,
DDL_LOG_STATE *ddl_log_state,
DDL_LOG_STATE *ddl_log_state_tmp_file);
bool drop_trigger(THD *thd, TABLE_LIST *table,
LEX_CSTRING *sp_name,
String *stmt_query, DDL_LOG_STATE *ddl_log_state);
bool process_triggers(THD *thd, trg_event_type event,
trg_action_time_type time_type,
bool old_row_is_record1,
bool *skip_row_indicator,
List<Item> *fields_in_update_stmt= nullptr);
void empty_lists();
bool create_lists_needed_for_files(MEM_ROOT *root);
bool save_trigger_file(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name);
static bool check_n_load(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name,
TABLE *table, bool names_only);
static bool drop_all_triggers(THD *thd, const LEX_CSTRING *db,
const LEX_CSTRING *table_name, myf MyFlags);
static bool prepare_for_rename(THD *thd, TRIGGER_RENAME_PARAM *param,
const Lex_ident_db &db,
const Lex_ident_table &old_alias,
const Lex_ident_table &old_table,
const Lex_ident_db &new_db,
const Lex_ident_table &new_table);
static bool change_table_name(THD *thd, TRIGGER_RENAME_PARAM *param,
const LEX_CSTRING *db,
const LEX_CSTRING *old_alias,
const LEX_CSTRING *old_table,
const LEX_CSTRING *new_db,
const LEX_CSTRING *new_table);
void add_trigger(trg_event_set trg_events,
trg_action_time_type action_time,
trigger_order_type ordering_clause,
const Lex_ident_trigger &anchor_trigger_name,
Trigger *trigger);
void add_trigger(trg_event_type event_type,
trg_action_time_type action_time,
trigger_order_type ordering_clause,
const Lex_ident_trigger &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 get_trigger(event_type,action_time) != 0 ;
}
bool has_delete_triggers()
{
return (has_triggers(TRG_EVENT_DELETE,TRG_ACTION_BEFORE) ||
has_triggers(TRG_EVENT_DELETE,TRG_ACTION_AFTER));
}
bool match_updatable_columns(List<Item> *fields);
void mark_fields_used(trg_event_type event);
void set_parse_error_message(char *error_message);
friend class Item_trigger_field;
bool add_tables_and_routines_for_triggers(THD *thd,
Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list);
Field **nullable_fields() { return record0_field; }
void clear_extra_null_bitmap()
{
if (size_t null_bytes= extra_null_bitmap_init - extra_null_bitmap)
bzero(extra_null_bitmap, null_bytes);
}
void default_extra_null_bitmap()
{
if (size_t null_bytes= extra_null_bitmap_init - extra_null_bitmap)
memcpy(extra_null_bitmap, extra_null_bitmap_init, null_bytes);
}
Trigger *find_trigger(const LEX_CSTRING *name, bool remove_from_list);
Trigger* for_all_triggers(Triggers_processor func, void *arg);
private :
bool prepare_record_accessors(TABLE *table);
Trigger *change_table_name_in_trignames(const LEX_CSTRING *old_db_name,
const LEX_CSTRING *new_db_name,
const LEX_CSTRING *new_table_name,
Trigger *trigger);
bool change_table_name_in_triggers(THD *thd,
const LEX_CSTRING *old_db_name,
const LEX_CSTRING *new_db_name,
const LEX_CSTRING *old_table_name,
const LEX_CSTRING *new_table_name);
bool check_for_broken_triggers()
{
if (m_has_unparseable_trigger)
{
my_message(ER_PARSE_ERROR, m_parse_error_message, MYF(0 ));
return true ;
}
return false ;
}
public :
TABLE *get_subject_table()
{
return trigger_table;
}
};
bool add_table_for_trigger(THD *thd,
const sp_name *trg_name,
bool continue_if_not_exist,
TABLE_LIST **table);
void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path);
bool check_trn_exists(const LEX_CSTRING *trn_path);
bool load_table_name_for_trigger(THD *thd,
const sp_name *trg_name,
const LEX_CSTRING *trn_path,
LEX_CSTRING *tbl_name);
bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create);
bool rm_trigname_file(char *path, const LEX_CSTRING *db,
const LEX_CSTRING *trigger_name, myf MyFlags);
extern const char * const TRG_EXT;
extern const char * const TRN_EXT;
extern const LEX_CSTRING trg_event_type_names[];
#endif /* SQL_TRIGGER_INCLUDED */
Messung V0.5 in Prozent C=92 H=100 G=95
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland