mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			391 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			391 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#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>
 | 
						|
 | 
						|
/** 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
 | 
						|
};
 | 
						|
 | 
						|
static inline uint8 trg2bit(enum trg_event_type trg)
 | 
						|
{ return static_cast<uint8>(1 << static_cast<int>(trg)); }
 | 
						|
 | 
						|
#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
 | 
						|
*/
 | 
						|
 | 
						|
class Trigger :public Sql_alloc
 | 
						|
{
 | 
						|
public:
 | 
						|
    Trigger(Table_triggers_list *base_arg, sp_head *code):
 | 
						|
    base(base_arg), body(code), next(0),
 | 
						|
    sql_mode{0},
 | 
						|
    hr_create_time{(unsigned long long)-1},
 | 
						|
    event{TRG_EVENT_MAX},
 | 
						|
    action_time{TRG_ACTION_MAX},
 | 
						|
    action_order{0},
 | 
						|
    updatable_columns{nullptr}
 | 
						|
  {
 | 
						|
    bzero((char *)&subject_table_grants, sizeof(subject_table_grants));
 | 
						|
  }
 | 
						|
  ~Trigger();
 | 
						|
  Table_triggers_list *base;
 | 
						|
  sp_head *body;
 | 
						|
  Trigger *next;                                /* Next trigger of same type */
 | 
						|
 | 
						|
  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
 | 
						|
  trg_event_type event;
 | 
						|
  trg_action_time_type action_time;
 | 
						|
  uint action_order;
 | 
						|
  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. */
 | 
						|
 | 
						|
  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_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;
 | 
						|
 | 
						|
#endif /* SQL_TRIGGER_INCLUDED */
 |