event_data_objects.cc 55 KB
Newer Older
1
/* Copyright (C) 2004-2006 MySQL AB
unknown's avatar
unknown committed
2 3 4

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
unknown's avatar
unknown committed
6 7 8 9 10 11 12 13 14 15

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

16
#define MYSQL_LEX 1
17 18
#include "mysql_priv.h"
#include "events.h"
19
#include "event_data_objects.h"
unknown's avatar
unknown committed
20
#include "event_db_repository.h"
21 22
#include "sp_head.h"

23 24
/* That's a provisional solution */
extern Event_db_repository events_event_db_repository;
25

26
#define EVEX_MAX_INTERVAL_VALUE 1000000000L
27

28 29
static bool
event_change_security_context(THD *thd, LEX_STRING user, LEX_STRING host,
unknown's avatar
unknown committed
30
                              LEX_STRING db, Security_context *backup);
31 32

static void
unknown's avatar
unknown committed
33
event_restore_security_context(THD *thd, Security_context *backup);
34

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75

/*
  Initiliazes dbname and name of an Event_queue_element_for_exec
  object

  SYNOPSIS
    Event_queue_element_for_exec::init()

  RETURN VALUE
    FALSE  OK
    TRUE   Error (OOM)
*/

bool
Event_queue_element_for_exec::init(LEX_STRING db, LEX_STRING n)
{
  if (!(dbname.str= my_strndup(db.str, dbname.length= db.length, MYF(MY_WME))))
    return TRUE;
  if (!(name.str= my_strndup(n.str, name.length= n.length, MYF(MY_WME))))
  {
    my_free((gptr) dbname.str, MYF(0));
    return TRUE;
  }
  return FALSE;
}


/*
  Destructor

  SYNOPSIS
    Event_queue_element_for_exec::~Event_queue_element_for_exec()
*/

Event_queue_element_for_exec::~Event_queue_element_for_exec()
{
  my_free((gptr) dbname.str, MYF(0));
  my_free((gptr) name.str, MYF(0));
}


76
/*
77
  Returns a new instance
78 79

  SYNOPSIS
80
    Event_parse_data::new_instance()
81

82 83 84 85 86
  RETURN VALUE
    Address or NULL in case of error
  
  NOTE
    Created on THD's mem_root
87 88
*/

89 90
Event_parse_data *
Event_parse_data::new_instance(THD *thd)
91
{
92
  return new (thd->mem_root) Event_parse_data;
93 94
}

unknown's avatar
unknown committed
95 96

/*
97
  Constructor
unknown's avatar
unknown committed
98

99
  SYNOPSIS
100
    Event_parse_data::Event_parse_data()
unknown's avatar
unknown committed
101 102
*/

103
Event_parse_data::Event_parse_data()
104
  :on_completion(Event_basic::ON_COMPLETION_DROP), 
105
   status(Event_basic::ENABLED), do_not_create(FALSE),
106 107 108
   item_starts(NULL), item_ends(NULL), item_execute_at(NULL),
   starts_null(TRUE), ends_null(TRUE), execute_at_null(TRUE),
   item_expression(NULL), expression(0)
unknown's avatar
unknown committed
109
{
110
  DBUG_ENTER("Event_parse_data::Event_parse_data");
unknown's avatar
unknown committed
111

unknown's avatar
unknown committed
112
  /* Actually in the parser STARTS is always set */
113
  starts= ends= execute_at= 0;
unknown's avatar
unknown committed
114

115
  body.str= comment.str= NULL;
unknown's avatar
unknown committed
116
  body.length= comment.length= 0;
117

unknown's avatar
unknown committed
118 119 120 121 122
  DBUG_VOID_RETURN;
}


/*
123
  Set a name of the event
unknown's avatar
unknown committed
124

125
  SYNOPSIS
unknown's avatar
unknown committed
126
    Event_parse_data::init_name()
127 128
      thd   THD
      spn   the name extracted in the parser
unknown's avatar
unknown committed
129 130 131
*/

void
unknown's avatar
unknown committed
132
Event_parse_data::init_name(THD *thd, sp_name *spn)
unknown's avatar
unknown committed
133
{
unknown's avatar
unknown committed
134
  DBUG_ENTER("Event_parse_data::init_name");
unknown's avatar
unknown committed
135 136

  /* We have to copy strings to get them into the right memroot */
unknown's avatar
unknown committed
137
  dbname.length= spn->m_db.length;
unknown's avatar
unknown committed
138
  dbname.str= thd->strmake(spn->m_db.str, spn->m_db.length);
unknown's avatar
unknown committed
139
  name.length= spn->m_name.length;
unknown's avatar
unknown committed
140
  name.str= thd->strmake(spn->m_name.str, spn->m_name.length);
unknown's avatar
unknown committed
141 142 143

  if (spn->m_qname.length == 0)
    spn->init_qname(thd);
144

unknown's avatar
unknown committed
145 146 147 148 149
  DBUG_VOID_RETURN;
}


/*
150
  Set body of the event - what should be executed.
unknown's avatar
unknown committed
151

152
  SYNOPSIS
unknown's avatar
unknown committed
153
    Event_parse_data::init_body()
154
      thd   THD
unknown's avatar
unknown committed
155 156 157 158

  NOTE
    The body is extracted by copying all data between the
    start of the body set by another method and the current pointer in Lex.
unknown's avatar
unknown committed
159

160 161
    Some questionable removal of characters is done in here, and that part
    should be refactored when the parser is smarter.
unknown's avatar
unknown committed
162 163 164
*/

void
unknown's avatar
unknown committed
165
Event_parse_data::init_body(THD *thd)
unknown's avatar
unknown committed
166
{
unknown's avatar
unknown committed
167
  DBUG_ENTER("Event_parse_data::init_body");
unknown's avatar
unknown committed
168 169
  DBUG_PRINT("info", ("body: '%s'  body_begin: 0x%lx end: 0x%lx", body_begin,
                      (long) body_begin, (long) thd->lex->ptr));
unknown's avatar
unknown committed
170

unknown's avatar
unknown committed
171
  body.length= thd->lex->ptr - body_begin;
172 173 174 175 176 177 178 179 180 181 182 183 184 185
  const uchar *body_end= body_begin + body.length - 1;

  /* Trim nuls or close-comments ('*'+'/') or spaces at the end */
  while (body_begin < body_end)
  {

    if ((*body_end == '\0') || 
        (my_isspace(thd->variables.character_set_client, *body_end)))
    { /* consume NULs and meaningless whitespace */
      --body.length;
      --body_end;
      continue;
    }

186
    /*
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
       consume closing comments

       This is arguably wrong, but it's the best we have until the parser is
       changed to be smarter.   FIXME PARSER 

       See also the sp_head code, where something like this is done also.

       One idea is to keep in the lexer structure the count of the number of
       open-comments we've entered, and scan left-to-right looking for a
       closing comment IFF the count is greater than zero.

       Another idea is to remove the closing comment-characters wholly in the
       parser, since that's where it "removes" the opening characters.
    */
    if ((*(body_end - 1) == '*') && (*body_end == '/'))
    {
203
      DBUG_PRINT("info", ("consumend one '*" "/' comment in the query '%s'",
204 205 206 207 208 209 210 211
          body_begin));
      body.length-= 2;
      body_end-= 2;
      continue;
    }

    break;  /* none were found, so we have excised all we can. */
  }
unknown's avatar
unknown committed
212

213 214 215 216 217 218
  /* the first is always whitespace which I cannot skip in the parser */
  while (my_isspace(thd->variables.character_set_client, *body_begin))
  {
    ++body_begin;
    --body.length;
  }
unknown's avatar
unknown committed
219
  body.str= thd->strmake((char *)body_begin, body.length);
unknown's avatar
unknown committed
220 221 222 223 224

  DBUG_VOID_RETURN;
}


225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
/*
  This function is called on CREATE EVENT or ALTER EVENT.  When either
  ENDS or AT is in the past, we are trying to create an event that
  will never be executed.  If it has ON COMPLETION NOT PRESERVE
  (default), then it would normally be dropped already, so on CREATE
  EVENT we give a warning, and do not create anyting.  On ALTER EVENT
  we give a error, and do not change the event.

  If the event has ON COMPLETION PRESERVE, then we see if the event is
  created or altered to the ENABLED (default) state.  If so, then we
  give a warning, and change the state to DISABLED.

  Otherwise it is a valid event in ON COMPLETION PRESERVE DISABLE
  state.
*/

void
Event_parse_data::check_if_in_the_past(THD *thd, my_time_t ltime_utc)
{
  if (ltime_utc >= (my_time_t) thd->query_start())
    return;

  if (on_completion == ON_COMPLETION_DROP)
  {
    switch (thd->lex->sql_command) {
    case SQLCOM_CREATE_EVENT:
      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                   ER_EVENT_CANNOT_CREATE_IN_THE_PAST,
                   ER(ER_EVENT_CANNOT_CREATE_IN_THE_PAST));
      break;
    case SQLCOM_ALTER_EVENT:
      my_error(ER_EVENT_CANNOT_ALTER_IN_THE_PAST, MYF(0));
      break;
    default:
      DBUG_ASSERT(0);
    }

    do_not_create= TRUE;
  }
  else if (status == ENABLED)
  {
    status= DISABLED;
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                 ER_EVENT_EXEC_TIME_IN_THE_PAST,
                 ER(ER_EVENT_EXEC_TIME_IN_THE_PAST));
  }
}


unknown's avatar
unknown committed
274
/*
275
  Sets time for execution for one-time event.
unknown's avatar
unknown committed
276

277
  SYNOPSIS
unknown's avatar
unknown committed
278
    Event_parse_data::init_execute_at()
279
      thd  Thread
unknown's avatar
unknown committed
280

281
  RETURN VALUE
282 283
    0               OK
    ER_WRONG_VALUE  Wrong value for execute at (reported)
unknown's avatar
unknown committed
284 285 286
*/

int
287
Event_parse_data::init_execute_at(THD *thd)
unknown's avatar
unknown committed
288 289 290
{
  my_bool not_used;
  TIME ltime;
291
  my_time_t ltime_utc;
unknown's avatar
unknown committed
292

unknown's avatar
unknown committed
293
  DBUG_ENTER("Event_parse_data::init_execute_at");
unknown's avatar
unknown committed
294

295 296 297 298 299
  if (!item_execute_at)
    DBUG_RETURN(0);

  if (item_execute_at->fix_fields(thd, &item_execute_at))
    goto wrong_value;
300 301 302
  
  /* no starts and/or ends in case of execute_at */
  DBUG_PRINT("info", ("starts_null && ends_null should be 1 is %d",
unknown's avatar
unknown committed
303
                      (starts_null && ends_null)));
304
  DBUG_ASSERT(starts_null && ends_null);
unknown's avatar
unknown committed
305

306 307
  if ((not_used= item_execute_at->get_date(&ltime, TIME_NO_ZERO_DATE)))
    goto wrong_value;
308

309 310
  ltime_utc= TIME_to_timestamp(thd,&ltime,&not_used);
  if (!ltime_utc)
311 312
  {
    DBUG_PRINT("error", ("Execute AT after year 2037"));
313
    goto wrong_value;
314
  }
unknown's avatar
unknown committed
315

316 317
  check_if_in_the_past(thd, ltime_utc);

318
  execute_at_null= FALSE;
319
  execute_at= ltime_utc;
unknown's avatar
unknown committed
320
  DBUG_RETURN(0);
321 322 323 324

wrong_value:
  report_bad_value("AT", item_execute_at);
  DBUG_RETURN(ER_WRONG_VALUE);
unknown's avatar
unknown committed
325 326 327 328
}


/*
329
  Sets time for execution of multi-time event.s
unknown's avatar
unknown committed
330

331
  SYNOPSIS
unknown's avatar
unknown committed
332
    Event_parse_data::init_interval()
333
      thd  Thread
unknown's avatar
unknown committed
334

335
  RETURN VALUE
336 337 338
    0                OK
    EVEX_BAD_PARAMS  Interval is not positive or MICROSECOND (reported)
    ER_WRONG_VALUE   Wrong value for interval (reported)
unknown's avatar
unknown committed
339 340 341
*/

int
342
Event_parse_data::init_interval(THD *thd)
unknown's avatar
unknown committed
343
{
344
  String value;
345
  INTERVAL interval_tmp;
346

unknown's avatar
unknown committed
347
  DBUG_ENTER("Event_parse_data::init_interval");
348 349 350 351 352 353 354 355 356 357 358 359 360 361
  if (!item_expression)
    DBUG_RETURN(0);

  switch (interval) {
  case INTERVAL_MINUTE_MICROSECOND:
  case INTERVAL_HOUR_MICROSECOND:
  case INTERVAL_DAY_MICROSECOND:
  case INTERVAL_SECOND_MICROSECOND:
  case INTERVAL_MICROSECOND:
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND");
    DBUG_RETURN(EVEX_BAD_PARAMS);
  default:
    break;
  }
unknown's avatar
unknown committed
362

363 364
  if (item_expression->fix_fields(thd, &item_expression))
    goto wrong_value;
unknown's avatar
unknown committed
365

366
  value.alloc(MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN);
367 368
  if (get_interval_value(item_expression, interval, &value, &interval_tmp))
    goto wrong_value;
unknown's avatar
unknown committed
369

370 371
  expression= 0;

372
  switch (interval) {
373
  case INTERVAL_YEAR:
374
    expression= interval_tmp.year;
375 376 377
    break;
  case INTERVAL_QUARTER:
  case INTERVAL_MONTH:
378
    expression= interval_tmp.month;
379 380 381
    break;
  case INTERVAL_WEEK:
  case INTERVAL_DAY:
382
    expression= interval_tmp.day;
383 384
    break;
  case INTERVAL_HOUR:
385
    expression= interval_tmp.hour;
386 387
    break;
  case INTERVAL_MINUTE:
388
    expression= interval_tmp.minute;
389 390
    break;
  case INTERVAL_SECOND:
391
    expression= interval_tmp.second;
392
    break;
393
  case INTERVAL_YEAR_MONTH:                     // Allow YEAR-MONTH YYYYYMM
394
    expression= interval_tmp.year* 12 + interval_tmp.month;
395 396
    break;
  case INTERVAL_DAY_HOUR:
397
    expression= interval_tmp.day* 24 + interval_tmp.hour;
398 399
    break;
  case INTERVAL_DAY_MINUTE:
400 401
    expression= (interval_tmp.day* 24 + interval_tmp.hour) * 60 +
                interval_tmp.minute;
402
    break;
unknown's avatar
unknown committed
403
  case INTERVAL_HOUR_SECOND: /* day is anyway 0 */
404 405
  case INTERVAL_DAY_SECOND:
    /* DAY_SECOND having problems because of leap seconds? */
406 407 408
    expression= ((interval_tmp.day* 24 + interval_tmp.hour) * 60 +
                  interval_tmp.minute)*60
                 + interval_tmp.second;
409 410
    break;
  case INTERVAL_HOUR_MINUTE:
411
    expression= interval_tmp.hour * 60 + interval_tmp.minute;
412 413
    break;
  case INTERVAL_MINUTE_SECOND:
414
    expression= interval_tmp.minute * 60 + interval_tmp.second;
415
    break;
416 417
  case INTERVAL_LAST:
    DBUG_ASSERT(0);
418 419
  default:
    ;/* these are the microsec stuff */
420
  }
421
  if (interval_tmp.neg || expression > EVEX_MAX_INTERVAL_VALUE)
422 423
  {
    my_error(ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG, MYF(0));
424
    DBUG_RETURN(EVEX_BAD_PARAMS);
425
  }
426

unknown's avatar
unknown committed
427
  DBUG_RETURN(0);
428 429

wrong_value:
430
  report_bad_value("INTERVAL", item_expression);
431
  DBUG_RETURN(ER_WRONG_VALUE);
unknown's avatar
unknown committed
432 433 434 435
}


/*
436
  Sets STARTS.
437 438

  SYNOPSIS
unknown's avatar
unknown committed
439 440
    Event_parse_data::init_starts()
      expr      how much?
441 442 443 444 445 446 447 448 449

  NOTES
    Note that activation time is not execution time.
    EVERY 5 MINUTE STARTS "2004-12-12 10:00:00" means that
    the event will be executed every 5 minutes but this will
    start at the date shown above. Expressions are possible :
    DATE_ADD(NOW(), INTERVAL 1 DAY)  -- start tommorow at
    same time.

450
  RETURN VALUE
451 452
    0                OK
    ER_WRONG_VALUE  Starts before now
unknown's avatar
unknown committed
453 454 455
*/

int
456
Event_parse_data::init_starts(THD *thd)
unknown's avatar
unknown committed
457 458
{
  my_bool not_used;
459 460
  TIME ltime;
  my_time_t ltime_utc;
unknown's avatar
unknown committed
461

unknown's avatar
unknown committed
462
  DBUG_ENTER("Event_parse_data::init_starts");
463 464
  if (!item_starts)
    DBUG_RETURN(0);
unknown's avatar
unknown committed
465

466 467
  if (item_starts->fix_fields(thd, &item_starts))
    goto wrong_value;
unknown's avatar
unknown committed
468

469 470
  if ((not_used= item_starts->get_date(&ltime, TIME_NO_ZERO_DATE)))
    goto wrong_value;
unknown's avatar
unknown committed
471

472 473
  ltime_utc= TIME_to_timestamp(thd, &ltime, &not_used);
  if (!ltime_utc)
474
    goto wrong_value;
475

476 477
  DBUG_PRINT("info",("now: %ld  starts: %ld",
                     (long) thd->query_start(), (long) ltime_utc));
unknown's avatar
unknown committed
478

479
  starts_null= FALSE;
480
  starts= ltime_utc;
unknown's avatar
unknown committed
481
  DBUG_RETURN(0);
482 483 484 485

wrong_value:
  report_bad_value("STARTS", item_starts);
  DBUG_RETURN(ER_WRONG_VALUE);
unknown's avatar
unknown committed
486 487 488 489
}


/*
490
  Sets ENDS (deactivation time).
491 492

  SYNOPSIS
unknown's avatar
unknown committed
493
    Event_parse_data::init_ends()
494 495 496 497 498 499 500 501 502 503
      thd       THD

  NOTES
    Note that activation time is not execution time.
    EVERY 5 MINUTE ENDS "2004-12-12 10:00:00" means that
    the event will be executed every 5 minutes but this will
    end at the date shown above. Expressions are possible :
    DATE_ADD(NOW(), INTERVAL 1 DAY)  -- end tommorow at
    same time.

504
  RETURN VALUE
505
    0                  OK
506
    EVEX_BAD_PARAMS    Error (reported)
unknown's avatar
unknown committed
507 508
*/

509
int
510
Event_parse_data::init_ends(THD *thd)
unknown's avatar
unknown committed
511 512
{
  my_bool not_used;
513 514
  TIME ltime;
  my_time_t ltime_utc;
unknown's avatar
unknown committed
515

unknown's avatar
unknown committed
516
  DBUG_ENTER("Event_parse_data::init_ends");
517 518
  if (!item_ends)
    DBUG_RETURN(0);
unknown's avatar
unknown committed
519

520 521
  if (item_ends->fix_fields(thd, &item_ends))
    goto error_bad_params;
unknown's avatar
unknown committed
522

523
  DBUG_PRINT("info", ("convert to TIME"));
524 525
  if ((not_used= item_ends->get_date(&ltime, TIME_NO_ZERO_DATE)))
    goto error_bad_params;
unknown's avatar
unknown committed
526

527 528
  ltime_utc= TIME_to_timestamp(thd, &ltime, &not_used);
  if (!ltime_utc)
529
    goto error_bad_params;
530

531 532
  /* Check whether ends is after starts */
  DBUG_PRINT("info", ("ENDS after STARTS?"));
533
  if (!starts_null && starts >= ltime_utc)
534
    goto error_bad_params;
535

536
  check_if_in_the_past(thd, ltime_utc);
unknown's avatar
unknown committed
537

538
  ends_null= FALSE;
539
  ends= ltime_utc;
unknown's avatar
unknown committed
540
  DBUG_RETURN(0);
541 542 543 544

error_bad_params:
  my_error(ER_EVENT_ENDS_BEFORE_STARTS, MYF(0));
  DBUG_RETURN(EVEX_BAD_PARAMS);
unknown's avatar
unknown committed
545 546 547 548
}


/*
549 550
  Prints an error message about invalid value. Internally used
  during input data verification
unknown's avatar
unknown committed
551

552
  SYNOPSIS
553 554 555
    Event_parse_data::report_bad_value()
      item_name The name of the parameter
      bad_item  The parameter
unknown's avatar
unknown committed
556 557 558
*/

void
559 560 561 562 563 564 565 566 567 568
Event_parse_data::report_bad_value(const char *item_name, Item *bad_item)
{
  char buff[120];
  String str(buff,(uint32) sizeof(buff), system_charset_info);
  String *str2= bad_item->fixed? bad_item->val_str(&str):NULL;
  my_error(ER_WRONG_VALUE, MYF(0), item_name, str2? str2->c_ptr_safe():"NULL");
}


/*
569
  Checks for validity the data gathered during the parsing phase.
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584

  SYNOPSIS
    Event_parse_data::check_parse_data()
      thd  Thread

  RETURN VALUE
    FALSE  OK
    TRUE   Error (reported)
*/

bool
Event_parse_data::check_parse_data(THD *thd)
{
  bool ret;
  DBUG_ENTER("Event_parse_data::check_parse_data");
unknown's avatar
unknown committed
585 586 587
  DBUG_PRINT("info", ("execute_at: 0x%lx  expr=0x%lx  starts=0x%lx  ends=0x%lx",
                      (long) item_execute_at, (long) item_expression,
                      (long) item_starts, (long) item_ends));
588 589 590 591 592 593

  init_name(thd, identifier);

  init_definer(thd);
  ret= init_execute_at(thd) || init_interval(thd) || init_starts(thd) ||
       init_ends(thd);
594
  check_originator_id(thd);
595 596 597
  DBUG_RETURN(ret);
}

598

unknown's avatar
unknown committed
599 600 601 602 603 604 605 606 607 608 609 610 611
/*
  Inits definer (definer_user and definer_host) during parsing.

  SYNOPSIS
    Event_parse_data::init_definer()
      thd  Thread
*/

void
Event_parse_data::init_definer(THD *thd)
{
  DBUG_ENTER("Event_parse_data::init_definer");

612 613 614 615 616 617
  DBUG_ASSERT(thd->lex->definer);

  const char *definer_user= thd->lex->definer->user.str;
  const char *definer_host= thd->lex->definer->host.str;
  int definer_user_len= thd->lex->definer->user.length;
  int definer_host_len= thd->lex->definer->host.length;
unknown's avatar
unknown committed
618

619 620 621
  DBUG_PRINT("info",("init definer_user thd->mem_root: 0x%lx  "
                     "definer_user: 0x%lx", (long) thd->mem_root,
                     (long) definer_user));
unknown's avatar
unknown committed
622 623 624 625 626 627 628

  /* + 1 for @ */
  DBUG_PRINT("info",("init definer as whole"));
  definer.length= definer_user_len + definer_host_len + 1;
  definer.str= thd->alloc(definer.length + 1);

  DBUG_PRINT("info",("copy the user"));
629
  memcpy(definer.str, definer_user, definer_user_len);
unknown's avatar
unknown committed
630 631 632
  definer.str[definer_user_len]= '@';

  DBUG_PRINT("info",("copy the host"));
633
  memcpy(definer.str + definer_user_len + 1, definer_host, definer_host_len);
unknown's avatar
unknown committed
634 635 636 637 638 639 640
  definer.str[definer.length]= '\0';
  DBUG_PRINT("info",("definer [%s] initted", definer.str));

  DBUG_VOID_RETURN;
}


641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
/*
  Set the originator id of the event to the server_id if executing on
  the master or set to the server_id of the master if executing on 
  the slave. If executing on slave, also set status to SLAVESIDE_DISABLED.

  SYNOPSIS
    Event_parse_data::check_originator_id()
*/
void Event_parse_data::check_originator_id(THD *thd)
{
  /* Disable replicated events on slave. */
  if ((thd->system_thread == SYSTEM_THREAD_SLAVE_SQL) ||
      (thd->system_thread == SYSTEM_THREAD_SLAVE_IO))
  {
    DBUG_PRINT("info", ("Invoked object status set to SLAVESIDE_DISABLED."));
656 657
    if ((status == Event_basic::ENABLED) ||
        (status == Event_basic::DISABLED))
658 659 660 661 662 663 664 665
      status = Event_basic::SLAVESIDE_DISABLED;
    originator = thd->server_id;
  }
  else
    originator = server_id;
}


666 667 668 669 670 671 672 673
/*
  Constructor

  SYNOPSIS
    Event_basic::Event_basic()
*/

Event_basic::Event_basic()
unknown's avatar
unknown committed
674
{
675 676 677 678 679
  DBUG_ENTER("Event_basic::Event_basic");
  /* init memory root */
  init_alloc_root(&mem_root, 256, 512);
  dbname.str= name.str= NULL;
  dbname.length= name.length= 0;
680
  time_zone= NULL;
681 682
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
683

684 685 686

/*
  Destructor
unknown's avatar
unknown committed
687

688 689 690
  SYNOPSIS
    Event_basic::Event_basic()
*/
unknown's avatar
unknown committed
691

692 693 694 695
Event_basic::~Event_basic()
{
  DBUG_ENTER("Event_basic::~Event_basic");
  free_root(&mem_root, MYF(0));
unknown's avatar
unknown committed
696 697 698 699 700
  DBUG_VOID_RETURN;
}


/*
701
  Short function to load a char column into a LEX_STRING
unknown's avatar
unknown committed
702

703
  SYNOPSIS
704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
    Event_basic::load_string_field()
      field_name  The field( enum_events_table_field is not actually used
                  because it's unknown in event_data_objects.h)
      fields      The Field array
      field_value The value
*/

bool
Event_basic::load_string_fields(Field **fields, ...)
{
  bool ret= FALSE;
  va_list args;
  enum enum_events_table_field field_name;
  LEX_STRING *field_value;

  DBUG_ENTER("Event_basic::load_string_fields");
unknown's avatar
unknown committed
720

721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
  va_start(args, fields);
  field_name= (enum enum_events_table_field) va_arg(args, int);
  while (field_name != ET_FIELD_COUNT)
  {
    field_value= va_arg(args, LEX_STRING *);
    if ((field_value->str= get_field(&mem_root, fields[field_name])) == NullS)
    {
      ret= TRUE;
      break;
    }
    field_value->length= strlen(field_value->str);  

    field_name= (enum enum_events_table_field) va_arg(args, int);
  }
  va_end(args);
unknown's avatar
unknown committed
736

737 738 739 740
  DBUG_RETURN(ret);
}


741 742 743 744 745 746 747 748 749 750
bool
Event_basic::load_time_zone(THD *thd, const LEX_STRING tz_name)
{
  String str(tz_name.str, &my_charset_latin1);
  time_zone= my_tz_find(thd, &str);

  return (time_zone == NULL);
}


751 752 753 754 755
/*
  Constructor

  SYNOPSIS
    Event_queue_element::Event_queue_element()
unknown's avatar
unknown committed
756 757
*/

758 759 760
Event_queue_element::Event_queue_element():
  status_changed(FALSE), last_executed_changed(FALSE),
  on_completion(ON_COMPLETION_DROP), status(ENABLED),
unknown's avatar
unknown committed
761
  expression(0), dropped(FALSE), execution_count(0)
unknown's avatar
unknown committed
762
{
763
  DBUG_ENTER("Event_queue_element::Event_queue_element");
unknown's avatar
unknown committed
764

765
  starts= ends= execute_at= last_executed= 0;
766
  starts_null= ends_null= execute_at_null= TRUE;
unknown's avatar
unknown committed
767

768 769
  DBUG_VOID_RETURN;
}
770

771

772 773
/*
  Destructor
774

775 776 777 778 779
  SYNOPSIS
    Event_queue_element::Event_queue_element()
*/
Event_queue_element::~Event_queue_element()
{
unknown's avatar
unknown committed
780 781 782 783
}


/*
unknown's avatar
unknown committed
784
  Constructor
unknown's avatar
unknown committed
785

786
  SYNOPSIS
unknown's avatar
unknown committed
787
    Event_timed::Event_timed()
unknown's avatar
unknown committed
788 789
*/

790 791
Event_timed::Event_timed():
  created(0), modified(0), sql_mode(0)
unknown's avatar
unknown committed
792
{
793
  DBUG_ENTER("Event_timed::Event_timed");
unknown's avatar
unknown committed
794
  init();
795
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
796 797 798 799
}


/*
unknown's avatar
unknown committed
800
  Destructor
801 802

  SYNOPSIS
unknown's avatar
unknown committed
803 804 805 806 807
    Event_timed::~Event_timed()
*/

Event_timed::~Event_timed()
{    
808 809 810 811 812 813 814 815 816 817
}


/*
  Constructor

  SYNOPSIS
    Event_job_data::Event_job_data()
*/

818
Event_job_data::Event_job_data()
819
  :sphead(NULL), sql_mode(0)
820 821 822 823 824 825 826 827 828 829 830 831
{
}


/*
  Destructor

  SYNOPSIS
    Event_timed::~Event_timed()
*/

Event_job_data::~Event_job_data()
unknown's avatar
unknown committed
832
{
unknown's avatar
unknown committed
833 834 835 836
  DBUG_ENTER("Event_job_data::~Event_job_data");
  delete sphead;
  sphead= NULL;
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
}


/*
  Init all member variables

  SYNOPSIS
    Event_timed::init()
*/

void
Event_timed::init()
{
  DBUG_ENTER("Event_timed::init");

852 853
  definer_user.str= definer_host.str= body.str= comment.str= NULL;
  definer_user.length= definer_host.length= body.length= comment.length= 0;
unknown's avatar
unknown committed
854 855 856 857

  sql_mode= 0;

  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
858 859 860 861
}


/*
862
  Loads an event's body from a row from mysql.event
863 864

  SYNOPSIS
865
    Event_job_data::load_from_row(THD *thd, TABLE *table)
866

867 868 869 870
  RETURN VALUE
    0                      OK
    EVEX_GET_FIELD_FAILED  Error

871 872 873 874
  NOTES
    This method is silent on errors and should behave like that. Callers
    should handle throwing of error messages. The reason is that the class
    should not know about how to deal with communication.
unknown's avatar
unknown committed
875 876 877
*/

int
878
Event_job_data::load_from_row(THD *thd, TABLE *table)
unknown's avatar
unknown committed
879 880 881
{
  char *ptr;
  uint len;
882
  DBUG_ENTER("Event_job_data::load_from_row");
unknown's avatar
unknown committed
883 884 885 886

  if (!table)
    goto error;

unknown's avatar
unknown committed
887
  if (table->s->fields != ET_FIELD_COUNT)
unknown's avatar
unknown committed
888 889
    goto error;

890
  LEX_STRING tz_name;
891 892
  load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name,
                     ET_FIELD_BODY, &body, ET_FIELD_DEFINER, &definer,
893 894 895
                     ET_FIELD_TIME_ZONE, &tz_name, ET_FIELD_COUNT);
  if (load_time_zone(thd, tz_name))
    goto error;
896

897 898 899 900
  ptr= strchr(definer.str, '@');

  if (! ptr)
    ptr= definer.str;
unknown's avatar
unknown committed
901

902 903 904 905 906
  len= ptr - definer.str;
  definer_user.str= strmake_root(&mem_root, definer.str, len);
  definer_user.length= len;
  len= definer.length - len - 1;
  /* 1:because of @ */
unknown's avatar
unknown committed
907
  definer_host.str= strmake_root(&mem_root, ptr + 1, len);
908
  definer_host.length= len;
unknown's avatar
unknown committed
909

910 911 912 913 914 915 916 917 918 919 920 921
  sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();

  DBUG_RETURN(0);
error:
  DBUG_RETURN(EVEX_GET_FIELD_FAILED);
}


/*
  Loads an event from a row from mysql.event

  SYNOPSIS
922
    Event_queue_element::load_from_row(THD *thd, TABLE *table)
923 924 925 926 927 928 929 930 931 932 933 934

  RETURN VALUE
    0                      OK
    EVEX_GET_FIELD_FAILED  Error

  NOTES
    This method is silent on errors and should behave like that. Callers
    should handle throwing of error messages. The reason is that the class
    should not know about how to deal with communication.
*/

int
935
Event_queue_element::load_from_row(THD *thd, TABLE *table)
936 937
{
  char *ptr;
938
  TIME time;
unknown's avatar
unknown committed
939

940
  DBUG_ENTER("Event_queue_element::load_from_row");
unknown's avatar
unknown committed
941

942
  if (!table)
unknown's avatar
unknown committed
943 944
    goto error;

945 946
  if (table->s->fields != ET_FIELD_COUNT)
    goto error;
unknown's avatar
unknown committed
947

948
  LEX_STRING tz_name;
949
  load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name,
950 951 952 953
                     ET_FIELD_DEFINER, &definer,
                     ET_FIELD_TIME_ZONE, &tz_name, ET_FIELD_COUNT);
  if (load_time_zone(thd, tz_name))
    goto error;
unknown's avatar
unknown committed
954

955
  starts_null= table->field[ET_FIELD_STARTS]->is_null();
956 957 958 959 960
  if (!starts_null)
  {
    table->field[ET_FIELD_STARTS]->get_date(&time, TIME_NO_ZERO_DATE);
    starts= sec_since_epoch_TIME(&time);
  }
961

962
  ends_null= table->field[ET_FIELD_ENDS]->is_null();
963 964 965 966 967
  if (!ends_null)
  {
    table->field[ET_FIELD_ENDS]->get_date(&time, TIME_NO_ZERO_DATE);
    ends= sec_since_epoch_TIME(&time);
  }
unknown's avatar
unknown committed
968

unknown's avatar
unknown committed
969
  if (!table->field[ET_FIELD_INTERVAL_EXPR]->is_null())
970
    expression= table->field[ET_FIELD_INTERVAL_EXPR]->val_int();
971
  else
972
    expression= 0;
unknown's avatar
unknown committed
973
  /*
974
    If neigher STARTS and ENDS is set, then both fields are empty.
975
    Hence, if ET_FIELD_EXECUTE_AT is empty there is an error.
unknown's avatar
unknown committed
976
  */
977 978
  execute_at_null= table->field[ET_FIELD_EXECUTE_AT]->is_null();
  DBUG_ASSERT(!(starts_null && ends_null && !expression && execute_at_null));
979 980 981 982 983 984 985
  if (!expression && !execute_at_null)
  {
    if (table->field[ET_FIELD_EXECUTE_AT]->get_date(&time,
                                                    TIME_NO_ZERO_DATE))
      goto error;
    execute_at= sec_since_epoch_TIME(&time);
  }
unknown's avatar
unknown committed
986 987

  /*
988 989 990 991
    We load the interval type from disk as string and then map it to
    an integer. This decouples the values of enum interval_type
    and values actually stored on disk. Therefore the type can be
    reordered without risking incompatibilities of data between versions.
unknown's avatar
unknown committed
992
  */
unknown's avatar
unknown committed
993
  if (!table->field[ET_FIELD_TRANSIENT_INTERVAL]->is_null())
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
  {
    int i;
    char buff[MAX_FIELD_WIDTH];
    String str(buff, sizeof(buff), &my_charset_bin);
    LEX_STRING tmp;

    table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_str(&str);
    if (!(tmp.length= str.length()))
      goto error;

    tmp.str= str.c_ptr_safe();

    i= find_string_in_array(interval_type_to_name, &tmp, system_charset_info);
    if (i < 0)
      goto error;
    interval= (interval_type) i;
  }
unknown's avatar
unknown committed
1011

1012 1013 1014 1015 1016 1017
  if (!table->field[ET_FIELD_LAST_EXECUTED]->is_null())
  {
    table->field[ET_FIELD_LAST_EXECUTED]->get_date(&time,
                                                   TIME_NO_ZERO_DATE);
    last_executed= sec_since_epoch_TIME(&time);
  }
1018
  last_executed_changed= FALSE;
unknown's avatar
unknown committed
1019

1020
  if ((ptr= get_field(&mem_root, table->field[ET_FIELD_STATUS])) == NullS)
unknown's avatar
unknown committed
1021
    goto error;
1022

1023
  DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", name.str, ptr));
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040

  /* Set event status (ENABLED | SLAVESIDE_DISABLED | DISABLED) */
  switch (ptr[0])
  {
  case 'E' :
    status = Event_queue_element::ENABLED;
    break;
  case 'S' :
    status = Event_queue_element::SLAVESIDE_DISABLED;
    break;
  case 'D' :
    status = Event_queue_element::DISABLED;
    break;
  }
  if ((ptr= get_field(&mem_root, table->field[ET_FIELD_ORIGINATOR])) == NullS)
    goto error;
  originator = table->field[ET_FIELD_ORIGINATOR]->val_int(); 
unknown's avatar
unknown committed
1041

unknown's avatar
unknown committed
1042
  /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
1043
  if ((ptr= get_field(&mem_root,
1044
                      table->field[ET_FIELD_ON_COMPLETION])) == NullS)
unknown's avatar
unknown committed
1045
    goto error;
1046

1047 1048
  on_completion= (ptr[0]=='D'? Event_queue_element::ON_COMPLETION_DROP:
                               Event_queue_element::ON_COMPLETION_PRESERVE);
unknown's avatar
unknown committed
1049

1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
  DBUG_RETURN(0);
error:
  DBUG_RETURN(EVEX_GET_FIELD_FAILED);
}


/*
  Loads an event from a row from mysql.event

  SYNOPSIS
1060
    Event_timed::load_from_row(THD *thd, TABLE *table)
1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072

  RETURN VALUE
    0                      OK
    EVEX_GET_FIELD_FAILED  Error

  NOTES
    This method is silent on errors and should behave like that. Callers
    should handle throwing of error messages. The reason is that the class
    should not know about how to deal with communication.
*/

int
1073
Event_timed::load_from_row(THD *thd, TABLE *table)
1074 1075 1076 1077 1078 1079
{
  char *ptr;
  uint len;

  DBUG_ENTER("Event_timed::load_from_row");

1080
  if (Event_queue_element::load_from_row(thd, table))
unknown's avatar
unknown committed
1081 1082
    goto error;

1083
  load_string_fields(table->field, ET_FIELD_BODY, &body, ET_FIELD_COUNT);
unknown's avatar
unknown committed
1084

1085
  ptr= strchr(definer.str, '@');
unknown's avatar
unknown committed
1086

1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103
  if (! ptr)
    ptr= definer.str;

  len= ptr - definer.str;
  definer_user.str= strmake_root(&mem_root, definer.str, len);
  definer_user.length= len;
  len= definer.length - len - 1;
  /* 1:because of @ */
  definer_host.str= strmake_root(&mem_root, ptr + 1,  len);
  definer_host.length= len;

  created= table->field[ET_FIELD_CREATED]->val_int();
  modified= table->field[ET_FIELD_MODIFIED]->val_int();

  comment.str= get_field(&mem_root, table->field[ET_FIELD_COMMENT]);
  if (comment.str != NullS)
    comment.length= strlen(comment.str);
unknown's avatar
unknown committed
1104
  else
1105
    comment.length= 0;
1106

1107
  sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
1108

unknown's avatar
unknown committed
1109 1110 1111 1112 1113 1114
  DBUG_RETURN(0);
error:
  DBUG_RETURN(EVEX_GET_FIELD_FAILED);
}


unknown's avatar
unknown committed
1115
/*
1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135
  add_interval() adds a specified interval to time 'ltime' in time
  zone 'time_zone', and returns the result converted to the number of
  seconds since epoch (aka Unix time; in UTC time zone).  Zero result
  means an error.
*/
static
my_time_t
add_interval(TIME *ltime, const Time_zone *time_zone,
             interval_type scale, INTERVAL interval)
{
  if (date_add_interval(ltime, scale, interval))
    return 0;

  my_bool not_used;
  return time_zone->TIME_to_gmt_sec(ltime, &not_used);
}


/*
  Computes the sum of a timestamp plus interval.
1136 1137

  SYNOPSIS
1138 1139
    get_next_time()
      time_zone     event time zone
1140 1141
      next          the sum
      start         add interval_value to this time
1142
      time_now      current time
1143 1144
      i_value       quantity of time type interval to add
      i_type        type of interval to add (SECOND, MINUTE, HOUR, WEEK ...)
unknown's avatar
unknown committed
1145

1146
  RETURN VALUE
1147 1148 1149 1150 1151 1152 1153 1154 1155
    0  OK
    1  Error

  NOTES
    1) If the interval is conversible to SECOND, like MINUTE, HOUR, DAY, WEEK.
       Then we use TIMEDIFF()'s implementation as underlying and number of
       seconds as resolution for computation.
    2) In all other cases - MONTH, QUARTER, YEAR we use MONTH as resolution
       and PERIOD_DIFF()'s implementation
1156
*/
unknown's avatar
unknown committed
1157

1158
static
1159 1160
bool get_next_time(const Time_zone *time_zone, my_time_t *next,
                   my_time_t start, my_time_t time_now,
1161
                   int i_value, interval_type i_type)
1162
{
1163
  DBUG_ENTER("get_next_time");
1164
  DBUG_PRINT("enter", ("start: %lu  now: %lu", (long) start, (long) time_now));
1165

1166 1167 1168
  DBUG_ASSERT(start <= time_now);

  longlong months=0, seconds=0;
1169 1170 1171

  switch (i_type) {
  case INTERVAL_YEAR:
1172
    months= i_value*12;
1173 1174
    break;
  case INTERVAL_QUARTER:
1175
    /* Has already been converted to months */
1176 1177
  case INTERVAL_YEAR_MONTH:
  case INTERVAL_MONTH:
1178
    months= i_value;
1179 1180
    break;
  case INTERVAL_WEEK:
1181
    /* WEEK has already been converted to days */
1182
  case INTERVAL_DAY:
1183
    seconds= i_value*24*3600;
1184 1185 1186
    break;
  case INTERVAL_DAY_HOUR:
  case INTERVAL_HOUR:
1187
    seconds= i_value*3600;
1188 1189 1190 1191
    break;
  case INTERVAL_DAY_MINUTE:
  case INTERVAL_HOUR_MINUTE:
  case INTERVAL_MINUTE:
1192
    seconds= i_value*60;
1193 1194 1195 1196 1197
    break;
  case INTERVAL_DAY_SECOND:
  case INTERVAL_HOUR_SECOND:
  case INTERVAL_MINUTE_SECOND:
  case INTERVAL_SECOND:
1198
    seconds= i_value;
1199 1200 1201 1202 1203 1204
    break;
  case INTERVAL_DAY_MICROSECOND:
  case INTERVAL_HOUR_MICROSECOND:
  case INTERVAL_MINUTE_MICROSECOND:
  case INTERVAL_SECOND_MICROSECOND:
  case INTERVAL_MICROSECOND:
1205 1206 1207 1208 1209
    /*
     We should return an error here so SHOW EVENTS/ SELECT FROM I_S.EVENTS
     would give an error then.
    */
    DBUG_RETURN(1);
1210
    break;
1211 1212
  case INTERVAL_LAST:
    DBUG_ASSERT(0);
1213
  }
unknown's avatar
unknown committed
1214
  DBUG_PRINT("info", ("seconds: %ld  months: %ld", (long) seconds, (long) months));
1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228

  TIME local_start;
  TIME local_now;

  /* Convert times from UTC to local. */
  {
    time_zone->gmt_sec_to_TIME(&local_start, start);
    time_zone->gmt_sec_to_TIME(&local_now, time_now);
  }

  INTERVAL interval;
  bzero(&interval, sizeof(interval));
  my_time_t next_time= 0;

1229 1230 1231 1232
  if (seconds)
  {
    longlong seconds_diff;
    long microsec_diff;
1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246
    bool negative= calc_time_diff(&local_now, &local_start, 1,
                                  &seconds_diff, &microsec_diff);
    if (!negative)
    {
      /*
        The formula below returns the interval that, when added to
        local_start, will always give the time in the future.
      */
      interval.second= seconds_diff - seconds_diff % seconds + seconds;
      next_time= add_interval(&local_start, time_zone,
                              INTERVAL_SECOND, interval);
      if (next_time == 0)
        goto done;
    }
1247

1248
    if (next_time <= time_now)
1249
    {
1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312
      /*
        If 'negative' is true above, then 'next_time == 0', and
        'next_time <= time_now' is also true.  If negative is false,
        then next_time was set, but perhaps to the value that is less
        then time_now.  See below for elaboration.
      */
      DBUG_ASSERT(negative || next_time > 0);

      /*
        If local_now < local_start, i.e. STARTS time is in the future
        according to the local time (it always in the past according
        to UTC---this is a prerequisite of this function), then
        STARTS is almost always in the past according to the local
        time too.  However, in the time zone that has backward
        Daylight Saving Time shift, the following may happen: suppose
        we have a backward DST shift at certain date after 2:59:59,
        i.e. local time goes 1:59:59, 2:00:00, ... , 2:59:59, (shift
        here) 2:00:00 (again), ... , 2:59:59 (again), 3:00:00, ... .
        Now suppose the time has passed the first 2:59:59, has been
        shifted backward, and now is (the second) 2:20:00.  The user
        does CREATE EVENT with STARTS 'current-date 2:40:00'.  Local
        time 2:40:00 from create statement is treated by time
        functions as the first such time, so according to UTC it comes
        before the second 2:20:00.  But according to local time it is
        obviously in the future, so we end up in this branch.

        Since we are in the second pass through 2:00:00--2:59:59, and
        any local time form this interval is treated by system
        functions as the time from the first pass, we have to find the
        time for the next execution that is past the DST-affected
        interval (past the second 2:59:59 for our example,
        i.e. starting from 3:00:00).  We do this in the loop until the
        local time is mapped onto future UTC time.  'start' time is in
        the past, so we may use 'do { } while' here, and add the first
        interval right away.

        Alternatively, it could be that local_now >= local_start.  Now
        for the example above imagine we do CREATE EVENT with STARTS
        'current-date 2:10:00'.  Local start 2:10 is in the past (now
        is local 2:20), so we add an interval, and get next execution
        time, say, 2:40.  It is in the future according to local time,
        but, again, since we are in the second pass through
        2:00:00--2:59:59, 2:40 will be converted into UTC time in the
        past.  So we will end up in this branch again, and may add
        intervals in a 'do { } while' loop.

        Note that for any given event we may end up here only if event
        next execution time will map to the time interval that is
        passed twice, and only if the server was started during the
        second pass, or the event is being created during the second
        pass.  After that, we never will get here (unless we again
        start the server during the second pass).  In other words,
        such a condition is extremely rare.
      */
      interval.second= seconds;
      do
      {
        next_time= add_interval(&local_start, time_zone,
                                INTERVAL_SECOND, interval);
        if (next_time == 0)
          goto done;
      }
      while (next_time <= time_now);
1313 1314 1315 1316
    }
  }
  else
  {
1317 1318
    long diff_months= (long) (local_now.year - local_start.year)*12 +
                      (local_now.month - local_start.month);
1319
    /*
1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335
      Unlike for seconds above, the formula below returns the interval
      that, when added to the local_start, will give the time in the
      past, or somewhere in the current month.  We are interested in
      the latter case, to see if this time has already passed, or is
      yet to come this month.

      Note that the time is guaranteed to be in the past unless
      (diff_months % months == 0), but no good optimization is
      possible here, because (diff_months % months == 0) is what will
      happen most of the time, as get_next_time() will be called right
      after the execution of the event.  We could pass last_executed
      time to this function, and see if the execution has already
      happened this month, but for that we will have to convert
      last_executed from seconds since epoch to local broken-down
      time, and this will greatly reduce the effect of the
      optimization.  So instead we keep the code simple and clean.
1336
    */
1337
    interval.month= (ulong) (diff_months - diff_months % months);
1338 1339 1340
    next_time= add_interval(&local_start, time_zone,
                            INTERVAL_MONTH, interval);
    if (next_time == 0)
1341
      goto done;
1342

1343 1344
    if (next_time <= time_now)
    {
1345
      interval.month= (ulong) months;
1346 1347 1348
      next_time= add_interval(&local_start, time_zone,
                              INTERVAL_MONTH, interval);
      if (next_time == 0)
1349 1350 1351
        goto done;
    }
  }
1352

1353 1354 1355 1356
  DBUG_ASSERT(time_now < next_time);

  *next= next_time;

1357
done:
1358 1359
  DBUG_PRINT("info", ("next_time: %ld", (long) next_time));
  DBUG_RETURN(next_time == 0);
1360 1361 1362 1363
}


/*
1364 1365 1366
  Computes next execution time.

  SYNOPSIS
1367
    Event_queue_element::compute_next_execution_time()
1368

1369 1370 1371 1372
  RETURN VALUE
    FALSE  OK
    TRUE   Error

1373
  NOTES
1374 1375
    The time is set in execute_at, if no more executions the latter is
    set to 0.
unknown's avatar
unknown committed
1376 1377
*/

unknown's avatar
unknown committed
1378
bool
1379
Event_queue_element::compute_next_execution_time()
unknown's avatar
unknown committed
1380
{
1381
  my_time_t time_now;
1382
  DBUG_ENTER("Event_queue_element::compute_next_execution_time");
unknown's avatar
unknown committed
1383
  DBUG_PRINT("enter", ("starts: %lu  ends: %lu  last_executed: %lu  this: 0x%lx",
1384
                       (long) starts, (long) ends, (long) last_executed,
unknown's avatar
unknown committed
1385
                       (long) this));
unknown's avatar
unknown committed
1386

1387
  if (status != Event_queue_element::ENABLED)
unknown's avatar
unknown committed
1388 1389
  {
    DBUG_PRINT("compute_next_execution_time",
unknown's avatar
unknown committed
1390
               ("Event %s is DISABLED", name.str));
unknown's avatar
unknown committed
1391 1392
    goto ret;
  }
1393
  /* If one-time, no need to do computation */
unknown's avatar
unknown committed
1394
  if (!expression)
unknown's avatar
unknown committed
1395
  {
1396
    /* Let's check whether it was executed */
1397
    if (last_executed)
unknown's avatar
unknown committed
1398
    {
1399
      DBUG_PRINT("info",("One-time event %s.%s of was already executed",
unknown's avatar
unknown committed
1400
                         dbname.str, name.str));
1401
      dropped= (on_completion == Event_queue_element::ON_COMPLETION_DROP);
unknown's avatar
unknown committed
1402
      DBUG_PRINT("info",("One-time event will be dropped: %d.", dropped));
1403

1404 1405
      status= Event_queue_element::DISABLED;
      status_changed= TRUE;
unknown's avatar
unknown committed
1406 1407 1408
    }
    goto ret;
  }
1409

unknown's avatar
unknown committed
1410
  time_now= (my_time_t) current_thd->query_start();
1411

1412
  DBUG_PRINT("info",("NOW: [%lu]", (ulong) time_now));
1413

unknown's avatar
unknown committed
1414
  /* if time_now is after ends don't execute anymore */
1415
  if (!ends_null && ends < time_now)
unknown's avatar
unknown committed
1416
  {
1417
    DBUG_PRINT("info", ("NOW after ENDS, don't execute anymore"));
1418
    /* time_now is after ends. don't execute anymore */
1419
    execute_at= 0;
1420
    execute_at_null= TRUE;
1421 1422
    if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
      dropped= TRUE;
unknown's avatar
unknown committed
1423
    DBUG_PRINT("info", ("Dropped: %d", dropped));
1424 1425
    status= Event_queue_element::DISABLED;
    status_changed= TRUE;
1426
    dropped= TRUE;
unknown's avatar
unknown committed
1427 1428 1429

    goto ret;
  }
1430 1431 1432 1433 1434

  /*
    Here time_now is before or equals ends if the latter is set.
    Let's check whether time_now is before starts.
    If so schedule for starts.
unknown's avatar
unknown committed
1435
  */
1436
  if (!starts_null && time_now <= starts)
unknown's avatar
unknown committed
1437
  {
1438
    if (time_now == starts && starts == last_executed)
unknown's avatar
unknown committed
1439
    {
1440
      /*
unknown's avatar
unknown committed
1441
        do nothing or we will schedule for second time execution at starts.
unknown's avatar
unknown committed
1442 1443 1444 1445
      */
    }
    else
    {
1446
      DBUG_PRINT("info", ("STARTS is future, NOW <= STARTS,sched for STARTS"));
unknown's avatar
unknown committed
1447 1448 1449 1450 1451
      /*
        starts is in the future
        time_now before starts. Scheduling for starts
      */
      execute_at= starts;
1452
      execute_at_null= FALSE;
unknown's avatar
unknown committed
1453 1454 1455
      goto ret;
    }
  }
unknown's avatar
unknown committed
1456

1457
  if (!starts_null && !ends_null)
unknown's avatar
unknown committed
1458
  {
1459
    /*
unknown's avatar
unknown committed
1460 1461 1462
      Both starts and m_ends are set and time_now is between them (incl.)
      If last_executed is set then increase with m_expression. The new TIME is
      after m_ends set execute_at to 0. And check for on_completion
unknown's avatar
unknown committed
1463 1464
      If not set then schedule for now.
    */
1465
    DBUG_PRINT("info", ("Both STARTS & ENDS are set"));
1466
    if (!last_executed)
1467
    {
1468
      DBUG_PRINT("info", ("Not executed so far."));
1469
    }
1470

unknown's avatar
unknown committed
1471
    {
1472
      my_time_t next_exec;
1473

1474
      if (get_next_time(time_zone, &next_exec, starts, time_now,
1475
                        (int) expression, interval))
1476
        goto err;
1477 1478

      /* There was previous execution */
1479
      if (ends < next_exec)
unknown's avatar
unknown committed
1480
      {
1481 1482
        DBUG_PRINT("info", ("Next execution of %s after ENDS. Stop executing.",
                   name.str));
1483
        /* Next execution after ends. No more executions */
1484
        execute_at= 0;
1485
        execute_at_null= TRUE;
1486 1487 1488 1489
        if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
          dropped= TRUE;
        status= Event_queue_element::DISABLED;
        status_changed= TRUE;
unknown's avatar
unknown committed
1490 1491
      }
      else
1492
      {
1493
        DBUG_PRINT("info",("Next[%lu]", (ulong) next_exec));
1494
        execute_at= next_exec;
1495 1496
        execute_at_null= FALSE;
      }
unknown's avatar
unknown committed
1497 1498 1499
    }
    goto ret;
  }
1500
  else if (starts_null && ends_null)
unknown's avatar
unknown committed
1501
  {
1502
    /* starts is always set, so this is a dead branch !! */
1503
    DBUG_PRINT("info", ("Neither STARTS nor ENDS are set"));
1504 1505 1506 1507
    /*
      Both starts and m_ends are not set, so we schedule for the next
      based on last_executed.
    */
1508
    if (last_executed)
1509
    {
1510 1511
      my_time_t next_exec;
      if (get_next_time(time_zone, &next_exec, starts, time_now,
1512
                        (int) expression, interval))
1513
        goto err;
1514
      execute_at= next_exec;
1515
      DBUG_PRINT("info",("Next[%lu]", (ulong) next_exec));
1516 1517
    }
    else
1518 1519
    {
      /* last_executed not set. Schedule the event for now */
1520
      DBUG_PRINT("info", ("Execute NOW"));
unknown's avatar
unknown committed
1521
      execute_at= time_now;
1522
    }
1523
    execute_at_null= FALSE;
unknown's avatar
unknown committed
1524 1525 1526
  }
  else
  {
unknown's avatar
unknown committed
1527
    /* either starts or m_ends is set */
1528
    if (!starts_null)
unknown's avatar
unknown committed
1529
    {
1530
      DBUG_PRINT("info", ("STARTS is set"));
unknown's avatar
unknown committed
1531
      /*
unknown's avatar
unknown committed
1532 1533 1534 1535
        - starts is set.
        - starts is not in the future according to check made before
        Hence schedule for starts + m_expression in case last_executed
        is not set, otherwise to last_executed + m_expression
unknown's avatar
unknown committed
1536
      */
1537
      if (!last_executed)
1538 1539 1540 1541
      {
        DBUG_PRINT("info", ("Not executed so far."));
      }

1542
      {
1543 1544
        my_time_t next_exec;
        if (get_next_time(time_zone, &next_exec, starts, time_now,
1545
                          (int) expression, interval))
1546
          goto err;
1547
        execute_at= next_exec;
1548
        DBUG_PRINT("info",("Next[%lu]", (ulong) next_exec));
1549
      }
1550
      execute_at_null= FALSE;
unknown's avatar
unknown committed
1551 1552 1553
    }
    else
    {
1554
      /* this is a dead branch, because starts is always set !!! */
1555
      DBUG_PRINT("info", ("STARTS is not set. ENDS is set"));
unknown's avatar
unknown committed
1556 1557 1558
      /*
        - m_ends is set
        - m_ends is after time_now or is equal
unknown's avatar
unknown committed
1559 1560
        Hence check for m_last_execute and increment with m_expression.
        If last_executed is not set then schedule for now
unknown's avatar
unknown committed
1561 1562
      */

1563
      if (!last_executed)
unknown's avatar
unknown committed
1564
        execute_at= time_now;
unknown's avatar
unknown committed
1565 1566
      else
      {
1567
        my_time_t next_exec;
1568

1569
        if (get_next_time(time_zone, &next_exec, starts, time_now,
1570
                          (int) expression, interval))
1571 1572
          goto err;

1573
        if (ends < next_exec)
unknown's avatar
unknown committed
1574
        {
1575
          DBUG_PRINT("info", ("Next execution after ENDS. Stop executing."));
1576
          execute_at= 0;
1577
          execute_at_null= TRUE;
1578 1579 1580 1581
          status= Event_queue_element::DISABLED;
          status_changed= TRUE;
          if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
            dropped= TRUE;
unknown's avatar
unknown committed
1582 1583
        }
        else
1584
        {
1585
          DBUG_PRINT("info", ("Next[%lu]", (ulong) next_exec));
1586
          execute_at= next_exec;
1587 1588
          execute_at_null= FALSE;
        }
unknown's avatar
unknown committed
1589 1590 1591 1592 1593
      }
    }
    goto ret;
  }
ret:
1594
  DBUG_PRINT("info", ("ret: 0 execute_at: %lu", (long) execute_at));
1595
  DBUG_RETURN(FALSE);
1596
err:
1597
  DBUG_PRINT("info", ("ret=1"));
1598
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1599 1600 1601
}


1602 1603 1604
/*
  Set the internal last_executed TIME struct to now. NOW is the
  time according to thd->query_start(), so the THD's clock.
1605 1606

  SYNOPSIS
1607
    Event_queue_element::mark_last_executed()
1608
      thd   thread context
1609 1610
*/

unknown's avatar
unknown committed
1611
void
1612
Event_queue_element::mark_last_executed(THD *thd)
unknown's avatar
unknown committed
1613
{
1614
  thd->end_time();
unknown's avatar
unknown committed
1615

unknown's avatar
unknown committed
1616
  last_executed= (my_time_t) thd->query_start();
1617
  last_executed_changed= TRUE;
unknown's avatar
unknown committed
1618 1619
  
  execution_count++;
unknown's avatar
unknown committed
1620 1621 1622
}


1623 1624
/*
  Saves status and last_executed_at to the disk if changed.
1625 1626

  SYNOPSIS
1627
    Event_queue_element::update_timing_fields()
1628 1629
      thd - thread context

1630
  RETURN VALUE
1631
    FALSE   OK
1632 1633
    TRUE    Error while opening mysql.event for writing or during
            write on disk
1634 1635
*/

unknown's avatar
unknown committed
1636
bool
1637
Event_queue_element::update_timing_fields(THD *thd)
unknown's avatar
unknown committed
1638 1639
{
  TABLE *table;
1640
  Field **fields;
unknown's avatar
unknown committed
1641
  Open_tables_state backup;
1642
  int ret= FALSE;
unknown's avatar
unknown committed
1643

1644
  DBUG_ENTER("Event_queue_element::update_timing_fields");
unknown's avatar
unknown committed
1645

unknown's avatar
unknown committed
1646
  DBUG_PRINT("enter", ("name: %*s", name.length, name.str));
1647 1648

  /* No need to update if nothing has changed */
unknown's avatar
unknown committed
1649
  if (!(status_changed || last_executed_changed))
1650
    DBUG_RETURN(0);
1651

unknown's avatar
unknown committed
1652 1653
  thd->reset_n_backup_open_tables_state(&backup);

1654
  if (events_event_db_repository.open_event_table(thd, TL_WRITE, &table))
unknown's avatar
unknown committed
1655
  {
1656
    ret= TRUE;
unknown's avatar
unknown committed
1657 1658
    goto done;
  }
1659
  fields= table->field;
1660
  if ((ret= events_event_db_repository.
1661
                                 find_named_event(thd, dbname, name, table)))
unknown's avatar
unknown committed
1662 1663 1664
    goto done;

  store_record(table,record[1]);
1665 1666
  /* Don't update create on row update. */
  table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
unknown's avatar
unknown committed
1667

unknown's avatar
unknown committed
1668
  if (last_executed_changed)
unknown's avatar
unknown committed
1669
  {
1670 1671 1672
    TIME time;
    my_tz_UTC->gmt_sec_to_TIME(&time, last_executed);

1673
    fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
1674
    fields[ET_FIELD_LAST_EXECUTED]->store_time(&time,
1675
                                               MYSQL_TIMESTAMP_DATETIME);
1676
    last_executed_changed= FALSE;
unknown's avatar
unknown committed
1677
  }
unknown's avatar
unknown committed
1678
  if (status_changed)
unknown's avatar
unknown committed
1679
  {
1680 1681
    fields[ET_FIELD_STATUS]->set_notnull();
    fields[ET_FIELD_STATUS]->store((longlong)status, TRUE);
1682
    status_changed= FALSE;
unknown's avatar
unknown committed
1683
  }
1684

1685 1686 1687 1688 1689 1690 1691
  /* 
    Turn off row binlogging of event timing updates. These are not used
    for RBR of events replicated to the slave.
  */
  if (thd->current_stmt_binlog_row_based)
    thd->clear_current_stmt_binlog_row_based();

1692 1693
  if ((table->file->ha_update_row(table->record[1], table->record[0])))
    ret= TRUE;
unknown's avatar
unknown committed
1694 1695 1696

done:
  close_thread_tables(thd);
unknown's avatar
unknown committed
1697
  thd->restore_backup_open_tables_state(&backup);
unknown's avatar
unknown committed
1698 1699 1700 1701 1702

  DBUG_RETURN(ret);
}


1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722
static
void
append_datetime(String *buf, Time_zone *time_zone, my_time_t secs,
                const char *name, uint len)
{
  char dtime_buff[20*2+32];/* +32 to make my_snprintf_{8bit|ucs2} happy */
  buf->append(STRING_WITH_LEN(" "));
  buf->append(name, len);
  buf->append(STRING_WITH_LEN(" '"));
  /*
    Pass the buffer and the second param tells fills the buffer and
    returns the number of chars to copy.
  */
  TIME time;
  time_zone->gmt_sec_to_TIME(&time, secs);
  buf->append(dtime_buff, my_datetime_to_str(&time, dtime_buff));
  buf->append(STRING_WITH_LEN("'"));
}


1723 1724
/*
  Get SHOW CREATE EVENT as string
1725 1726

  SYNOPSIS
unknown's avatar
unknown committed
1727
    Event_timed::get_create_event(THD *thd, String *buf)
1728 1729 1730 1731
      thd    Thread
      buf    String*, should be already allocated. CREATE EVENT goes inside.

  RETURN VALUE
1732 1733 1734 1735
    0                       OK
    EVEX_MICROSECOND_UNSUP  Error (for now if mysql.event has been
                            tampered and MICROSECONDS interval or
                            derivative has been put there.
1736 1737 1738
*/

int
unknown's avatar
unknown committed
1739
Event_timed::get_create_event(THD *thd, String *buf)
unknown's avatar
unknown committed
1740
{
1741 1742
  char tmp_buf[2 * STRING_BUFFER_USUAL_SIZE];
  String expr_buf(tmp_buf, sizeof(tmp_buf), system_charset_info);
1743 1744 1745
  expr_buf.length(0);

  DBUG_ENTER("get_create_event");
1746
  DBUG_PRINT("ret_info",("body_len=[%d]body=[%s]", body.length, body.str));
unknown's avatar
unknown committed
1747

1748 1749
  if (expression && Events::reconstruct_interval_expression(&expr_buf, interval,
                                                            expression))
1750
    DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
1751 1752 1753 1754 1755 1756

  buf->append(STRING_WITH_LEN("CREATE EVENT "));
  append_identifier(thd, buf, name.str, name.length);

  if (expression)
  {
1757
    buf->append(STRING_WITH_LEN(" ON SCHEDULE EVERY "));
1758
    buf->append(expr_buf);
unknown's avatar
unknown committed
1759 1760 1761
    buf->append(' ');
    LEX_STRING *ival= &interval_type_to_name[interval];
    buf->append(ival->str, ival->length);
1762 1763 1764 1765 1766 1767

    if (!starts_null)
      append_datetime(buf, time_zone, starts, STRING_WITH_LEN("STARTS"));

    if (!ends_null)
      append_datetime(buf, time_zone, ends, STRING_WITH_LEN("ENDS"));
1768 1769 1770
  }
  else
  {
1771 1772
    append_datetime(buf, time_zone, execute_at,
                    STRING_WITH_LEN("ON SCHEDULE AT"));
1773 1774
  }

1775
  if (on_completion == Event_timed::ON_COMPLETION_DROP)
1776 1777 1778 1779
    buf->append(STRING_WITH_LEN(" ON COMPLETION NOT PRESERVE "));
  else
    buf->append(STRING_WITH_LEN(" ON COMPLETION PRESERVE "));

1780
  if (status == Event_timed::ENABLED)
1781
    buf->append(STRING_WITH_LEN("ENABLE"));
1782 1783
  else if (status == Event_timed::SLAVESIDE_DISABLED)
    buf->append(STRING_WITH_LEN("SLAVESIDE_DISABLE"));
1784 1785 1786 1787 1788 1789 1790 1791 1792
  else
    buf->append(STRING_WITH_LEN("DISABLE"));

  if (comment.length)
  {
    buf->append(STRING_WITH_LEN(" COMMENT "));
    append_unescaped(buf, comment.str, comment.length);
  }
  buf->append(STRING_WITH_LEN(" DO "));
1793
  buf->append(body.str, body.length);
1794 1795

  DBUG_RETURN(0);
unknown's avatar
unknown committed
1796 1797 1798
}


1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817
/*
  Get SHOW CREATE EVENT as string

  SYNOPSIS
    Event_job_data::get_create_event(THD *thd, String *buf)
      thd    Thread
      buf    String*, should be already allocated. CREATE EVENT goes inside.

  RETURN VALUE
    0                       OK
    EVEX_MICROSECOND_UNSUP  Error (for now if mysql.event has been
                            tampered and MICROSECONDS interval or
                            derivative has been put there.
*/

int
Event_job_data::get_fake_create_event(THD *thd, String *buf)
{
  DBUG_ENTER("Event_job_data::get_create_event");
1818
  /* FIXME: "EVERY 3337 HOUR" is asking for trouble. */
1819
  buf->append(STRING_WITH_LEN("CREATE EVENT anonymous ON SCHEDULE "
1820 1821 1822 1823 1824 1825 1826
                              "EVERY 3337 HOUR DO "));
  buf->append(body.str, body.length);

  DBUG_RETURN(0);
}


unknown's avatar
unknown committed
1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855
/*
  Executes the event (the underlying sp_head object);

  SYNOPSIS
    Event_job_data::execute()
      thd       THD

  RETURN VALUE
    0        success
    -99      No rights on this.dbname.str
    others   retcodes of sp_head::execute_procedure()
*/

int
Event_job_data::execute(THD *thd)
{
  Security_context save_ctx;
  /* this one is local and not needed after exec */
  int ret= 0;

  DBUG_ENTER("Event_job_data::execute");
  DBUG_PRINT("info", ("EXECUTING %s.%s", dbname.str, name.str));

  if ((ret= compile(thd, NULL)))
    goto done;

  event_change_security_context(thd, definer_user, definer_host, dbname,
                                &save_ctx);
  /*
1856 1857 1858
    THD::~THD will clean this or if there is DROP DATABASE in the
    SP then it will be free there. It should not point to our buffer
    which is allocated on a mem_root.
unknown's avatar
unknown committed
1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869
  */
  thd->db= my_strdup(dbname.str, MYF(0));
  thd->db_length= dbname.length;
  if (!check_access(thd, EVENT_ACL,dbname.str, 0, 0, 0,is_schema_db(dbname.str)))
  {
    List<Item> empty_item_list;
    empty_item_list.empty();
    if (thd->enable_slow_log)
      sphead->m_flags|= sp_head::LOG_SLOW_STATEMENTS;
    sphead->m_flags|= sp_head::LOG_GENERAL_LOG;

1870 1871 1872
    /* Execute the event in its time zone. */
    thd->variables.time_zone= time_zone;

unknown's avatar
unknown committed
1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886
    ret= sphead->execute_procedure(thd, &empty_item_list);
  }
  else
  {
    DBUG_PRINT("error", ("%s@%s has no rights on %s", definer_user.str,
               definer_host.str, dbname.str));
    ret= -99;
  }

  event_restore_security_context(thd, &save_ctx);
done:
  thd->end_statement();
  thd->cleanup_after_query();

unknown's avatar
unknown committed
1887
  DBUG_PRINT("info", ("EXECUTED %s.%s  ret: %d", dbname.str, name.str, ret));
unknown's avatar
unknown committed
1888 1889 1890 1891 1892

  DBUG_RETURN(ret);
}


1893
/*
1894 1895
  Compiles an event before it's execution. Compiles the anonymous
  sp_head object held by the event
1896 1897

  SYNOPSIS
1898
    Event_job_data::compile()
1899 1900 1901
      thd        thread context, used for memory allocation mostly
      mem_root   if != NULL then this memory root is used for allocs
                 instead of thd->mem_root
1902

1903
  RETURN VALUE
1904 1905 1906
    0                       success
    EVEX_COMPILE_ERROR      error during compilation
    EVEX_MICROSECOND_UNSUP  mysql.event was tampered 
1907 1908
*/

unknown's avatar
unknown committed
1909
int
1910
Event_job_data::compile(THD *thd, MEM_ROOT *mem_root)
unknown's avatar
unknown committed
1911
{
unknown's avatar
unknown committed
1912
  int ret= 0;
unknown's avatar
unknown committed
1913 1914 1915
  MEM_ROOT *tmp_mem_root= 0;
  LEX *old_lex= thd->lex, lex;
  char *old_db;
1916
  int old_db_length;
unknown's avatar
unknown committed
1917 1918
  char *old_query;
  uint old_query_len;
1919
  ulong old_sql_mode= thd->variables.sql_mode;
1920
  char create_buf[15 * STRING_BUFFER_USUAL_SIZE];
1921 1922 1923
  String show_create(create_buf, sizeof(create_buf), system_charset_info);
  CHARSET_INFO *old_character_set_client,
               *old_collation_connection,
unknown's avatar
unknown committed
1924
               *old_character_set_results;
unknown's avatar
unknown committed
1925
  Security_context save_ctx;
1926

1927
  DBUG_ENTER("Event_job_data::compile");
1928

1929
  show_create.length(0);
1930

1931
  switch (get_fake_create_event(thd, &show_create)) {
1932 1933 1934 1935 1936 1937 1938 1939
  case EVEX_MICROSECOND_UNSUP:
    DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
  case 0:
    break;
  default:
    DBUG_ASSERT(0);
  }

unknown's avatar
unknown committed
1940 1941 1942
  old_character_set_client= thd->variables.character_set_client;
  old_character_set_results= thd->variables.character_set_results;
  old_collation_connection= thd->variables.collation_connection;
1943 1944

  thd->variables.character_set_client=
unknown's avatar
unknown committed
1945 1946 1947 1948 1949
    thd->variables.character_set_results=
      thd->variables.collation_connection=
           get_charset_by_csname("utf8", MY_CS_PRIMARY, MYF(MY_WME));

  thd->update_charset();
1950

unknown's avatar
unknown committed
1951
  DBUG_PRINT("info",("old_sql_mode: %lu  new_sql_mode: %lu",old_sql_mode, sql_mode));
1952
  thd->variables.sql_mode= this->sql_mode;
1953
  /* Change the memory root for the execution time */
unknown's avatar
unknown committed
1954 1955 1956 1957 1958 1959 1960 1961
  if (mem_root)
  {
    tmp_mem_root= thd->mem_root;
    thd->mem_root= mem_root;
  }
  old_query_len= thd->query_length;
  old_query= thd->query;
  old_db= thd->db;
1962
  old_db_length= thd->db_length;
unknown's avatar
unknown committed
1963
  thd->db= dbname.str;
1964
  thd->db_length= dbname.length;
1965

1966
  thd->query= show_create.c_ptr_safe();
1967
  thd->query_length= show_create.length();
unknown's avatar
unknown committed
1968
  DBUG_PRINT("info", ("query: %s",thd->query));
unknown's avatar
unknown committed
1969

1970 1971
  event_change_security_context(thd, definer_user, definer_host, dbname,
                                &save_ctx);
unknown's avatar
unknown committed
1972
  thd->lex= &lex;
unknown's avatar
unknown committed
1973
  mysql_init_query(thd, (uchar*) thd->query, thd->query_length);
1974
  if (MYSQLparse((void *)thd) || thd->is_fatal_error)
unknown's avatar
unknown committed
1975
  {
unknown's avatar
unknown committed
1976
    DBUG_PRINT("error", ("error during compile or thd->is_fatal_error: %d",
1977
                          thd->is_fatal_error));
1978 1979
    lex.unit.cleanup();

1980
    sql_print_error("SCHEDULER: Error during compilation of %s.%s or "
unknown's avatar
unknown committed
1981
                    "thd->is_fatal_error: %d",
1982
                    dbname.str, name.str, thd->is_fatal_error);
unknown's avatar
unknown committed
1983

unknown's avatar
unknown committed
1984 1985
    ret= EVEX_COMPILE_ERROR;
    goto done;
unknown's avatar
unknown committed
1986
  }
1987
  DBUG_PRINT("note", ("success compiling %s.%s", dbname.str, name.str));
1988

unknown's avatar
unknown committed
1989
  sphead= lex.sphead;
unknown's avatar
unknown committed
1990

unknown's avatar
unknown committed
1991
  sphead->set_definer(definer.str, definer.length);
1992
  sphead->set_info(0, 0, &lex.sp_chistics, sql_mode);
unknown's avatar
unknown committed
1993
  sphead->optimize();
unknown's avatar
unknown committed
1994 1995
  ret= 0;
done:
1996

unknown's avatar
unknown committed
1997
  lex_end(&lex);
1998
  event_restore_security_context(thd, &save_ctx);
1999 2000
  DBUG_PRINT("note", ("return old data on its place. set back NAMES"));

unknown's avatar
unknown committed
2001 2002 2003 2004
  thd->lex= old_lex;
  thd->query= old_query;
  thd->query_length= old_query_len;
  thd->db= old_db;
unknown's avatar
unknown committed
2005

2006
  thd->variables.sql_mode= old_sql_mode;
unknown's avatar
unknown committed
2007 2008 2009 2010 2011
  thd->variables.character_set_client= old_character_set_client;
  thd->variables.character_set_results= old_character_set_results;
  thd->variables.collation_connection= old_collation_connection;
  thd->update_charset();

2012
  /* Change the memory root for the execution time. */
unknown's avatar
unknown committed
2013 2014 2015
  if (mem_root)
    thd->mem_root= tmp_mem_root;

unknown's avatar
unknown committed
2016
  DBUG_RETURN(ret);
unknown's avatar
unknown committed
2017 2018
}

2019 2020

/*
2021 2022 2023
  Checks whether two events are in the same schema

  SYNOPSIS
2024
    event_basic_db_equal()
2025 2026
      db  Schema
      et  Compare et->dbname to `db`
2027 2028

  RETURN VALUE
2029 2030
    TRUE   Equal
    FALSE  Not equal
2031 2032 2033
*/

bool
2034
event_basic_db_equal(LEX_STRING db, Event_basic *et)
2035
{
2036
  return !sortcmp_lex_string(et->dbname, db, system_charset_info);
2037 2038 2039 2040
}


/*
2041
  Checks whether an event has equal `db` and `name`
2042 2043

  SYNOPSIS
2044
    event_basic_identifier_equal()
2045 2046 2047
      db   Schema
      name Name
      et   The event object
2048 2049

  RETURN VALUE
2050 2051
    TRUE   Equal
    FALSE  Not equal
2052 2053 2054
*/

bool
2055
event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b)
2056
{
unknown's avatar
unknown committed
2057 2058
  return !sortcmp_lex_string(name, b->name, system_charset_info) &&
         !sortcmp_lex_string(db, b->dbname, system_charset_info);
2059
}
unknown's avatar
unknown committed
2060 2061 2062


/*
2063 2064
  Switches the security context.

unknown's avatar
unknown committed
2065 2066 2067 2068 2069 2070 2071
  SYNOPSIS
    event_change_security_context()
      thd     Thread
      user    The user
      host    The host of the user
      db      The schema for which the security_ctx will be loaded
      backup  Where to store the old context
2072

unknown's avatar
unknown committed
2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099
  RETURN VALUE
    FALSE  OK
    TRUE   Error (generates error too)
*/

static bool
event_change_security_context(THD *thd, LEX_STRING user, LEX_STRING host,
                              LEX_STRING db, Security_context *backup)
{
  DBUG_ENTER("event_change_security_context");
  DBUG_PRINT("info",("%s@%s@%s", user.str, host.str, db.str));
#ifndef NO_EMBEDDED_ACCESS_CHECKS

  *backup= thd->main_security_ctx;
  if (acl_getroot_no_password(&thd->main_security_ctx, user.str, host.str,
                              host.str, db.str))
  {
    my_error(ER_NO_SUCH_USER, MYF(0), user.str, host.str);
    DBUG_RETURN(TRUE);
  }
  thd->security_ctx= &thd->main_security_ctx;
#endif
  DBUG_RETURN(FALSE);
} 


/*
2100 2101
  Restores the security context.

unknown's avatar
unknown committed
2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120
  SYNOPSIS
    event_restore_security_context()
      thd     Thread
      backup  Context to switch to
*/

static void
event_restore_security_context(THD *thd, Security_context *backup)
{
  DBUG_ENTER("event_restore_security_context");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  if (backup)
  {
    thd->main_security_ctx= *backup;
    thd->security_ctx= &thd->main_security_ctx;
  }
#endif
  DBUG_VOID_RETURN;
}