event_data_objects.cc 49.6 KB
Newer Older
1
/* Copyright (C) 2004-2006 MySQL AB
unknown's avatar
unknown committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

   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
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   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 */

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


25
#define EVEX_MAX_INTERVAL_VALUE 1000000000L
26

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

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

34
/*
35
  Returns a new instance
36 37

  SYNOPSIS
38
    Event_parse_data::new_instance()
39

40 41 42 43 44
  RETURN VALUE
    Address or NULL in case of error
  
  NOTE
    Created on THD's mem_root
45 46
*/

47 48
Event_parse_data *
Event_parse_data::new_instance(THD *thd)
49
{
50
  return new (thd->mem_root) Event_parse_data;
51 52
}

unknown's avatar
unknown committed
53 54

/*
55
  Constructor
unknown's avatar
unknown committed
56

57
  SYNOPSIS
58
    Event_parse_data::Event_parse_data()
unknown's avatar
unknown committed
59 60
*/

61
Event_parse_data::Event_parse_data()
62 63 64 65
  :on_completion(ON_COMPLETION_DROP), status(ENABLED),
   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
66
{
67
  DBUG_ENTER("Event_parse_data::Event_parse_data");
unknown's avatar
unknown committed
68

unknown's avatar
unknown committed
69
  /* Actually in the parser STARTS is always set */
unknown's avatar
unknown committed
70 71 72
  set_zero_time(&starts, MYSQL_TIMESTAMP_DATETIME);
  set_zero_time(&ends, MYSQL_TIMESTAMP_DATETIME);
  set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
unknown's avatar
unknown committed
73

74
  body.str= comment.str= NULL;
unknown's avatar
unknown committed
75
  body.length= comment.length= 0;
76

unknown's avatar
unknown committed
77 78 79 80 81
  DBUG_VOID_RETURN;
}


/*
82
  Set a name of the event
unknown's avatar
unknown committed
83

84
  SYNOPSIS
unknown's avatar
unknown committed
85
    Event_parse_data::init_name()
86 87
      thd   THD
      spn   the name extracted in the parser
unknown's avatar
unknown committed
88 89 90
*/

void
unknown's avatar
unknown committed
91
Event_parse_data::init_name(THD *thd, sp_name *spn)
unknown's avatar
unknown committed
92
{
unknown's avatar
unknown committed
93
  DBUG_ENTER("Event_parse_data::init_name");
unknown's avatar
unknown committed
94 95

  /* We have to copy strings to get them into the right memroot */
unknown's avatar
unknown committed
96
  dbname.length= spn->m_db.length;
unknown's avatar
unknown committed
97
  dbname.str= thd->strmake(spn->m_db.str, spn->m_db.length);
unknown's avatar
unknown committed
98
  name.length= spn->m_name.length;
unknown's avatar
unknown committed
99
  name.str= thd->strmake(spn->m_name.str, spn->m_name.length);
unknown's avatar
unknown committed
100 101 102

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

unknown's avatar
unknown committed
104 105 106 107 108
  DBUG_VOID_RETURN;
}


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

111
  SYNOPSIS
unknown's avatar
unknown committed
112
    Event_parse_data::init_body()
113
      thd   THD
unknown's avatar
unknown committed
114 115 116 117

  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
118

119 120
    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
121 122 123
*/

void
unknown's avatar
unknown committed
124
Event_parse_data::init_body(THD *thd)
unknown's avatar
unknown committed
125
{
unknown's avatar
unknown committed
126
  DBUG_ENTER("Event_parse_data::init_body");
127
  DBUG_PRINT("info", ("body=[%s] body_begin=0x%lx end=0x%lx", body_begin,
128
             body_begin, thd->lex->ptr));
unknown's avatar
unknown committed
129

unknown's avatar
unknown committed
130
  body.length= thd->lex->ptr - body_begin;
131 132 133 134 135 136 137 138 139 140 141 142 143 144
  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;
    }

145
    /*
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
       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 == '/'))
    {
162
      DBUG_PRINT("info", ("consumend one '*" "/' comment in the query '%s'",
163 164 165 166 167 168 169 170
          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
171

172 173 174 175 176 177
  /* 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
178
  body.str= thd->strmake((char *)body_begin, body.length);
unknown's avatar
unknown committed
179 180 181 182 183 184

  DBUG_VOID_RETURN;
}


/*
185
  Sets time for execution for one-time event.
unknown's avatar
unknown committed
186

187
  SYNOPSIS
unknown's avatar
unknown committed
188
    Event_parse_data::init_execute_at()
189
      thd  Thread
unknown's avatar
unknown committed
190

191
  RETURN VALUE
192 193
    0               OK
    ER_WRONG_VALUE  Wrong value for execute at (reported)
unknown's avatar
unknown committed
194 195 196
*/

int
197
Event_parse_data::init_execute_at(THD *thd)
unknown's avatar
unknown committed
198 199 200
{
  my_bool not_used;
  TIME ltime;
201
  my_time_t t;
unknown's avatar
unknown committed
202 203
  TIME time_tmp;

unknown's avatar
unknown committed
204
  DBUG_ENTER("Event_parse_data::init_execute_at");
unknown's avatar
unknown committed
205

206 207 208 209 210
  if (!item_execute_at)
    DBUG_RETURN(0);

  if (item_execute_at->fix_fields(thd, &item_execute_at))
    goto wrong_value;
211 212 213
  
  /* 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
214
                      (starts_null && ends_null)));
215
  DBUG_ASSERT(starts_null && ends_null);
unknown's avatar
unknown committed
216

unknown's avatar
unknown committed
217
  /* let's check whether time is in the past */
218
  thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,
unknown's avatar
unknown committed
219
                                            (my_time_t) thd->query_start());
unknown's avatar
unknown committed
220

221 222
  if ((not_used= item_execute_at->get_date(&ltime, TIME_NO_ZERO_DATE)))
    goto wrong_value;
223 224 225

  if (TIME_to_ulonglong_datetime(&ltime) <
      TIME_to_ulonglong_datetime(&time_tmp))
226 227 228 229
  {
    my_error(ER_EVENT_EXEC_TIME_IN_THE_PAST, MYF(0));
    DBUG_RETURN(ER_WRONG_VALUE);
  }
unknown's avatar
unknown committed
230 231

  /*
232 233
    This may result in a 1970-01-01 date if ltime is > 2037-xx-xx.
    CONVERT_TZ has similar problem.
234 235
    mysql_priv.h currently lists 
      #define TIMESTAMP_MAX_YEAR 2038 (see TIME_to_timestamp())
unknown's avatar
unknown committed
236
  */
237 238 239 240
  my_tz_UTC->gmt_sec_to_TIME(&ltime,t=TIME_to_timestamp(thd,&ltime,&not_used));
  if (!t)
  {
    DBUG_PRINT("error", ("Execute AT after year 2037"));
241
    goto wrong_value;
242
  }
unknown's avatar
unknown committed
243

244
  execute_at_null= FALSE;
unknown's avatar
unknown committed
245
  execute_at= ltime;
unknown's avatar
unknown committed
246
  DBUG_RETURN(0);
247 248 249 250

wrong_value:
  report_bad_value("AT", item_execute_at);
  DBUG_RETURN(ER_WRONG_VALUE);
unknown's avatar
unknown committed
251 252 253 254
}


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

257
  SYNOPSIS
unknown's avatar
unknown committed
258
    Event_parse_data::init_interval()
259
      thd  Thread
unknown's avatar
unknown committed
260

261
  RETURN VALUE
262 263 264
    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
265 266 267
*/

int
268
Event_parse_data::init_interval(THD *thd)
unknown's avatar
unknown committed
269
{
270
  String value;
271
  INTERVAL interval_tmp;
272

unknown's avatar
unknown committed
273
  DBUG_ENTER("Event_parse_data::init_interval");
274 275 276 277 278 279 280 281 282 283 284 285 286 287
  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
288

289 290
  if (item_expression->fix_fields(thd, &item_expression))
    goto wrong_value;
unknown's avatar
unknown committed
291

292
  value.alloc(MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN);
293 294
  if (get_interval_value(item_expression, interval, &value, &interval_tmp))
    goto wrong_value;
unknown's avatar
unknown committed
295

296 297
  expression= 0;

298
  switch (interval) {
299
  case INTERVAL_YEAR:
300
    expression= interval_tmp.year;
301 302 303
    break;
  case INTERVAL_QUARTER:
  case INTERVAL_MONTH:
304
    expression= interval_tmp.month;
305 306 307
    break;
  case INTERVAL_WEEK:
  case INTERVAL_DAY:
308
    expression= interval_tmp.day;
309 310
    break;
  case INTERVAL_HOUR:
311
    expression= interval_tmp.hour;
312 313
    break;
  case INTERVAL_MINUTE:
314
    expression= interval_tmp.minute;
315 316
    break;
  case INTERVAL_SECOND:
317
    expression= interval_tmp.second;
318
    break;
319
  case INTERVAL_YEAR_MONTH:                     // Allow YEAR-MONTH YYYYYMM
320
    expression= interval_tmp.year* 12 + interval_tmp.month;
321 322
    break;
  case INTERVAL_DAY_HOUR:
323
    expression= interval_tmp.day* 24 + interval_tmp.hour;
324 325
    break;
  case INTERVAL_DAY_MINUTE:
326 327
    expression= (interval_tmp.day* 24 + interval_tmp.hour) * 60 +
                interval_tmp.minute;
328
    break;
unknown's avatar
unknown committed
329
  case INTERVAL_HOUR_SECOND: /* day is anyway 0 */
330 331
  case INTERVAL_DAY_SECOND:
    /* DAY_SECOND having problems because of leap seconds? */
332 333 334
    expression= ((interval_tmp.day* 24 + interval_tmp.hour) * 60 +
                  interval_tmp.minute)*60
                 + interval_tmp.second;
335 336
    break;
  case INTERVAL_HOUR_MINUTE:
337
    expression= interval_tmp.hour * 60 + interval_tmp.minute;
338 339
    break;
  case INTERVAL_MINUTE_SECOND:
340
    expression= interval_tmp.minute * 60 + interval_tmp.second;
341
    break;
342 343
  case INTERVAL_LAST:
    DBUG_ASSERT(0);
344 345
  default:
    ;/* these are the microsec stuff */
346
  }
347
  if (interval_tmp.neg || expression > EVEX_MAX_INTERVAL_VALUE)
348 349
  {
    my_error(ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG, MYF(0));
350
    DBUG_RETURN(EVEX_BAD_PARAMS);
351
  }
352

unknown's avatar
unknown committed
353
  DBUG_RETURN(0);
354 355

wrong_value:
356
  report_bad_value("INTERVAL", item_expression);
357
  DBUG_RETURN(ER_WRONG_VALUE);
unknown's avatar
unknown committed
358 359 360 361
}


/*
362
  Sets STARTS.
363 364

  SYNOPSIS
unknown's avatar
unknown committed
365 366
    Event_parse_data::init_starts()
      expr      how much?
367 368 369 370 371 372 373 374 375

  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.

376
  RETURN VALUE
377 378
    0                OK
    ER_WRONG_VALUE  Starts before now
unknown's avatar
unknown committed
379 380 381
*/

int
382
Event_parse_data::init_starts(THD *thd)
unknown's avatar
unknown committed
383 384
{
  my_bool not_used;
385
  TIME ltime, time_tmp;
386
  my_time_t t;
unknown's avatar
unknown committed
387

unknown's avatar
unknown committed
388
  DBUG_ENTER("Event_parse_data::init_starts");
389 390
  if (!item_starts)
    DBUG_RETURN(0);
unknown's avatar
unknown committed
391

392 393
  if (item_starts->fix_fields(thd, &item_starts))
    goto wrong_value;
unknown's avatar
unknown committed
394

395 396
  if ((not_used= item_starts->get_date(&ltime, TIME_NO_ZERO_DATE)))
    goto wrong_value;
unknown's avatar
unknown committed
397

398 399 400
  /* Let's check whether time is in the past */
  thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,
                                            (my_time_t) thd->query_start());
401

402 403
  DBUG_PRINT("info",("now   =%lld", TIME_to_ulonglong_datetime(&time_tmp)));
  DBUG_PRINT("info",("starts=%lld", TIME_to_ulonglong_datetime(&ltime)));
404 405
  if (TIME_to_ulonglong_datetime(&ltime) <
      TIME_to_ulonglong_datetime(&time_tmp))
406
    goto wrong_value;
407

unknown's avatar
unknown committed
408
  /*
409 410 411 412
    This may result in a 1970-01-01 date if ltime is > 2037-xx-xx.
    CONVERT_TZ has similar problem.
    mysql_priv.h currently lists 
      #define TIMESTAMP_MAX_YEAR 2038 (see TIME_to_timestamp())
unknown's avatar
unknown committed
413
  */
414 415
  my_tz_UTC->gmt_sec_to_TIME(&ltime,t=TIME_to_timestamp(thd, &ltime, &not_used));
  if (!t)
416
    goto wrong_value;
unknown's avatar
unknown committed
417

unknown's avatar
unknown committed
418
  starts= ltime;
419
  starts_null= FALSE;
unknown's avatar
unknown committed
420
  DBUG_RETURN(0);
421 422 423 424

wrong_value:
  report_bad_value("STARTS", item_starts);
  DBUG_RETURN(ER_WRONG_VALUE);
unknown's avatar
unknown committed
425 426 427 428
}


/*
429
  Sets ENDS (deactivation time).
430 431

  SYNOPSIS
unknown's avatar
unknown committed
432
    Event_parse_data::init_ends()
433 434 435 436 437 438 439 440 441 442
      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.

443
  RETURN VALUE
444
    0                  OK
445
    EVEX_BAD_PARAMS    Error (reported)
unknown's avatar
unknown committed
446 447
*/

448
int
449
Event_parse_data::init_ends(THD *thd)
unknown's avatar
unknown committed
450
{
451
  TIME ltime, ltime_now;
unknown's avatar
unknown committed
452
  my_bool not_used;
453
  my_time_t t;
unknown's avatar
unknown committed
454

unknown's avatar
unknown committed
455
  DBUG_ENTER("Event_parse_data::init_ends");
456 457
  if (!item_ends)
    DBUG_RETURN(0);
unknown's avatar
unknown committed
458

459 460
  if (item_ends->fix_fields(thd, &item_ends))
    goto error_bad_params;
unknown's avatar
unknown committed
461

462
  DBUG_PRINT("info", ("convert to TIME"));
463 464
  if ((not_used= item_ends->get_date(&ltime, TIME_NO_ZERO_DATE)))
    goto error_bad_params;
unknown's avatar
unknown committed
465 466

  /*
467 468 469 470
    This may result in a 1970-01-01 date if ltime is > 2037-xx-xx.
    CONVERT_TZ has similar problem.
    mysql_priv.h currently lists 
      #define TIMESTAMP_MAX_YEAR 2038 (see TIME_to_timestamp())
unknown's avatar
unknown committed
471
  */
472
  DBUG_PRINT("info", ("get the UTC time"));
473 474
  my_tz_UTC->gmt_sec_to_TIME(&ltime,t=TIME_to_timestamp(thd, &ltime, &not_used));
  if (!t)
475
    goto error_bad_params;
476

477 478 479
  /* Check whether ends is after starts */
  DBUG_PRINT("info", ("ENDS after STARTS?"));
  if (!starts_null && my_time_compare(&starts, &ltime) != -1)
480
    goto error_bad_params;
481 482 483 484 485 486 487

  /*
    The parser forces starts to be provided but one day STARTS could be
    set before NOW() and in this case the following check should be done.
    Check whether ENDS is not in the past.
  */
  DBUG_PRINT("info", ("ENDS after NOW?"));
unknown's avatar
unknown committed
488
  my_tz_UTC->gmt_sec_to_TIME(&ltime_now, thd->query_start());
489
  if (my_time_compare(&ltime_now, &ltime) == 1)
490
    goto error_bad_params;
unknown's avatar
unknown committed
491

unknown's avatar
unknown committed
492
  ends= ltime;
493
  ends_null= FALSE;
unknown's avatar
unknown committed
494
  DBUG_RETURN(0);
495 496 497 498

error_bad_params:
  my_error(ER_EVENT_ENDS_BEFORE_STARTS, MYF(0));
  DBUG_RETURN(EVEX_BAD_PARAMS);
unknown's avatar
unknown committed
499 500 501 502
}


/*
503 504
  Prints an error message about invalid value. Internally used
  during input data verification
unknown's avatar
unknown committed
505

506
  SYNOPSIS
507 508 509
    Event_parse_data::report_bad_value()
      item_name The name of the parameter
      bad_item  The parameter
unknown's avatar
unknown committed
510 511 512
*/

void
513 514 515 516 517 518 519 520 521 522
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");
}


/*
523
  Checks for validity the data gathered during the parsing phase.
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550

  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");
  DBUG_PRINT("info", ("execute_at=0x%lx expr=0x%lx starts=0x%lx ends=0x%lx",
             item_execute_at, item_expression, item_starts, item_ends));

  init_name(thd, identifier);

  init_definer(thd);

  ret= init_execute_at(thd) || init_interval(thd) || init_starts(thd) ||
       init_ends(thd);
  DBUG_RETURN(ret);
}

551

unknown's avatar
unknown committed
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
/*
  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)
{
  int definer_user_len;
  int definer_host_len;
  DBUG_ENTER("Event_parse_data::init_definer");

  DBUG_PRINT("info",("init definer_user thd->mem_root=0x%lx "
                     "thd->sec_ctx->priv_user=0x%lx", thd->mem_root,
                     thd->security_ctx->priv_user));

  definer_user_len= strlen(thd->security_ctx->priv_user);
  definer_host_len= strlen(thd->security_ctx->priv_host);

  /* + 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"));
  memcpy(definer.str, thd->security_ctx->priv_user, definer_user_len);
  definer.str[definer_user_len]= '@';

  DBUG_PRINT("info",("copy the host"));
  memcpy(definer.str + definer_user_len + 1, thd->security_ctx->priv_host,
         definer_host_len);
  definer.str[definer.length]= '\0';
  DBUG_PRINT("info",("definer [%s] initted", definer.str));

  DBUG_VOID_RETURN;
}


593 594 595 596 597 598 599 600
/*
  Constructor

  SYNOPSIS
    Event_basic::Event_basic()
*/

Event_basic::Event_basic()
unknown's avatar
unknown committed
601
{
602 603 604 605 606 607 608
  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;
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
609

610 611 612

/*
  Destructor
unknown's avatar
unknown committed
613

614 615 616
  SYNOPSIS
    Event_basic::Event_basic()
*/
unknown's avatar
unknown committed
617

618 619 620 621
Event_basic::~Event_basic()
{
  DBUG_ENTER("Event_basic::~Event_basic");
  free_root(&mem_root, MYF(0));
unknown's avatar
unknown committed
622 623 624 625 626
  DBUG_VOID_RETURN;
}


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

629
  SYNOPSIS
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
    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
646

647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
  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
662

663 664 665 666 667 668 669 670 671
  DBUG_RETURN(ret);
}


/*
  Constructor

  SYNOPSIS
    Event_queue_element::Event_queue_element()
unknown's avatar
unknown committed
672 673
*/

674 675 676
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
677
  expression(0), dropped(FALSE), execution_count(0)
unknown's avatar
unknown committed
678
{
679
  DBUG_ENTER("Event_queue_element::Event_queue_element");
unknown's avatar
unknown committed
680

681 682 683 684 685
  set_zero_time(&starts, MYSQL_TIMESTAMP_DATETIME);
  set_zero_time(&ends, MYSQL_TIMESTAMP_DATETIME);
  set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
  set_zero_time(&last_executed, MYSQL_TIMESTAMP_DATETIME);
  starts_null= ends_null= execute_at_null= TRUE;
unknown's avatar
unknown committed
686

687 688
  DBUG_VOID_RETURN;
}
689

690

691 692
/*
  Destructor
693

694 695 696 697 698
  SYNOPSIS
    Event_queue_element::Event_queue_element()
*/
Event_queue_element::~Event_queue_element()
{
unknown's avatar
unknown committed
699 700 701 702
}


/*
unknown's avatar
unknown committed
703
  Constructor
unknown's avatar
unknown committed
704

705
  SYNOPSIS
unknown's avatar
unknown committed
706
    Event_timed::Event_timed()
unknown's avatar
unknown committed
707 708
*/

709 710
Event_timed::Event_timed():
  created(0), modified(0), sql_mode(0)
unknown's avatar
unknown committed
711
{
712
  DBUG_ENTER("Event_timed::Event_timed");
unknown's avatar
unknown committed
713
  init();
714
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
715 716 717 718
}


/*
unknown's avatar
unknown committed
719
  Destructor
720 721

  SYNOPSIS
unknown's avatar
unknown committed
722 723 724 725 726
    Event_timed::~Event_timed()
*/

Event_timed::~Event_timed()
{    
727 728 729 730 731 732 733 734 735 736
}


/*
  Constructor

  SYNOPSIS
    Event_job_data::Event_job_data()
*/

737 738
Event_job_data::Event_job_data()
  :thd(NULL), sphead(NULL), sql_mode(0)
739 740 741 742 743 744 745 746 747 748 749 750
{
}


/*
  Destructor

  SYNOPSIS
    Event_timed::~Event_timed()
*/

Event_job_data::~Event_job_data()
unknown's avatar
unknown committed
751
{
unknown's avatar
unknown committed
752 753 754 755
  DBUG_ENTER("Event_job_data::~Event_job_data");
  delete sphead;
  sphead= NULL;
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
}


/*
  Init all member variables

  SYNOPSIS
    Event_timed::init()
*/

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

771 772
  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
773 774 775 776

  sql_mode= 0;

  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
777 778 779 780
}


/*
781
  Loads an event's body from a row from mysql.event
782 783

  SYNOPSIS
784
    Event_job_data::load_from_row(MEM_ROOT *mem_root, TABLE *table)
785

786 787 788 789
  RETURN VALUE
    0                      OK
    EVEX_GET_FIELD_FAILED  Error

790 791 792 793
  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
794 795 796
*/

int
797
Event_job_data::load_from_row(TABLE *table)
unknown's avatar
unknown committed
798 799 800
{
  char *ptr;
  uint len;
801
  DBUG_ENTER("Event_job_data::load_from_row");
unknown's avatar
unknown committed
802 803 804 805

  if (!table)
    goto error;

unknown's avatar
unknown committed
806
  if (table->s->fields != ET_FIELD_COUNT)
unknown's avatar
unknown committed
807 808
    goto error;

809 810 811
  load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name,
                     ET_FIELD_BODY, &body, ET_FIELD_DEFINER, &definer,
                     ET_FIELD_COUNT);
812

813 814 815 816
  ptr= strchr(definer.str, '@');

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

818 819 820 821 822
  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
823
  definer_host.str= strmake_root(&mem_root, ptr + 1, len);
824
  definer_host.length= len;
unknown's avatar
unknown committed
825

826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
  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
    Event_queue_element::load_from_row(MEM_ROOT *mem_root, TABLE *table)

  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
Event_queue_element::load_from_row(TABLE *table)
{
  char *ptr;
  bool res1, res2;
unknown's avatar
unknown committed
855

856
  DBUG_ENTER("Event_queue_element::load_from_row");
unknown's avatar
unknown committed
857

858
  if (!table)
unknown's avatar
unknown committed
859 860
    goto error;

861 862
  if (table->s->fields != ET_FIELD_COUNT)
    goto error;
unknown's avatar
unknown committed
863

864 865
  load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name,
                     ET_FIELD_DEFINER, &definer, ET_FIELD_COUNT);
unknown's avatar
unknown committed
866

867 868
  starts_null= table->field[ET_FIELD_STARTS]->is_null();
  res1= table->field[ET_FIELD_STARTS]->get_date(&starts, TIME_NO_ZERO_DATE);
869

870 871
  ends_null= table->field[ET_FIELD_ENDS]->is_null();
  res2= table->field[ET_FIELD_ENDS]->get_date(&ends, TIME_NO_ZERO_DATE);
unknown's avatar
unknown committed
872

unknown's avatar
unknown committed
873
  if (!table->field[ET_FIELD_INTERVAL_EXPR]->is_null())
874
    expression= table->field[ET_FIELD_INTERVAL_EXPR]->val_int();
875
  else
876
    expression= 0;
unknown's avatar
unknown committed
877
  /*
878
    If res1 and res2 are TRUE then both fields are empty.
879
    Hence, if ET_FIELD_EXECUTE_AT is empty there is an error.
unknown's avatar
unknown committed
880
  */
881 882 883 884 885
  execute_at_null= table->field[ET_FIELD_EXECUTE_AT]->is_null();
  DBUG_ASSERT(!(starts_null && ends_null && !expression && execute_at_null));
  if (!expression &&
      table->field[ET_FIELD_EXECUTE_AT]->get_date(&execute_at,
                                                  TIME_NO_ZERO_DATE))
unknown's avatar
unknown committed
886 887 888 889 890 891
    goto error;

  /*
    In DB the values start from 1 but enum interval_type starts
    from 0
  */
unknown's avatar
unknown committed
892
  if (!table->field[ET_FIELD_TRANSIENT_INTERVAL]->is_null())
893
    interval= (interval_type) ((ulonglong)
unknown's avatar
unknown committed
894
          table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_int() - 1);
895
  else
896
    interval= (interval_type) 0;
unknown's avatar
unknown committed
897

898 899 900
  table->field[ET_FIELD_LAST_EXECUTED]->get_date(&last_executed,
                                                 TIME_NO_ZERO_DATE);
  last_executed_changed= FALSE;
unknown's avatar
unknown committed
901 902


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

906 907 908
  DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", name.str, ptr));
  status= (ptr[0]=='E'? Event_queue_element::ENABLED:
                        Event_queue_element::DISABLED);
unknown's avatar
unknown committed
909

unknown's avatar
unknown committed
910
  /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
911
  if ((ptr= get_field(&mem_root,
912
                      table->field[ET_FIELD_ON_COMPLETION])) == NullS)
unknown's avatar
unknown committed
913
    goto error;
914

915 916
  on_completion= (ptr[0]=='D'? Event_queue_element::ON_COMPLETION_DROP:
                               Event_queue_element::ON_COMPLETION_PRESERVE);
unknown's avatar
unknown committed
917

918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948
  DBUG_RETURN(0);
error:
  DBUG_RETURN(EVEX_GET_FIELD_FAILED);
}


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

  SYNOPSIS
    Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)

  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
Event_timed::load_from_row(TABLE *table)
{
  char *ptr;
  uint len;

  DBUG_ENTER("Event_timed::load_from_row");

  if (Event_queue_element::load_from_row(table))
unknown's avatar
unknown committed
949 950
    goto error;

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

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

955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
  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
972
  else
973
    comment.length= 0;
974

975
  sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
976

unknown's avatar
unknown committed
977 978 979 980 981 982
  DBUG_RETURN(0);
error:
  DBUG_RETURN(EVEX_GET_FIELD_FAILED);
}


unknown's avatar
unknown committed
983
/*
984 985
  Computes the sum of a timestamp plus interval. Presumed is that at least one
  previous execution has occured.
986 987 988 989 990

  SYNOPSIS
    get_next_time(TIME *start, int interval_value, interval_type interval)
      next          the sum
      start         add interval_value to this time
991
      time_now      current time
992 993
      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
994

995
  RETURN VALUE
996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
    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
    3) We get the difference between time_now and `start`, then divide it
       by the months, respectively seconds and round up. Then we multiply
       monts/seconds by the rounded value and add it to `start` -> we get
       the next execution time.
1009
*/
unknown's avatar
unknown committed
1010

1011
static
1012 1013
bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec,
                   int i_value, interval_type i_type)
1014 1015 1016 1017
{
  bool ret;
  INTERVAL interval;
  TIME tmp;
1018 1019 1020 1021
  longlong months=0, seconds=0;
  DBUG_ENTER("get_next_time");
  DBUG_PRINT("enter", ("start=%llu now=%llu", TIME_to_ulonglong_datetime(start),
                      TIME_to_ulonglong_datetime(time_now)));
1022

1023 1024 1025 1026
  bzero(&interval, sizeof(interval));

  switch (i_type) {
  case INTERVAL_YEAR:
1027
    months= i_value*12;
1028 1029
    break;
  case INTERVAL_QUARTER:
1030
    /* Has already been converted to months */
1031 1032
  case INTERVAL_YEAR_MONTH:
  case INTERVAL_MONTH:
1033
    months= i_value;
1034 1035
    break;
  case INTERVAL_WEEK:
1036
    /* WEEK has already been converted to days */
1037
  case INTERVAL_DAY:
1038
    seconds= i_value*24*3600;
1039 1040 1041
    break;
  case INTERVAL_DAY_HOUR:
  case INTERVAL_HOUR:
1042
    seconds= i_value*3600;
1043 1044 1045 1046
    break;
  case INTERVAL_DAY_MINUTE:
  case INTERVAL_HOUR_MINUTE:
  case INTERVAL_MINUTE:
1047
    seconds= i_value*60;
1048 1049 1050 1051 1052
    break;
  case INTERVAL_DAY_SECOND:
  case INTERVAL_HOUR_SECOND:
  case INTERVAL_MINUTE_SECOND:
  case INTERVAL_SECOND:
1053
    seconds= i_value;
1054 1055 1056 1057 1058 1059
    break;
  case INTERVAL_DAY_MICROSECOND:
  case INTERVAL_HOUR_MICROSECOND:
  case INTERVAL_MINUTE_MICROSECOND:
  case INTERVAL_SECOND_MICROSECOND:
  case INTERVAL_MICROSECOND:
1060 1061 1062 1063 1064
    /*
     We should return an error here so SHOW EVENTS/ SELECT FROM I_S.EVENTS
     would give an error then.
    */
    DBUG_RETURN(1);
1065
    break;
1066 1067
  case INTERVAL_LAST:
    DBUG_ASSERT(0);
1068
  }
1069 1070 1071 1072 1073
  DBUG_PRINT("info", ("seconds=%ld months=%ld", seconds, months));
  if (seconds)
  {
    longlong seconds_diff;
    long microsec_diff;
1074

1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087
    if (calc_time_diff(time_now, start, 1, &seconds_diff, &microsec_diff))
    {
      DBUG_PRINT("error", ("negative difference"));
      DBUG_ASSERT(0);
    }
    uint multiplier= seconds_diff / seconds;
    /*
      Increase the multiplier is the modulus is not zero to make round up.
      Or if time_now==start then we should not execute the same 
      event two times for the same time
      get the next exec if the modulus is not
    */
    DBUG_PRINT("info", ("multiplier=%d", multiplier));
1088 1089 1090
    if (seconds_diff % seconds || (!seconds_diff && last_exec->year) ||
        TIME_to_ulonglong_datetime(time_now) ==
          TIME_to_ulonglong_datetime(last_exec))
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115
      ++multiplier;
    interval.second= seconds * multiplier;
    DBUG_PRINT("info", ("multiplier=%u interval.second=%u", multiplier,
                        interval.second));
    tmp= *start;
    if (!(ret= date_add_interval(&tmp, INTERVAL_SECOND, interval)))
      *next= tmp;
  }
  else
  {
    /* PRESUMED is that at least one execution took already place */
    int diff_months= (time_now->year - start->year)*12 +
                     (time_now->month - start->month);
    /*
      Note: If diff_months is 0 that means we are in the same month as the
      last execution which is also the first execution.
    */
    /*
      First we try with the smaller if not then + 1, because if we try with
      directly with +1 we will be after the current date but it could be that
      we will be 1 month ahead, so 2 steps are necessary.
    */
    interval.month= (diff_months / months)*months;
    /*
      Check if the same month as last_exec (always set - prerequisite)
1116 1117 1118 1119 1120 1121 1122
      An event happens at most once per month so there is no way to
      schedule it two times for the current month. This saves us from two
      calls to date_add_interval() if the event was just executed.  But if
      the scheduler is started and there was at least 1 scheduled date
      skipped this one does not help and two calls to date_add_interval()
      will be done, which is a bit more expensive but compared to the
      rareness of the case is neglectable.
1123
    */
1124 1125
    if (time_now->year == last_exec->year &&
        time_now->month == last_exec->month)
1126 1127 1128 1129 1130
      interval.month+= months;

    tmp= *start;
    if ((ret= date_add_interval(&tmp, INTERVAL_MONTH, interval)))
      goto done;
1131

1132 1133 1134 1135 1136 1137 1138 1139
    /* If `tmp` is still before time_now just add one more time the interval */
    if (my_time_compare(&tmp, time_now) == -1)
    { 
      interval.month+= months;
      tmp= *start;
      if ((ret= date_add_interval(&tmp, INTERVAL_MONTH, interval)))
        goto done;
    }
1140
    *next= tmp;
1141 1142 1143
    /* assert on that the next is after now */
    DBUG_ASSERT(1==my_time_compare(next, time_now));
  }
1144

1145 1146 1147
done:
  DBUG_PRINT("info", ("next=%llu", TIME_to_ulonglong_datetime(next)));
  DBUG_RETURN(ret);
1148 1149 1150 1151
}


/*
1152 1153 1154
  Computes next execution time.

  SYNOPSIS
1155
    Event_queue_element::compute_next_execution_time()
1156

1157 1158 1159 1160
  RETURN VALUE
    FALSE  OK
    TRUE   Error

1161 1162 1163
  NOTES
    The time is set in execute_at, if no more executions the latter is set to
    0000-00-00.
unknown's avatar
unknown committed
1164 1165
*/

unknown's avatar
unknown committed
1166
bool
1167
Event_queue_element::compute_next_execution_time()
unknown's avatar
unknown committed
1168 1169 1170 1171
{
  TIME time_now;
  int tmp;

1172 1173
  DBUG_ENTER("Event_queue_element::compute_next_execution_time");
  DBUG_PRINT("enter", ("starts=%llu ends=%llu last_executed=%llu this=0x%lx",
1174 1175
                        TIME_to_ulonglong_datetime(&starts),
                        TIME_to_ulonglong_datetime(&ends),
1176
                        TIME_to_ulonglong_datetime(&last_executed), this));
unknown's avatar
unknown committed
1177

1178
  if (status == Event_queue_element::DISABLED)
unknown's avatar
unknown committed
1179 1180
  {
    DBUG_PRINT("compute_next_execution_time",
unknown's avatar
unknown committed
1181
                  ("Event %s is DISABLED", name.str));
unknown's avatar
unknown committed
1182 1183
    goto ret;
  }
1184
  /* If one-time, no need to do computation */
unknown's avatar
unknown committed
1185
  if (!expression)
unknown's avatar
unknown committed
1186
  {
1187
    /* Let's check whether it was executed */
unknown's avatar
unknown committed
1188
    if (last_executed.year)
unknown's avatar
unknown committed
1189
    {
1190 1191
      DBUG_PRINT("info",("One-time event %s.%s of was already executed",
                         dbname.str, name.str, definer.str));
1192
      dropped= (on_completion == Event_queue_element::ON_COMPLETION_DROP);
1193 1194
      DBUG_PRINT("info",("One-time event will be dropped=%d.", dropped));

1195 1196
      status= Event_queue_element::DISABLED;
      status_changed= TRUE;
unknown's avatar
unknown committed
1197 1198 1199
    }
    goto ret;
  }
1200

1201
  my_tz_UTC->gmt_sec_to_TIME(&time_now, current_thd->query_start());
1202

1203
  DBUG_PRINT("info",("NOW=[%llu]", TIME_to_ulonglong_datetime(&time_now)));
1204

unknown's avatar
unknown committed
1205
  /* if time_now is after ends don't execute anymore */
1206
  if (!ends_null && (tmp= my_time_compare(&ends, &time_now)) == -1)
unknown's avatar
unknown committed
1207
  {
1208
    DBUG_PRINT("info", ("NOW after ENDS, don't execute anymore"));
1209
    /* time_now is after ends. don't execute anymore */
unknown's avatar
unknown committed
1210
    set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
1211
    execute_at_null= TRUE;
1212 1213
    if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
      dropped= TRUE;
1214
    DBUG_PRINT("info", ("Dropped=%d", dropped));
1215 1216
    status= Event_queue_element::DISABLED;
    status_changed= TRUE;
unknown's avatar
unknown committed
1217 1218 1219

    goto ret;
  }
1220 1221 1222 1223 1224

  /*
    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
1225
  */
1226
  if (!starts_null && (tmp= my_time_compare(&time_now, &starts)) < 1)
unknown's avatar
unknown committed
1227
  {
unknown's avatar
unknown committed
1228
    if (tmp == 0 && my_time_compare(&starts, &last_executed) == 0)
unknown's avatar
unknown committed
1229
    {
1230
      /*
unknown's avatar
unknown committed
1231 1232
        time_now = starts = last_executed
        do nothing or we will schedule for second time execution at starts.
unknown's avatar
unknown committed
1233 1234 1235 1236
      */
    }
    else
    {
1237
      DBUG_PRINT("info", ("STARTS is future, NOW <= STARTS,sched for STARTS"));
unknown's avatar
unknown committed
1238 1239 1240 1241 1242
      /*
        starts is in the future
        time_now before starts. Scheduling for starts
      */
      execute_at= starts;
1243
      execute_at_null= FALSE;
unknown's avatar
unknown committed
1244 1245 1246
      goto ret;
    }
  }
unknown's avatar
unknown committed
1247

1248
  if (!starts_null && !ends_null)
unknown's avatar
unknown committed
1249
  {
1250
    /*
unknown's avatar
unknown committed
1251 1252 1253
      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
1254 1255
      If not set then schedule for now.
    */
1256
    DBUG_PRINT("info", ("Both STARTS & ENDS are set"));
unknown's avatar
unknown committed
1257
    if (!last_executed.year)
1258
    {
1259
      DBUG_PRINT("info", ("Not executed so far."));
1260
    }
1261

unknown's avatar
unknown committed
1262
    {
1263
      TIME next_exec;
1264

1265 1266
      if (get_next_time(&next_exec, &starts, &time_now,
                        last_executed.year? &last_executed:&starts,
1267
                        expression, interval))
1268
        goto err;
1269 1270

      /* There was previous execution */
1271
      if (my_time_compare(&ends, &next_exec) == -1)
unknown's avatar
unknown committed
1272
      {
1273 1274
        DBUG_PRINT("info", ("Next execution of %s after ENDS. Stop executing.",
                   name.str));
1275
        /* Next execution after ends. No more executions */
unknown's avatar
unknown committed
1276
        set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
1277
        execute_at_null= TRUE;
1278 1279 1280 1281
        if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
          dropped= TRUE;
        status= Event_queue_element::DISABLED;
        status_changed= TRUE;
unknown's avatar
unknown committed
1282 1283
      }
      else
1284
      {
1285
        DBUG_PRINT("info",("Next[%llu]",TIME_to_ulonglong_datetime(&next_exec)));
1286
        execute_at= next_exec;
1287 1288
        execute_at_null= FALSE;
      }
unknown's avatar
unknown committed
1289 1290 1291
    }
    goto ret;
  }
1292
  else if (starts_null && ends_null)
unknown's avatar
unknown committed
1293
  {
1294
    /* starts is always set, so this is a dead branch !! */
1295
    DBUG_PRINT("info", ("Neither STARTS nor ENDS are set"));
1296 1297 1298 1299
    /*
      Both starts and m_ends are not set, so we schedule for the next
      based on last_executed.
    */
1300 1301
    if (last_executed.year)
    {
1302 1303 1304
      TIME next_exec;
      if (get_next_time(&next_exec, &starts, &time_now, &last_executed,
                        expression, interval))
1305
        goto err;
1306 1307
      execute_at= next_exec;
      DBUG_PRINT("info",("Next[%llu]",TIME_to_ulonglong_datetime(&next_exec)));
1308 1309
    }
    else
1310 1311
    {
      /* last_executed not set. Schedule the event for now */
1312
      DBUG_PRINT("info", ("Execute NOW"));
unknown's avatar
unknown committed
1313
      execute_at= time_now;
1314
    }
1315
    execute_at_null= FALSE;
unknown's avatar
unknown committed
1316 1317 1318
  }
  else
  {
unknown's avatar
unknown committed
1319
    /* either starts or m_ends is set */
1320
    if (!starts_null)
unknown's avatar
unknown committed
1321
    {
1322
      DBUG_PRINT("info", ("STARTS is set"));
unknown's avatar
unknown committed
1323
      /*
unknown's avatar
unknown committed
1324 1325 1326 1327
        - 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
1328
      */
1329 1330 1331 1332 1333
      if (!last_executed.year)
      {
        DBUG_PRINT("info", ("Not executed so far."));
      }

1334
      {
1335
        TIME next_exec;
1336
        if (get_next_time(&next_exec, &starts, &time_now,
1337
                          last_executed.year? &last_executed:&starts,
1338
                          expression, interval))
1339
          goto err;
1340 1341
        execute_at= next_exec;
        DBUG_PRINT("info",("Next[%llu]",TIME_to_ulonglong_datetime(&next_exec)));
1342
      }
1343
      execute_at_null= FALSE;
unknown's avatar
unknown committed
1344 1345 1346
    }
    else
    {
1347
      /* this is a dead branch, because starts is always set !!! */
1348
      DBUG_PRINT("info", ("STARTS is not set. ENDS is set"));
unknown's avatar
unknown committed
1349 1350 1351
      /*
        - m_ends is set
        - m_ends is after time_now or is equal
unknown's avatar
unknown committed
1352 1353
        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
1354 1355
      */

unknown's avatar
unknown committed
1356 1357
      if (!last_executed.year)
        execute_at= time_now;
unknown's avatar
unknown committed
1358 1359
      else
      {
1360 1361
        TIME next_exec;

1362 1363
        if (get_next_time(&next_exec, &starts, &time_now, &last_executed,
                          expression, interval))
1364 1365 1366
          goto err;

        if (my_time_compare(&ends, &next_exec) == -1)
unknown's avatar
unknown committed
1367
        {
1368
          DBUG_PRINT("info", ("Next execution after ENDS. Stop executing."));
unknown's avatar
unknown committed
1369
          set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
1370
          execute_at_null= TRUE;
1371 1372 1373 1374
          status= Event_queue_element::DISABLED;
          status_changed= TRUE;
          if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
            dropped= TRUE;
unknown's avatar
unknown committed
1375 1376
        }
        else
1377
        {
1378 1379
          DBUG_PRINT("info", ("Next[%llu]",
                              TIME_to_ulonglong_datetime(&next_exec)));
1380
          execute_at= next_exec;
1381 1382
          execute_at_null= FALSE;
        }
unknown's avatar
unknown committed
1383 1384 1385 1386 1387
      }
    }
    goto ret;
  }
ret:
1388 1389
  DBUG_PRINT("info", ("ret=0 execute_at=%llu",
             TIME_to_ulonglong_datetime(&execute_at)));
1390
  DBUG_RETURN(FALSE);
1391
err:
1392
  DBUG_PRINT("info", ("ret=1"));
1393
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1394 1395 1396
}


1397 1398 1399
/*
  Set the internal last_executed TIME struct to now. NOW is the
  time according to thd->query_start(), so the THD's clock.
1400 1401

  SYNOPSIS
1402
    Event_queue_element::mark_last_executed()
1403
      thd   thread context
1404 1405
*/

unknown's avatar
unknown committed
1406
void
1407
Event_queue_element::mark_last_executed(THD *thd)
unknown's avatar
unknown committed
1408 1409 1410
{
  TIME time_now;

1411 1412
  thd->end_time();
  my_tz_UTC->gmt_sec_to_TIME(&time_now, (my_time_t) thd->query_start());
unknown's avatar
unknown committed
1413

unknown's avatar
unknown committed
1414
  last_executed= time_now; /* was execute_at */
1415
  last_executed_changed= TRUE;
unknown's avatar
unknown committed
1416 1417
  
  execution_count++;
unknown's avatar
unknown committed
1418 1419 1420
}


unknown's avatar
unknown committed
1421
/*
1422
  Drops the event
1423 1424

  SYNOPSIS
1425
    Event_queue_element::drop()
1426
      thd   thread context
1427

1428 1429 1430 1431 1432 1433
  RETURN VALUE
    0       OK
   -1       Cannot open mysql.event
   -2       Cannot find the event in mysql.event (already deleted?)

   others   return code from SE in case deletion of the event row
unknown's avatar
unknown committed
1434 1435 1436 1437
            failed.
*/

int
1438
Event_queue_element::drop(THD *thd)
unknown's avatar
unknown committed
1439
{
1440
  DBUG_ENTER("Event_queue_element::drop");
unknown's avatar
unknown committed
1441

1442
  DBUG_RETURN(Events::get_instance()->
1443
                    drop_event(thd, dbname, name, FALSE, TRUE));
unknown's avatar
unknown committed
1444 1445 1446
}


1447 1448
/*
  Saves status and last_executed_at to the disk if changed.
1449 1450

  SYNOPSIS
1451
    Event_queue_element::update_timing_fields()
1452 1453
      thd - thread context

1454
  RETURN VALUE
1455
    FALSE   OK
1456 1457
    TRUE    Error while opening mysql.event for writing or during
            write on disk
1458 1459
*/

unknown's avatar
unknown committed
1460
bool
1461
Event_queue_element::update_timing_fields(THD *thd)
unknown's avatar
unknown committed
1462 1463
{
  TABLE *table;
1464
  Field **fields;
unknown's avatar
unknown committed
1465
  Open_tables_state backup;
1466
  int ret= FALSE;
unknown's avatar
unknown committed
1467

1468
  DBUG_ENTER("Event_queue_element::update_timing_fields");
unknown's avatar
unknown committed
1469

unknown's avatar
unknown committed
1470
  DBUG_PRINT("enter", ("name: %*s", name.length, name.str));
1471 1472

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

unknown's avatar
unknown committed
1476 1477
  thd->reset_n_backup_open_tables_state(&backup);

unknown's avatar
unknown committed
1478
  if (Events::get_instance()->open_event_table(thd, TL_WRITE, &table))
unknown's avatar
unknown committed
1479
  {
1480
    ret= TRUE;
unknown's avatar
unknown committed
1481 1482
    goto done;
  }
1483
  fields= table->field;
1484
  if ((ret= Events::get_instance()->db_repository->
1485
                                 find_named_event(thd, dbname, name, table)))
unknown's avatar
unknown committed
1486 1487 1488
    goto done;

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

unknown's avatar
unknown committed
1492
  if (last_executed_changed)
unknown's avatar
unknown committed
1493
  {
1494 1495
    fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
    fields[ET_FIELD_LAST_EXECUTED]->store_time(&last_executed,
1496
                                               MYSQL_TIMESTAMP_DATETIME);
1497
    last_executed_changed= FALSE;
unknown's avatar
unknown committed
1498
  }
unknown's avatar
unknown committed
1499
  if (status_changed)
unknown's avatar
unknown committed
1500
  {
1501 1502
    fields[ET_FIELD_STATUS]->set_notnull();
    fields[ET_FIELD_STATUS]->store((longlong)status, TRUE);
1503
    status_changed= FALSE;
unknown's avatar
unknown committed
1504
  }
1505

1506 1507
  if ((table->file->ha_update_row(table->record[1], table->record[0])))
    ret= TRUE;
unknown's avatar
unknown committed
1508 1509 1510

done:
  close_thread_tables(thd);
unknown's avatar
unknown committed
1511
  thd->restore_backup_open_tables_state(&backup);
unknown's avatar
unknown committed
1512 1513 1514 1515 1516

  DBUG_RETURN(ret);
}


1517 1518
/*
  Get SHOW CREATE EVENT as string
1519 1520

  SYNOPSIS
unknown's avatar
unknown committed
1521
    Event_timed::get_create_event(THD *thd, String *buf)
1522 1523 1524 1525
      thd    Thread
      buf    String*, should be already allocated. CREATE EVENT goes inside.

  RETURN VALUE
1526 1527 1528 1529
    0                       OK
    EVEX_MICROSECOND_UNSUP  Error (for now if mysql.event has been
                            tampered and MICROSECONDS interval or
                            derivative has been put there.
1530 1531 1532
*/

int
unknown's avatar
unknown committed
1533
Event_timed::get_create_event(THD *thd, String *buf)
unknown's avatar
unknown committed
1534
{
1535
  int multipl= 0;
1536 1537
  char tmp_buf[2 * STRING_BUFFER_USUAL_SIZE];
  String expr_buf(tmp_buf, sizeof(tmp_buf), system_charset_info);
1538 1539 1540
  expr_buf.length(0);

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

1543 1544
  if (expression && Events::reconstruct_interval_expression(&expr_buf, interval,
                                                            expression))
1545
    DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
1546 1547 1548 1549 1550 1551

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

  if (expression)
  {
1552
    buf->append(STRING_WITH_LEN(" ON SCHEDULE EVERY "));
1553
    buf->append(expr_buf);
unknown's avatar
unknown committed
1554 1555 1556
    buf->append(' ');
    LEX_STRING *ival= &interval_type_to_name[interval];
    buf->append(ival->str, ival->length);
1557 1558 1559
  }
  else
  {
unknown's avatar
unknown committed
1560
    char dtime_buff[20*2+32];/* +32 to make my_snprintf_{8bit|ucs2} happy */
1561
    buf->append(STRING_WITH_LEN(" ON SCHEDULE AT '"));
1562 1563 1564
    /*
      Pass the buffer and the second param tells fills the buffer and
      returns the number of chars to copy.
1565 1566 1567 1568 1569
    */
    buf->append(dtime_buff, my_datetime_to_str(&execute_at, dtime_buff));
    buf->append(STRING_WITH_LEN("'"));
  }

1570
  if (on_completion == Event_timed::ON_COMPLETION_DROP)
1571 1572 1573 1574
    buf->append(STRING_WITH_LEN(" ON COMPLETION NOT PRESERVE "));
  else
    buf->append(STRING_WITH_LEN(" ON COMPLETION PRESERVE "));

1575
  if (status == Event_timed::ENABLED)
1576 1577 1578 1579 1580 1581 1582 1583 1584 1585
    buf->append(STRING_WITH_LEN("ENABLE"));
  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 "));
1586
  buf->append(body.str, body.length);
1587 1588

  DBUG_RETURN(0);
unknown's avatar
unknown committed
1589 1590 1591
}


1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610
/*
  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");
1611
  buf->append(STRING_WITH_LEN("CREATE EVENT anonymous ON SCHEDULE "
1612 1613 1614 1615 1616 1617 1618
                              "EVERY 3337 HOUR DO "));
  buf->append(body.str, body.length);

  DBUG_RETURN(0);
}


unknown's avatar
unknown committed
1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647
/*
  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);
  /*
1648 1649 1650
    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
1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681
  */
  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;

    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();

  DBUG_PRINT("info", ("EXECUTED %s.%s ret=%d", dbname.str, name.str, ret));

  DBUG_RETURN(ret);
}


1682
/*
1683 1684
  Compiles an event before it's execution. Compiles the anonymous
  sp_head object held by the event
1685 1686

  SYNOPSIS
1687
    Event_job_data::compile()
1688 1689 1690
      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
1691

1692
  RETURN VALUE
1693 1694 1695
    0                       success
    EVEX_COMPILE_ERROR      error during compilation
    EVEX_MICROSECOND_UNSUP  mysql.event was tampered 
1696 1697
*/

unknown's avatar
unknown committed
1698
int
1699
Event_job_data::compile(THD *thd, MEM_ROOT *mem_root)
unknown's avatar
unknown committed
1700
{
unknown's avatar
unknown committed
1701
  int ret= 0;
unknown's avatar
unknown committed
1702 1703 1704
  MEM_ROOT *tmp_mem_root= 0;
  LEX *old_lex= thd->lex, lex;
  char *old_db;
1705
  int old_db_length;
unknown's avatar
unknown committed
1706 1707
  char *old_query;
  uint old_query_len;
1708
  ulong old_sql_mode= thd->variables.sql_mode;
1709
  char create_buf[15 * STRING_BUFFER_USUAL_SIZE];
1710 1711 1712
  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
1713
               *old_character_set_results;
unknown's avatar
unknown committed
1714
  Security_context save_ctx;
1715

1716
  DBUG_ENTER("Event_job_data::compile");
1717

1718
  show_create.length(0);
1719

1720
  switch (get_fake_create_event(thd, &show_create)) {
1721 1722 1723 1724 1725 1726 1727 1728
  case EVEX_MICROSECOND_UNSUP:
    DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
  case 0:
    break;
  default:
    DBUG_ASSERT(0);
  }

unknown's avatar
unknown committed
1729 1730 1731
  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;
1732 1733

  thd->variables.character_set_client=
unknown's avatar
unknown committed
1734 1735 1736 1737 1738
    thd->variables.character_set_results=
      thd->variables.collation_connection=
           get_charset_by_csname("utf8", MY_CS_PRIMARY, MYF(MY_WME));

  thd->update_charset();
1739

1740 1741
  DBUG_PRINT("info",("old_sql_mode=%d new_sql_mode=%d",old_sql_mode, sql_mode));
  thd->variables.sql_mode= this->sql_mode;
1742
  /* Change the memory root for the execution time */
unknown's avatar
unknown committed
1743 1744 1745 1746 1747 1748 1749 1750
  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;
1751
  old_db_length= thd->db_length;
unknown's avatar
unknown committed
1752
  thd->db= dbname.str;
1753
  thd->db_length= dbname.length;
1754

1755
  thd->query= show_create.c_ptr_safe();
1756
  thd->query_length= show_create.length();
1757
  DBUG_PRINT("info", ("query:%s",thd->query));
unknown's avatar
unknown committed
1758

1759 1760
  event_change_security_context(thd, definer_user, definer_host, dbname,
                                &save_ctx);
unknown's avatar
unknown committed
1761
  thd->lex= &lex;
unknown's avatar
unknown committed
1762
  mysql_init_query(thd, (uchar*) thd->query, thd->query_length);
1763
  if (MYSQLparse((void *)thd) || thd->is_fatal_error)
unknown's avatar
unknown committed
1764
  {
1765 1766
    DBUG_PRINT("error", ("error during compile or thd->is_fatal_error=%d",
                          thd->is_fatal_error));
1767 1768 1769 1770
    /*
      Free lex associated resources
      QQ: Do we really need all this stuff here?
    */
1771 1772
    sql_print_error("SCHEDULER: Error during compilation of %s.%s or "
                    "thd->is_fatal_error=%d",
1773
                    dbname.str, name.str, thd->is_fatal_error);
unknown's avatar
unknown committed
1774 1775 1776 1777

    lex.unit.cleanup();
    delete lex.sphead;
    sphead= lex.sphead= NULL;
unknown's avatar
unknown committed
1778 1779
    ret= EVEX_COMPILE_ERROR;
    goto done;
unknown's avatar
unknown committed
1780
  }
1781
  DBUG_PRINT("note", ("success compiling %s.%s", dbname.str, name.str));
1782

unknown's avatar
unknown committed
1783
  sphead= lex.sphead;
unknown's avatar
unknown committed
1784

unknown's avatar
unknown committed
1785
  sphead->set_definer(definer.str, definer.length);
1786
  sphead->set_info(0, 0, &lex.sp_chistics, sql_mode);
unknown's avatar
unknown committed
1787
  sphead->optimize();
unknown's avatar
unknown committed
1788 1789
  ret= 0;
done:
1790

unknown's avatar
unknown committed
1791
  lex_end(&lex);
1792
  event_restore_security_context(thd, &save_ctx);
1793 1794
  DBUG_PRINT("note", ("return old data on its place. set back NAMES"));

unknown's avatar
unknown committed
1795 1796 1797 1798
  thd->lex= old_lex;
  thd->query= old_query;
  thd->query_length= old_query_len;
  thd->db= old_db;
unknown's avatar
unknown committed
1799

1800
  thd->variables.sql_mode= old_sql_mode;
unknown's avatar
unknown committed
1801 1802 1803 1804 1805
  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();

1806
  /* Change the memory root for the execution time. */
unknown's avatar
unknown committed
1807 1808 1809
  if (mem_root)
    thd->mem_root= tmp_mem_root;

unknown's avatar
unknown committed
1810
  DBUG_RETURN(ret);
unknown's avatar
unknown committed
1811 1812
}

1813 1814

/*
1815 1816 1817
  Checks whether two events are in the same schema

  SYNOPSIS
1818
    event_basic_db_equal()
1819 1820
      db  Schema
      et  Compare et->dbname to `db`
1821 1822

  RETURN VALUE
1823 1824
    TRUE   Equal
    FALSE  Not equal
1825 1826 1827
*/

bool
1828
event_basic_db_equal(LEX_STRING db, Event_basic *et)
1829
{
1830
  return !sortcmp_lex_string(et->dbname, db, system_charset_info);
1831 1832 1833 1834
}


/*
1835
  Checks whether an event has equal `db` and `name`
1836 1837

  SYNOPSIS
1838
    event_basic_identifier_equal()
1839 1840 1841
      db   Schema
      name Name
      et   The event object
1842 1843

  RETURN VALUE
1844 1845
    TRUE   Equal
    FALSE  Not equal
1846 1847 1848
*/

bool
1849
event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b)
1850
{
unknown's avatar
unknown committed
1851 1852
  return !sortcmp_lex_string(name, b->name, system_charset_info) &&
         !sortcmp_lex_string(db, b->dbname, system_charset_info);
1853
}
unknown's avatar
unknown committed
1854 1855 1856


/*
1857 1858
  Switches the security context.

unknown's avatar
unknown committed
1859 1860 1861 1862 1863 1864 1865
  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
1866

unknown's avatar
unknown committed
1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893
  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);
} 


/*
1894 1895
  Restores the security context.

unknown's avatar
unknown committed
1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914
  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;
}