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