event_data_objects.cc 50.1 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
    goto error;

  /*
889 890 891 892
    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
893
  */
unknown's avatar
unknown committed
894
  if (!table->field[ET_FIELD_TRANSIENT_INTERVAL]->is_null())
895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
  {
    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
912

913 914 915
  table->field[ET_FIELD_LAST_EXECUTED]->get_date(&last_executed,
                                                 TIME_NO_ZERO_DATE);
  last_executed_changed= FALSE;
unknown's avatar
unknown committed
916 917


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

921 922 923
  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
924

unknown's avatar
unknown committed
925
  /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
926
  if ((ptr= get_field(&mem_root,
927
                      table->field[ET_FIELD_ON_COMPLETION])) == NullS)
unknown's avatar
unknown committed
928
    goto error;
929

930 931
  on_completion= (ptr[0]=='D'? Event_queue_element::ON_COMPLETION_DROP:
                               Event_queue_element::ON_COMPLETION_PRESERVE);
unknown's avatar
unknown committed
932

933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963
  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
964 965
    goto error;

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

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

970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986
  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
987
  else
988
    comment.length= 0;
989

990
  sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
991

unknown's avatar
unknown committed
992 993 994 995 996 997
  DBUG_RETURN(0);
error:
  DBUG_RETURN(EVEX_GET_FIELD_FAILED);
}


unknown's avatar
unknown committed
998
/*
999 1000
  Computes the sum of a timestamp plus interval. Presumed is that at least one
  previous execution has occured.
1001 1002 1003 1004 1005

  SYNOPSIS
    get_next_time(TIME *start, int interval_value, interval_type interval)
      next          the sum
      start         add interval_value to this time
1006
      time_now      current time
1007 1008
      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
1009

1010
  RETURN VALUE
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
    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.
1024
*/
unknown's avatar
unknown committed
1025

1026
static
1027 1028
bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec,
                   int i_value, interval_type i_type)
1029 1030 1031 1032
{
  bool ret;
  INTERVAL interval;
  TIME tmp;
1033 1034 1035 1036
  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)));
1037

1038 1039 1040 1041
  bzero(&interval, sizeof(interval));

  switch (i_type) {
  case INTERVAL_YEAR:
1042
    months= i_value*12;
1043 1044
    break;
  case INTERVAL_QUARTER:
1045
    /* Has already been converted to months */
1046 1047
  case INTERVAL_YEAR_MONTH:
  case INTERVAL_MONTH:
1048
    months= i_value;
1049 1050
    break;
  case INTERVAL_WEEK:
1051
    /* WEEK has already been converted to days */
1052
  case INTERVAL_DAY:
1053
    seconds= i_value*24*3600;
1054 1055 1056
    break;
  case INTERVAL_DAY_HOUR:
  case INTERVAL_HOUR:
1057
    seconds= i_value*3600;
1058 1059 1060 1061
    break;
  case INTERVAL_DAY_MINUTE:
  case INTERVAL_HOUR_MINUTE:
  case INTERVAL_MINUTE:
1062
    seconds= i_value*60;
1063 1064 1065 1066 1067
    break;
  case INTERVAL_DAY_SECOND:
  case INTERVAL_HOUR_SECOND:
  case INTERVAL_MINUTE_SECOND:
  case INTERVAL_SECOND:
1068
    seconds= i_value;
1069 1070 1071 1072 1073 1074
    break;
  case INTERVAL_DAY_MICROSECOND:
  case INTERVAL_HOUR_MICROSECOND:
  case INTERVAL_MINUTE_MICROSECOND:
  case INTERVAL_SECOND_MICROSECOND:
  case INTERVAL_MICROSECOND:
1075 1076 1077 1078 1079
    /*
     We should return an error here so SHOW EVENTS/ SELECT FROM I_S.EVENTS
     would give an error then.
    */
    DBUG_RETURN(1);
1080
    break;
1081 1082
  case INTERVAL_LAST:
    DBUG_ASSERT(0);
1083
  }
1084 1085 1086 1087 1088
  DBUG_PRINT("info", ("seconds=%ld months=%ld", seconds, months));
  if (seconds)
  {
    longlong seconds_diff;
    long microsec_diff;
1089

1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102
    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));
1103 1104 1105
    if (seconds_diff % seconds || (!seconds_diff && last_exec->year) ||
        TIME_to_ulonglong_datetime(time_now) ==
          TIME_to_ulonglong_datetime(last_exec))
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130
      ++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)
1131 1132 1133 1134 1135 1136 1137
      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.
1138
    */
1139 1140
    if (time_now->year == last_exec->year &&
        time_now->month == last_exec->month)
1141 1142 1143 1144 1145
      interval.month+= months;

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

1147 1148 1149 1150 1151 1152 1153 1154
    /* 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;
    }
1155
    *next= tmp;
1156 1157 1158
    /* assert on that the next is after now */
    DBUG_ASSERT(1==my_time_compare(next, time_now));
  }
1159

1160 1161 1162
done:
  DBUG_PRINT("info", ("next=%llu", TIME_to_ulonglong_datetime(next)));
  DBUG_RETURN(ret);
1163 1164 1165 1166
}


/*
1167 1168 1169
  Computes next execution time.

  SYNOPSIS
1170
    Event_queue_element::compute_next_execution_time()
1171

1172 1173 1174 1175
  RETURN VALUE
    FALSE  OK
    TRUE   Error

1176 1177 1178
  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
1179 1180
*/

unknown's avatar
unknown committed
1181
bool
1182
Event_queue_element::compute_next_execution_time()
unknown's avatar
unknown committed
1183 1184 1185 1186
{
  TIME time_now;
  int tmp;

1187 1188
  DBUG_ENTER("Event_queue_element::compute_next_execution_time");
  DBUG_PRINT("enter", ("starts=%llu ends=%llu last_executed=%llu this=0x%lx",
1189 1190
                        TIME_to_ulonglong_datetime(&starts),
                        TIME_to_ulonglong_datetime(&ends),
1191
                        TIME_to_ulonglong_datetime(&last_executed), this));
unknown's avatar
unknown committed
1192

1193
  if (status == Event_queue_element::DISABLED)
unknown's avatar
unknown committed
1194 1195
  {
    DBUG_PRINT("compute_next_execution_time",
unknown's avatar
unknown committed
1196
                  ("Event %s is DISABLED", name.str));
unknown's avatar
unknown committed
1197 1198
    goto ret;
  }
1199
  /* If one-time, no need to do computation */
unknown's avatar
unknown committed
1200
  if (!expression)
unknown's avatar
unknown committed
1201
  {
1202
    /* Let's check whether it was executed */
unknown's avatar
unknown committed
1203
    if (last_executed.year)
unknown's avatar
unknown committed
1204
    {
1205 1206
      DBUG_PRINT("info",("One-time event %s.%s of was already executed",
                         dbname.str, name.str, definer.str));
1207
      dropped= (on_completion == Event_queue_element::ON_COMPLETION_DROP);
1208 1209
      DBUG_PRINT("info",("One-time event will be dropped=%d.", dropped));

1210 1211
      status= Event_queue_element::DISABLED;
      status_changed= TRUE;
unknown's avatar
unknown committed
1212 1213 1214
    }
    goto ret;
  }
1215

1216
  my_tz_UTC->gmt_sec_to_TIME(&time_now, current_thd->query_start());
1217

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

unknown's avatar
unknown committed
1220
  /* if time_now is after ends don't execute anymore */
1221
  if (!ends_null && (tmp= my_time_compare(&ends, &time_now)) == -1)
unknown's avatar
unknown committed
1222
  {
1223
    DBUG_PRINT("info", ("NOW after ENDS, don't execute anymore"));
1224
    /* time_now is after ends. don't execute anymore */
unknown's avatar
unknown committed
1225
    set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
1226
    execute_at_null= TRUE;
1227 1228
    if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
      dropped= TRUE;
1229
    DBUG_PRINT("info", ("Dropped=%d", dropped));
1230 1231
    status= Event_queue_element::DISABLED;
    status_changed= TRUE;
unknown's avatar
unknown committed
1232 1233 1234

    goto ret;
  }
1235 1236 1237 1238 1239

  /*
    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
1240
  */
1241
  if (!starts_null && (tmp= my_time_compare(&time_now, &starts)) < 1)
unknown's avatar
unknown committed
1242
  {
unknown's avatar
unknown committed
1243
    if (tmp == 0 && my_time_compare(&starts, &last_executed) == 0)
unknown's avatar
unknown committed
1244
    {
1245
      /*
unknown's avatar
unknown committed
1246 1247
        time_now = starts = last_executed
        do nothing or we will schedule for second time execution at starts.
unknown's avatar
unknown committed
1248 1249 1250 1251
      */
    }
    else
    {
1252
      DBUG_PRINT("info", ("STARTS is future, NOW <= STARTS,sched for STARTS"));
unknown's avatar
unknown committed
1253 1254 1255 1256 1257
      /*
        starts is in the future
        time_now before starts. Scheduling for starts
      */
      execute_at= starts;
1258
      execute_at_null= FALSE;
unknown's avatar
unknown committed
1259 1260 1261
      goto ret;
    }
  }
unknown's avatar
unknown committed
1262

1263
  if (!starts_null && !ends_null)
unknown's avatar
unknown committed
1264
  {
1265
    /*
unknown's avatar
unknown committed
1266 1267 1268
      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
1269 1270
      If not set then schedule for now.
    */
1271
    DBUG_PRINT("info", ("Both STARTS & ENDS are set"));
unknown's avatar
unknown committed
1272
    if (!last_executed.year)
1273
    {
1274
      DBUG_PRINT("info", ("Not executed so far."));
1275
    }
1276

unknown's avatar
unknown committed
1277
    {
1278
      TIME next_exec;
1279

1280 1281
      if (get_next_time(&next_exec, &starts, &time_now,
                        last_executed.year? &last_executed:&starts,
1282
                        expression, interval))
1283
        goto err;
1284 1285

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

1349
      {
1350
        TIME next_exec;
1351
        if (get_next_time(&next_exec, &starts, &time_now,
1352
                          last_executed.year? &last_executed:&starts,
1353
                          expression, interval))
1354
          goto err;
1355 1356
        execute_at= next_exec;
        DBUG_PRINT("info",("Next[%llu]",TIME_to_ulonglong_datetime(&next_exec)));
1357
      }
1358
      execute_at_null= FALSE;
unknown's avatar
unknown committed
1359 1360 1361
    }
    else
    {
1362
      /* this is a dead branch, because starts is always set !!! */
1363
      DBUG_PRINT("info", ("STARTS is not set. ENDS is set"));
unknown's avatar
unknown committed
1364 1365 1366
      /*
        - m_ends is set
        - m_ends is after time_now or is equal
unknown's avatar
unknown committed
1367 1368
        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
1369 1370
      */

unknown's avatar
unknown committed
1371 1372
      if (!last_executed.year)
        execute_at= time_now;
unknown's avatar
unknown committed
1373 1374
      else
      {
1375 1376
        TIME next_exec;

1377 1378
        if (get_next_time(&next_exec, &starts, &time_now, &last_executed,
                          expression, interval))
1379 1380 1381
          goto err;

        if (my_time_compare(&ends, &next_exec) == -1)
unknown's avatar
unknown committed
1382
        {
1383
          DBUG_PRINT("info", ("Next execution after ENDS. Stop executing."));
unknown's avatar
unknown committed
1384
          set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
1385
          execute_at_null= TRUE;
1386 1387 1388 1389
          status= Event_queue_element::DISABLED;
          status_changed= TRUE;
          if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
            dropped= TRUE;
unknown's avatar
unknown committed
1390 1391
        }
        else
1392
        {
1393 1394
          DBUG_PRINT("info", ("Next[%llu]",
                              TIME_to_ulonglong_datetime(&next_exec)));
1395
          execute_at= next_exec;
1396 1397
          execute_at_null= FALSE;
        }
unknown's avatar
unknown committed
1398 1399 1400 1401 1402
      }
    }
    goto ret;
  }
ret:
1403 1404
  DBUG_PRINT("info", ("ret=0 execute_at=%llu",
             TIME_to_ulonglong_datetime(&execute_at)));
1405
  DBUG_RETURN(FALSE);
1406
err:
1407
  DBUG_PRINT("info", ("ret=1"));
1408
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1409 1410 1411
}


1412 1413 1414
/*
  Set the internal last_executed TIME struct to now. NOW is the
  time according to thd->query_start(), so the THD's clock.
1415 1416

  SYNOPSIS
1417
    Event_queue_element::mark_last_executed()
1418
      thd   thread context
1419 1420
*/

unknown's avatar
unknown committed
1421
void
1422
Event_queue_element::mark_last_executed(THD *thd)
unknown's avatar
unknown committed
1423 1424 1425
{
  TIME time_now;

1426 1427
  thd->end_time();
  my_tz_UTC->gmt_sec_to_TIME(&time_now, (my_time_t) thd->query_start());
unknown's avatar
unknown committed
1428

unknown's avatar
unknown committed
1429
  last_executed= time_now; /* was execute_at */
1430
  last_executed_changed= TRUE;
unknown's avatar
unknown committed
1431 1432
  
  execution_count++;
unknown's avatar
unknown committed
1433 1434 1435
}


unknown's avatar
unknown committed
1436
/*
1437
  Drops the event
1438 1439

  SYNOPSIS
1440
    Event_queue_element::drop()
1441
      thd   thread context
1442

1443 1444 1445 1446 1447 1448
  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
1449 1450 1451 1452
            failed.
*/

int
1453
Event_queue_element::drop(THD *thd)
unknown's avatar
unknown committed
1454
{
1455
  DBUG_ENTER("Event_queue_element::drop");
unknown's avatar
unknown committed
1456

1457
  DBUG_RETURN(Events::get_instance()->
1458
                    drop_event(thd, dbname, name, FALSE, TRUE));
unknown's avatar
unknown committed
1459 1460 1461
}


1462 1463
/*
  Saves status and last_executed_at to the disk if changed.
1464 1465

  SYNOPSIS
1466
    Event_queue_element::update_timing_fields()
1467 1468
      thd - thread context

1469
  RETURN VALUE
1470
    FALSE   OK
1471 1472
    TRUE    Error while opening mysql.event for writing or during
            write on disk
1473 1474
*/

unknown's avatar
unknown committed
1475
bool
1476
Event_queue_element::update_timing_fields(THD *thd)
unknown's avatar
unknown committed
1477 1478
{
  TABLE *table;
1479
  Field **fields;
unknown's avatar
unknown committed
1480
  Open_tables_state backup;
1481
  int ret= FALSE;
unknown's avatar
unknown committed
1482

1483
  DBUG_ENTER("Event_queue_element::update_timing_fields");
unknown's avatar
unknown committed
1484

unknown's avatar
unknown committed
1485
  DBUG_PRINT("enter", ("name: %*s", name.length, name.str));
1486 1487

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

unknown's avatar
unknown committed
1491 1492
  thd->reset_n_backup_open_tables_state(&backup);

unknown's avatar
unknown committed
1493
  if (Events::get_instance()->open_event_table(thd, TL_WRITE, &table))
unknown's avatar
unknown committed
1494
  {
1495
    ret= TRUE;
unknown's avatar
unknown committed
1496 1497
    goto done;
  }
1498
  fields= table->field;
1499
  if ((ret= Events::get_instance()->db_repository->
1500
                                 find_named_event(thd, dbname, name, table)))
unknown's avatar
unknown committed
1501 1502 1503
    goto done;

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

unknown's avatar
unknown committed
1507
  if (last_executed_changed)
unknown's avatar
unknown committed
1508
  {
1509 1510
    fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
    fields[ET_FIELD_LAST_EXECUTED]->store_time(&last_executed,
1511
                                               MYSQL_TIMESTAMP_DATETIME);
1512
    last_executed_changed= FALSE;
unknown's avatar
unknown committed
1513
  }
unknown's avatar
unknown committed
1514
  if (status_changed)
unknown's avatar
unknown committed
1515
  {
1516 1517
    fields[ET_FIELD_STATUS]->set_notnull();
    fields[ET_FIELD_STATUS]->store((longlong)status, TRUE);
1518
    status_changed= FALSE;
unknown's avatar
unknown committed
1519
  }
1520

1521 1522
  if ((table->file->ha_update_row(table->record[1], table->record[0])))
    ret= TRUE;
unknown's avatar
unknown committed
1523 1524 1525

done:
  close_thread_tables(thd);
unknown's avatar
unknown committed
1526
  thd->restore_backup_open_tables_state(&backup);
unknown's avatar
unknown committed
1527 1528 1529 1530 1531

  DBUG_RETURN(ret);
}


1532 1533
/*
  Get SHOW CREATE EVENT as string
1534 1535

  SYNOPSIS
unknown's avatar
unknown committed
1536
    Event_timed::get_create_event(THD *thd, String *buf)
1537 1538 1539 1540
      thd    Thread
      buf    String*, should be already allocated. CREATE EVENT goes inside.

  RETURN VALUE
1541 1542 1543 1544
    0                       OK
    EVEX_MICROSECOND_UNSUP  Error (for now if mysql.event has been
                            tampered and MICROSECONDS interval or
                            derivative has been put there.
1545 1546 1547
*/

int
unknown's avatar
unknown committed
1548
Event_timed::get_create_event(THD *thd, String *buf)
unknown's avatar
unknown committed
1549
{
1550
  int multipl= 0;
1551 1552
  char tmp_buf[2 * STRING_BUFFER_USUAL_SIZE];
  String expr_buf(tmp_buf, sizeof(tmp_buf), system_charset_info);
1553 1554 1555
  expr_buf.length(0);

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

1558 1559
  if (expression && Events::reconstruct_interval_expression(&expr_buf, interval,
                                                            expression))
1560
    DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
1561 1562 1563 1564 1565 1566

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

  if (expression)
  {
1567
    buf->append(STRING_WITH_LEN(" ON SCHEDULE EVERY "));
1568
    buf->append(expr_buf);
unknown's avatar
unknown committed
1569 1570 1571
    buf->append(' ');
    LEX_STRING *ival= &interval_type_to_name[interval];
    buf->append(ival->str, ival->length);
1572 1573 1574
  }
  else
  {
unknown's avatar
unknown committed
1575
    char dtime_buff[20*2+32];/* +32 to make my_snprintf_{8bit|ucs2} happy */
1576
    buf->append(STRING_WITH_LEN(" ON SCHEDULE AT '"));
1577 1578 1579
    /*
      Pass the buffer and the second param tells fills the buffer and
      returns the number of chars to copy.
1580 1581 1582 1583 1584
    */
    buf->append(dtime_buff, my_datetime_to_str(&execute_at, dtime_buff));
    buf->append(STRING_WITH_LEN("'"));
  }

1585
  if (on_completion == Event_timed::ON_COMPLETION_DROP)
1586 1587 1588 1589
    buf->append(STRING_WITH_LEN(" ON COMPLETION NOT PRESERVE "));
  else
    buf->append(STRING_WITH_LEN(" ON COMPLETION PRESERVE "));

1590
  if (status == Event_timed::ENABLED)
1591 1592 1593 1594 1595 1596 1597 1598 1599 1600
    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 "));
1601
  buf->append(body.str, body.length);
1602 1603

  DBUG_RETURN(0);
unknown's avatar
unknown committed
1604 1605 1606
}


1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625
/*
  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");
1626
  buf->append(STRING_WITH_LEN("CREATE EVENT anonymous ON SCHEDULE "
1627 1628 1629 1630 1631 1632 1633
                              "EVERY 3337 HOUR DO "));
  buf->append(body.str, body.length);

  DBUG_RETURN(0);
}


unknown's avatar
unknown committed
1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662
/*
  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);
  /*
1663 1664 1665
    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
1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696
  */
  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);
}


1697
/*
1698 1699
  Compiles an event before it's execution. Compiles the anonymous
  sp_head object held by the event
1700 1701

  SYNOPSIS
1702
    Event_job_data::compile()
1703 1704 1705
      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
1706

1707
  RETURN VALUE
1708 1709 1710
    0                       success
    EVEX_COMPILE_ERROR      error during compilation
    EVEX_MICROSECOND_UNSUP  mysql.event was tampered 
1711 1712
*/

unknown's avatar
unknown committed
1713
int
1714
Event_job_data::compile(THD *thd, MEM_ROOT *mem_root)
unknown's avatar
unknown committed
1715
{
unknown's avatar
unknown committed
1716
  int ret= 0;
unknown's avatar
unknown committed
1717 1718 1719
  MEM_ROOT *tmp_mem_root= 0;
  LEX *old_lex= thd->lex, lex;
  char *old_db;
1720
  int old_db_length;
unknown's avatar
unknown committed
1721 1722
  char *old_query;
  uint old_query_len;
1723
  ulong old_sql_mode= thd->variables.sql_mode;
1724
  char create_buf[15 * STRING_BUFFER_USUAL_SIZE];
1725 1726 1727
  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
1728
               *old_character_set_results;
unknown's avatar
unknown committed
1729
  Security_context save_ctx;
1730

1731
  DBUG_ENTER("Event_job_data::compile");
1732

1733
  show_create.length(0);
1734

1735
  switch (get_fake_create_event(thd, &show_create)) {
1736 1737 1738 1739 1740 1741 1742 1743
  case EVEX_MICROSECOND_UNSUP:
    DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
  case 0:
    break;
  default:
    DBUG_ASSERT(0);
  }

unknown's avatar
unknown committed
1744 1745 1746
  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;
1747 1748

  thd->variables.character_set_client=
unknown's avatar
unknown committed
1749 1750 1751 1752 1753
    thd->variables.character_set_results=
      thd->variables.collation_connection=
           get_charset_by_csname("utf8", MY_CS_PRIMARY, MYF(MY_WME));

  thd->update_charset();
1754

1755 1756
  DBUG_PRINT("info",("old_sql_mode=%d new_sql_mode=%d",old_sql_mode, sql_mode));
  thd->variables.sql_mode= this->sql_mode;
1757
  /* Change the memory root for the execution time */
unknown's avatar
unknown committed
1758 1759 1760 1761 1762 1763 1764 1765
  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;
1766
  old_db_length= thd->db_length;
unknown's avatar
unknown committed
1767
  thd->db= dbname.str;
1768
  thd->db_length= dbname.length;
1769

1770
  thd->query= show_create.c_ptr_safe();
1771
  thd->query_length= show_create.length();
1772
  DBUG_PRINT("info", ("query:%s",thd->query));
unknown's avatar
unknown committed
1773

1774 1775
  event_change_security_context(thd, definer_user, definer_host, dbname,
                                &save_ctx);
unknown's avatar
unknown committed
1776
  thd->lex= &lex;
unknown's avatar
unknown committed
1777
  mysql_init_query(thd, (uchar*) thd->query, thd->query_length);
1778
  if (MYSQLparse((void *)thd) || thd->is_fatal_error)
unknown's avatar
unknown committed
1779
  {
1780 1781
    DBUG_PRINT("error", ("error during compile or thd->is_fatal_error=%d",
                          thd->is_fatal_error));
1782 1783 1784 1785
    /*
      Free lex associated resources
      QQ: Do we really need all this stuff here?
    */
1786 1787
    sql_print_error("SCHEDULER: Error during compilation of %s.%s or "
                    "thd->is_fatal_error=%d",
1788
                    dbname.str, name.str, thd->is_fatal_error);
unknown's avatar
unknown committed
1789 1790 1791 1792

    lex.unit.cleanup();
    delete lex.sphead;
    sphead= lex.sphead= NULL;
unknown's avatar
unknown committed
1793 1794
    ret= EVEX_COMPILE_ERROR;
    goto done;
unknown's avatar
unknown committed
1795
  }
1796
  DBUG_PRINT("note", ("success compiling %s.%s", dbname.str, name.str));
1797

unknown's avatar
unknown committed
1798
  sphead= lex.sphead;
unknown's avatar
unknown committed
1799

unknown's avatar
unknown committed
1800
  sphead->set_definer(definer.str, definer.length);
1801
  sphead->set_info(0, 0, &lex.sp_chistics, sql_mode);
unknown's avatar
unknown committed
1802
  sphead->optimize();
unknown's avatar
unknown committed
1803 1804
  ret= 0;
done:
1805

unknown's avatar
unknown committed
1806
  lex_end(&lex);
1807
  event_restore_security_context(thd, &save_ctx);
1808 1809
  DBUG_PRINT("note", ("return old data on its place. set back NAMES"));

unknown's avatar
unknown committed
1810 1811 1812 1813
  thd->lex= old_lex;
  thd->query= old_query;
  thd->query_length= old_query_len;
  thd->db= old_db;
unknown's avatar
unknown committed
1814

1815
  thd->variables.sql_mode= old_sql_mode;
unknown's avatar
unknown committed
1816 1817 1818 1819 1820
  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();

1821
  /* Change the memory root for the execution time. */
unknown's avatar
unknown committed
1822 1823 1824
  if (mem_root)
    thd->mem_root= tmp_mem_root;

unknown's avatar
unknown committed
1825
  DBUG_RETURN(ret);
unknown's avatar
unknown committed
1826 1827
}

1828 1829

/*
1830 1831 1832
  Checks whether two events are in the same schema

  SYNOPSIS
1833
    event_basic_db_equal()
1834 1835
      db  Schema
      et  Compare et->dbname to `db`
1836 1837

  RETURN VALUE
1838 1839
    TRUE   Equal
    FALSE  Not equal
1840 1841 1842
*/

bool
1843
event_basic_db_equal(LEX_STRING db, Event_basic *et)
1844
{
1845
  return !sortcmp_lex_string(et->dbname, db, system_charset_info);
1846 1847 1848 1849
}


/*
1850
  Checks whether an event has equal `db` and `name`
1851 1852

  SYNOPSIS
1853
    event_basic_identifier_equal()
1854 1855 1856
      db   Schema
      name Name
      et   The event object
1857 1858

  RETURN VALUE
1859 1860
    TRUE   Equal
    FALSE  Not equal
1861 1862 1863
*/

bool
1864
event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b)
1865
{
unknown's avatar
unknown committed
1866 1867
  return !sortcmp_lex_string(name, b->name, system_charset_info) &&
         !sortcmp_lex_string(db, b->dbname, system_charset_info);
1868
}
unknown's avatar
unknown committed
1869 1870 1871


/*
1872 1873
  Switches the security context.

unknown's avatar
unknown committed
1874 1875 1876 1877 1878 1879 1880
  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
1881

unknown's avatar
unknown committed
1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908
  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);
} 


/*
1909 1910
  Restores the security context.

unknown's avatar
unknown committed
1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929
  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;
}