~ubuntu-branches/ubuntu/precise/mysql-5.1/precise

« back to all changes in this revision

Viewing changes to sql/event_data_objects.cc

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Tretkowski
  • Date: 2010-03-17 14:56:02 UTC
  • Revision ID: james.westby@ubuntu.com-20100317145602-x7e30l1b2sb5s6w6
Tags: upstream-5.1.45
ImportĀ upstreamĀ versionĀ 5.1.45

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2004-2006 MySQL AB
 
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 as published by
 
5
   the Free Software Foundation; version 2 of the License.
 
6
 
 
7
   This program is distributed in the hope that it will be useful,
 
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
   GNU General Public License for more details.
 
11
 
 
12
   You should have received a copy of the GNU General Public License
 
13
   along with this program; if not, write to the Free Software
 
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
15
 
 
16
#define MYSQL_LEX 1
 
17
#include "mysql_priv.h"
 
18
#include "events.h"
 
19
#include "event_data_objects.h"
 
20
#include "event_db_repository.h"
 
21
#include "sp_head.h"
 
22
 
 
23
/**
 
24
  @addtogroup Event_Scheduler
 
25
  @{
 
26
*/
 
27
 
 
28
/*************************************************************************/
 
29
 
 
30
/**
 
31
  Event_creation_ctx -- creation context of events.
 
32
*/
 
33
 
 
34
class Event_creation_ctx :public Stored_program_creation_ctx,
 
35
                          public Sql_alloc
 
36
{
 
37
public:
 
38
  static bool load_from_db(THD *thd,
 
39
                           MEM_ROOT *event_mem_root,
 
40
                           const char *db_name,
 
41
                           const char *event_name,
 
42
                           TABLE *event_tbl,
 
43
                           Stored_program_creation_ctx **ctx);
 
44
 
 
45
public:
 
46
  virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root)
 
47
  {
 
48
    return new (mem_root)
 
49
               Event_creation_ctx(m_client_cs, m_connection_cl, m_db_cl);
 
50
  }
 
51
 
 
52
protected:
 
53
  virtual Object_creation_ctx *create_backup_ctx(THD *thd) const
 
54
  {
 
55
    /*
 
56
      We can avoid usual backup/restore employed in stored programs since we
 
57
      know that this is a top level statement and the worker thread is
 
58
      allocated exclusively to execute this event.
 
59
    */
 
60
 
 
61
    return NULL;
 
62
  }
 
63
 
 
64
private:
 
65
  Event_creation_ctx(CHARSET_INFO *client_cs,
 
66
                     CHARSET_INFO *connection_cl,
 
67
                     CHARSET_INFO *db_cl)
 
68
    : Stored_program_creation_ctx(client_cs, connection_cl, db_cl)
 
69
  { }
 
70
};
 
71
 
 
72
/**************************************************************************
 
73
  Event_creation_ctx implementation.
 
74
**************************************************************************/
 
75
 
 
76
bool
 
77
Event_creation_ctx::load_from_db(THD *thd,
 
78
                                 MEM_ROOT *event_mem_root,
 
79
                                 const char *db_name,
 
80
                                 const char *event_name,
 
81
                                 TABLE *event_tbl,
 
82
                                 Stored_program_creation_ctx **ctx)
 
83
{
 
84
  /* Load character set/collation attributes. */
 
85
 
 
86
  CHARSET_INFO *client_cs;
 
87
  CHARSET_INFO *connection_cl;
 
88
  CHARSET_INFO *db_cl;
 
89
 
 
90
  bool invalid_creation_ctx= FALSE;
 
91
 
 
92
  if (load_charset(event_mem_root,
 
93
                   event_tbl->field[ET_FIELD_CHARACTER_SET_CLIENT],
 
94
                   thd->variables.character_set_client,
 
95
                   &client_cs))
 
96
  {
 
97
    sql_print_warning("Event '%s'.'%s': invalid value "
 
98
                      "in column mysql.event.character_set_client.",
 
99
                      (const char *) db_name,
 
100
                      (const char *) event_name);
 
101
 
 
102
    invalid_creation_ctx= TRUE;
 
103
  }
 
104
 
 
105
  if (load_collation(event_mem_root,
 
106
                     event_tbl->field[ET_FIELD_COLLATION_CONNECTION],
 
107
                     thd->variables.collation_connection,
 
108
                     &connection_cl))
 
109
  {
 
110
    sql_print_warning("Event '%s'.'%s': invalid value "
 
111
                      "in column mysql.event.collation_connection.",
 
112
                      (const char *) db_name,
 
113
                      (const char *) event_name);
 
114
 
 
115
    invalid_creation_ctx= TRUE;
 
116
  }
 
117
 
 
118
  if (load_collation(event_mem_root,
 
119
                     event_tbl->field[ET_FIELD_DB_COLLATION],
 
120
                     NULL,
 
121
                     &db_cl))
 
122
  {
 
123
    sql_print_warning("Event '%s'.'%s': invalid value "
 
124
                      "in column mysql.event.db_collation.",
 
125
                      (const char *) db_name,
 
126
                      (const char *) event_name);
 
127
 
 
128
    invalid_creation_ctx= TRUE;
 
129
  }
 
130
 
 
131
  /*
 
132
    If we failed to resolve the database collation, load the default one
 
133
    from the disk.
 
134
  */
 
135
 
 
136
  if (!db_cl)
 
137
    db_cl= get_default_db_collation(thd, db_name);
 
138
 
 
139
  /* Create the context. */
 
140
 
 
141
  *ctx= new Event_creation_ctx(client_cs, connection_cl, db_cl);
 
142
 
 
143
  return invalid_creation_ctx;
 
144
}
 
145
 
 
146
/*************************************************************************/
 
147
 
 
148
/*
 
149
  Initiliazes dbname and name of an Event_queue_element_for_exec
 
150
  object
 
151
 
 
152
  SYNOPSIS
 
153
    Event_queue_element_for_exec::init()
 
154
 
 
155
  RETURN VALUE
 
156
    FALSE  OK
 
157
    TRUE   Error (OOM)
 
158
*/
 
159
 
 
160
bool
 
161
Event_queue_element_for_exec::init(LEX_STRING db, LEX_STRING n)
 
162
{
 
163
  if (!(dbname.str= my_strndup(db.str, dbname.length= db.length, MYF(MY_WME))))
 
164
    return TRUE;
 
165
  if (!(name.str= my_strndup(n.str, name.length= n.length, MYF(MY_WME))))
 
166
  {
 
167
    my_free((uchar*) dbname.str, MYF(0));
 
168
    return TRUE;
 
169
  }
 
170
  return FALSE;
 
171
}
 
172
 
 
173
 
 
174
/*
 
175
  Destructor
 
176
 
 
177
  SYNOPSIS
 
178
    Event_queue_element_for_exec::~Event_queue_element_for_exec()
 
179
*/
 
180
 
 
181
Event_queue_element_for_exec::~Event_queue_element_for_exec()
 
182
{
 
183
  my_free((uchar*) dbname.str, MYF(0));
 
184
  my_free((uchar*) name.str, MYF(0));
 
185
}
 
186
 
 
187
 
 
188
/*
 
189
  Constructor
 
190
 
 
191
  SYNOPSIS
 
192
    Event_basic::Event_basic()
 
193
*/
 
194
 
 
195
Event_basic::Event_basic()
 
196
{
 
197
  DBUG_ENTER("Event_basic::Event_basic");
 
198
  /* init memory root */
 
199
  init_alloc_root(&mem_root, 256, 512);
 
200
  dbname.str= name.str= NULL;
 
201
  dbname.length= name.length= 0;
 
202
  time_zone= NULL;
 
203
  DBUG_VOID_RETURN;
 
204
}
 
205
 
 
206
 
 
207
/*
 
208
  Destructor
 
209
 
 
210
  SYNOPSIS
 
211
    Event_basic::Event_basic()
 
212
*/
 
213
 
 
214
Event_basic::~Event_basic()
 
215
{
 
216
  DBUG_ENTER("Event_basic::~Event_basic");
 
217
  free_root(&mem_root, MYF(0));
 
218
  DBUG_VOID_RETURN;
 
219
}
 
220
 
 
221
 
 
222
/*
 
223
  Short function to load a char column into a LEX_STRING
 
224
 
 
225
  SYNOPSIS
 
226
    Event_basic::load_string_field()
 
227
      field_name  The field( enum_events_table_field is not actually used
 
228
                  because it's unknown in event_data_objects.h)
 
229
      fields      The Field array
 
230
      field_value The value
 
231
*/
 
232
 
 
233
bool
 
234
Event_basic::load_string_fields(Field **fields, ...)
 
235
{
 
236
  bool ret= FALSE;
 
237
  va_list args;
 
238
  enum enum_events_table_field field_name;
 
239
  LEX_STRING *field_value;
 
240
 
 
241
  DBUG_ENTER("Event_basic::load_string_fields");
 
242
 
 
243
  va_start(args, fields);
 
244
  field_name= (enum enum_events_table_field) va_arg(args, int);
 
245
  while (field_name < ET_FIELD_COUNT)
 
246
  {
 
247
    field_value= va_arg(args, LEX_STRING *);
 
248
    if ((field_value->str= get_field(&mem_root, fields[field_name])) == NullS)
 
249
    {
 
250
      ret= TRUE;
 
251
      break;
 
252
    }
 
253
    field_value->length= strlen(field_value->str);
 
254
 
 
255
    field_name= (enum enum_events_table_field) va_arg(args, int);
 
256
  }
 
257
  va_end(args);
 
258
 
 
259
  DBUG_RETURN(ret);
 
260
}
 
261
 
 
262
 
 
263
bool
 
264
Event_basic::load_time_zone(THD *thd, const LEX_STRING tz_name)
 
265
{
 
266
  String str(tz_name.str, &my_charset_latin1);
 
267
  time_zone= my_tz_find(thd, &str);
 
268
 
 
269
  return (time_zone == NULL);
 
270
}
 
271
 
 
272
 
 
273
/*
 
274
  Constructor
 
275
 
 
276
  SYNOPSIS
 
277
    Event_queue_element::Event_queue_element()
 
278
*/
 
279
 
 
280
Event_queue_element::Event_queue_element():
 
281
  status_changed(FALSE), last_executed_changed(FALSE),
 
282
  on_completion(Event_parse_data::ON_COMPLETION_DROP),
 
283
  status(Event_parse_data::ENABLED), expression(0), dropped(FALSE),
 
284
  execution_count(0)
 
285
{
 
286
  DBUG_ENTER("Event_queue_element::Event_queue_element");
 
287
 
 
288
  starts= ends= execute_at= last_executed= 0;
 
289
  starts_null= ends_null= execute_at_null= TRUE;
 
290
 
 
291
  DBUG_VOID_RETURN;
 
292
}
 
293
 
 
294
 
 
295
/*
 
296
  Destructor
 
297
 
 
298
  SYNOPSIS
 
299
    Event_queue_element::Event_queue_element()
 
300
*/
 
301
Event_queue_element::~Event_queue_element()
 
302
{
 
303
}
 
304
 
 
305
 
 
306
/*
 
307
  Constructor
 
308
 
 
309
  SYNOPSIS
 
310
    Event_timed::Event_timed()
 
311
*/
 
312
 
 
313
Event_timed::Event_timed():
 
314
  created(0), modified(0), sql_mode(0)
 
315
{
 
316
  DBUG_ENTER("Event_timed::Event_timed");
 
317
  init();
 
318
  DBUG_VOID_RETURN;
 
319
}
 
320
 
 
321
 
 
322
/*
 
323
  Destructor
 
324
 
 
325
  SYNOPSIS
 
326
    Event_timed::~Event_timed()
 
327
*/
 
328
 
 
329
Event_timed::~Event_timed()
 
330
{
 
331
}
 
332
 
 
333
 
 
334
/*
 
335
  Constructor
 
336
 
 
337
  SYNOPSIS
 
338
    Event_job_data::Event_job_data()
 
339
*/
 
340
 
 
341
Event_job_data::Event_job_data()
 
342
  :sql_mode(0)
 
343
{
 
344
}
 
345
 
 
346
/*
 
347
  Init all member variables
 
348
 
 
349
  SYNOPSIS
 
350
    Event_timed::init()
 
351
*/
 
352
 
 
353
void
 
354
Event_timed::init()
 
355
{
 
356
  DBUG_ENTER("Event_timed::init");
 
357
 
 
358
  definer_user.str= definer_host.str= body.str= comment.str= NULL;
 
359
  definer_user.length= definer_host.length= body.length= comment.length= 0;
 
360
 
 
361
  sql_mode= 0;
 
362
 
 
363
  DBUG_VOID_RETURN;
 
364
}
 
365
 
 
366
 
 
367
/**
 
368
  Load an event's body from a row from mysql.event.
 
369
 
 
370
  @details This method is silent on errors and should behave like that.
 
371
  Callers should handle throwing of error messages. The reason is that the
 
372
  class should not know about how to deal with communication.
 
373
 
 
374
  @return Operation status
 
375
    @retval FALSE OK
 
376
    @retval TRUE  Error
 
377
*/
 
378
 
 
379
bool
 
380
Event_job_data::load_from_row(THD *thd, TABLE *table)
 
381
{
 
382
  char *ptr;
 
383
  size_t len;
 
384
  LEX_STRING tz_name;
 
385
 
 
386
  DBUG_ENTER("Event_job_data::load_from_row");
 
387
 
 
388
  if (!table)
 
389
    DBUG_RETURN(TRUE);
 
390
 
 
391
  if (table->s->fields < ET_FIELD_COUNT)
 
392
    DBUG_RETURN(TRUE);
 
393
 
 
394
  if (load_string_fields(table->field,
 
395
                         ET_FIELD_DB, &dbname,
 
396
                         ET_FIELD_NAME, &name,
 
397
                         ET_FIELD_BODY, &body,
 
398
                         ET_FIELD_DEFINER, &definer,
 
399
                         ET_FIELD_TIME_ZONE, &tz_name,
 
400
                         ET_FIELD_COUNT))
 
401
    DBUG_RETURN(TRUE);
 
402
 
 
403
  if (load_time_zone(thd, tz_name))
 
404
    DBUG_RETURN(TRUE);
 
405
 
 
406
  Event_creation_ctx::load_from_db(thd, &mem_root, dbname.str, name.str, table,
 
407
                                   &creation_ctx);
 
408
 
 
409
  ptr= strchr(definer.str, '@');
 
410
 
 
411
  if (! ptr)
 
412
    ptr= definer.str;
 
413
 
 
414
  len= ptr - definer.str;
 
415
  definer_user.str= strmake_root(&mem_root, definer.str, len);
 
416
  definer_user.length= len;
 
417
  len= definer.length - len - 1;
 
418
  /* 1:because of @ */
 
419
  definer_host.str= strmake_root(&mem_root, ptr + 1, len);
 
420
  definer_host.length= len;
 
421
 
 
422
  sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
 
423
 
 
424
  DBUG_RETURN(FALSE);
 
425
}
 
426
 
 
427
 
 
428
/**
 
429
  Load an event's body from a row from mysql.event.
 
430
 
 
431
  @details This method is silent on errors and should behave like that.
 
432
  Callers should handle throwing of error messages. The reason is that the
 
433
  class should not know about how to deal with communication.
 
434
 
 
435
  @return Operation status
 
436
    @retval FALSE OK
 
437
    @retval TRUE  Error
 
438
*/
 
439
 
 
440
bool
 
441
Event_queue_element::load_from_row(THD *thd, TABLE *table)
 
442
{
 
443
  char *ptr;
 
444
  MYSQL_TIME time;
 
445
  LEX_STRING tz_name;
 
446
 
 
447
  DBUG_ENTER("Event_queue_element::load_from_row");
 
448
 
 
449
  if (!table)
 
450
    DBUG_RETURN(TRUE);
 
451
 
 
452
  if (table->s->fields < ET_FIELD_COUNT)
 
453
    DBUG_RETURN(TRUE);
 
454
 
 
455
  if (load_string_fields(table->field,
 
456
                         ET_FIELD_DB, &dbname,
 
457
                         ET_FIELD_NAME, &name,
 
458
                         ET_FIELD_DEFINER, &definer,
 
459
                         ET_FIELD_TIME_ZONE, &tz_name,
 
460
                         ET_FIELD_COUNT))
 
461
    DBUG_RETURN(TRUE);
 
462
 
 
463
  if (load_time_zone(thd, tz_name))
 
464
    DBUG_RETURN(TRUE);
 
465
 
 
466
  starts_null= table->field[ET_FIELD_STARTS]->is_null();
 
467
  my_bool not_used= FALSE;
 
468
  if (!starts_null)
 
469
  {
 
470
    table->field[ET_FIELD_STARTS]->get_date(&time, TIME_NO_ZERO_DATE);
 
471
    starts= my_tz_OFFSET0->TIME_to_gmt_sec(&time,&not_used);
 
472
  }
 
473
 
 
474
  ends_null= table->field[ET_FIELD_ENDS]->is_null();
 
475
  if (!ends_null)
 
476
  {
 
477
    table->field[ET_FIELD_ENDS]->get_date(&time, TIME_NO_ZERO_DATE);
 
478
    ends= my_tz_OFFSET0->TIME_to_gmt_sec(&time,&not_used);
 
479
  }
 
480
 
 
481
  if (!table->field[ET_FIELD_INTERVAL_EXPR]->is_null())
 
482
    expression= table->field[ET_FIELD_INTERVAL_EXPR]->val_int();
 
483
  else
 
484
    expression= 0;
 
485
  /*
 
486
    If neigher STARTS and ENDS is set, then both fields are empty.
 
487
    Hence, if ET_FIELD_EXECUTE_AT is empty there is an error.
 
488
  */
 
489
  execute_at_null= table->field[ET_FIELD_EXECUTE_AT]->is_null();
 
490
  DBUG_ASSERT(!(starts_null && ends_null && !expression && execute_at_null));
 
491
  if (!expression && !execute_at_null)
 
492
  {
 
493
    if (table->field[ET_FIELD_EXECUTE_AT]->get_date(&time,
 
494
                                                    TIME_NO_ZERO_DATE))
 
495
      DBUG_RETURN(TRUE);
 
496
    execute_at= my_tz_OFFSET0->TIME_to_gmt_sec(&time,&not_used);
 
497
  }
 
498
 
 
499
  /*
 
500
    We load the interval type from disk as string and then map it to
 
501
    an integer. This decouples the values of enum interval_type
 
502
    and values actually stored on disk. Therefore the type can be
 
503
    reordered without risking incompatibilities of data between versions.
 
504
  */
 
505
  if (!table->field[ET_FIELD_TRANSIENT_INTERVAL]->is_null())
 
506
  {
 
507
    int i;
 
508
    char buff[MAX_FIELD_WIDTH];
 
509
    String str(buff, sizeof(buff), &my_charset_bin);
 
510
    LEX_STRING tmp;
 
511
 
 
512
    table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_str(&str);
 
513
    if (!(tmp.length= str.length()))
 
514
      DBUG_RETURN(TRUE);
 
515
 
 
516
    tmp.str= str.c_ptr_safe();
 
517
 
 
518
    i= find_string_in_array(interval_type_to_name, &tmp, system_charset_info);
 
519
    if (i < 0)
 
520
      DBUG_RETURN(TRUE);
 
521
    interval= (interval_type) i;
 
522
  }
 
523
 
 
524
  if (!table->field[ET_FIELD_LAST_EXECUTED]->is_null())
 
525
  {
 
526
    table->field[ET_FIELD_LAST_EXECUTED]->get_date(&time,
 
527
                                                   TIME_NO_ZERO_DATE);
 
528
    last_executed= my_tz_OFFSET0->TIME_to_gmt_sec(&time,&not_used);
 
529
  }
 
530
  last_executed_changed= FALSE;
 
531
 
 
532
  if ((ptr= get_field(&mem_root, table->field[ET_FIELD_STATUS])) == NullS)
 
533
    DBUG_RETURN(TRUE);
 
534
 
 
535
  DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", name.str, ptr));
 
536
 
 
537
  /* Set event status (ENABLED | SLAVESIDE_DISABLED | DISABLED) */
 
538
  switch (ptr[0])
 
539
  {
 
540
  case 'E' :
 
541
    status = Event_parse_data::ENABLED;
 
542
    break;
 
543
  case 'S' :
 
544
    status = Event_parse_data::SLAVESIDE_DISABLED;
 
545
    break;
 
546
  case 'D' :
 
547
  default:
 
548
    status = Event_parse_data::DISABLED;
 
549
    break;
 
550
  }
 
551
  if ((ptr= get_field(&mem_root, table->field[ET_FIELD_ORIGINATOR])) == NullS)
 
552
    DBUG_RETURN(TRUE);
 
553
  originator = table->field[ET_FIELD_ORIGINATOR]->val_int(); 
 
554
 
 
555
  /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
 
556
  if ((ptr= get_field(&mem_root,
 
557
                      table->field[ET_FIELD_ON_COMPLETION])) == NullS)
 
558
    DBUG_RETURN(TRUE);
 
559
 
 
560
  on_completion= (ptr[0]=='D'? Event_parse_data::ON_COMPLETION_DROP:
 
561
                               Event_parse_data::ON_COMPLETION_PRESERVE);
 
562
 
 
563
  DBUG_RETURN(FALSE);
 
564
}
 
565
 
 
566
 
 
567
/**
 
568
  Load an event's body from a row from mysql.event.
 
569
 
 
570
  @details This method is silent on errors and should behave like that.
 
571
  Callers should handle throwing of error messages. The reason is that the
 
572
  class should not know about how to deal with communication.
 
573
 
 
574
  @return Operation status
 
575
    @retval FALSE OK
 
576
    @retval TRUE  Error
 
577
*/
 
578
 
 
579
bool
 
580
Event_timed::load_from_row(THD *thd, TABLE *table)
 
581
{
 
582
  char *ptr;
 
583
  size_t len;
 
584
 
 
585
  DBUG_ENTER("Event_timed::load_from_row");
 
586
 
 
587
  if (Event_queue_element::load_from_row(thd, table))
 
588
    DBUG_RETURN(TRUE);
 
589
 
 
590
  if (load_string_fields(table->field,
 
591
                         ET_FIELD_BODY, &body,
 
592
                         ET_FIELD_BODY_UTF8, &body_utf8,
 
593
                         ET_FIELD_COUNT))
 
594
    DBUG_RETURN(TRUE);
 
595
 
 
596
  if (Event_creation_ctx::load_from_db(thd, &mem_root, dbname.str, name.str,
 
597
                                       table, &creation_ctx))
 
598
  {
 
599
    push_warning_printf(thd,
 
600
                        MYSQL_ERROR::WARN_LEVEL_WARN,
 
601
                        ER_EVENT_INVALID_CREATION_CTX,
 
602
                        ER(ER_EVENT_INVALID_CREATION_CTX),
 
603
                        (const char *) dbname.str,
 
604
                        (const char *) name.str);
 
605
  }
 
606
 
 
607
  ptr= strchr(definer.str, '@');
 
608
 
 
609
  if (! ptr)
 
610
    ptr= definer.str;
 
611
 
 
612
  len= ptr - definer.str;
 
613
  definer_user.str= strmake_root(&mem_root, definer.str, len);
 
614
  definer_user.length= len;
 
615
  len= definer.length - len - 1;
 
616
  /* 1:because of @ */
 
617
  definer_host.str= strmake_root(&mem_root, ptr + 1,  len);
 
618
  definer_host.length= len;
 
619
 
 
620
  created= table->field[ET_FIELD_CREATED]->val_int();
 
621
  modified= table->field[ET_FIELD_MODIFIED]->val_int();
 
622
 
 
623
  comment.str= get_field(&mem_root, table->field[ET_FIELD_COMMENT]);
 
624
  if (comment.str != NullS)
 
625
    comment.length= strlen(comment.str);
 
626
  else
 
627
    comment.length= 0;
 
628
 
 
629
  sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
 
630
 
 
631
  DBUG_RETURN(FALSE);
 
632
}
 
633
 
 
634
 
 
635
/*
 
636
  add_interval() adds a specified interval to time 'ltime' in time
 
637
  zone 'time_zone', and returns the result converted to the number of
 
638
  seconds since epoch (aka Unix time; in UTC time zone).  Zero result
 
639
  means an error.
 
640
*/
 
641
static
 
642
my_time_t
 
643
add_interval(MYSQL_TIME *ltime, const Time_zone *time_zone,
 
644
             interval_type scale, INTERVAL interval)
 
645
{
 
646
  if (date_add_interval(ltime, scale, interval))
 
647
    return 0;
 
648
 
 
649
  my_bool not_used;
 
650
  return time_zone->TIME_to_gmt_sec(ltime, &not_used);
 
651
}
 
652
 
 
653
 
 
654
/*
 
655
  Computes the sum of a timestamp plus interval.
 
656
 
 
657
  SYNOPSIS
 
658
    get_next_time()
 
659
      time_zone     event time zone
 
660
      next          the sum
 
661
      start         add interval_value to this time
 
662
      time_now      current time
 
663
      i_value       quantity of time type interval to add
 
664
      i_type        type of interval to add (SECOND, MINUTE, HOUR, WEEK ...)
 
665
 
 
666
  RETURN VALUE
 
667
    0  OK
 
668
    1  Error
 
669
 
 
670
  NOTES
 
671
    1) If the interval is conversible to SECOND, like MINUTE, HOUR, DAY, WEEK.
 
672
       Then we use TIMEDIFF()'s implementation as underlying and number of
 
673
       seconds as resolution for computation.
 
674
    2) In all other cases - MONTH, QUARTER, YEAR we use MONTH as resolution
 
675
       and PERIOD_DIFF()'s implementation
 
676
*/
 
677
 
 
678
static
 
679
bool get_next_time(const Time_zone *time_zone, my_time_t *next,
 
680
                   my_time_t start, my_time_t time_now,
 
681
                   int i_value, interval_type i_type)
 
682
{
 
683
  DBUG_ENTER("get_next_time");
 
684
  DBUG_PRINT("enter", ("start: %lu  now: %lu", (long) start, (long) time_now));
 
685
 
 
686
  DBUG_ASSERT(start <= time_now);
 
687
 
 
688
  longlong months=0, seconds=0;
 
689
 
 
690
  switch (i_type) {
 
691
  case INTERVAL_YEAR:
 
692
    months= i_value*12;
 
693
    break;
 
694
  case INTERVAL_QUARTER:
 
695
    /* Has already been converted to months */
 
696
  case INTERVAL_YEAR_MONTH:
 
697
  case INTERVAL_MONTH:
 
698
    months= i_value;
 
699
    break;
 
700
  case INTERVAL_WEEK:
 
701
    /* WEEK has already been converted to days */
 
702
  case INTERVAL_DAY:
 
703
    seconds= i_value*24*3600;
 
704
    break;
 
705
  case INTERVAL_DAY_HOUR:
 
706
  case INTERVAL_HOUR:
 
707
    seconds= i_value*3600;
 
708
    break;
 
709
  case INTERVAL_DAY_MINUTE:
 
710
  case INTERVAL_HOUR_MINUTE:
 
711
  case INTERVAL_MINUTE:
 
712
    seconds= i_value*60;
 
713
    break;
 
714
  case INTERVAL_DAY_SECOND:
 
715
  case INTERVAL_HOUR_SECOND:
 
716
  case INTERVAL_MINUTE_SECOND:
 
717
  case INTERVAL_SECOND:
 
718
    seconds= i_value;
 
719
    break;
 
720
  case INTERVAL_DAY_MICROSECOND:
 
721
  case INTERVAL_HOUR_MICROSECOND:
 
722
  case INTERVAL_MINUTE_MICROSECOND:
 
723
  case INTERVAL_SECOND_MICROSECOND:
 
724
  case INTERVAL_MICROSECOND:
 
725
    /*
 
726
     We should return an error here so SHOW EVENTS/ SELECT FROM I_S.EVENTS
 
727
     would give an error then.
 
728
    */
 
729
    DBUG_RETURN(1);
 
730
    break;
 
731
  case INTERVAL_LAST:
 
732
    DBUG_ASSERT(0);
 
733
  }
 
734
  DBUG_PRINT("info", ("seconds: %ld  months: %ld", (long) seconds, (long) months));
 
735
 
 
736
  MYSQL_TIME local_start;
 
737
  MYSQL_TIME local_now;
 
738
 
 
739
  /* Convert times from UTC to local. */
 
740
  {
 
741
    time_zone->gmt_sec_to_TIME(&local_start, start);
 
742
    time_zone->gmt_sec_to_TIME(&local_now, time_now);
 
743
  }
 
744
 
 
745
  INTERVAL interval;
 
746
  bzero(&interval, sizeof(interval));
 
747
  my_time_t next_time= 0;
 
748
 
 
749
  if (seconds)
 
750
  {
 
751
    longlong seconds_diff;
 
752
    long microsec_diff;
 
753
    bool negative= calc_time_diff(&local_now, &local_start, 1,
 
754
                                  &seconds_diff, &microsec_diff);
 
755
    if (!negative)
 
756
    {
 
757
      /*
 
758
        The formula below returns the interval that, when added to
 
759
        local_start, will always give the time in the future.
 
760
      */
 
761
      interval.second= seconds_diff - seconds_diff % seconds + seconds;
 
762
      next_time= add_interval(&local_start, time_zone,
 
763
                              INTERVAL_SECOND, interval);
 
764
      if (next_time == 0)
 
765
        goto done;
 
766
    }
 
767
 
 
768
    if (next_time <= time_now)
 
769
    {
 
770
      /*
 
771
        If 'negative' is true above, then 'next_time == 0', and
 
772
        'next_time <= time_now' is also true.  If negative is false,
 
773
        then next_time was set, but perhaps to the value that is less
 
774
        then time_now.  See below for elaboration.
 
775
      */
 
776
      DBUG_ASSERT(negative || next_time > 0);
 
777
 
 
778
      /*
 
779
        If local_now < local_start, i.e. STARTS time is in the future
 
780
        according to the local time (it always in the past according
 
781
        to UTC---this is a prerequisite of this function), then
 
782
        STARTS is almost always in the past according to the local
 
783
        time too.  However, in the time zone that has backward
 
784
        Daylight Saving Time shift, the following may happen: suppose
 
785
        we have a backward DST shift at certain date after 2:59:59,
 
786
        i.e. local time goes 1:59:59, 2:00:00, ... , 2:59:59, (shift
 
787
        here) 2:00:00 (again), ... , 2:59:59 (again), 3:00:00, ... .
 
788
        Now suppose the time has passed the first 2:59:59, has been
 
789
        shifted backward, and now is (the second) 2:20:00.  The user
 
790
        does CREATE EVENT with STARTS 'current-date 2:40:00'.  Local
 
791
        time 2:40:00 from create statement is treated by time
 
792
        functions as the first such time, so according to UTC it comes
 
793
        before the second 2:20:00.  But according to local time it is
 
794
        obviously in the future, so we end up in this branch.
 
795
 
 
796
        Since we are in the second pass through 2:00:00--2:59:59, and
 
797
        any local time form this interval is treated by system
 
798
        functions as the time from the first pass, we have to find the
 
799
        time for the next execution that is past the DST-affected
 
800
        interval (past the second 2:59:59 for our example,
 
801
        i.e. starting from 3:00:00).  We do this in the loop until the
 
802
        local time is mapped onto future UTC time.  'start' time is in
 
803
        the past, so we may use 'do { } while' here, and add the first
 
804
        interval right away.
 
805
 
 
806
        Alternatively, it could be that local_now >= local_start.  Now
 
807
        for the example above imagine we do CREATE EVENT with STARTS
 
808
        'current-date 2:10:00'.  Local start 2:10 is in the past (now
 
809
        is local 2:20), so we add an interval, and get next execution
 
810
        time, say, 2:40.  It is in the future according to local time,
 
811
        but, again, since we are in the second pass through
 
812
        2:00:00--2:59:59, 2:40 will be converted into UTC time in the
 
813
        past.  So we will end up in this branch again, and may add
 
814
        intervals in a 'do { } while' loop.
 
815
 
 
816
        Note that for any given event we may end up here only if event
 
817
        next execution time will map to the time interval that is
 
818
        passed twice, and only if the server was started during the
 
819
        second pass, or the event is being created during the second
 
820
        pass.  After that, we never will get here (unless we again
 
821
        start the server during the second pass).  In other words,
 
822
        such a condition is extremely rare.
 
823
      */
 
824
      interval.second= seconds;
 
825
      do
 
826
      {
 
827
        next_time= add_interval(&local_start, time_zone,
 
828
                                INTERVAL_SECOND, interval);
 
829
        if (next_time == 0)
 
830
          goto done;
 
831
      }
 
832
      while (next_time <= time_now);
 
833
    }
 
834
  }
 
835
  else
 
836
  {
 
837
    long diff_months= (long) (local_now.year - local_start.year)*12 +
 
838
                      (local_now.month - local_start.month);
 
839
    /*
 
840
      Unlike for seconds above, the formula below returns the interval
 
841
      that, when added to the local_start, will give the time in the
 
842
      past, or somewhere in the current month.  We are interested in
 
843
      the latter case, to see if this time has already passed, or is
 
844
      yet to come this month.
 
845
 
 
846
      Note that the time is guaranteed to be in the past unless
 
847
      (diff_months % months == 0), but no good optimization is
 
848
      possible here, because (diff_months % months == 0) is what will
 
849
      happen most of the time, as get_next_time() will be called right
 
850
      after the execution of the event.  We could pass last_executed
 
851
      time to this function, and see if the execution has already
 
852
      happened this month, but for that we will have to convert
 
853
      last_executed from seconds since epoch to local broken-down
 
854
      time, and this will greatly reduce the effect of the
 
855
      optimization.  So instead we keep the code simple and clean.
 
856
    */
 
857
    interval.month= (ulong) (diff_months - diff_months % months);
 
858
    next_time= add_interval(&local_start, time_zone,
 
859
                            INTERVAL_MONTH, interval);
 
860
    if (next_time == 0)
 
861
      goto done;
 
862
 
 
863
    if (next_time <= time_now)
 
864
    {
 
865
      interval.month= (ulong) months;
 
866
      next_time= add_interval(&local_start, time_zone,
 
867
                              INTERVAL_MONTH, interval);
 
868
      if (next_time == 0)
 
869
        goto done;
 
870
    }
 
871
  }
 
872
 
 
873
  DBUG_ASSERT(time_now < next_time);
 
874
 
 
875
  *next= next_time;
 
876
 
 
877
done:
 
878
  DBUG_PRINT("info", ("next_time: %ld", (long) next_time));
 
879
  DBUG_RETURN(next_time == 0);
 
880
}
 
881
 
 
882
 
 
883
/*
 
884
  Computes next execution time.
 
885
 
 
886
  SYNOPSIS
 
887
    Event_queue_element::compute_next_execution_time()
 
888
 
 
889
  RETURN VALUE
 
890
    FALSE  OK
 
891
    TRUE   Error
 
892
 
 
893
  NOTES
 
894
    The time is set in execute_at, if no more executions the latter is
 
895
    set to 0.
 
896
*/
 
897
 
 
898
bool
 
899
Event_queue_element::compute_next_execution_time()
 
900
{
 
901
  my_time_t time_now;
 
902
  DBUG_ENTER("Event_queue_element::compute_next_execution_time");
 
903
  DBUG_PRINT("enter", ("starts: %lu  ends: %lu  last_executed: %lu  this: 0x%lx",
 
904
                       (long) starts, (long) ends, (long) last_executed,
 
905
                       (long) this));
 
906
 
 
907
  if (status != Event_parse_data::ENABLED)
 
908
  {
 
909
    DBUG_PRINT("compute_next_execution_time",
 
910
               ("Event %s is DISABLED", name.str));
 
911
    goto ret;
 
912
  }
 
913
  /* If one-time, no need to do computation */
 
914
  if (!expression)
 
915
  {
 
916
    /* Let's check whether it was executed */
 
917
    if (last_executed)
 
918
    {
 
919
      DBUG_PRINT("info",("One-time event %s.%s of was already executed",
 
920
                         dbname.str, name.str));
 
921
      dropped= (on_completion == Event_parse_data::ON_COMPLETION_DROP);
 
922
      DBUG_PRINT("info",("One-time event will be dropped: %d.", dropped));
 
923
 
 
924
      status= Event_parse_data::DISABLED;
 
925
      status_changed= TRUE;
 
926
    }
 
927
    goto ret;
 
928
  }
 
929
 
 
930
  time_now= (my_time_t) current_thd->query_start();
 
931
 
 
932
  DBUG_PRINT("info",("NOW: [%lu]", (ulong) time_now));
 
933
 
 
934
  /* if time_now is after ends don't execute anymore */
 
935
  if (!ends_null && ends < time_now)
 
936
  {
 
937
    DBUG_PRINT("info", ("NOW after ENDS, don't execute anymore"));
 
938
    /* time_now is after ends. don't execute anymore */
 
939
    execute_at= 0;
 
940
    execute_at_null= TRUE;
 
941
    if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
 
942
      dropped= TRUE;
 
943
    DBUG_PRINT("info", ("Dropped: %d", dropped));
 
944
    status= Event_parse_data::DISABLED;
 
945
    status_changed= TRUE;
 
946
 
 
947
    goto ret;
 
948
  }
 
949
 
 
950
  /*
 
951
    Here time_now is before or equals ends if the latter is set.
 
952
    Let's check whether time_now is before starts.
 
953
    If so schedule for starts.
 
954
  */
 
955
  if (!starts_null && time_now <= starts)
 
956
  {
 
957
    if (time_now == starts && starts == last_executed)
 
958
    {
 
959
      /*
 
960
        do nothing or we will schedule for second time execution at starts.
 
961
      */
 
962
    }
 
963
    else
 
964
    {
 
965
      DBUG_PRINT("info", ("STARTS is future, NOW <= STARTS,sched for STARTS"));
 
966
      /*
 
967
        starts is in the future
 
968
        time_now before starts. Scheduling for starts
 
969
      */
 
970
      execute_at= starts;
 
971
      execute_at_null= FALSE;
 
972
      goto ret;
 
973
    }
 
974
  }
 
975
 
 
976
  if (!starts_null && !ends_null)
 
977
  {
 
978
    /*
 
979
      Both starts and m_ends are set and time_now is between them (incl.)
 
980
      If last_executed is set then increase with m_expression. The new MYSQL_TIME is
 
981
      after m_ends set execute_at to 0. And check for on_completion
 
982
      If not set then schedule for now.
 
983
    */
 
984
    DBUG_PRINT("info", ("Both STARTS & ENDS are set"));
 
985
    if (!last_executed)
 
986
    {
 
987
      DBUG_PRINT("info", ("Not executed so far."));
 
988
    }
 
989
 
 
990
    {
 
991
      my_time_t next_exec;
 
992
 
 
993
      if (get_next_time(time_zone, &next_exec, starts, time_now,
 
994
                        (int) expression, interval))
 
995
        goto err;
 
996
 
 
997
      /* There was previous execution */
 
998
      if (ends < next_exec)
 
999
      {
 
1000
        DBUG_PRINT("info", ("Next execution of %s after ENDS. Stop executing.",
 
1001
                   name.str));
 
1002
        /* Next execution after ends. No more executions */
 
1003
        execute_at= 0;
 
1004
        execute_at_null= TRUE;
 
1005
        if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
 
1006
          dropped= TRUE;
 
1007
        status= Event_parse_data::DISABLED;
 
1008
        status_changed= TRUE;
 
1009
      }
 
1010
      else
 
1011
      {
 
1012
        DBUG_PRINT("info",("Next[%lu]", (ulong) next_exec));
 
1013
        execute_at= next_exec;
 
1014
        execute_at_null= FALSE;
 
1015
      }
 
1016
    }
 
1017
    goto ret;
 
1018
  }
 
1019
  else if (starts_null && ends_null)
 
1020
  {
 
1021
    /* starts is always set, so this is a dead branch !! */
 
1022
    DBUG_PRINT("info", ("Neither STARTS nor ENDS are set"));
 
1023
    /*
 
1024
      Both starts and m_ends are not set, so we schedule for the next
 
1025
      based on last_executed.
 
1026
    */
 
1027
    if (last_executed)
 
1028
    {
 
1029
      my_time_t next_exec;
 
1030
      if (get_next_time(time_zone, &next_exec, starts, time_now,
 
1031
                        (int) expression, interval))
 
1032
        goto err;
 
1033
      execute_at= next_exec;
 
1034
      DBUG_PRINT("info",("Next[%lu]", (ulong) next_exec));
 
1035
    }
 
1036
    else
 
1037
    {
 
1038
      /* last_executed not set. Schedule the event for now */
 
1039
      DBUG_PRINT("info", ("Execute NOW"));
 
1040
      execute_at= time_now;
 
1041
    }
 
1042
    execute_at_null= FALSE;
 
1043
  }
 
1044
  else
 
1045
  {
 
1046
    /* either starts or m_ends is set */
 
1047
    if (!starts_null)
 
1048
    {
 
1049
      DBUG_PRINT("info", ("STARTS is set"));
 
1050
      /*
 
1051
        - starts is set.
 
1052
        - starts is not in the future according to check made before
 
1053
        Hence schedule for starts + m_expression in case last_executed
 
1054
        is not set, otherwise to last_executed + m_expression
 
1055
      */
 
1056
      if (!last_executed)
 
1057
      {
 
1058
        DBUG_PRINT("info", ("Not executed so far."));
 
1059
      }
 
1060
 
 
1061
      {
 
1062
        my_time_t next_exec;
 
1063
        if (get_next_time(time_zone, &next_exec, starts, time_now,
 
1064
                          (int) expression, interval))
 
1065
          goto err;
 
1066
        execute_at= next_exec;
 
1067
        DBUG_PRINT("info",("Next[%lu]", (ulong) next_exec));
 
1068
      }
 
1069
      execute_at_null= FALSE;
 
1070
    }
 
1071
    else
 
1072
    {
 
1073
      /* this is a dead branch, because starts is always set !!! */
 
1074
      DBUG_PRINT("info", ("STARTS is not set. ENDS is set"));
 
1075
      /*
 
1076
        - m_ends is set
 
1077
        - m_ends is after time_now or is equal
 
1078
        Hence check for m_last_execute and increment with m_expression.
 
1079
        If last_executed is not set then schedule for now
 
1080
      */
 
1081
 
 
1082
      if (!last_executed)
 
1083
        execute_at= time_now;
 
1084
      else
 
1085
      {
 
1086
        my_time_t next_exec;
 
1087
 
 
1088
        if (get_next_time(time_zone, &next_exec, starts, time_now,
 
1089
                          (int) expression, interval))
 
1090
          goto err;
 
1091
 
 
1092
        if (ends < next_exec)
 
1093
        {
 
1094
          DBUG_PRINT("info", ("Next execution after ENDS. Stop executing."));
 
1095
          execute_at= 0;
 
1096
          execute_at_null= TRUE;
 
1097
          status= Event_parse_data::DISABLED;
 
1098
          status_changed= TRUE;
 
1099
          if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
 
1100
            dropped= TRUE;
 
1101
        }
 
1102
        else
 
1103
        {
 
1104
          DBUG_PRINT("info", ("Next[%lu]", (ulong) next_exec));
 
1105
          execute_at= next_exec;
 
1106
          execute_at_null= FALSE;
 
1107
        }
 
1108
      }
 
1109
    }
 
1110
    goto ret;
 
1111
  }
 
1112
ret:
 
1113
  DBUG_PRINT("info", ("ret: 0 execute_at: %lu", (long) execute_at));
 
1114
  DBUG_RETURN(FALSE);
 
1115
err:
 
1116
  DBUG_PRINT("info", ("ret=1"));
 
1117
  DBUG_RETURN(TRUE);
 
1118
}
 
1119
 
 
1120
 
 
1121
/*
 
1122
  Set the internal last_executed MYSQL_TIME struct to now. NOW is the
 
1123
  time according to thd->query_start(), so the THD's clock.
 
1124
 
 
1125
  SYNOPSIS
 
1126
    Event_queue_element::mark_last_executed()
 
1127
      thd   thread context
 
1128
*/
 
1129
 
 
1130
void
 
1131
Event_queue_element::mark_last_executed(THD *thd)
 
1132
{
 
1133
  last_executed= (my_time_t) thd->query_start();
 
1134
  last_executed_changed= TRUE;
 
1135
 
 
1136
  execution_count++;
 
1137
}
 
1138
 
 
1139
 
 
1140
/*
 
1141
  Saves status and last_executed_at to the disk if changed.
 
1142
 
 
1143
  SYNOPSIS
 
1144
    Event_queue_element::update_timing_fields()
 
1145
      thd - thread context
 
1146
 
 
1147
  RETURN VALUE
 
1148
    FALSE   OK
 
1149
    TRUE    Error while opening mysql.event for writing or during
 
1150
            write on disk
 
1151
*/
 
1152
 
 
1153
bool
 
1154
Event_queue_element::update_timing_fields(THD *thd)
 
1155
{
 
1156
  Event_db_repository *db_repository= Events::get_db_repository();
 
1157
  int ret;
 
1158
 
 
1159
  DBUG_ENTER("Event_queue_element::update_timing_fields");
 
1160
 
 
1161
  DBUG_PRINT("enter", ("name: %*s", (int) name.length, name.str));
 
1162
 
 
1163
  /* No need to update if nothing has changed */
 
1164
  if (!(status_changed || last_executed_changed))
 
1165
    DBUG_RETURN(0);
 
1166
 
 
1167
  ret= db_repository->update_timing_fields_for_event(thd,
 
1168
                                                     dbname, name,
 
1169
                                                     last_executed_changed,
 
1170
                                                     last_executed,
 
1171
                                                     status_changed,
 
1172
                                                     (ulonglong) status);
 
1173
  last_executed_changed= status_changed= FALSE;
 
1174
  DBUG_RETURN(ret);
 
1175
}
 
1176
 
 
1177
 
 
1178
static
 
1179
void
 
1180
append_datetime(String *buf, Time_zone *time_zone, my_time_t secs,
 
1181
                const char *name, uint len)
 
1182
{
 
1183
  char dtime_buff[20*2+32];/* +32 to make my_snprintf_{8bit|ucs2} happy */
 
1184
  buf->append(STRING_WITH_LEN(" "));
 
1185
  buf->append(name, len);
 
1186
  buf->append(STRING_WITH_LEN(" '"));
 
1187
  /*
 
1188
    Pass the buffer and the second param tells fills the buffer and
 
1189
    returns the number of chars to copy.
 
1190
  */
 
1191
  MYSQL_TIME time;
 
1192
  time_zone->gmt_sec_to_TIME(&time, secs);
 
1193
  buf->append(dtime_buff, my_datetime_to_str(&time, dtime_buff));
 
1194
  buf->append(STRING_WITH_LEN("'"));
 
1195
}
 
1196
 
 
1197
 
 
1198
/*
 
1199
  Get SHOW CREATE EVENT as string
 
1200
 
 
1201
  SYNOPSIS
 
1202
    Event_timed::get_create_event(THD *thd, String *buf)
 
1203
      thd    Thread
 
1204
      buf    String*, should be already allocated. CREATE EVENT goes inside.
 
1205
 
 
1206
  RETURN VALUE
 
1207
    0                       OK
 
1208
    EVEX_MICROSECOND_UNSUP  Error (for now if mysql.event has been
 
1209
                            tampered and MICROSECONDS interval or
 
1210
                            derivative has been put there.
 
1211
*/
 
1212
 
 
1213
int
 
1214
Event_timed::get_create_event(THD *thd, String *buf)
 
1215
{
 
1216
  char tmp_buf[2 * STRING_BUFFER_USUAL_SIZE];
 
1217
  String expr_buf(tmp_buf, sizeof(tmp_buf), system_charset_info);
 
1218
  expr_buf.length(0);
 
1219
 
 
1220
  DBUG_ENTER("get_create_event");
 
1221
  DBUG_PRINT("ret_info",("body_len=[%d]body=[%s]",
 
1222
                         (int) body.length, body.str));
 
1223
 
 
1224
  if (expression && Events::reconstruct_interval_expression(&expr_buf, interval,
 
1225
                                                            expression))
 
1226
    DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
 
1227
 
 
1228
  buf->append(STRING_WITH_LEN("CREATE EVENT "));
 
1229
  append_identifier(thd, buf, name.str, name.length);
 
1230
 
 
1231
  if (expression)
 
1232
  {
 
1233
    buf->append(STRING_WITH_LEN(" ON SCHEDULE EVERY "));
 
1234
    buf->append(expr_buf);
 
1235
    buf->append(' ');
 
1236
    LEX_STRING *ival= &interval_type_to_name[interval];
 
1237
    buf->append(ival->str, ival->length);
 
1238
 
 
1239
    if (!starts_null)
 
1240
      append_datetime(buf, time_zone, starts, STRING_WITH_LEN("STARTS"));
 
1241
 
 
1242
    if (!ends_null)
 
1243
      append_datetime(buf, time_zone, ends, STRING_WITH_LEN("ENDS"));
 
1244
  }
 
1245
  else
 
1246
  {
 
1247
    append_datetime(buf, time_zone, execute_at,
 
1248
                    STRING_WITH_LEN("ON SCHEDULE AT"));
 
1249
  }
 
1250
 
 
1251
  if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
 
1252
    buf->append(STRING_WITH_LEN(" ON COMPLETION NOT PRESERVE "));
 
1253
  else
 
1254
    buf->append(STRING_WITH_LEN(" ON COMPLETION PRESERVE "));
 
1255
 
 
1256
  if (status == Event_parse_data::ENABLED)
 
1257
    buf->append(STRING_WITH_LEN("ENABLE"));
 
1258
  else if (status == Event_parse_data::SLAVESIDE_DISABLED)
 
1259
    buf->append(STRING_WITH_LEN("DISABLE ON SLAVE"));
 
1260
  else
 
1261
    buf->append(STRING_WITH_LEN("DISABLE"));
 
1262
 
 
1263
  if (comment.length)
 
1264
  {
 
1265
    buf->append(STRING_WITH_LEN(" COMMENT "));
 
1266
    append_unescaped(buf, comment.str, comment.length);
 
1267
  }
 
1268
  buf->append(STRING_WITH_LEN(" DO "));
 
1269
  buf->append(body.str, body.length);
 
1270
 
 
1271
  DBUG_RETURN(0);
 
1272
}
 
1273
 
 
1274
 
 
1275
/**
 
1276
  Get an artificial stored procedure to parse as an event definition.
 
1277
*/
 
1278
 
 
1279
bool
 
1280
Event_job_data::construct_sp_sql(THD *thd, String *sp_sql)
 
1281
{
 
1282
  LEX_STRING buffer;
 
1283
  const uint STATIC_SQL_LENGTH= 44;
 
1284
 
 
1285
  DBUG_ENTER("Event_job_data::construct_sp_sql");
 
1286
 
 
1287
  /*
 
1288
    Allocate a large enough buffer on the thread execution memory
 
1289
    root to avoid multiple [re]allocations on system heap
 
1290
  */
 
1291
  buffer.length= STATIC_SQL_LENGTH + name.length + body.length;
 
1292
  if (! (buffer.str= (char*) thd->alloc(buffer.length)))
 
1293
    DBUG_RETURN(TRUE);
 
1294
 
 
1295
  sp_sql->set(buffer.str, buffer.length, system_charset_info);
 
1296
  sp_sql->length(0);
 
1297
 
 
1298
 
 
1299
  sp_sql->append(C_STRING_WITH_LEN("CREATE "));
 
1300
  sp_sql->append(C_STRING_WITH_LEN("PROCEDURE "));
 
1301
  /*
 
1302
    Let's use the same name as the event name to perhaps produce a
 
1303
    better error message in case it is a part of some parse error.
 
1304
    We're using append_identifier here to successfully parse
 
1305
    events with reserved names.
 
1306
  */
 
1307
  append_identifier(thd, sp_sql, name.str, name.length);
 
1308
 
 
1309
  /*
 
1310
    The default SQL security of a stored procedure is DEFINER. We
 
1311
    have already activated the security context of the event, so
 
1312
    let's execute the procedure with the invoker rights to save on
 
1313
    resets of security contexts.
 
1314
  */
 
1315
  sp_sql->append(C_STRING_WITH_LEN("() SQL SECURITY INVOKER "));
 
1316
 
 
1317
  sp_sql->append(body.str, body.length);
 
1318
 
 
1319
  DBUG_RETURN(thd->is_fatal_error);
 
1320
}
 
1321
 
 
1322
 
 
1323
/**
 
1324
  Get DROP EVENT statement to binlog the drop of ON COMPLETION NOT
 
1325
  PRESERVE event.
 
1326
*/
 
1327
 
 
1328
bool
 
1329
Event_job_data::construct_drop_event_sql(THD *thd, String *sp_sql)
 
1330
{
 
1331
  LEX_STRING buffer;
 
1332
  const uint STATIC_SQL_LENGTH= 14;
 
1333
 
 
1334
  DBUG_ENTER("Event_job_data::construct_drop_event_sql");
 
1335
 
 
1336
  buffer.length= STATIC_SQL_LENGTH + name.length*2 + dbname.length*2;
 
1337
  if (! (buffer.str= (char*) thd->alloc(buffer.length)))
 
1338
    DBUG_RETURN(TRUE);
 
1339
 
 
1340
  sp_sql->set(buffer.str, buffer.length, system_charset_info);
 
1341
  sp_sql->length(0);
 
1342
 
 
1343
  sp_sql->append(C_STRING_WITH_LEN("DROP EVENT "));
 
1344
  append_identifier(thd, sp_sql, dbname.str, dbname.length);
 
1345
  sp_sql->append('.');
 
1346
  append_identifier(thd, sp_sql, name.str, name.length);
 
1347
 
 
1348
  DBUG_RETURN(thd->is_fatal_error);
 
1349
}
 
1350
 
 
1351
/**
 
1352
  Compiles and executes the event (the underlying sp_head object)
 
1353
 
 
1354
  @retval TRUE  error (reported to the error log)
 
1355
  @retval FALSE success
 
1356
*/
 
1357
 
 
1358
bool
 
1359
Event_job_data::execute(THD *thd, bool drop)
 
1360
{
 
1361
  String sp_sql;
 
1362
#ifndef NO_EMBEDDED_ACCESS_CHECKS
 
1363
  Security_context event_sctx, *save_sctx= NULL;
 
1364
#endif
 
1365
  List<Item> empty_item_list;
 
1366
  bool ret= TRUE;
 
1367
 
 
1368
  DBUG_ENTER("Event_job_data::execute");
 
1369
 
 
1370
  mysql_reset_thd_for_next_command(thd);
 
1371
 
 
1372
  /*
 
1373
    MySQL parser currently assumes that current database is either
 
1374
    present in THD or all names in all statements are fully specified.
 
1375
    And yet not fully specified names inside stored programs must be 
 
1376
    be supported, even if the current database is not set:
 
1377
    CREATE PROCEDURE db1.p1() BEGIN CREATE TABLE t1; END//
 
1378
    -- in this example t1 should be always created in db1 and the statement
 
1379
    must parse even if there is no current database.
 
1380
 
 
1381
    To support this feature and still address the parser limitation,
 
1382
    we need to set the current database here.
 
1383
    We don't have to call mysql_change_db, since the checks performed
 
1384
    in it are unnecessary for the purpose of parsing, and
 
1385
    mysql_change_db will be invoked anyway later, to activate the
 
1386
    procedure database before it's executed.
 
1387
  */
 
1388
  thd->set_db(dbname.str, dbname.length);
 
1389
 
 
1390
#ifndef NO_EMBEDDED_ACCESS_CHECKS
 
1391
  if (event_sctx.change_security_context(thd,
 
1392
                                         &definer_user, &definer_host,
 
1393
                                         &dbname, &save_sctx))
 
1394
  {
 
1395
    sql_print_error("Event Scheduler: "
 
1396
                    "[%s].[%s.%s] execution failed, "
 
1397
                    "failed to authenticate the user.",
 
1398
                    definer.str, dbname.str, name.str);
 
1399
    goto end_no_lex_start;
 
1400
  }
 
1401
#endif
 
1402
 
 
1403
  if (check_access(thd, EVENT_ACL, dbname.str,
 
1404
                   0, 0, 0, is_schema_db(dbname.str, dbname.length)))
 
1405
  {
 
1406
    /*
 
1407
      This aspect of behavior is defined in the worklog,
 
1408
      and this is how triggers work too: if TRIGGER
 
1409
      privilege is revoked from trigger definer,
 
1410
      triggers are not executed.
 
1411
    */
 
1412
    sql_print_error("Event Scheduler: "
 
1413
                    "[%s].[%s.%s] execution failed, "
 
1414
                    "user no longer has EVENT privilege.",
 
1415
                    definer.str, dbname.str, name.str);
 
1416
    goto end_no_lex_start;
 
1417
  }
 
1418
 
 
1419
  if (construct_sp_sql(thd, &sp_sql))
 
1420
    goto end_no_lex_start;
 
1421
 
 
1422
  /*
 
1423
    Set up global thread attributes to reflect the properties of
 
1424
    this Event. We can simply reset these instead of usual
 
1425
    backup/restore employed in stored programs since we know that
 
1426
    this is a top level statement and the worker thread is
 
1427
    allocated exclusively to execute this event.
 
1428
  */
 
1429
 
 
1430
  thd->variables.sql_mode= sql_mode;
 
1431
  thd->variables.time_zone= time_zone;
 
1432
 
 
1433
  thd->set_query(sp_sql.c_ptr_safe(), sp_sql.length());
 
1434
 
 
1435
  {
 
1436
    Parser_state parser_state(thd, thd->query(), thd->query_length());
 
1437
    lex_start(thd);
 
1438
 
 
1439
    if (parse_sql(thd, & parser_state, creation_ctx))
 
1440
    {
 
1441
      sql_print_error("Event Scheduler: "
 
1442
                      "%serror during compilation of %s.%s",
 
1443
                      thd->is_fatal_error ? "fatal " : "",
 
1444
                      (const char *) dbname.str, (const char *) name.str);
 
1445
      goto end;
 
1446
    }
 
1447
  }
 
1448
 
 
1449
  {
 
1450
    sp_head *sphead= thd->lex->sphead;
 
1451
 
 
1452
    DBUG_ASSERT(sphead);
 
1453
 
 
1454
    if (thd->enable_slow_log)
 
1455
      sphead->m_flags|= sp_head::LOG_SLOW_STATEMENTS;
 
1456
    sphead->m_flags|= sp_head::LOG_GENERAL_LOG;
 
1457
 
 
1458
    sphead->set_info(0, 0, &thd->lex->sp_chistics, sql_mode);
 
1459
    sphead->set_creation_ctx(creation_ctx);
 
1460
    sphead->optimize();
 
1461
 
 
1462
    ret= sphead->execute_procedure(thd, &empty_item_list);
 
1463
    /*
 
1464
      There is no pre-locking and therefore there should be no
 
1465
      tables open and locked left after execute_procedure.
 
1466
    */
 
1467
  }
 
1468
 
 
1469
end:
 
1470
  if (thd->lex->sphead)                        /* NULL only if a parse error */
 
1471
  {
 
1472
    delete thd->lex->sphead;
 
1473
    thd->lex->sphead= NULL;
 
1474
  }
 
1475
 
 
1476
end_no_lex_start:
 
1477
  if (drop && !thd->is_fatal_error)
 
1478
  {
 
1479
    /*
 
1480
      We must do it here since here we're under the right authentication
 
1481
      ID of the event definer.
 
1482
    */
 
1483
    sql_print_information("Event Scheduler: Dropping %s.%s",
 
1484
                          (const char *) dbname.str, (const char *) name.str);
 
1485
    /*
 
1486
      Construct a query for the binary log, to ensure the event is dropped
 
1487
      on the slave
 
1488
    */
 
1489
    if (construct_drop_event_sql(thd, &sp_sql))
 
1490
      ret= 1;
 
1491
    else
 
1492
    {
 
1493
      ulong saved_master_access;
 
1494
 
 
1495
      thd->set_query(sp_sql.c_ptr_safe(), sp_sql.length());
 
1496
 
 
1497
      /*
 
1498
        NOTE: even if we run in read-only mode, we should be able to lock
 
1499
        the mysql.event table for writing. In order to achieve this, we
 
1500
        should call mysql_lock_tables() under the super-user.
 
1501
      */
 
1502
 
 
1503
      saved_master_access= thd->security_ctx->master_access;
 
1504
      thd->security_ctx->master_access |= SUPER_ACL;
 
1505
 
 
1506
      ret= Events::drop_event(thd, dbname, name, FALSE);
 
1507
 
 
1508
      thd->security_ctx->master_access= saved_master_access;
 
1509
    }
 
1510
  }
 
1511
#ifndef NO_EMBEDDED_ACCESS_CHECKS
 
1512
  if (save_sctx)
 
1513
    event_sctx.restore_security_context(thd, save_sctx);
 
1514
#endif
 
1515
  lex_end(thd->lex);
 
1516
  thd->lex->unit.cleanup();
 
1517
  thd->end_statement();
 
1518
  thd->cleanup_after_query();
 
1519
  /* Avoid races with SHOW PROCESSLIST */
 
1520
  thd->set_query(NULL, 0);
 
1521
 
 
1522
  DBUG_PRINT("info", ("EXECUTED %s.%s  ret: %d", dbname.str, name.str, ret));
 
1523
 
 
1524
  DBUG_RETURN(ret);
 
1525
}
 
1526
 
 
1527
 
 
1528
/*
 
1529
  Checks whether two events are in the same schema
 
1530
 
 
1531
  SYNOPSIS
 
1532
    event_basic_db_equal()
 
1533
      db  Schema
 
1534
      et  Compare et->dbname to `db`
 
1535
 
 
1536
  RETURN VALUE
 
1537
    TRUE   Equal
 
1538
    FALSE  Not equal
 
1539
*/
 
1540
 
 
1541
bool
 
1542
event_basic_db_equal(LEX_STRING db, Event_basic *et)
 
1543
{
 
1544
  return !sortcmp_lex_string(et->dbname, db, system_charset_info);
 
1545
}
 
1546
 
 
1547
 
 
1548
/*
 
1549
  Checks whether an event has equal `db` and `name`
 
1550
 
 
1551
  SYNOPSIS
 
1552
    event_basic_identifier_equal()
 
1553
      db   Schema
 
1554
      name Name
 
1555
      et   The event object
 
1556
 
 
1557
  RETURN VALUE
 
1558
    TRUE   Equal
 
1559
    FALSE  Not equal
 
1560
*/
 
1561
 
 
1562
bool
 
1563
event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b)
 
1564
{
 
1565
  return !sortcmp_lex_string(name, b->name, system_charset_info) &&
 
1566
         !sortcmp_lex_string(db, b->dbname, system_charset_info);
 
1567
}
 
1568
 
 
1569
/**
 
1570
  @} (End of group Event_Scheduler)
 
1571
*/