event_timed.cc 50.5 KB
Newer Older
1
/* Copyright (C) 2004-2006 MySQL AB
andrey@lmy004's avatar
andrey@lmy004 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
andrey@lmy004's avatar
andrey@lmy004 committed
18
#include "event_priv.h"
andrey@lmy004's avatar
andrey@lmy004 committed
19
#include "event.h"
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
#include "sp_head.h"


/*
  Constructor

  SYNOPSIS
    Event_timed::Event_timed()
*/

Event_timed::Event_timed():in_spawned_thread(0),locked_by_thread_id(0),
                           running(0), thread_id(0), status_changed(false),
                           last_executed_changed(false), expression(0),
                           created(0), modified(0),
                           on_completion(Event_timed::ON_COMPLETION_DROP),
                           status(Event_timed::ENABLED), sphead(0),
                           sql_mode(0), body_begin(0), dropped(false),
                           free_sphead_on_delete(true), flags(0)
                
{
  pthread_mutex_init(&this->LOCK_running, MY_MUTEX_INIT_FAST);
  pthread_cond_init(&this->COND_finished, NULL);
  init();
}


/*
  Destructor

  SYNOPSIS
    Event_timed::~Event_timed()
*/

Event_timed::~Event_timed()
{    
  deinit_mutexes();

  if (free_sphead_on_delete)
    free_sp();
}


/*
  Destructor

  SYNOPSIS
    Event_timed::~deinit_mutexes()
*/

void
Event_timed::deinit_mutexes()
{
  pthread_mutex_destroy(&this->LOCK_running);
  pthread_cond_destroy(&this->COND_finished);
}


/*
  Checks whether the event is running

  SYNOPSIS
    Event_timed::is_running()
*/

bool
Event_timed::is_running()
{
  bool ret;

  VOID(pthread_mutex_lock(&this->LOCK_running));
  ret= running;
  VOID(pthread_mutex_unlock(&this->LOCK_running));

  return ret;
}

andrey@lmy004's avatar
andrey@lmy004 committed
96 97

/*
98
  Init all member variables
andrey@lmy004's avatar
andrey@lmy004 committed
99

100
  SYNOPSIS
101
    Event_timed::init()
andrey@lmy004's avatar
andrey@lmy004 committed
102 103 104
*/

void
105
Event_timed::init()
andrey@lmy004's avatar
andrey@lmy004 committed
106
{
107
  DBUG_ENTER("Event_timed::init");
andrey@lmy004's avatar
andrey@lmy004 committed
108

109 110
  dbname.str= name.str= body.str= comment.str= 0;
  dbname.length= name.length= body.length= comment.length= 0;
111

112 113 114 115
  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);
116
  starts_null= ends_null= execute_at_null= TRUE;
andrey@lmy004's avatar
andrey@lmy004 committed
117

118 119
  definer_user.str= definer_host.str= 0;
  definer_user.length= definer_host.length= 0;
120

121 122
  sql_mode= 0;

andrey@lmy004's avatar
andrey@lmy004 committed
123 124 125 126 127
  DBUG_VOID_RETURN;
}


/*
128
  Set a name of the event
andrey@lmy004's avatar
andrey@lmy004 committed
129

130
  SYNOPSIS
131
    Event_timed::init_name()
132 133
      thd   THD
      spn   the name extracted in the parser
andrey@lmy004's avatar
andrey@lmy004 committed
134 135 136
*/

void
137
Event_timed::init_name(THD *thd, sp_name *spn)
andrey@lmy004's avatar
andrey@lmy004 committed
138
{
139
  DBUG_ENTER("Event_timed::init_name");
andrey@lmy004's avatar
andrey@lmy004 committed
140 141 142 143
  /* During parsing, we must use thd->mem_root */
  MEM_ROOT *root= thd->mem_root;

  /* We have to copy strings to get them into the right memroot */
144
  if (spn)
andrey@lmy004's avatar
andrey@lmy004 committed
145
  {
146 147 148
    dbname.length= spn->m_db.length;
    if (spn->m_db.length == 0)
      dbname.str= NULL;
andrey@lmy004's avatar
andrey@lmy004 committed
149
    else
150 151 152
      dbname.str= strmake_root(root, spn->m_db.str, spn->m_db.length);
    name.length= spn->m_name.length;
    name.str= strmake_root(root, spn->m_name.str, spn->m_name.length);
andrey@lmy004's avatar
andrey@lmy004 committed
153

154 155
    if (spn->m_qname.length == 0)
      spn->init_qname(thd);
andrey@lmy004's avatar
andrey@lmy004 committed
156 157 158
  }
  else if (thd->db)
  {
159 160
    dbname.length= thd->db_length;
    dbname.str= strmake_root(root, thd->db, dbname.length);
andrey@lmy004's avatar
andrey@lmy004 committed
161
  }
162 163 164

  DBUG_PRINT("dbname", ("len=%d db=%s",dbname.length, dbname.str));
  DBUG_PRINT("name", ("len=%d name=%s",name.length, name.str));
andrey@lmy004's avatar
andrey@lmy004 committed
165 166 167 168 169 170

  DBUG_VOID_RETURN;
}


/*
171
  Set body of the event - what should be executed.
andrey@lmy004's avatar
andrey@lmy004 committed
172

173
  SYNOPSIS
174
    Event_timed::init_body()
175
      thd   THD
andrey@lmy004's avatar
andrey@lmy004 committed
176 177 178 179

  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.
180 181 182
 
    Some questionable removal of characters is done in here, and that part
    should be refactored when the parser is smarter.
andrey@lmy004's avatar
andrey@lmy004 committed
183 184 185
*/

void
186
Event_timed::init_body(THD *thd)
andrey@lmy004's avatar
andrey@lmy004 committed
187
{
188
  DBUG_ENTER("Event_timed::init_body");
189 190
  DBUG_PRINT("info", ("body=[%s] body_begin=0x%ld end=0x%ld", body_begin,
             body_begin, thd->lex->ptr));
andrey@lmy004's avatar
andrey@lmy004 committed
191

192
  body.length= thd->lex->ptr - body_begin;
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
  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;
    }

    /*  
       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 == '/'))
    {
      DBUG_PRINT("info", ("consumend one '*" "/' comment in the query '%s'", 
          body_begin));
      body.length-= 2;
      body_end-= 2;
      continue;
    }

    break;  /* none were found, so we have excised all we can. */
  }
andrey@lmy004's avatar
andrey@lmy004 committed
233

234 235 236 237 238 239 240
  /* 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;
  }
  body.str= strmake_root(thd->mem_root, (char *)body_begin, body.length);
andrey@lmy004's avatar
andrey@lmy004 committed
241 242 243 244 245 246

  DBUG_VOID_RETURN;
}


/*
247
  Set time for execution for one time events.
andrey@lmy004's avatar
andrey@lmy004 committed
248

249
  SYNOPSIS
250
    Event_timed::init_execute_at()
251
      expr   when (datetime)
andrey@lmy004's avatar
andrey@lmy004 committed
252

253 254 255 256 257
  RETURN VALUE
    0                  OK
    EVEX_PARSE_ERROR   fix_fields failed
    EVEX_BAD_PARAMS    datetime is in the past
    ER_WRONG_VALUE     wrong value for execute at
andrey@lmy004's avatar
andrey@lmy004 committed
258 259 260
*/

int
261
Event_timed::init_execute_at(THD *thd, Item *expr)
andrey@lmy004's avatar
andrey@lmy004 committed
262 263 264
{
  my_bool not_used;
  TIME ltime;
265
  my_time_t t;
andrey@lmy004's avatar
andrey@lmy004 committed
266 267

  TIME time_tmp;
268
  DBUG_ENTER("Event_timed::init_execute_at");
andrey@lmy004's avatar
andrey@lmy004 committed
269 270 271

  if (expr->fix_fields(thd, &expr))
    DBUG_RETURN(EVEX_PARSE_ERROR);
272 273 274
  
  /* no starts and/or ends in case of execute_at */
  DBUG_PRINT("info", ("starts_null && ends_null should be 1 is %d",
andrey@lmy004's avatar
andrey@lmy004 committed
275
                      (starts_null && ends_null)));
276 277
  DBUG_ASSERT(starts_null && ends_null);
  
andrey@lmy004's avatar
andrey@lmy004 committed
278
  /* let's check whether time is in the past */
andrey@lmy004's avatar
andrey@lmy004 committed
279
  thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, 
andrey@lmy004's avatar
andrey@lmy004 committed
280
                                            (my_time_t) thd->query_start());
andrey@lmy004's avatar
andrey@lmy004 committed
281 282

  if ((not_used= expr->get_date(&ltime, TIME_NO_ZERO_DATE)))
283 284 285 286
    DBUG_RETURN(ER_WRONG_VALUE);

  if (TIME_to_ulonglong_datetime(&ltime) <
      TIME_to_ulonglong_datetime(&time_tmp))
andrey@lmy004's avatar
andrey@lmy004 committed
287 288 289
    DBUG_RETURN(EVEX_BAD_PARAMS);

  /*
290 291
    This may result in a 1970-01-01 date if ltime is > 2037-xx-xx.
    CONVERT_TZ has similar problem.
292 293
    mysql_priv.h currently lists 
      #define TIMESTAMP_MAX_YEAR 2038 (see TIME_to_timestamp())
andrey@lmy004's avatar
andrey@lmy004 committed
294
  */
295 296 297 298 299 300
  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"));
    DBUG_RETURN(ER_WRONG_VALUE);
  }
andrey@lmy004's avatar
andrey@lmy004 committed
301

302
  execute_at_null= FALSE;
303
  execute_at= ltime;
andrey@lmy004's avatar
andrey@lmy004 committed
304 305 306 307 308
  DBUG_RETURN(0);
}


/*
309
  Set time for execution for transient events.
andrey@lmy004's avatar
andrey@lmy004 committed
310

311
  SYNOPSIS
312
    Event_timed::init_interval()
313 314
      expr      how much?
      new_interval  what is the interval
andrey@lmy004's avatar
andrey@lmy004 committed
315

316
  RETURN VALUE
317 318 319
    0                  OK
    EVEX_PARSE_ERROR   fix_fields failed
    EVEX_BAD_PARAMS    Interval is not positive
320
    EVEX_MICROSECOND_UNSUP  Microseconds are not supported.
andrey@lmy004's avatar
andrey@lmy004 committed
321 322 323
*/

int
324
Event_timed::init_interval(THD *thd, Item *expr, interval_type new_interval)
andrey@lmy004's avatar
andrey@lmy004 committed
325
{
326
  String value;
327
  INTERVAL interval_tmp;
328

329
  DBUG_ENTER("Event_timed::init_interval");
andrey@lmy004's avatar
andrey@lmy004 committed
330 331 332 333

  if (expr->fix_fields(thd, &expr))
    DBUG_RETURN(EVEX_PARSE_ERROR);

334
  value.alloc(MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN);
335
  if (get_interval_value(expr, new_interval, &value, &interval_tmp))
336
    DBUG_RETURN(EVEX_PARSE_ERROR);
andrey@lmy004's avatar
andrey@lmy004 committed
337

338 339 340 341
  expression= 0;

  switch (new_interval) {
  case INTERVAL_YEAR:
342
    expression= interval_tmp.year;
343 344 345
    break;
  case INTERVAL_QUARTER:
  case INTERVAL_MONTH:
346
    expression= interval_tmp.month;
347 348 349
    break;
  case INTERVAL_WEEK:
  case INTERVAL_DAY:
350
    expression= interval_tmp.day;
351 352
    break;
  case INTERVAL_HOUR:
353
    expression= interval_tmp.hour;
354 355
    break;
  case INTERVAL_MINUTE:
356
    expression= interval_tmp.minute;
357 358
    break;
  case INTERVAL_SECOND:
359
    expression= interval_tmp.second;
360
    break;
361
  case INTERVAL_YEAR_MONTH:                     // Allow YEAR-MONTH YYYYYMM
362
    expression= interval_tmp.year* 12 + interval_tmp.month;
363 364
    break;
  case INTERVAL_DAY_HOUR:
365
    expression= interval_tmp.day* 24 + interval_tmp.hour;
366 367
    break;
  case INTERVAL_DAY_MINUTE:
368 369
    expression= (interval_tmp.day* 24 + interval_tmp.hour) * 60 +
                interval_tmp.minute;
370
    break;
andrey@lmy004's avatar
andrey@lmy004 committed
371
  case INTERVAL_HOUR_SECOND: /* day is anyway 0 */
372 373
  case INTERVAL_DAY_SECOND:
    /* DAY_SECOND having problems because of leap seconds? */
374 375 376
    expression= ((interval_tmp.day* 24 + interval_tmp.hour) * 60 +
                  interval_tmp.minute)*60
                 + interval_tmp.second;
377
    break;
andrey@lmy004's avatar
andrey@lmy004 committed
378 379
  case INTERVAL_MINUTE_MICROSECOND: /* day and hour are 0 */
  case INTERVAL_HOUR_MICROSECOND:   /* day is anyway 0    */
380
  case INTERVAL_DAY_MICROSECOND:
381
    DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
382 383 384
    expression= ((((interval_tmp.day*24) + interval_tmp.hour)*60+
                  interval_tmp.minute)*60 +
                 interval_tmp.second) * 1000000L + interval_tmp.second_part;
385 386
    break;
  case INTERVAL_HOUR_MINUTE:
387
    expression= interval_tmp.hour * 60 + interval_tmp.minute;
388 389
    break;
  case INTERVAL_MINUTE_SECOND:
390
    expression= interval_tmp.minute * 60 + interval_tmp.second;
391 392
    break;
  case INTERVAL_SECOND_MICROSECOND:
393
    DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
394
    expression= interval_tmp.second * 1000000L + interval_tmp.second_part;
395
    break;
396 397
  case INTERVAL_MICROSECOND:
    DBUG_RETURN(EVEX_MICROSECOND_UNSUP);  
398
  }
399
  if (interval_tmp.neg || expression > EVEX_MAX_INTERVAL_VALUE)
400
    DBUG_RETURN(EVEX_BAD_PARAMS);
401

402
  interval= new_interval;
andrey@lmy004's avatar
andrey@lmy004 committed
403 404 405 406 407
  DBUG_RETURN(0);
}


/*
408 409 410
  Set activation time.

  SYNOPSIS
411
    Event_timed::init_starts()
412 413 414 415 416 417 418 419 420 421 422
    expr      how much?
    interval  what is the interval

  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.

423
  RETURN VALUE
424 425
    0                  OK
    EVEX_PARSE_ERROR   fix_fields failed
426
    EVEX_BAD_PARAMS    starts before now
andrey@lmy004's avatar
andrey@lmy004 committed
427 428 429
*/

int
430
Event_timed::init_starts(THD *thd, Item *new_starts)
andrey@lmy004's avatar
andrey@lmy004 committed
431 432
{
  my_bool not_used;
433
  TIME ltime, time_tmp;
434
  my_time_t t;
andrey@lmy004's avatar
andrey@lmy004 committed
435

436
  DBUG_ENTER("Event_timed::init_starts");
andrey@lmy004's avatar
andrey@lmy004 committed
437

438
  if (new_starts->fix_fields(thd, &new_starts))
andrey@lmy004's avatar
andrey@lmy004 committed
439 440
    DBUG_RETURN(EVEX_PARSE_ERROR);

441
  if ((not_used= new_starts->get_date(&ltime, TIME_NO_ZERO_DATE)))
andrey@lmy004's avatar
andrey@lmy004 committed
442 443
    DBUG_RETURN(EVEX_BAD_PARAMS);

444 445 446
  /* 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());
447

448 449
  DBUG_PRINT("info",("now   =%lld", TIME_to_ulonglong_datetime(&time_tmp)));
  DBUG_PRINT("info",("starts=%lld", TIME_to_ulonglong_datetime(&ltime)));
450 451 452 453
  if (TIME_to_ulonglong_datetime(&ltime) <
      TIME_to_ulonglong_datetime(&time_tmp))
    DBUG_RETURN(EVEX_BAD_PARAMS);

andrey@lmy004's avatar
andrey@lmy004 committed
454
  /*
455 456 457 458
    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())
andrey@lmy004's avatar
andrey@lmy004 committed
459
  */
460 461 462 463 464 465
  my_tz_UTC->gmt_sec_to_TIME(&ltime,t=TIME_to_timestamp(thd, &ltime, &not_used));
  if (!t)
  {
    DBUG_PRINT("error", ("STARTS after year 2037"));
    DBUG_RETURN(EVEX_BAD_PARAMS);
  }
andrey@lmy004's avatar
andrey@lmy004 committed
466

467
  starts= ltime;
468
  starts_null= FALSE;
andrey@lmy004's avatar
andrey@lmy004 committed
469 470 471 472 473
  DBUG_RETURN(0);
}


/*
474 475 476
  Set deactivation time.

  SYNOPSIS
477
    Event_timed::init_ends()
478 479 480 481 482 483 484 485 486 487 488
      thd       THD
      new_ends  when?

  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.

489
  RETURN VALUE
490 491
    0                  OK
    EVEX_PARSE_ERROR   fix_fields failed
492
    ER_WRONG_VALUE     starts distant date (after year 2037)
493
    EVEX_BAD_PARAMS    ENDS before STARTS
andrey@lmy004's avatar
andrey@lmy004 committed
494 495
*/

496
int
497
Event_timed::init_ends(THD *thd, Item *new_ends)
andrey@lmy004's avatar
andrey@lmy004 committed
498
{
499
  TIME ltime, ltime_now;
andrey@lmy004's avatar
andrey@lmy004 committed
500
  my_bool not_used;
501
  my_time_t t;
andrey@lmy004's avatar
andrey@lmy004 committed
502

503
  DBUG_ENTER("Event_timed::init_ends");
andrey@lmy004's avatar
andrey@lmy004 committed
504

505
  if (new_ends->fix_fields(thd, &new_ends))
andrey@lmy004's avatar
andrey@lmy004 committed
506 507
    DBUG_RETURN(EVEX_PARSE_ERROR);

508
  DBUG_PRINT("info", ("convert to TIME"));
509
  if ((not_used= new_ends->get_date(&ltime, TIME_NO_ZERO_DATE)))
andrey@lmy004's avatar
andrey@lmy004 committed
510 511 512
    DBUG_RETURN(EVEX_BAD_PARAMS);

  /*
513 514 515 516
    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())
andrey@lmy004's avatar
andrey@lmy004 committed
517
  */
518
  DBUG_PRINT("info", ("get the UTC time"));
519 520 521 522 523 524
  my_tz_UTC->gmt_sec_to_TIME(&ltime,t=TIME_to_timestamp(thd, &ltime, &not_used));
  if (!t)
  {
    DBUG_PRINT("error", ("ENDS after year 2037"));
    DBUG_RETURN(EVEX_BAD_PARAMS);
  }
525

526 527 528 529 530 531 532 533 534 535 536
  /* Check whether ends is after starts */
  DBUG_PRINT("info", ("ENDS after STARTS?"));
  if (!starts_null && my_time_compare(&starts, &ltime) != -1)
    DBUG_RETURN(EVEX_BAD_PARAMS);

  /*
    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?"));
andrey@lmy004's avatar
andrey@lmy004 committed
537
  my_tz_UTC->gmt_sec_to_TIME(&ltime_now, thd->query_start());
538
  if (my_time_compare(&ltime_now, &ltime) == 1)
andrey@lmy004's avatar
andrey@lmy004 committed
539 540
    DBUG_RETURN(EVEX_BAD_PARAMS);

541
  ends= ltime;
542
  ends_null= FALSE;
andrey@lmy004's avatar
andrey@lmy004 committed
543 544 545 546 547
  DBUG_RETURN(0);
}


/*
548
  Sets comment.
andrey@lmy004's avatar
andrey@lmy004 committed
549

550
  SYNOPSIS
551
    Event_timed::init_comment()
552 553
      thd      THD - used for memory allocation
      comment  the string.
andrey@lmy004's avatar
andrey@lmy004 committed
554 555 556
*/

void
557
Event_timed::init_comment(THD *thd, LEX_STRING *set_comment)
andrey@lmy004's avatar
andrey@lmy004 committed
558
{
559
  DBUG_ENTER("Event_timed::init_comment");
andrey@lmy004's avatar
andrey@lmy004 committed
560

561
  comment.str= strmake_root(thd->mem_root, set_comment->str,
562
                            comment.length= set_comment->length);
andrey@lmy004's avatar
andrey@lmy004 committed
563 564 565 566 567 568

  DBUG_VOID_RETURN;
}


/*
569
  Inits definer (definer_user and definer_host) during parsing.
andrey@lmy004's avatar
andrey@lmy004 committed
570

571
  SYNOPSIS
572
    Event_timed::init_definer()
573 574 575
  
  RETURN VALUE
    0  OK
andrey@lmy004's avatar
andrey@lmy004 committed
576 577 578
*/

int
579
Event_timed::init_definer(THD *thd)
andrey@lmy004's avatar
andrey@lmy004 committed
580
{
581
  DBUG_ENTER("Event_timed::init_definer");
andrey@lmy004's avatar
andrey@lmy004 committed
582

583 584 585
  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));
586 587
  definer_user.str= strdup_root(thd->mem_root, thd->security_ctx->priv_user);
  definer_user.length= strlen(thd->security_ctx->priv_user);
andrey@lmy004's avatar
andrey@lmy004 committed
588

589 590
  DBUG_PRINT("info",("init definer_host thd->s_c->priv_host=0x%lx",
                     thd->security_ctx->priv_host));
591 592
  definer_host.str= strdup_root(thd->mem_root, thd->security_ctx->priv_host);
  definer_host.length= strlen(thd->security_ctx->priv_host);
593

594
  DBUG_PRINT("info",("init definer as whole"));
595 596 597
  definer.length= definer_user.length + definer_host.length + 1;
  definer.str= alloc_root(thd->mem_root, definer.length + 1);

598
  DBUG_PRINT("info",("copy the user"));
599 600
  memcpy(definer.str, definer_user.str, definer_user.length);
  definer.str[definer_user.length]= '@';
601

602
  DBUG_PRINT("info",("copy the host"));
603 604
  memcpy(definer.str + definer_user.length + 1, definer_host.str,
         definer_host.length);
605
  definer.str[definer.length]= '\0';
606
  DBUG_PRINT("info",("definer initted"));
andrey@lmy004's avatar
andrey@lmy004 committed
607 608 609 610 611 612

  DBUG_RETURN(0);
}


/*
613 614 615
  Loads an event from a row from mysql.event

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

618 619 620 621
  RETURN VALUE
    0                      OK
    EVEX_GET_FIELD_FAILED  Error

622 623 624 625
  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.
andrey@lmy004's avatar
andrey@lmy004 committed
626 627 628
*/

int
629
Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
andrey@lmy004's avatar
andrey@lmy004 committed
630 631
{
  char *ptr;
632
  Event_timed *et;
andrey@lmy004's avatar
andrey@lmy004 committed
633 634 635
  uint len;
  bool res1, res2;

636
  DBUG_ENTER("Event_timed::load_from_row");
andrey@lmy004's avatar
andrey@lmy004 committed
637 638 639 640 641

  if (!table)
    goto error;

  et= this;
642

643
  if (table->s->fields != Events::FIELD_COUNT)
andrey@lmy004's avatar
andrey@lmy004 committed
644 645
    goto error;

646
  if ((et->dbname.str= get_field(mem_root,
647
                                 table->field[Events::FIELD_DB])) == NULL)
andrey@lmy004's avatar
andrey@lmy004 committed
648 649
    goto error;

650
  et->dbname.length= strlen(et->dbname.str);
andrey@lmy004's avatar
andrey@lmy004 committed
651

652
  if ((et->name.str= get_field(mem_root,
653
                               table->field[Events::FIELD_NAME])) == NULL)
andrey@lmy004's avatar
andrey@lmy004 committed
654 655
    goto error;

656
  et->name.length= strlen(et->name.str);
andrey@lmy004's avatar
andrey@lmy004 committed
657

658
  if ((et->body.str= get_field(mem_root,
659
                               table->field[Events::FIELD_BODY])) == NULL)
andrey@lmy004's avatar
andrey@lmy004 committed
660 661
    goto error;

662
  et->body.length= strlen(et->body.str);
andrey@lmy004's avatar
andrey@lmy004 committed
663

664
  if ((et->definer.str= get_field(mem_root,
665
                                  table->field[Events::FIELD_DEFINER])) == NullS)
andrey@lmy004's avatar
andrey@lmy004 committed
666
    goto error;
667
  et->definer.length= strlen(et->definer.str);
andrey@lmy004's avatar
andrey@lmy004 committed
668

669
  ptr= strchr(et->definer.str, '@');
andrey@lmy004's avatar
andrey@lmy004 committed
670 671

  if (! ptr)
672
    ptr= et->definer.str;
andrey@lmy004's avatar
andrey@lmy004 committed
673

674
  len= ptr - et->definer.str;
andrey@lmy004's avatar
andrey@lmy004 committed
675

676 677
  et->definer_user.str= strmake_root(mem_root, et->definer.str, len);
  et->definer_user.length= len;
andrey@lmy004's avatar
andrey@lmy004 committed
678 679
  len= et->definer.length - len - 1;            //1 is because of @
  et->definer_host.str= strmake_root(mem_root, ptr + 1, len);/* 1:because of @*/
680
  et->definer_host.length= len;
andrey@lmy004's avatar
andrey@lmy004 committed
681
  
682 683 684
  et->starts_null= table->field[Events::FIELD_STARTS]->is_null();
  res1= table->field[Events::FIELD_STARTS]->
                                    get_date(&et->starts,TIME_NO_ZERO_DATE);
685

686 687
  et->ends_null= table->field[Events::FIELD_ENDS]->is_null();
  res2= table->field[Events::FIELD_ENDS]->get_date(&et->ends, TIME_NO_ZERO_DATE);
andrey@lmy004's avatar
andrey@lmy004 committed
688
  
689 690
  if (!table->field[Events::FIELD_INTERVAL_EXPR]->is_null())
    et->expression= table->field[Events::FIELD_INTERVAL_EXPR]->val_int();
691 692
  else
    et->expression= 0;
andrey@lmy004's avatar
andrey@lmy004 committed
693 694
  /*
    If res1 and res2 are true then both fields are empty.
695
    Hence if Events::FIELD_EXECUTE_AT is empty there is an error.
andrey@lmy004's avatar
andrey@lmy004 committed
696
  */
697 698
  et->execute_at_null=
            table->field[Events::FIELD_EXECUTE_AT]->is_null();
699 700 701
  DBUG_ASSERT(!(et->starts_null && et->ends_null && !et->expression &&
              et->execute_at_null));
  if (!et->expression &&
702 703
      table->field[Events::FIELD_EXECUTE_AT]-> get_date(&et->execute_at,
                                                        TIME_NO_ZERO_DATE))
andrey@lmy004's avatar
andrey@lmy004 committed
704 705 706 707 708 709
    goto error;

  /*
    In DB the values start from 1 but enum interval_type starts
    from 0
  */
710 711 712
  if (!table->field[Events::FIELD_TRANSIENT_INTERVAL]->is_null())
    et->interval= (interval_type) ((ulonglong)
          table->field[Events::FIELD_TRANSIENT_INTERVAL]->val_int() - 1);
713 714
  else
    et->interval= (interval_type) 0;
andrey@lmy004's avatar
andrey@lmy004 committed
715

716 717
  et->created= table->field[Events::FIELD_CREATED]->val_int();
  et->modified= table->field[Events::FIELD_MODIFIED]->val_int();
andrey@lmy004's avatar
andrey@lmy004 committed
718

719
  table->field[Events::FIELD_LAST_EXECUTED]->
720
                     get_date(&et->last_executed, TIME_NO_ZERO_DATE);
721

722
  last_executed_changed= false;
andrey@lmy004's avatar
andrey@lmy004 committed
723

andrey@lmy004's avatar
andrey@lmy004 committed
724
  /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
725
  if ((ptr= get_field(mem_root, table->field[Events::FIELD_STATUS])) == NullS)
andrey@lmy004's avatar
andrey@lmy004 committed
726
    goto error;
727

728
  DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->name.str, ptr));
729
  et->status= (ptr[0]=='E'? Event_timed::ENABLED:Event_timed::DISABLED);
andrey@lmy004's avatar
andrey@lmy004 committed
730

andrey@lmy004's avatar
andrey@lmy004 committed
731
  /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
andrey@lmy004's avatar
andrey@lmy004 committed
732
  if ((ptr= get_field(mem_root,
733
                  table->field[Events::FIELD_ON_COMPLETION])) == NullS)
andrey@lmy004's avatar
andrey@lmy004 committed
734 735
    goto error;

736 737
  et->on_completion= (ptr[0]=='D'? Event_timed::ON_COMPLETION_DROP:
                                   Event_timed::ON_COMPLETION_PRESERVE);
andrey@lmy004's avatar
andrey@lmy004 committed
738

739
  et->comment.str= get_field(mem_root, table->field[Events::FIELD_COMMENT]);
740 741
  if (et->comment.str != NullS)
    et->comment.length= strlen(et->comment.str);
andrey@lmy004's avatar
andrey@lmy004 committed
742
  else
743
    et->comment.length= 0;
andrey@lmy004's avatar
andrey@lmy004 committed
744
    
745

746
  et->sql_mode= (ulong) table->field[Events::FIELD_SQL_MODE]->val_int();
747

andrey@lmy004's avatar
andrey@lmy004 committed
748 749 750 751 752 753
  DBUG_RETURN(0);
error:
  DBUG_RETURN(EVEX_GET_FIELD_FAILED);
}


754
/*
755 756
  Computes the sum of a timestamp plus interval. Presumed is that at least one
  previous execution has occured.
757 758 759 760 761

  SYNOPSIS
    get_next_time(TIME *start, int interval_value, interval_type interval)
      next          the sum
      start         add interval_value to this time
762
      time_now      current time
763 764
      i_value       quantity of time type interval to add
      i_type        type of interval to add (SECOND, MINUTE, HOUR, WEEK ...)
765
  
766
  RETURN VALUE
767 768 769 770 771 772 773 774 775 776 777 778 779
    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.
780
*/
781

782
static
783 784
bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec,
                   int i_value, interval_type i_type)
785 786 787 788
{
  bool ret;
  INTERVAL interval;
  TIME tmp;
789 790 791 792
  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)));
793

794 795 796 797
  bzero(&interval, sizeof(interval));

  switch (i_type) {
  case INTERVAL_YEAR:
798
    months= i_value*12;
799 800
    break;
  case INTERVAL_QUARTER:
801
    /* Has already been converted to months */
802 803
  case INTERVAL_YEAR_MONTH:
  case INTERVAL_MONTH:
804
    months= i_value;
805 806
    break;
  case INTERVAL_WEEK:
807
    /* WEEK has already been converted to days */
808
  case INTERVAL_DAY:
809
    seconds= i_value*24*3600;
810 811 812
    break;
  case INTERVAL_DAY_HOUR:
  case INTERVAL_HOUR:
813
    seconds= i_value*3600;
814 815 816 817
    break;
  case INTERVAL_DAY_MINUTE:
  case INTERVAL_HOUR_MINUTE:
  case INTERVAL_MINUTE:
818
    seconds= i_value*60;
819 820 821 822 823
    break;
  case INTERVAL_DAY_SECOND:
  case INTERVAL_HOUR_SECOND:
  case INTERVAL_MINUTE_SECOND:
  case INTERVAL_SECOND:
824
    seconds= i_value;
825 826 827 828 829 830
    break;
  case INTERVAL_DAY_MICROSECOND:
  case INTERVAL_HOUR_MICROSECOND:
  case INTERVAL_MINUTE_MICROSECOND:
  case INTERVAL_SECOND_MICROSECOND:
  case INTERVAL_MICROSECOND:
831 832 833 834 835
    /*
     We should return an error here so SHOW EVENTS/ SELECT FROM I_S.EVENTS
     would give an error then.
    */
    DBUG_RETURN(1);
836 837
    break;
  }
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
  DBUG_PRINT("info", ("seconds=%ld months=%ld", seconds, months));
  if (seconds)
  {
    longlong seconds_diff;
    long microsec_diff;
    
    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));
857 858 859
    if (seconds_diff % seconds || (!seconds_diff && last_exec->year) ||
        TIME_to_ulonglong_datetime(time_now) ==
          TIME_to_ulonglong_datetime(last_exec))
860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906
      ++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)
      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.
    */
    if (time_now->year==last_exec->year && time_now->month==last_exec->month)
      interval.month+= months;

    tmp= *start;
    if ((ret= date_add_interval(&tmp, INTERVAL_MONTH, interval)))
      goto done;
    
    /* 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;
    }
907
    *next= tmp;
908 909 910
    /* assert on that the next is after now */
    DBUG_ASSERT(1==my_time_compare(next, time_now));
  }
911

912 913 914
done:
  DBUG_PRINT("info", ("next=%llu", TIME_to_ulonglong_datetime(next)));
  DBUG_RETURN(ret);
915 916 917 918
}


/*
919 920 921
  Computes next execution time.

  SYNOPSIS
922
    Event_timed::compute_next_execution_time()
923

924 925 926 927
  RETURN VALUE
    FALSE  OK
    TRUE   Error

928 929 930
  NOTES
    The time is set in execute_at, if no more executions the latter is set to
    0000-00-00.
931 932
*/

andrey@lmy004's avatar
andrey@lmy004 committed
933
bool
934
Event_timed::compute_next_execution_time()
andrey@lmy004's avatar
andrey@lmy004 committed
935 936 937 938
{
  TIME time_now;
  int tmp;

939
  DBUG_ENTER("Event_timed::compute_next_execution_time");
940 941 942 943
  DBUG_PRINT("enter", ("starts=%llu ends=%llu last_executed=%llu",
                        TIME_to_ulonglong_datetime(&starts),
                        TIME_to_ulonglong_datetime(&ends),
                        TIME_to_ulonglong_datetime(&last_executed)));
andrey@lmy004's avatar
andrey@lmy004 committed
944

945
  if (status == Event_timed::DISABLED)
andrey@lmy004's avatar
andrey@lmy004 committed
946 947
  {
    DBUG_PRINT("compute_next_execution_time",
948
                  ("Event %s is DISABLED", name.str));
andrey@lmy004's avatar
andrey@lmy004 committed
949 950
    goto ret;
  }
951
  /* If one-time, no need to do computation */
952
  if (!expression)
andrey@lmy004's avatar
andrey@lmy004 committed
953
  {
954
    /* Let's check whether it was executed */
955
    if (last_executed.year)
andrey@lmy004's avatar
andrey@lmy004 committed
956
    {
957 958
      DBUG_PRINT("info",("One-time event %s.%s of was already executed",
                         dbname.str, name.str, definer.str));
959
      dropped= (on_completion == Event_timed::ON_COMPLETION_DROP);
960 961
      DBUG_PRINT("info",("One-time event will be dropped=%d.", dropped));

962
      status= Event_timed::DISABLED;
963
      status_changed= true;
andrey@lmy004's avatar
andrey@lmy004 committed
964 965 966
    }
    goto ret;
  }
967

968
  my_tz_UTC->gmt_sec_to_TIME(&time_now, current_thd->query_start());
969

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

andrey@lmy004's avatar
andrey@lmy004 committed
972
  /* if time_now is after ends don't execute anymore */
973
  if (!ends_null && (tmp= my_time_compare(&ends, &time_now)) == -1)
andrey@lmy004's avatar
andrey@lmy004 committed
974
  {
975
    DBUG_PRINT("info", ("NOW after ENDS, don't execute anymore"));
976
    /* time_now is after ends. don't execute anymore */
977
    set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
978
    execute_at_null= TRUE;
979
    if (on_completion == Event_timed::ON_COMPLETION_DROP)
980
      dropped= true;
981
    DBUG_PRINT("info", ("Dropped=%d", dropped));
982
    status= Event_timed::DISABLED;
983
    status_changed= true;
andrey@lmy004's avatar
andrey@lmy004 committed
984 985 986

    goto ret;
  }
987 988 989 990 991

  /*
    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.
andrey@lmy004's avatar
andrey@lmy004 committed
992
  */
993
  if (!starts_null && (tmp= my_time_compare(&time_now, &starts)) < 1)
andrey@lmy004's avatar
andrey@lmy004 committed
994
  {
995
    if (tmp == 0 && my_time_compare(&starts, &last_executed) == 0)
andrey@lmy004's avatar
andrey@lmy004 committed
996
    {
997
      /*
998 999
        time_now = starts = last_executed
        do nothing or we will schedule for second time execution at starts.
andrey@lmy004's avatar
andrey@lmy004 committed
1000 1001 1002 1003
      */
    }
    else
    {
1004
      DBUG_PRINT("info", ("STARTS is future, NOW <= STARTS,sched for STARTS"));
1005 1006 1007 1008 1009
      /*
        starts is in the future
        time_now before starts. Scheduling for starts
      */
      execute_at= starts;
1010
      execute_at_null= FALSE;
andrey@lmy004's avatar
andrey@lmy004 committed
1011 1012 1013
      goto ret;
    }
  }
andrey@lmy004's avatar
andrey@lmy004 committed
1014

1015
  if (!starts_null && !ends_null)
andrey@lmy004's avatar
andrey@lmy004 committed
1016
  {
1017
    /*
1018 1019 1020
      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
andrey@lmy004's avatar
andrey@lmy004 committed
1021 1022
      If not set then schedule for now.
    */
1023
    DBUG_PRINT("info", ("Both STARTS & ENDS are set"));
1024
    if (!last_executed.year)
1025
    {
1026
      DBUG_PRINT("info", ("Not executed so far."));
1027
    }
1028

andrey@lmy004's avatar
andrey@lmy004 committed
1029
    {
1030
      TIME next_exec;
1031

1032 1033
      if (get_next_time(&next_exec, &starts, &time_now,
                        last_executed.year? &last_executed:&starts,
1034
                        expression, interval))
1035
        goto err;
1036 1037

      /* There was previous execution */
1038
      if (my_time_compare(&ends, &next_exec) == -1)
andrey@lmy004's avatar
andrey@lmy004 committed
1039
      {
1040 1041
        DBUG_PRINT("info", ("Next execution of %s after ENDS. Stop executing.",
                   name.str));
1042
        /* Next execution after ends. No more executions */
1043
        set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
1044
        execute_at_null= TRUE;
1045
        if (on_completion == Event_timed::ON_COMPLETION_DROP)
1046
          dropped= true;
1047 1048
        status= Event_timed::DISABLED;
        status_changed= true;
andrey@lmy004's avatar
andrey@lmy004 committed
1049 1050
      }
      else
1051
      {
1052
        DBUG_PRINT("info",("Next[%llu]",TIME_to_ulonglong_datetime(&next_exec)));
1053
        execute_at= next_exec;
1054 1055
        execute_at_null= FALSE;
      }
andrey@lmy004's avatar
andrey@lmy004 committed
1056 1057 1058
    }
    goto ret;
  }
1059
  else if (starts_null && ends_null) 
andrey@lmy004's avatar
andrey@lmy004 committed
1060
  {
1061
    /* starts is always set, so this is a dead branch !! */
1062
    DBUG_PRINT("info", ("Neither STARTS nor ENDS are set"));
1063 1064 1065 1066
    /*
      Both starts and m_ends are not set, so we schedule for the next
      based on last_executed.
    */
1067 1068
    if (last_executed.year)
    {
1069 1070 1071
      TIME next_exec;
      if (get_next_time(&next_exec, &starts, &time_now, &last_executed,
                        expression, interval))
1072
        goto err;
1073 1074
      execute_at= next_exec;
      DBUG_PRINT("info",("Next[%llu]",TIME_to_ulonglong_datetime(&next_exec)));
1075 1076
    }
    else
1077 1078
    {
      /* last_executed not set. Schedule the event for now */
1079
      DBUG_PRINT("info", ("Execute NOW"));
1080
      execute_at= time_now;
1081
    }
1082
    execute_at_null= FALSE;
andrey@lmy004's avatar
andrey@lmy004 committed
1083 1084 1085
  }
  else
  {
andrey@lmy004's avatar
andrey@lmy004 committed
1086
    /* either starts or m_ends is set */
1087
    if (!starts_null)
andrey@lmy004's avatar
andrey@lmy004 committed
1088
    {
1089
      DBUG_PRINT("info", ("STARTS is set"));
andrey@lmy004's avatar
andrey@lmy004 committed
1090
      /*
1091 1092 1093 1094
        - 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
andrey@lmy004's avatar
andrey@lmy004 committed
1095
      */
1096 1097 1098 1099 1100
      if (!last_executed.year)
      {
        DBUG_PRINT("info", ("Not executed so far."));
      }

1101
      {
1102
        TIME next_exec;
1103 1104
        if (get_next_time(&next_exec, &starts, &time_now, 
                          last_executed.year? &last_executed:&starts,
1105
                          expression, interval))
1106
          goto err;
1107 1108
        execute_at= next_exec;
        DBUG_PRINT("info",("Next[%llu]",TIME_to_ulonglong_datetime(&next_exec)));
1109
      }
1110
      execute_at_null= FALSE;
andrey@lmy004's avatar
andrey@lmy004 committed
1111 1112 1113
    }
    else
    {
1114
      /* this is a dead branch, because starts is always set !!! */
1115
      DBUG_PRINT("info", ("STARTS is not set. ENDS is set"));
andrey@lmy004's avatar
andrey@lmy004 committed
1116 1117 1118
      /*
        - m_ends is set
        - m_ends is after time_now or is equal
1119 1120
        Hence check for m_last_execute and increment with m_expression.
        If last_executed is not set then schedule for now
andrey@lmy004's avatar
andrey@lmy004 committed
1121 1122
      */

1123 1124
      if (!last_executed.year)
        execute_at= time_now;
andrey@lmy004's avatar
andrey@lmy004 committed
1125 1126
      else
      {
1127 1128
        TIME next_exec;

1129 1130
        if (get_next_time(&next_exec, &starts, &time_now, &last_executed,
                          expression, interval))
1131 1132 1133
          goto err;

        if (my_time_compare(&ends, &next_exec) == -1)
andrey@lmy004's avatar
andrey@lmy004 committed
1134
        {
1135
          DBUG_PRINT("info", ("Next execution after ENDS. Stop executing."));
1136
          set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
1137
          execute_at_null= TRUE;
1138 1139 1140
          status= Event_timed::DISABLED;
          status_changed= true;
          if (on_completion == Event_timed::ON_COMPLETION_DROP)
1141
            dropped= true;
andrey@lmy004's avatar
andrey@lmy004 committed
1142 1143
        }
        else
1144
        {
1145 1146
          DBUG_PRINT("info", ("Next[%llu]",
                              TIME_to_ulonglong_datetime(&next_exec)));
1147
          execute_at= next_exec;
1148 1149
          execute_at_null= FALSE;
        }
andrey@lmy004's avatar
andrey@lmy004 committed
1150 1151 1152 1153 1154
      }
    }
    goto ret;
  }
ret:
1155
  DBUG_PRINT("info", ("ret=0"));
andrey@lmy004's avatar
andrey@lmy004 committed
1156
  DBUG_RETURN(false);
1157
err:
1158
  DBUG_PRINT("info", ("ret=1"));
1159
  DBUG_RETURN(true);
andrey@lmy004's avatar
andrey@lmy004 committed
1160 1161 1162
}


1163 1164 1165
/*
  Set the internal last_executed TIME struct to now. NOW is the
  time according to thd->query_start(), so the THD's clock.
1166 1167

  SYNOPSIS
1168
    Event_timed::drop()
1169
      thd   thread context
1170 1171
*/

andrey@lmy004's avatar
andrey@lmy004 committed
1172
void
1173
Event_timed::mark_last_executed(THD *thd)
andrey@lmy004's avatar
andrey@lmy004 committed
1174 1175 1176
{
  TIME time_now;

1177 1178
  thd->end_time();
  my_tz_UTC->gmt_sec_to_TIME(&time_now, (my_time_t) thd->query_start());
andrey@lmy004's avatar
andrey@lmy004 committed
1179

andrey@lmy004's avatar
andrey@lmy004 committed
1180
  last_executed= time_now; /* was execute_at */
1181
  last_executed_changed= true;
andrey@lmy004's avatar
andrey@lmy004 committed
1182 1183 1184
}


andrey@lmy004's avatar
andrey@lmy004 committed
1185
/*
1186
  Drops the event
1187 1188

  SYNOPSIS
1189
    Event_timed::drop()
1190
      thd   thread context
1191

1192 1193 1194 1195 1196 1197
  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
andrey@lmy004's avatar
andrey@lmy004 committed
1198 1199 1200 1201
            failed.
*/

int
1202
Event_timed::drop(THD *thd)
andrey@lmy004's avatar
andrey@lmy004 committed
1203
{
1204
  uint tmp= 0;
1205
  DBUG_ENTER("Event_timed::drop");
andrey@lmy004's avatar
andrey@lmy004 committed
1206

1207
  DBUG_RETURN(db_drop_event(thd, this, false, &tmp));
andrey@lmy004's avatar
andrey@lmy004 committed
1208 1209 1210
}


1211 1212
/*
  Saves status and last_executed_at to the disk if changed.
1213 1214

  SYNOPSIS
1215
    Event_timed::update_fields()
1216 1217
      thd - thread context

1218 1219
  RETURN VALUE
    0   OK
1220
    EVEX_OPEN_TABLE_FAILED    Error while opening mysql.event for writing
1221 1222 1223 1224
    EVEX_WRITE_ROW_FAILED   On error to write to disk

   others                   return code from SE in case deletion of the event
                            row failed.
1225 1226
*/

andrey@lmy004's avatar
andrey@lmy004 committed
1227
bool
1228
Event_timed::update_fields(THD *thd)
andrey@lmy004's avatar
andrey@lmy004 committed
1229 1230
{
  TABLE *table;
andrey@lmy004's avatar
andrey@lmy004 committed
1231
  Open_tables_state backup;
1232
  int ret;
andrey@lmy004's avatar
andrey@lmy004 committed
1233

1234
  DBUG_ENTER("Event_timed::update_time_fields");
andrey@lmy004's avatar
andrey@lmy004 committed
1235

1236
  DBUG_PRINT("enter", ("name: %*s", name.length, name.str));
1237 1238

  /* No need to update if nothing has changed */
1239
  if (!(status_changed || last_executed_changed))
1240
    DBUG_RETURN(0);
1241

andrey@lmy004's avatar
andrey@lmy004 committed
1242 1243
  thd->reset_n_backup_open_tables_state(&backup);

1244
  if (Events::open_event_table(thd, TL_WRITE, &table))
andrey@lmy004's avatar
andrey@lmy004 committed
1245
  {
1246
    ret= EVEX_OPEN_TABLE_FAILED;
andrey@lmy004's avatar
andrey@lmy004 committed
1247 1248 1249
    goto done;
  }

andrey@lmy004's avatar
andrey@lmy004 committed
1250

1251
  if ((ret= evex_db_find_event_by_name(thd, dbname, name, table)))
andrey@lmy004's avatar
andrey@lmy004 committed
1252 1253 1254
    goto done;

  store_record(table,record[1]);
1255 1256
  /* Don't update create on row update. */
  table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
andrey@lmy004's avatar
andrey@lmy004 committed
1257

1258
  if (last_executed_changed)
andrey@lmy004's avatar
andrey@lmy004 committed
1259
  {
1260 1261 1262
    table->field[Events::FIELD_LAST_EXECUTED]->set_notnull();
    table->field[Events::FIELD_LAST_EXECUTED]->store_time(&last_executed,
                                               MYSQL_TIMESTAMP_DATETIME);
1263
    last_executed_changed= false;
andrey@lmy004's avatar
andrey@lmy004 committed
1264
  }
1265
  if (status_changed)
andrey@lmy004's avatar
andrey@lmy004 committed
1266
  {
1267 1268
    table->field[Events::FIELD_STATUS]->set_notnull();
    table->field[Events::FIELD_STATUS]->store((longlong)status, true);
1269
    status_changed= false;
andrey@lmy004's avatar
andrey@lmy004 committed
1270
  }
1271

1272
  if ((table->file->ha_update_row(table->record[1],table->record[0])))
andrey@lmy004's avatar
andrey@lmy004 committed
1273 1274 1275 1276
    ret= EVEX_WRITE_ROW_FAILED;

done:
  close_thread_tables(thd);
andrey@lmy004's avatar
andrey@lmy004 committed
1277
  thd->restore_backup_open_tables_state(&backup);
andrey@lmy004's avatar
andrey@lmy004 committed
1278 1279 1280 1281

  DBUG_RETURN(ret);
}

1282
extern LEX_STRING interval_type_to_name[];
andrey@lmy004's avatar
andrey@lmy004 committed
1283

1284 1285
/*
  Get SHOW CREATE EVENT as string
1286 1287

  SYNOPSIS
1288
    Event_timed::get_create_event(THD *thd, String *buf)
1289 1290 1291 1292
      thd    Thread
      buf    String*, should be already allocated. CREATE EVENT goes inside.

  RETURN VALUE
1293 1294 1295 1296
    0                       OK
    EVEX_MICROSECOND_UNSUP  Error (for now if mysql.event has been
                            tampered and MICROSECONDS interval or
                            derivative has been put there.
1297 1298 1299
*/

int
1300
Event_timed::get_create_event(THD *thd, String *buf)
andrey@lmy004's avatar
andrey@lmy004 committed
1301
{
1302 1303 1304 1305 1306 1307
  int multipl= 0;
  char tmp_buff[128];
  String expr_buf(tmp_buff, sizeof(tmp_buff), system_charset_info);
  expr_buf.length(0);

  DBUG_ENTER("get_create_event");
1308
  DBUG_PRINT("ret_info",("body_len=[%d]body=[%s]", body.length, body.str));
andrey@lmy004's avatar
andrey@lmy004 committed
1309

1310 1311
  if (expression && Events::reconstruct_interval_expression(&expr_buf, interval,
                                                            expression))
1312
    DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
1313 1314 1315 1316 1317 1318 1319 1320 1321

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

  buf->append(STRING_WITH_LEN(" ON SCHEDULE "));
  if (expression)
  {
    buf->append(STRING_WITH_LEN("EVERY "));
    buf->append(expr_buf);
1322 1323 1324
    buf->append(' ');
    LEX_STRING *ival= &interval_type_to_name[interval];
    buf->append(ival->str, ival->length);
1325 1326 1327
  }
  else
  {
andrey@lmy004's avatar
andrey@lmy004 committed
1328
    char dtime_buff[20*2+32];/* +32 to make my_snprintf_{8bit|ucs2} happy */
1329
    buf->append(STRING_WITH_LEN("AT '"));
1330 1331 1332
    /*
      Pass the buffer and the second param tells fills the buffer and
      returns the number of chars to copy.
1333 1334 1335 1336 1337
    */
    buf->append(dtime_buff, my_datetime_to_str(&execute_at, dtime_buff));
    buf->append(STRING_WITH_LEN("'"));
  }

1338
  if (on_completion == Event_timed::ON_COMPLETION_DROP)
1339 1340 1341 1342
    buf->append(STRING_WITH_LEN(" ON COMPLETION NOT PRESERVE "));
  else
    buf->append(STRING_WITH_LEN(" ON COMPLETION PRESERVE "));

1343
  if (status == Event_timed::ENABLED)
1344 1345 1346 1347 1348 1349 1350 1351 1352 1353
    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 "));
1354
  buf->append(body.str, body.length);
1355 1356

  DBUG_RETURN(0);
andrey@lmy004's avatar
andrey@lmy004 committed
1357 1358 1359 1360
}


/*
1361 1362 1363 1364 1365 1366 1367
  Executes the event (the underlying sp_head object);

  SYNOPSIS
    evex_fill_row()
      thd       THD
      mem_root  If != NULL use it to compile the event on it

1368
  RETURN VALUE
1369
    0        success
1370
    -99      No rights on this.dbname.str
1371 1372
    -100     event in execution (parallel execution is impossible)
    others   retcodes of sp_head::execute_procedure()
andrey@lmy004's avatar
andrey@lmy004 committed
1373 1374 1375
*/

int
1376
Event_timed::execute(THD *thd, MEM_ROOT *mem_root)
andrey@lmy004's avatar
andrey@lmy004 committed
1377
{
1378 1379
  /* this one is local and not needed after exec */
  Security_context security_ctx;
andrey@lmy004's avatar
andrey@lmy004 committed
1380
  int ret= 0;
1381

1382
  DBUG_ENTER("Event_timed::execute");
1383 1384
  DBUG_PRINT("info", ("    EVEX EXECUTING event %s.%s [EXPR:%d]",
               dbname.str, name.str, (int) expression));
andrey@lmy004's avatar
andrey@lmy004 committed
1385

andrey@lmy004's avatar
andrey@lmy004 committed
1386
  VOID(pthread_mutex_lock(&this->LOCK_running));
1387
  if (running)
andrey@lmy004's avatar
andrey@lmy004 committed
1388
  {
andrey@lmy004's avatar
andrey@lmy004 committed
1389
    VOID(pthread_mutex_unlock(&this->LOCK_running));
andrey@lmy004's avatar
andrey@lmy004 committed
1390 1391 1392
    DBUG_RETURN(-100);
  }
  running= true;
andrey@lmy004's avatar
andrey@lmy004 committed
1393
  VOID(pthread_mutex_unlock(&this->LOCK_running));
andrey@lmy004's avatar
andrey@lmy004 committed
1394

1395 1396
  if (!sphead && (ret= compile(thd, mem_root)))
    goto done;
andrey@lmy004's avatar
andrey@lmy004 committed
1397 1398 1399 1400 1401 1402
  /*
    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.
  */
  thd->db= my_strdup(dbname.str, MYF(0));
1403 1404
  thd->db_length= dbname.length;
  if (!check_access(thd, EVENT_ACL,dbname.str, 0, 0, 0,is_schema_db(dbname.str)))
1405 1406 1407
  {
    List<Item> empty_item_list;
    empty_item_list.empty();
andrey@lmy004's avatar
andrey@lmy004 committed
1408 1409
    if (thd->enable_slow_log)
      sphead->m_flags|= sp_head::LOG_SLOW_STATEMENTS;
1410 1411
    sphead->m_flags|= sp_head::LOG_GENERAL_LOG;

1412 1413 1414 1415 1416 1417 1418 1419
    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;
  }
andrey@lmy004's avatar
andrey@lmy004 committed
1420

andrey@lmy004's avatar
andrey@lmy004 committed
1421
  VOID(pthread_mutex_lock(&this->LOCK_running));
andrey@lmy004's avatar
andrey@lmy004 committed
1422
  running= false;
1423 1424
  /* Will compile every time a new sp_head on different root */
  free_sp();
andrey@lmy004's avatar
andrey@lmy004 committed
1425
  VOID(pthread_mutex_unlock(&this->LOCK_running));
andrey@lmy004's avatar
andrey@lmy004 committed
1426 1427

done:
1428 1429 1430 1431 1432
  /*
    1. Don't cache sphead if allocated on another mem_root
    2. Don't call security_ctx.destroy() because this will free our dbname.str
       name.str and definer.str
  */
1433
  if (mem_root && sphead)
andrey@lmy004's avatar
andrey@lmy004 committed
1434
  {
1435 1436
    delete sphead;
    sphead= 0;
andrey@lmy004's avatar
andrey@lmy004 committed
1437
  }
1438 1439
  DBUG_PRINT("info", ("    EVEX EXECUTED event %s.%s  [EXPR:%d]. RetCode=%d",
                      dbname.str, name.str, (int) expression, ret));
andrey@lmy004's avatar
andrey@lmy004 committed
1440 1441 1442 1443 1444

  DBUG_RETURN(ret);
}


1445
/*
1446 1447 1448
  Frees the memory of the sp_head object we hold
  SYNOPSIS
    Event_timed::free_sp()
andrey@lmy004's avatar
andrey@lmy004 committed
1449
*/
1450 1451

void
1452
Event_timed::free_sp()
1453
{
1454 1455
  delete sphead;
  sphead= 0;
1456 1457 1458
}


1459
/*
1460 1461
  Compiles an event before it's execution. Compiles the anonymous
  sp_head object held by the event
1462 1463

  SYNOPSIS
1464
    Event_timed::compile()
1465 1466 1467
      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
1468

1469
  RETURN VALUE
1470 1471 1472
    0                       success
    EVEX_COMPILE_ERROR      error during compilation
    EVEX_MICROSECOND_UNSUP  mysql.event was tampered 
1473 1474
*/

andrey@lmy004's avatar
andrey@lmy004 committed
1475
int
1476
Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
andrey@lmy004's avatar
andrey@lmy004 committed
1477
{
andrey@lmy004's avatar
andrey@lmy004 committed
1478
  int ret= 0;
andrey@lmy004's avatar
andrey@lmy004 committed
1479 1480 1481
  MEM_ROOT *tmp_mem_root= 0;
  LEX *old_lex= thd->lex, lex;
  char *old_db;
1482
  int old_db_length;
andrey@lmy004's avatar
andrey@lmy004 committed
1483 1484
  char *old_query;
  uint old_query_len;
1485
  ulong old_sql_mode= thd->variables.sql_mode;
1486 1487 1488 1489
  char create_buf[2048];
  String show_create(create_buf, sizeof(create_buf), system_charset_info);
  CHARSET_INFO *old_character_set_client,
               *old_collation_connection,
andrey@lmy004's avatar
andrey@lmy004 committed
1490
               *old_character_set_results;
1491 1492 1493
  Security_context *save_ctx;
  /* this one is local and not needed after exec */
  Security_context security_ctx;
1494

1495
  DBUG_ENTER("Event_timed::compile");
1496

1497
  show_create.length(0);
1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508

  switch (get_create_event(thd, &show_create)) {
  case EVEX_MICROSECOND_UNSUP:
    sql_print_error("Scheduler");
    DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
  case 0:
    break;
  default:
    DBUG_ASSERT(0);
  }

andrey@lmy004's avatar
andrey@lmy004 committed
1509 1510 1511
  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;
1512 1513

  thd->variables.character_set_client=
andrey@lmy004's avatar
andrey@lmy004 committed
1514 1515 1516 1517 1518
    thd->variables.character_set_results=
      thd->variables.collation_connection=
           get_charset_by_csname("utf8", MY_CS_PRIMARY, MYF(MY_WME));

  thd->update_charset();
1519

1520 1521
  DBUG_PRINT("info",("old_sql_mode=%d new_sql_mode=%d",old_sql_mode, sql_mode));
  thd->variables.sql_mode= this->sql_mode;
1522
  /* Change the memory root for the execution time */
andrey@lmy004's avatar
andrey@lmy004 committed
1523 1524 1525 1526 1527 1528 1529 1530
  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;
1531
  old_db_length= thd->db_length;
1532
  thd->db= dbname.str;
1533
  thd->db_length= dbname.length;
1534 1535 1536

  thd->query= show_create.c_ptr();
  thd->query_length= show_create.length();
1537
  DBUG_PRINT("info", ("query:%s",thd->query));
andrey@lmy004's avatar
andrey@lmy004 committed
1538

1539 1540
  change_security_context(thd, definer_user, definer_host, dbname,
                          &security_ctx, &save_ctx);
andrey@lmy004's avatar
andrey@lmy004 committed
1541 1542 1543
  thd->lex= &lex;
  lex_start(thd, (uchar*)thd->query, thd->query_length);
  lex.et_compile_phase= TRUE;
1544
  if (MYSQLparse((void *)thd) || thd->is_fatal_error)
andrey@lmy004's avatar
andrey@lmy004 committed
1545
  {
1546 1547
    DBUG_PRINT("error", ("error during compile or thd->is_fatal_error=%d",
                          thd->is_fatal_error));
1548 1549 1550 1551
    /*
      Free lex associated resources
      QQ: Do we really need all this stuff here?
    */
1552 1553
    sql_print_error("error during compile of %s.%s or thd->is_fatal_error=%d",
                    dbname.str, name.str, thd->is_fatal_error);
andrey@lmy004's avatar
andrey@lmy004 committed
1554 1555 1556 1557 1558 1559 1560
    if (lex.sphead)
    {
      if (&lex != thd->lex)
        thd->lex->sphead->restore_lex(thd);
      delete lex.sphead;
      lex.sphead= 0;
    }
andrey@lmy004's avatar
andrey@lmy004 committed
1561 1562
    ret= EVEX_COMPILE_ERROR;
    goto done;
andrey@lmy004's avatar
andrey@lmy004 committed
1563
  }
1564
  DBUG_PRINT("note", ("success compiling %s.%s", dbname.str, name.str));
1565

andrey@lmy004's avatar
andrey@lmy004 committed
1566
  sphead= lex.et->sphead;
1567
  sphead->m_db= dbname;
andrey@lmy004's avatar
andrey@lmy004 committed
1568

1569
  sphead->set_definer(definer.str, definer.length);
1570
  sphead->set_info(0, 0, &lex.sp_chistics, sql_mode);
1571
  sphead->optimize();
andrey@lmy004's avatar
andrey@lmy004 committed
1572 1573
  ret= 0;
done:
andrey@lmy004's avatar
andrey@lmy004 committed
1574
  lex.et->free_sphead_on_delete= false;
1575 1576
  lex.et->deinit_mutexes();

andrey@lmy004's avatar
andrey@lmy004 committed
1577
  lex_end(&lex);
1578
  restore_security_context(thd, save_ctx);
1579 1580
  DBUG_PRINT("note", ("return old data on its place. set back NAMES"));

andrey@lmy004's avatar
andrey@lmy004 committed
1581 1582 1583 1584
  thd->lex= old_lex;
  thd->query= old_query;
  thd->query_length= old_query_len;
  thd->db= old_db;
andrey@lmy004's avatar
andrey@lmy004 committed
1585

1586
  thd->variables.sql_mode= old_sql_mode;
andrey@lmy004's avatar
andrey@lmy004 committed
1587 1588 1589 1590 1591
  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();

1592
  /* Change the memory root for the execution time. */
andrey@lmy004's avatar
andrey@lmy004 committed
1593 1594 1595
  if (mem_root)
    thd->mem_root= tmp_mem_root;

andrey@lmy004's avatar
andrey@lmy004 committed
1596
  DBUG_RETURN(ret);
andrey@lmy004's avatar
andrey@lmy004 committed
1597 1598
}

1599 1600 1601 1602 1603

extern pthread_attr_t connection_attrib;

/*
  Checks whether is possible and forks a thread. Passes self as argument.
andrey@lmy004's avatar
andrey@lmy004 committed
1604

1605 1606 1607 1608
  RETURN VALUE
    EVENT_EXEC_STARTED       OK
    EVENT_EXEC_ALREADY_EXEC  Thread not forked, already working
    EVENT_EXEC_CANT_FORK     Unable to spawn thread (error)
1609 1610 1611
*/

int
1612
Event_timed::spawn_now(void * (*thread_func)(void*), void *arg)
1613
{  
1614
  THD *thd= current_thd;
1615
  int ret= EVENT_EXEC_STARTED;
1616
  DBUG_ENTER("Event_timed::spawn_now");
1617 1618 1619
  DBUG_PRINT("info", ("[%s.%s]", dbname.str, name.str));

  VOID(pthread_mutex_lock(&this->LOCK_running));
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

  DBUG_PRINT("info", ("SCHEDULER: execute_at of %s is %lld", name.str,
             TIME_to_ulonglong_datetime(&execute_at)));
  mark_last_executed(thd);
  if (compute_next_execution_time())
  {
    sql_print_error("SCHEDULER: Error while computing time of %s.%s . "
                    "Disabling after execution.", dbname.str, name.str);
    status= DISABLED;
  }
  DBUG_PRINT("evex manager", ("[%10s] next exec at [%llu]", name.str,
             TIME_to_ulonglong_datetime(&execute_at)));
   /*
    1. For one-time event : year is > 0 and expression is 0
    2. For recurring, expression is != -=> check execute_at_null in this case
  */
  if ((execute_at.year && !expression) || execute_at_null)
  {
    sql_print_information("SCHEDULER: [%s.%s of %s] no more executions "
                          "after this one", dbname.str, name.str,
                          definer.str);
    flags |= EVENT_EXEC_NO_MORE | EVENT_FREE_WHEN_FINISHED;
  }

  update_fields(thd);

1646 1647 1648 1649
  if (!in_spawned_thread)
  {
    pthread_t th;
    in_spawned_thread= true;
1650 1651

    if (pthread_create(&th, &connection_attrib, thread_func, arg))
1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668
    {
      DBUG_PRINT("info", ("problem while spawning thread"));
      ret= EVENT_EXEC_CANT_FORK;
      in_spawned_thread= false;
    }
  }
  else
  {
    DBUG_PRINT("info", ("already in spawned thread. skipping"));
    ret= EVENT_EXEC_ALREADY_EXEC;
  }
  VOID(pthread_mutex_unlock(&this->LOCK_running));

  DBUG_RETURN(ret);  
}


1669
bool
1670
Event_timed::spawn_thread_finish(THD *thd)
1671
{
1672
  bool should_free;
1673
  DBUG_ENTER("Event_timed::spawn_thread_finish");
1674
  VOID(pthread_mutex_lock(&LOCK_running));
1675
  in_spawned_thread= false;
1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706
  DBUG_PRINT("info", ("Sending COND_finished for thread %d", thread_id));
  thread_id= 0;
  if (dropped)
    drop(thd);
  pthread_cond_broadcast(&COND_finished);
  should_free= flags & EVENT_FREE_WHEN_FINISHED;
  VOID(pthread_mutex_unlock(&LOCK_running));
  DBUG_RETURN(should_free);
}


/*
  Kills a running event
  SYNOPSIS
    Event_timed::kill_thread()
    
  RETURN VALUE 
     0    OK
    -1    EVEX_CANT_KILL
    !0   Error 
*/

int
Event_timed::kill_thread(THD *thd)
{
  int ret= 0;
  DBUG_ENTER("Event_timed::kill_thread");
  pthread_mutex_lock(&LOCK_running);
  DBUG_PRINT("info", ("thread_id=%lu", thread_id));

  if (thread_id == thd->thread_id)
1707
  {
1708 1709 1710 1711 1712 1713 1714
    /*
      We don't kill ourselves in cases like :
      alter event e_43 do alter event e_43 do set @a = 4 because
      we will never receive COND_finished.
    */
    DBUG_PRINT("info", ("It's not safe to kill ourselves in self altering queries"));
    ret= EVEX_CANT_KILL;
1715
  }
1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757
  else if (thread_id && !(ret= kill_one_thread(thd, thread_id, false)))
  {
    thd->enter_cond(&COND_finished, &LOCK_running, "Waiting for finished");
    DBUG_PRINT("info", ("Waiting for COND_finished from thread %d", thread_id));
    while (thread_id)
      pthread_cond_wait(&COND_finished, &LOCK_running);

    DBUG_PRINT("info", ("Got COND_finished"));
    /* This will implicitly unlock LOCK_running. Hence we return before that */
    thd->exit_cond("");

    DBUG_RETURN(0);
  }
  else if (!thread_id && in_spawned_thread)
  {
    /*
      Because the manager thread waits for the forked thread to update thread_id
      this situation is impossible.
    */
    DBUG_ASSERT(0);
  }
  pthread_mutex_unlock(&LOCK_running);
  DBUG_PRINT("exit", ("%d", ret));
  DBUG_RETURN(ret);
}


/*
  Checks whether two events have the same name

  SYNOPSIS
    event_timed_name_equal()

  RETURN VALUE
    TRUE  names are equal
    FALSE names are not equal
*/

bool
event_timed_name_equal(Event_timed *et, LEX_STRING *name)
{
  return !sortcmp_lex_string(et->name, *name, system_charset_info);
1758 1759 1760 1761
}


/*
1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783
  Checks whether two events are in the same schema

  SYNOPSIS
    event_timed_db_equal()

  RETURN VALUE
    TRUE  schemas are equal
    FALSE schemas are not equal
*/

bool
event_timed_db_equal(Event_timed *et, LEX_STRING *db)
{
  return !sortcmp_lex_string(et->dbname, *db, system_charset_info);
}


/*
  Checks whether two events have the same definer

  SYNOPSIS
    event_timed_definer_equal()
1784 1785

  Returns
1786 1787
    TRUE  definers are equal
    FALSE definers are not equal
1788 1789
*/

1790 1791
bool
event_timed_definer_equal(Event_timed *et, LEX_STRING *definer)
1792
{
1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843
  return !sortcmp_lex_string(et->definer, *definer, system_charset_info);
}


/*
  Checks whether two events are equal by identifiers

  SYNOPSIS
    event_timed_identifier_equal()

  RETURN VALUE
    TRUE   equal
    FALSE  not equal
*/

bool
event_timed_identifier_equal(Event_timed *a, Event_timed *b)
{
  return event_timed_name_equal(a, &b->name) &&
         event_timed_db_equal(a, &b->dbname) &&
         event_timed_definer_equal(a, &b->definer);
}


/*
  Switches the security context
  SYNOPSIS
    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
      s_ctx   Security context to load state into
      backup  Where to store the old context
  
  RETURN VALUE
    0  - OK
    1  - Error (generates error too)
*/

bool
change_security_context(THD *thd, LEX_STRING user, LEX_STRING host,
                        LEX_STRING db, Security_context *s_ctx,
                        Security_context **backup)
{
  DBUG_ENTER("change_security_context");
  DBUG_PRINT("info",("%s@%s@%s", user.str, host.str, db.str));
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  s_ctx->init();
  *backup= 0;
  if (acl_getroot_no_password(s_ctx, user.str, host.str, host.str, db.str))
1844
  {
1845 1846
    my_error(ER_NO_SUCH_USER, MYF(0), user.str, host.str);
    DBUG_RETURN(TRUE);
1847
  }
1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871
  *backup= thd->security_ctx;
  thd->security_ctx= s_ctx;
#endif
  DBUG_RETURN(FALSE);
}


/*
  Restores the security context
  SYNOPSIS
    restore_security_context()
      thd    - thread
      backup - switch to this context
*/

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