1 /*
2    Copyright (c) 2013, 2021, Oracle and/or its affiliates.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 #include "my_global.h"
26 #include "sql_class.h"
27 #include "my_bitmap.h"
28 
29 #include "trigger_chain.h"
30 #include "table_trigger_dispatcher.h"
31 #include "table.h"
32 #include "sql_list.h"
33 #include "trigger_loader.h"
34 #include "trigger.h"
35 #include "sp_head.h"                  // sp_head::mark_used_trigger_fields
36 
37 
38 /**
39   Add a new trigger into the list of triggers with the same
40   ACTION/TIMING value combination. This method is called during
41   handling of statement CREATE TRIGGER.
42 
43   @param mem_root                 memory root for allocations
44   @param new_trigger              pointer to the Trigger to add into the list
45   @param ordering_clause          trigger ordering clause (FOLLOWS/PRECEDES)
46   @param referenced_trigger_name  trigger name referenced by clause
47                                   FOLLOWS/PRECEDES in the CREATE TRIGGER
48                                   statement
49 
50   @return Operation status.
51     @retval false Success.
52     @retval true  Failure: either trigger not found or OOM. Set error in
53     Diagnostic_area in case when trigger not found.
54 */
55 
add_trigger(MEM_ROOT * mem_root,Trigger * new_trigger,enum_trigger_order_type ordering_clause,const LEX_STRING & referenced_trigger_name)56 bool Trigger_chain::add_trigger(MEM_ROOT *mem_root,
57                                 Trigger *new_trigger,
58                                 enum_trigger_order_type ordering_clause,
59                                 const LEX_STRING &referenced_trigger_name)
60 {
61   switch (ordering_clause)
62   {
63   case TRG_ORDER_NONE:
64     return add_trigger(mem_root, new_trigger);
65 
66   case TRG_ORDER_FOLLOWS:
67   case TRG_ORDER_PRECEDES:
68     {
69       assert(referenced_trigger_name.str);
70 
71       List_iterator<Trigger> it(m_triggers);
72       List_iterator<Trigger> it2= it;
73 
74       while (true)
75       {
76         Trigger *t= it2++;
77 
78         if (!t)
79         {
80           my_error(ER_REFERENCED_TRG_DOES_NOT_EXIST,
81                    MYF(0), referenced_trigger_name.str);
82           return true;
83         }
84 
85         if (my_strcasecmp(table_alias_charset,
86                           t->get_trigger_name().str,
87                           referenced_trigger_name.str) == 0)
88         {
89           break;
90         }
91 
92         it= it2;
93       }
94 
95       if (ordering_clause == TRG_ORDER_FOLLOWS)
96         it= it2;
97 
98       bool rc;
99 
100       if (it.is_before_first())
101         rc= m_triggers.push_front(new_trigger, mem_root);
102       else
103         rc= it.after(new_trigger, mem_root);
104 
105       return rc;
106     }
107   }
108 
109   assert(false);
110   return true;
111 }
112 
113 
114 /**
115   Add a new trigger into the list of triggers with the same
116   ACTION/TIMING value combination. This method is called when a trigger
117   is loaded from Data Dictionary.
118 
119   @param mem_root        memory root for allocations
120   @param new_trigger     pointer to the Trigger to add into the list
121 
122   @return Operation status.
123     @retval false Success.
124     @retval true  Failure.
125 */
126 
add_trigger(MEM_ROOT * mem_root,Trigger * new_trigger)127 bool Trigger_chain::add_trigger(MEM_ROOT *mem_root, Trigger *new_trigger)
128 {
129   return m_triggers.push_back(new_trigger, mem_root);
130 }
131 
132 
133 /**
134   Run every trigger in the list of triggers.
135 
136   @param thd  Thread context
137   @return  Result of trigger execution
138     @retval false  all triggers in the list were executed successfully.
139     @retval true   some trigger was failed. We stop triggers execution
140                    on the first failed trigger and don't attempt to finish
141                    the rest of triggers located after the failed one.
142 */
143 
execute_triggers(THD * thd)144 bool Trigger_chain::execute_triggers(THD *thd)
145 {
146   List_iterator_fast<Trigger> it(m_triggers);
147   Trigger *t;
148 
149   while ((t= it++))
150   {
151     if (t->execute(thd))
152       return true;
153   }
154 
155   return false;
156 }
157 
158 
159 /**
160   Iterate over the list of triggers and add tables and routines used by trigger
161   to the set of elements used by statement.
162 
163   @param [in]     thd               thread context
164   @param [in out] prelocking_ctx    prelocking context of the statement
165   @param [in]     table_list        TABLE_LIST for the table
166 */
167 
add_tables_and_routines(THD * thd,Query_tables_list * prelocking_ctx,TABLE_LIST * table_list)168 void Trigger_chain::add_tables_and_routines(THD *thd,
169                                             Query_tables_list *prelocking_ctx,
170                                             TABLE_LIST *table_list)
171 {
172   List_iterator_fast<Trigger> it(m_triggers);
173   Trigger *t;
174 
175   while ((t= it++))
176     t->add_tables_and_routines(thd, prelocking_ctx, table_list);
177 }
178 
179 
180 /**
181   Iterate over the list of triggers and mark fields of subject table
182   which we read/set in every trigger.
183 
184   @param [in] subject_table trigger subject table
185 */
186 
mark_fields(TABLE * subject_table)187 void Trigger_chain::mark_fields(TABLE *subject_table)
188 {
189   List_iterator_fast<Trigger> it(m_triggers);
190   Trigger *t;
191 
192   while ((t= it++))
193     t->get_sp()->mark_used_trigger_fields(subject_table);
194 }
195 
196 
197 /**
198   Iterate over the list of triggers and check whether some
199   table's fields are used in any trigger.
200 
201   @param [in] used_fields       bitmap of fields to check
202 
203   @return Check result
204     @retval true   Some table fields are used in trigger
205     @retval false  None of table fields are used in trigger
206 */
207 
has_updated_trigger_fields(const MY_BITMAP * used_fields)208 bool Trigger_chain::has_updated_trigger_fields(const MY_BITMAP *used_fields)
209 {
210   List_iterator_fast<Trigger> it(m_triggers);
211   Trigger *t;
212 
213   while ((t= it++))
214   {
215     // Even if one trigger is unparseable, the whole thing is not usable.
216 
217     if (t->has_parse_error())
218       return false;
219 
220     if (t->get_sp()->has_updated_trigger_fields(used_fields))
221       return true;
222   }
223 
224   return false;
225 }
226 
227 
228 /**
229   Recalculate action_order value for every trigger in the list.
230 */
231 
renumerate_triggers()232 void Trigger_chain::renumerate_triggers()
233 {
234   ulonglong action_order= 1;
235 
236   List_iterator_fast<Trigger> it(m_triggers);
237   Trigger *t;
238 
239   while ((t= it++))
240   {
241     t->set_action_order(action_order);
242     ++action_order;
243   }
244 }
245