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