1 /* Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include "sql/event_db_repository.h"
24
25 #include <vector>
26
27 #include "lex_string.h"
28 #include "my_dbug.h"
29 #include "my_sys.h"
30 #include "mysqld_error.h"
31 #include "sql/auth/auth_acls.h"
32 #include "sql/auth/sql_security_ctx.h"
33 #include "sql/dd/cache/dictionary_client.h" // fetch_schema_components
34 #include "sql/dd/dd_event.h"
35 #include "sql/dd/string_type.h"
36 #include "sql/dd/types/event.h"
37 #include "sql/derror.h"
38 #include "sql/event_data_objects.h"
39 #include "sql/event_parse_data.h"
40 #include "sql/sp_head.h"
41 #include "sql/sql_class.h"
42 #include "sql/sql_error.h"
43 #include "sql/sql_lex.h"
44 #include "sql/thd_raii.h"
45 #include "sql/transaction.h"
46 #include "sql/tztime.h" // struct Time_zone
47
48 /**
49 @addtogroup Event_Scheduler
50 @{
51 */
52
53 /**
54 Creates an event object and persist to Data Dictionary.
55
56 @pre All semantic checks must be performed outside.
57
58 @param[in,out] thd THD
59 @param[in] parse_data Parsed event definition
60 @param[in] create_if_not true if IF NOT EXISTS clause was provided
61 to CREATE EVENT statement
62 @param[out] event_already_exists When method is completed successfully
63 set to true if event already exists else
64 set to false
65 @retval false Success
66 @retval true Error
67 */
68
create_event(THD * thd,Event_parse_data * parse_data,bool create_if_not,bool * event_already_exists)69 bool Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
70 bool create_if_not,
71 bool *event_already_exists) {
72 DBUG_TRACE;
73 sp_head *sp = thd->lex->sphead;
74 DBUG_ASSERT(sp);
75
76 const dd::Schema *schema = nullptr;
77 const dd::Event *event = nullptr;
78 if (thd->dd_client()->acquire(parse_data->dbname.str, &schema) ||
79 thd->dd_client()->acquire(parse_data->dbname.str, parse_data->name.str,
80 &event))
81 return true;
82
83 if (schema == nullptr) {
84 my_error(ER_BAD_DB_ERROR, MYF(0), parse_data->dbname.str);
85 return true;
86 }
87
88 *event_already_exists = (event != nullptr);
89
90 if (*event_already_exists) {
91 if (create_if_not) {
92 push_warning_printf(thd, Sql_condition::SL_NOTE, ER_EVENT_ALREADY_EXISTS,
93 ER_THD(thd, ER_EVENT_ALREADY_EXISTS),
94 parse_data->name.str);
95 return false;
96 }
97 my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str);
98 return true;
99 }
100
101 return dd::create_event(thd, *schema, parse_data->name.str, sp->m_body.str,
102 sp->m_body_utf8.str, thd->lex->definer, parse_data);
103 }
104
105 /**
106 Used to execute ALTER EVENT. Pendant to Events::update_event().
107
108 @param[in] thd THD context
109 @param[in] parse_data parsed event definition
110 @param[in] new_dbname not NULL if ALTER EVENT RENAME
111 points at a new database name
112 @param[in] new_name not NULL if ALTER EVENT RENAME
113 points at a new event name
114
115 @pre All semantic checks are performed outside this function.
116
117 @retval false Success
118 @retval true Error (reported)
119 */
120
update_event(THD * thd,Event_parse_data * parse_data,const LEX_CSTRING * new_dbname,const LEX_CSTRING * new_name)121 bool Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
122 const LEX_CSTRING *new_dbname,
123 const LEX_CSTRING *new_name) {
124 DBUG_TRACE;
125 sp_head *sp = thd->lex->sphead;
126
127 /* None or both must be set */
128 DBUG_ASSERT((new_dbname && new_name) || new_dbname == new_name);
129
130 DBUG_PRINT("info", ("dbname: %s", parse_data->dbname.str));
131 DBUG_PRINT("info", ("name: %s", parse_data->name.str));
132 DBUG_PRINT("info", ("user: %s", parse_data->definer.str));
133
134 /* first look whether we overwrite */
135 if (new_name) {
136 DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str));
137
138 const dd::Event *new_event = nullptr;
139 if (thd->dd_client()->acquire(new_dbname->str, new_name->str, &new_event))
140 return true;
141
142 if (new_event != nullptr) {
143 my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str);
144 return true;
145 }
146 }
147
148 const dd::Schema *new_schema = nullptr;
149 if (new_dbname != nullptr) {
150 if (thd->dd_client()->acquire(new_dbname->str, &new_schema)) return true;
151
152 if (new_schema == nullptr) {
153 my_error(ER_BAD_DB_ERROR, MYF(0), new_dbname->str);
154 return true;
155 }
156 }
157
158 const dd::Schema *schema = nullptr;
159 dd::Event *event = nullptr;
160 if (thd->dd_client()->acquire(parse_data->dbname.str, &schema) ||
161 thd->dd_client()->acquire_for_modification(parse_data->dbname.str,
162 parse_data->name.str, &event))
163 return true;
164
165 if (event == nullptr) {
166 my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), parse_data->name.str);
167 return true;
168 }
169 DBUG_ASSERT(schema != nullptr); // Must exist if event exists.
170
171 /*
172 If definer has the SYSTEM_USER privilege then invoker can alter event
173 only if latter also has same privilege.
174 */
175 Security_context *sctx = thd->security_context();
176 Auth_id definer(event->definer_user().c_str(), event->definer_host().c_str());
177 if (sctx->can_operate_with(definer, consts::system_user, true)) return true;
178
179 // Update Event in the data dictionary with altered event object attributes.
180 bool ret = dd::update_event(
181 thd, event, *schema, new_schema, new_name != nullptr ? new_name->str : "",
182 (parse_data->body_changed) ? sp->m_body.str : event->definition(),
183 (parse_data->body_changed) ? sp->m_body_utf8.str
184 : event->definition_utf8(),
185 thd->lex->definer, parse_data);
186 return ret;
187 }
188
189 /**
190 Delete event.
191
192 @param[in] thd THD context
193 @param[in] db Database name
194 @param[in] name Event name
195 @param[in] drop_if_exists DROP IF EXISTS clause was specified.
196 If set, and the event does not exist,
197 the error is downgraded to a warning.
198 @param[out] event_exists Set to true if event exists. Set to
199 false otherwise.
200
201 @retval false success
202 @retval true error (reported)
203 */
204
drop_event(THD * thd,LEX_CSTRING db,LEX_CSTRING name,bool drop_if_exists,bool * event_exists)205 bool Event_db_repository::drop_event(THD *thd, LEX_CSTRING db, LEX_CSTRING name,
206 bool drop_if_exists, bool *event_exists) {
207 DBUG_TRACE;
208 /*
209 Turn off row binlogging of this statement and use statement-based
210 so that all supporting tables are updated for CREATE EVENT command.
211 When we are going out of the function scope, the original binary
212 format state will be restored.
213 */
214 Save_and_Restore_binlog_format_state binlog_format_state(thd);
215
216 DBUG_PRINT("enter", ("%s@%s", db.str, name.str));
217
218 const dd::Event *event_ptr = nullptr;
219 if (thd->dd_client()->acquire(db.str, name.str, &event_ptr)) {
220 // Error is reported by the dictionary subsystem.
221 return true;
222 }
223
224 if (event_ptr == nullptr) {
225 *event_exists = false;
226
227 // Event not found
228 if (!drop_if_exists) {
229 my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
230 return true;
231 }
232
233 push_warning_printf(thd, Sql_condition::SL_NOTE, ER_SP_DOES_NOT_EXIST,
234 ER_THD(thd, ER_SP_DOES_NOT_EXIST), "Event", name.str);
235 return false;
236 }
237 /*
238 If definer has the SYSTEM_USER privilege then invoker can drop event
239 only if latter also has same privilege.
240 */
241 Auth_id definer(event_ptr->definer_user().c_str(),
242 event_ptr->definer_host().c_str());
243 Security_context *sctx = thd->security_context();
244 if (sctx->can_operate_with(definer, consts::system_user, true)) return true;
245
246 *event_exists = true;
247 return thd->dd_client()->drop(event_ptr);
248 }
249
250 /**
251 Drops all events in the selected database.
252
253 @param thd THD context
254 @param schema The database under which events are to be dropped.
255
256 @returns true on error, false on success.
257 */
258
drop_schema_events(THD * thd,const dd::Schema & schema)259 bool Event_db_repository::drop_schema_events(THD *thd,
260 const dd::Schema &schema) {
261 DBUG_TRACE;
262
263 dd::cache::Dictionary_client::Auto_releaser releaser(thd->dd_client());
264
265 std::vector<const dd::Event *> events;
266 if (thd->dd_client()->fetch_schema_components(&schema, &events)) return true;
267
268 for (const dd::Event *event_obj : events) {
269 if (thd->dd_client()->drop(event_obj)) {
270 my_error(ER_SP_DROP_FAILED, MYF(0), "Drop failed for Event: %s",
271 event_obj->name().c_str());
272 return true;
273 }
274 }
275
276 return false;
277 }
278
279 /**
280 Looks for a named event in the Data Dictionary and load it.
281
282 @pre The given thread does not have open tables.
283
284 @retval false success
285 @retval true error
286 */
287
load_named_event(THD * thd,LEX_CSTRING dbname,LEX_CSTRING name,Event_basic * etn)288 bool Event_db_repository::load_named_event(THD *thd, LEX_CSTRING dbname,
289 LEX_CSTRING name, Event_basic *etn) {
290 const dd::Event *event_obj = nullptr;
291
292 DBUG_TRACE;
293 DBUG_PRINT("enter", ("thd: %p name: %*s", thd, (int)name.length, name.str));
294
295 dd::cache::Dictionary_client::Auto_releaser releaser(thd->dd_client());
296
297 if (thd->dd_client()->acquire(dbname.str, name.str, &event_obj)) {
298 // Error is reported by the dictionary subsystem.
299 return true;
300 }
301
302 if (event_obj == nullptr) {
303 my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
304 return true;
305 }
306
307 if (etn->fill_event_info(thd, *event_obj, dbname.str)) {
308 my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0), "mysql", "events");
309 return true;
310 }
311
312 return false;
313 }
314
315 /**
316 Update the event in Data Dictionary with changed status
317 and/or last execution time.
318 */
319
update_timing_fields_for_event(THD * thd,LEX_CSTRING event_db_name,LEX_CSTRING event_name,my_time_t last_executed,ulonglong status)320 bool Event_db_repository::update_timing_fields_for_event(
321 THD *thd, LEX_CSTRING event_db_name, LEX_CSTRING event_name,
322 my_time_t last_executed, ulonglong status) {
323 DBUG_TRACE;
324 // Turn off autocommit.
325 Disable_autocommit_guard autocommit_guard(thd);
326
327 /*
328 Turn off row binlogging of this statement and use statement-based
329 so that all supporting tables are updated for CREATE EVENT command.
330 When we are going out of the function scope, the original binary
331 format state will be restored.
332 */
333 Save_and_Restore_binlog_format_state binlog_format_state(thd);
334
335 DBUG_ASSERT(thd->security_context()->check_access(SUPER_ACL));
336
337 dd::Event *event = nullptr;
338 dd::cache::Dictionary_client::Auto_releaser releaser(thd->dd_client());
339 if (thd->dd_client()->acquire_for_modification(event_db_name.str,
340 event_name.str, &event))
341 return true;
342 if (event == nullptr) return true;
343
344 if (dd::update_event_time_and_status(thd, event, last_executed, status)) {
345 trans_rollback_stmt(thd);
346 // Full rollback in case we have THD::transaction_rollback_request.
347 trans_rollback(thd);
348 return true;
349 }
350
351 return trans_commit_stmt(thd) || trans_commit(thd);
352 }
353
354 /**
355 @} (End of group Event_Scheduler)
356 */
357 // XXX:
358