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

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

#include "event_priv.h"
unknown's avatar
unknown committed
18
#include "event.h"
unknown's avatar
unknown committed
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
#include "sp.h"



extern int yyparse(void *thd);

/*
 Init all member variables

 SYNOPSIS
   event_timed::init()
*/

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

unknown's avatar
unknown committed
37 38
  dbname.str= name.str= body.str= comment.str= 0;
  dbname.length= name.length= body.length= comment.length= 0;
unknown's avatar
unknown committed
39
  
unknown's avatar
unknown committed
40 41 42 43
  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);
unknown's avatar
unknown committed
44

unknown's avatar
unknown committed
45 46
  definer_user.str= definer_host.str= 0;
  definer_user.length= definer_host.length= 0;
unknown's avatar
unknown committed
47 48 49 50 51 52 53 54 55 56 57
    
  DBUG_VOID_RETURN;
}


/*
 Set a name of the event

 SYNOPSIS
   event_timed::init_name()
     thd   THD
unknown's avatar
unknown committed
58
     spn  the name extracted in the parser
unknown's avatar
unknown committed
59 60 61
*/

void
unknown's avatar
unknown committed
62
event_timed::init_name(THD *thd, sp_name *spn)
unknown's avatar
unknown committed
63 64 65 66 67 68 69
{
  DBUG_ENTER("event_timed::init_name");
  uint n;			/* Counter for nul trimming */ 
  /* 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 */
unknown's avatar
unknown committed
70
  if (spn)
unknown's avatar
unknown committed
71
  {
unknown's avatar
unknown committed
72 73 74
    dbname.length= spn->m_db.length;
    if (spn->m_db.length == 0)
      dbname.str= NULL;
unknown's avatar
unknown committed
75
    else
unknown's avatar
unknown committed
76 77 78
      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);
unknown's avatar
unknown committed
79

unknown's avatar
unknown committed
80 81
    if (spn->m_qname.length == 0)
      spn->init_qname(thd);
unknown's avatar
unknown committed
82 83 84
  }
  else if (thd->db)
  {
unknown's avatar
unknown committed
85 86
    dbname.length= thd->db_length;
    dbname.str= strmake_root(root, thd->db, dbname.length);
unknown's avatar
unknown committed
87 88
  }
  
unknown's avatar
unknown committed
89 90
  DBUG_PRINT("dbname", ("len=%d db=%s",dbname.length, dbname.str));  
  DBUG_PRINT("name", ("len=%d name=%s",name.length, name.str));  
unknown's avatar
unknown committed
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113

  DBUG_VOID_RETURN;
}


/*
 Set body of the event - what should be executed.

 SYNOPSIS
   event_timed::init_body()
     thd   THD

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

void
event_timed::init_body(THD *thd)
{
  DBUG_ENTER("event_timed::init_body");
  MEM_ROOT *root= thd->mem_root;

unknown's avatar
unknown committed
114
  body.length= thd->lex->ptr - body_begin;
unknown's avatar
unknown committed
115
  // Trim nuls at the end 
unknown's avatar
unknown committed
116 117
  while (body.length && body_begin[body.length-1] == '\0')
    body.length--;
unknown's avatar
unknown committed
118

unknown's avatar
unknown committed
119
  body.str= strmake_root(root, (char *)body_begin, body.length);
unknown's avatar
unknown committed
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170

  DBUG_VOID_RETURN;
}


/*
 Set time for execution for one time events.

 SYNOPSIS
   event_timed::init_execute_at()
     expr   when (datetime)

 RETURNS
   0 - OK
   EVEX_PARSE_ERROR - fix_fields failed
   EVEX_BAD_PARAMS  - datetime is in the past
*/

int
event_timed::init_execute_at(THD *thd, Item *expr)
{
  my_bool not_used;
  TIME ltime;
  my_time_t my_time_tmp;

  TIME time_tmp;
  DBUG_ENTER("event_timed::init_execute_at");

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

  if (expr->val_int() == MYSQL_TIMESTAMP_ERROR)
    DBUG_RETURN(EVEX_BAD_PARAMS);

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

  if (expr->val_int() < TIME_to_ulonglong_datetime(&time_tmp))
    DBUG_RETURN(EVEX_BAD_PARAMS);

  if ((not_used= expr->get_date(&ltime, TIME_NO_ZERO_DATE)))
    DBUG_RETURN(EVEX_BAD_PARAMS);

  /*
      This may result in a 1970-01-01 date if ltime is > 2037-xx-xx
      CONVERT_TZ has similar problem
  */
  my_tz_UTC->gmt_sec_to_TIME(&ltime, TIME_to_timestamp(thd,&ltime, &not_used));


unknown's avatar
unknown committed
171
  execute_at= ltime;
unknown's avatar
unknown committed
172 173 174 175 176 177 178 179 180 181
  DBUG_RETURN(0);
}


/*
 Set time for execution for transient events.

 SYNOPSIS
   event_timed::init_interval()
     expr      how much?
unknown's avatar
unknown committed
182
     new_interval  what is the interval
unknown's avatar
unknown committed
183 184 185 186 187 188 189 190

 RETURNS
   0 - OK
   EVEX_PARSE_ERROR - fix_fields failed
   EVEX_BAD_PARAMS  - Interval is not positive
*/

int
unknown's avatar
unknown committed
191
event_timed::init_interval(THD *thd, Item *expr, interval_type new_interval)
unknown's avatar
unknown committed
192 193 194 195 196 197 198 199 200 201
{
  longlong tmp;
  DBUG_ENTER("event_timed::init_interval");

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

  if ((tmp= expr->val_int()) <= 0)
    DBUG_RETURN(EVEX_BAD_PARAMS);

unknown's avatar
unknown committed
202 203
  expression= tmp;
  interval= new_interval;
unknown's avatar
unknown committed
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
  DBUG_RETURN(0);
}


/*
 Set activation time.

 SYNOPSIS
   event_timed::init_starts()
     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.

 RETURNS
   0 - OK
   EVEX_PARSE_ERROR - fix_fields failed
*/

int
unknown's avatar
unknown committed
230
event_timed::init_starts(THD *thd, Item *new_starts)
unknown's avatar
unknown committed
231 232 233 234 235 236 237
{
  my_bool not_used;
  TIME ltime;
  my_time_t my_time_tmp;

  DBUG_ENTER("event_timed::init_starts");

unknown's avatar
unknown committed
238
  if (new_starts->fix_fields(thd, &new_starts))
unknown's avatar
unknown committed
239 240
    DBUG_RETURN(EVEX_PARSE_ERROR);

unknown's avatar
unknown committed
241
  if (new_starts->val_int() == MYSQL_TIMESTAMP_ERROR)
unknown's avatar
unknown committed
242 243
    DBUG_RETURN(EVEX_BAD_PARAMS);

unknown's avatar
unknown committed
244
  if ((not_used= new_starts->get_date(&ltime, TIME_NO_ZERO_DATE)))
unknown's avatar
unknown committed
245 246 247 248 249 250
    DBUG_RETURN(EVEX_BAD_PARAMS);

  /*
      This may result in a 1970-01-01 date if ltime is > 2037-xx-xx
      CONVERT_TZ has similar problem
  */
unknown's avatar
unknown committed
251
  my_tz_UTC->gmt_sec_to_TIME(&ltime, TIME_to_timestamp(thd, &ltime, &not_used));
unknown's avatar
unknown committed
252

unknown's avatar
unknown committed
253
  starts= ltime;
unknown's avatar
unknown committed
254 255 256 257 258 259 260 261 262 263
  DBUG_RETURN(0);
}


/*
 Set deactivation time.

 SYNOPSIS
   event_timed::init_ends()
     thd      THD
unknown's avatar
unknown committed
264
     new_ends  when?
unknown's avatar
unknown committed
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280

 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.

 RETURNS
   0 - OK
   EVEX_PARSE_ERROR - fix_fields failed
   EVEX_BAD_PARAMS  - ENDS before STARTS
*/

int 
unknown's avatar
unknown committed
281
event_timed::init_ends(THD *thd, Item *new_ends)
unknown's avatar
unknown committed
282 283 284 285 286 287 288
{
  TIME ltime;
  my_time_t my_time_tmp;
  my_bool not_used;

  DBUG_ENTER("event_timed::init_ends");

unknown's avatar
unknown committed
289
  if (new_ends->fix_fields(thd, &new_ends))
unknown's avatar
unknown committed
290 291
    DBUG_RETURN(EVEX_PARSE_ERROR);

unknown's avatar
unknown committed
292
  // the field was already fixed in init_ends
unknown's avatar
unknown committed
293
  if ((not_used= new_ends->get_date(&ltime, TIME_NO_ZERO_DATE)))
unknown's avatar
unknown committed
294 295 296 297 298 299 300 301
    DBUG_RETURN(EVEX_BAD_PARAMS);

  /*
    This may result in a 1970-01-01 date if ltime is > 2037-xx-xx
    CONVERT_TZ has similar problem
  */
  my_tz_UTC->gmt_sec_to_TIME(&ltime, TIME_to_timestamp(thd, &ltime, &not_used));
 
unknown's avatar
unknown committed
302
  if (starts.year && my_time_compare(&starts, &ltime) != -1)
unknown's avatar
unknown committed
303 304
    DBUG_RETURN(EVEX_BAD_PARAMS);

unknown's avatar
unknown committed
305
  ends= ltime;
unknown's avatar
unknown committed
306 307 308 309 310 311 312 313 314 315 316 317 318 319
  DBUG_RETURN(0);
}


/*
 Sets comment.

 SYNOPSIS
   event_timed::init_comment()
     thd      THD - used for memory allocation
     comment  the string.
*/

void
unknown's avatar
unknown committed
320
event_timed::init_comment(THD *thd, LEX_STRING *set_comment)
unknown's avatar
unknown committed
321 322 323
{
  DBUG_ENTER("event_timed::init_comment");

unknown's avatar
unknown committed
324 325
  comment.str= strmake_root(thd->mem_root, set_comment->str,
                              comment.length= set_comment->length);
unknown's avatar
unknown committed
326 327 328 329 330 331

  DBUG_VOID_RETURN;
}


/*
unknown's avatar
unknown committed
332
 Inits definer (definer_user and definer_host) during
unknown's avatar
unknown committed
333 334 335 336 337 338 339 340 341 342 343
 parsing.

 SYNOPSIS
   event_timed::init_definer()
*/

int
event_timed::init_definer(THD *thd)
{
  DBUG_ENTER("event_timed::init_definer");

unknown's avatar
unknown committed
344 345
  definer_user.str= strdup_root(thd->mem_root, thd->security_ctx->priv_user);
  definer_user.length= strlen(thd->security_ctx->priv_user);
unknown's avatar
unknown committed
346

unknown's avatar
unknown committed
347 348
  definer_host.str= strdup_root(thd->mem_root, thd->security_ctx->priv_host);
  definer_host.length= strlen(thd->security_ctx->priv_host);
unknown's avatar
unknown committed
349 350 351 352 353 354 355 356 357 358

  DBUG_RETURN(0);
}


/*
 Loads an event from a row from mysql.event
 
 SYNOPSIS
   event_timed::load_from_row()
359 360 361 362 363
   
 REMARKS
   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
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
*/

int
event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
{
  longlong created;
  longlong modified;
  char *ptr;
  event_timed *et;
  uint len;
  bool res1, res2;

  DBUG_ENTER("event_timed::load_from_row");

  if (!table)
    goto error;

  et= this;
  
  if (table->s->fields != EVEX_FIELD_COUNT)
    goto error;

unknown's avatar
unknown committed
386
  if ((et->dbname.str= get_field(mem_root,
unknown's avatar
unknown committed
387 388 389
                          table->field[EVEX_FIELD_DB])) == NULL)
    goto error;

unknown's avatar
unknown committed
390
  et->dbname.length= strlen(et->dbname.str);
unknown's avatar
unknown committed
391

unknown's avatar
unknown committed
392
  if ((et->name.str= get_field(mem_root,
unknown's avatar
unknown committed
393 394 395
                          table->field[EVEX_FIELD_NAME])) == NULL)
    goto error;

unknown's avatar
unknown committed
396
  et->name.length= strlen(et->name.str);
unknown's avatar
unknown committed
397

unknown's avatar
unknown committed
398
  if ((et->body.str= get_field(mem_root,
unknown's avatar
unknown committed
399 400 401
                          table->field[EVEX_FIELD_BODY])) == NULL)
    goto error;

unknown's avatar
unknown committed
402
  et->body.length= strlen(et->body.str);
unknown's avatar
unknown committed
403

unknown's avatar
unknown committed
404
  if ((et->definer.str= get_field(mem_root,
unknown's avatar
unknown committed
405 406
                          table->field[EVEX_FIELD_DEFINER])) == NullS)
    goto error;
unknown's avatar
unknown committed
407
  et->definer.length= strlen(et->definer.str);
unknown's avatar
unknown committed
408

unknown's avatar
unknown committed
409
  ptr= strchr(et->definer.str, '@');
unknown's avatar
unknown committed
410 411

  if (! ptr)
unknown's avatar
unknown committed
412
    ptr= et->definer.str;
unknown's avatar
unknown committed
413

unknown's avatar
unknown committed
414
  len= ptr - et->definer.str;
unknown's avatar
unknown committed
415

unknown's avatar
unknown committed
416 417 418 419 420
  et->definer_user.str= strmake_root(mem_root, et->definer.str, len);
  et->definer_user.length= len;
  len= et->definer.length - len - 1; //1 is because of @
  et->definer_host.str= strmake_root(mem_root, ptr + 1, len);//1: because of @
  et->definer_host.length= len;
unknown's avatar
unknown committed
421 422 423
  
  
  res1= table->field[EVEX_FIELD_STARTS]->
unknown's avatar
unknown committed
424
                     get_date(&et->starts, TIME_NO_ZERO_DATE);
unknown's avatar
unknown committed
425 426

  res2= table->field[EVEX_FIELD_ENDS]->
unknown's avatar
unknown committed
427
                     get_date(&et->ends, TIME_NO_ZERO_DATE);
unknown's avatar
unknown committed
428
  
unknown's avatar
unknown committed
429
  et->expression= table->field[EVEX_FIELD_INTERVAL_EXPR]->val_int();
unknown's avatar
unknown committed
430 431 432 433 434

  /*
    If res1 and res2 are true then both fields are empty.
	Hence if EVEX_FIELD_EXECUTE_AT is empty there is an error.
  */
unknown's avatar
unknown committed
435 436
  if (res1 && res2 && !et->expression && table->field[EVEX_FIELD_EXECUTE_AT]->
                get_date(&et->execute_at, TIME_NO_ZERO_DATE))
unknown's avatar
unknown committed
437 438 439 440 441 442
    goto error;

  /*
    In DB the values start from 1 but enum interval_type starts
    from 0
  */
unknown's avatar
unknown committed
443
  et->interval= (interval_type)
unknown's avatar
unknown committed
444 445
       ((ulonglong) table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->val_int() - 1);

unknown's avatar
unknown committed
446 447
  et->modified= table->field[EVEX_FIELD_CREATED]->val_int();
  et->created= table->field[EVEX_FIELD_MODIFIED]->val_int();
unknown's avatar
unknown committed
448 449 450 451 452 453 454 455 456 457 458 459

  /*
    ToDo Andrey : Ask PeterG & Serg what to do in this case.
                  Whether on load last_executed_at should be loaded
                  or it must be 0ed. If last_executed_at is loaded
                  then an event can be scheduled for execution
                  instantly. Let's say an event has to be executed
                  every 15 mins. The server has been stopped for
                  more than this time and then started. If L_E_AT
                  is loaded from DB, execution at L_E_AT+15min
                  will be scheduled. However this time is in the past.
                  Hence immediate execution. Due to patch of
unknown's avatar
unknown committed
460 461
                  ::mark_last_executed() last_executed gets time_now
                  and not execute_at. If not like this a big
unknown's avatar
unknown committed
462 463 464 465
                  queue can be scheduled for times which are still in
                  the past (2, 3 and more executions which will be
                  consequent).
  */
unknown's avatar
unknown committed
466
  set_zero_time(&last_executed, MYSQL_TIMESTAMP_DATETIME);
unknown's avatar
unknown committed
467 468
#ifdef ANDREY_0
  table->field[EVEX_FIELD_LAST_EXECUTED]->
unknown's avatar
unknown committed
469
                     get_date(&et->last_executed, TIME_NO_ZERO_DATE);
unknown's avatar
unknown committed
470
#endif
unknown's avatar
unknown committed
471
  last_executed_changed= false;
unknown's avatar
unknown committed
472 473 474 475 476

  // ToDo : Andrey . Find a way not to allocate ptr on event_mem_root
  if ((ptr= get_field(mem_root, table->field[EVEX_FIELD_STATUS])) == NullS)
    goto error;
  
unknown's avatar
unknown committed
477 478
  DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->name.str, ptr));
  et->status= (ptr[0]=='E'? MYSQL_EVENT_ENABLED:
unknown's avatar
unknown committed
479 480 481 482 483 484 485
                                     MYSQL_EVENT_DISABLED);

  // ToDo : Andrey . Find a way not to allocate ptr on event_mem_root
  if ((ptr= get_field(mem_root,
                  table->field[EVEX_FIELD_ON_COMPLETION])) == NullS)
    goto error;

unknown's avatar
unknown committed
486
  et->on_completion= (ptr[0]=='D'? MYSQL_EVENT_ON_COMPLETION_DROP:
unknown's avatar
unknown committed
487 488
                                     MYSQL_EVENT_ON_COMPLETION_PRESERVE);

unknown's avatar
unknown committed
489 490 491
  et->comment.str= get_field(mem_root, table->field[EVEX_FIELD_COMMENT]);
  if (et->comment.str != NullS)
    et->comment.length= strlen(et->comment.str);
unknown's avatar
unknown committed
492
  else
unknown's avatar
unknown committed
493
    et->comment.length= 0;
unknown's avatar
unknown committed
494 495 496 497 498 499 500
    
  DBUG_RETURN(0);
error:
  DBUG_RETURN(EVEX_GET_FIELD_FAILED);
}


unknown's avatar
unknown committed
501 502 503 504 505
/*
  Note: In the comments this->ends is referenced as m_ends

*/

unknown's avatar
unknown committed
506 507 508 509 510 511 512 513 514
bool
event_timed::compute_next_execution_time()
{
  TIME time_now;
  my_time_t now;
  int tmp;

  DBUG_ENTER("event_timed::compute_next_execution_time");

unknown's avatar
unknown committed
515
  if (status == MYSQL_EVENT_DISABLED)
unknown's avatar
unknown committed
516 517
  {
    DBUG_PRINT("compute_next_execution_time",
unknown's avatar
unknown committed
518
                  ("Event %s is DISABLED", name.str));
unknown's avatar
unknown committed
519 520 521
    goto ret;
  }
  //if one-time no need to do computation
unknown's avatar
unknown committed
522
  if (!expression)
unknown's avatar
unknown committed
523 524
  {
    //let's check whether it was executed
unknown's avatar
unknown committed
525
    if (last_executed.year)
unknown's avatar
unknown committed
526 527
    {
      DBUG_PRINT("compute_next_execution_time",
unknown's avatar
unknown committed
528 529
                ("One-time event %s was already executed", name.str));
      if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
unknown's avatar
unknown committed
530 531 532
      {
        DBUG_PRINT("compute_next_execution_time",
                          ("One-time event will be dropped."));
unknown's avatar
unknown committed
533
        dropped= true;
unknown's avatar
unknown committed
534
      }
unknown's avatar
unknown committed
535 536
      status= MYSQL_EVENT_DISABLED;
      status_changed= true;
unknown's avatar
unknown committed
537 538 539 540 541 542
    }
    goto ret;
  }
  time(&now);
  my_tz_UTC->gmt_sec_to_TIME(&time_now, now);
/*
unknown's avatar
unknown committed
543
  sql_print_information("[%s.%s]", dbname.str, name.str);
unknown's avatar
unknown committed
544 545 546
  sql_print_information("time_now : [%d-%d-%d %d:%d:%d ]", 
                         time_now.year, time_now.month, time_now.day,
                         time_now.hour, time_now.minute, time_now.second);
unknown's avatar
unknown committed
547 548 549 550 551 552 553 554 555 556
  sql_print_information("starts : [%d-%d-%d %d:%d:%d ]", starts.year,
                        starts.month, starts.day, starts.hour,
                        starts.minute, starts.second);
  sql_print_information("ends   : [%d-%d-%d %d:%d:%d ]", ends.year,
                        ends.month, ends.day, ends.hour,
                        ends.minute, ends.second);
  sql_print_information("m_last_ex: [%d-%d-%d %d:%d:%d ]", last_executed.year,
                        last_executed.month, last_executed.day,
                        last_executed.hour, last_executed.minute,
                        last_executed.second);
unknown's avatar
unknown committed
557
*/
unknown's avatar
unknown committed
558 559
  //if time_now is after ends don't execute anymore
  if (ends.year && (tmp= my_time_compare(&ends, &time_now)) == -1)
unknown's avatar
unknown committed
560
  {
unknown's avatar
unknown committed
561 562 563 564 565 566
    // time_now is after ends. don't execute anymore
    set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
    if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
      dropped= true;
    status= MYSQL_EVENT_DISABLED;
    status_changed= true;
unknown's avatar
unknown committed
567 568 569 570 571

    goto ret;
  }
  
  /* 
unknown's avatar
unknown committed
572 573 574
     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
575
  */
unknown's avatar
unknown committed
576
  if (starts.year && (tmp= my_time_compare(&time_now, &starts)) < 1)
unknown's avatar
unknown committed
577
  {
unknown's avatar
unknown committed
578
    if (tmp == 0 && my_time_compare(&starts, &last_executed) == 0)
unknown's avatar
unknown committed
579 580
    {
       /*
unknown's avatar
unknown committed
581 582
        time_now = starts = last_executed
        do nothing or we will schedule for second time execution at starts.
unknown's avatar
unknown committed
583 584 585 586
      */
    }
    else
    {
unknown's avatar
unknown committed
587 588 589 590 591
      /*
        starts is in the future
        time_now before starts. Scheduling for starts
      */
      execute_at= starts;
unknown's avatar
unknown committed
592 593 594 595
      goto ret;
    }
  }
  
unknown's avatar
unknown committed
596
  if (starts.year && ends.year)
unknown's avatar
unknown committed
597 598
  {
    /* 
unknown's avatar
unknown committed
599 600 601
      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
602 603
      If not set then schedule for now.
    */
unknown's avatar
unknown committed
604 605
    if (!last_executed.year)
      execute_at= time_now;
unknown's avatar
unknown committed
606 607 608 609 610
    else
    {
      my_time_t last, ll_ends;

      // There was previous execution     
unknown's avatar
unknown committed
611 612
      last= sec_since_epoch_TIME(&last_executed) + expression;
      ll_ends= sec_since_epoch_TIME(&ends);
unknown's avatar
unknown committed
613 614 615 616 617
      //now convert back to TIME
      //ToDo Andrey: maybe check for error here?
      if (ll_ends < last)
      {
        // Next execution after ends. No more executions
unknown's avatar
unknown committed
618 619 620
        set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
        if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
          dropped= true;
unknown's avatar
unknown committed
621 622
      }
      else
unknown's avatar
unknown committed
623
        my_tz_UTC->gmt_sec_to_TIME(&execute_at, last);
unknown's avatar
unknown committed
624 625 626
    }
    goto ret;
  }
unknown's avatar
unknown committed
627
  else if (!starts.year && !ends.year)
unknown's avatar
unknown committed
628
  {
unknown's avatar
unknown committed
629 630 631 632 633
    // both starts and m_ends are not set, se we schedule for the next
    // based on last_executed
    if (!last_executed.year)
       //last_executed not set. Schedule the event for now
      execute_at= time_now;
unknown's avatar
unknown committed
634 635
    else
      //ToDo Andrey: maybe check for error here?
unknown's avatar
unknown committed
636 637
      my_tz_UTC->gmt_sec_to_TIME(&execute_at, 
                   sec_since_epoch_TIME(&last_executed) + expression);
unknown's avatar
unknown committed
638 639 640 641
    goto ret;
  }
  else
  {
unknown's avatar
unknown committed
642 643
    //either starts or m_ends is set
    if (starts.year)
unknown's avatar
unknown committed
644 645
    {
      /*
unknown's avatar
unknown committed
646 647 648 649
        - 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
650 651 652
      */
      my_time_t last;

unknown's avatar
unknown committed
653 654 655
      //convert either last_executed or starts to seconds
      if (last_executed.year)
        last= sec_since_epoch_TIME(&last_executed) + expression;
unknown's avatar
unknown committed
656
      else
unknown's avatar
unknown committed
657
        last= sec_since_epoch_TIME(&starts);
unknown's avatar
unknown committed
658 659 660

      //now convert back to TIME
      //ToDo Andrey: maybe check for error here?
unknown's avatar
unknown committed
661
      my_tz_UTC->gmt_sec_to_TIME(&execute_at, last);
unknown's avatar
unknown committed
662 663 664 665 666 667
    }
    else
    {
      /*
        - m_ends is set
        - m_ends is after time_now or is equal
unknown's avatar
unknown committed
668 669
        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
670 671 672
      */
      my_time_t last, ll_ends;

unknown's avatar
unknown committed
673 674
      if (!last_executed.year)
        execute_at= time_now;
unknown's avatar
unknown committed
675 676
      else
      {
unknown's avatar
unknown committed
677 678 679
        last= sec_since_epoch_TIME(&last_executed);
        ll_ends= sec_since_epoch_TIME(&ends);
        last+= expression;
unknown's avatar
unknown committed
680 681 682 683
        //now convert back to TIME
        //ToDo Andrey: maybe check for error here?
        if (ll_ends < last)
        {
unknown's avatar
unknown committed
684 685 686
          set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
          if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
            dropped= true;
unknown's avatar
unknown committed
687 688
        }
        else
unknown's avatar
unknown committed
689
          my_tz_UTC->gmt_sec_to_TIME(&execute_at, last);
unknown's avatar
unknown committed
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
      }
    }
    goto ret;
  }
ret:

  DBUG_RETURN(false);
}


void
event_timed::mark_last_executed()
{
  TIME time_now;
  my_time_t now;

  time(&now);
  my_tz_UTC->gmt_sec_to_TIME(&time_now, now);

unknown's avatar
unknown committed
709
  last_executed= time_now; // was execute_at
unknown's avatar
unknown committed
710
#ifdef ANDREY_0
unknown's avatar
unknown committed
711
  last_executed= execute_at;
unknown's avatar
unknown committed
712
#endif
unknown's avatar
unknown committed
713
  last_executed_changed= true;
unknown's avatar
unknown committed
714 715 716
}


unknown's avatar
unknown committed
717 718 719 720 721 722 723 724 725 726 727
/*
  Returns :
    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
            failed.
*/

int
unknown's avatar
unknown committed
728 729
event_timed::drop(THD *thd)
{
unknown's avatar
unknown committed
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
  TABLE *table;
  int ret= 0;
  DBUG_ENTER("event_timed::drop");

  if (evex_open_event_table(thd, TL_WRITE, &table))
    DBUG_RETURN(-1);

  if (evex_db_find_event_aux(thd, dbname, name, table))
    DBUG_RETURN(-2);

  if ((ret= table->file->delete_row(table->record[0])))
    DBUG_RETURN(ret);
    
  close_thread_tables(thd);
  DBUG_RETURN(0);
unknown's avatar
unknown committed
745 746 747 748 749 750 751
}


bool
event_timed::update_fields(THD *thd)
{
  TABLE *table;
unknown's avatar
unknown committed
752
  Open_tables_state backup;
unknown's avatar
unknown committed
753 754 755 756 757
  int ret= 0;
  bool opened;

  DBUG_ENTER("event_timed::update_time_fields");

unknown's avatar
unknown committed
758
  DBUG_PRINT("enter", ("name: %*s", name.length, name.str));
unknown's avatar
unknown committed
759 760
 
  //no need to update if nothing has changed
unknown's avatar
unknown committed
761
  if (!(status_changed || last_executed_changed))
unknown's avatar
unknown committed
762 763
    goto done;
  
unknown's avatar
unknown committed
764 765
  thd->reset_n_backup_open_tables_state(&backup);

unknown's avatar
unknown committed
766
  if (evex_open_event_table(thd, TL_WRITE, &table))
unknown's avatar
unknown committed
767 768 769 770 771
  {
    ret= SP_OPEN_TABLE_FAILED;
    goto done;
  }

unknown's avatar
unknown committed
772

773
  if ((ret= evex_db_find_event_aux(thd, dbname, name, table)))
unknown's avatar
unknown committed
774 775 776 777 778
    goto done;

  store_record(table,record[1]);
  table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; // Don't update create on row update.

unknown's avatar
unknown committed
779
  if (last_executed_changed)
unknown's avatar
unknown committed
780 781
  {
    table->field[EVEX_FIELD_LAST_EXECUTED]->set_notnull();
unknown's avatar
unknown committed
782
    table->field[EVEX_FIELD_LAST_EXECUTED]->store_time(&last_executed,
unknown's avatar
unknown committed
783
                           MYSQL_TIMESTAMP_DATETIME);
unknown's avatar
unknown committed
784
    last_executed_changed= false;
unknown's avatar
unknown committed
785
  }
unknown's avatar
unknown committed
786
  if (status_changed)
unknown's avatar
unknown committed
787 788
  {
    table->field[EVEX_FIELD_STATUS]->set_notnull();
unknown's avatar
unknown committed
789 790
    table->field[EVEX_FIELD_STATUS]->store((longlong)status);
    status_changed= false;
unknown's avatar
unknown committed
791 792 793 794 795 796 797
  }
    
  if ((table->file->update_row(table->record[1],table->record[0])))
    ret= EVEX_WRITE_ROW_FAILED;

done:
  close_thread_tables(thd);
unknown's avatar
unknown committed
798
  thd->restore_backup_open_tables_state(&backup);
unknown's avatar
unknown committed
799 800 801 802 803 804 805 806 807 808 809

  DBUG_RETURN(ret);
}


char *
event_timed::get_show_create_event(THD *thd, uint *length)
{
  char *dst, *ret;
  uint len, tmp_len;

unknown's avatar
unknown committed
810 811
  len = strlen("CREATE EVENT `") + dbname.length + strlen(".") + name.length +
        strlen("` ON SCHEDULE EVERY 5 MINUTE DO ") + body.length + strlen(";");
unknown's avatar
unknown committed
812
  
unknown's avatar
unknown committed
813
  ret= dst= (char*) alloc_root(thd->mem_root, len + 1);
unknown's avatar
unknown committed
814
  memcpy(dst, "CREATE EVENT `", tmp_len= strlen("CREATE EVENT `"));
unknown's avatar
unknown committed
815
  dst+= tmp_len;
unknown's avatar
unknown committed
816
  memcpy(dst, dbname.str, tmp_len=dbname.length);
unknown's avatar
unknown committed
817 818 819
  dst+= tmp_len;
  memcpy(dst, ".", tmp_len= strlen("."));
  dst+= tmp_len;
unknown's avatar
unknown committed
820
  memcpy(dst, name.str, tmp_len= name.length);
unknown's avatar
unknown committed
821
  dst+= tmp_len;
unknown's avatar
unknown committed
822 823
  memcpy(dst, "` ON SCHEDULE EVERY 5 MINUTE DO ",
         tmp_len= strlen("` ON SCHEDULE EVERY 5 MINUTE DO "));
unknown's avatar
unknown committed
824 825
  dst+= tmp_len;

unknown's avatar
unknown committed
826
  memcpy(dst, body.str, tmp_len= body.length);
unknown's avatar
unknown committed
827 828 829 830
  dst+= tmp_len;
  memcpy(dst, ";", 1);
  ++dst;
  *dst= '\0';
unknown's avatar
unknown committed
831
 
unknown's avatar
unknown committed
832
  *length= len;
unknown's avatar
unknown committed
833

unknown's avatar
unknown committed
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
  return ret;
}


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

   Returns 
          0  - success
       -100  - event in execution (parallel execution is impossible)
      others - retcodes of sp_head::execute_procedure()
      
*/

int
event_timed::execute(THD *thd, MEM_ROOT *mem_root)
{
  List<Item> empty_item_list;
  int ret= 0;
   
  DBUG_ENTER("event_timed::execute");

unknown's avatar
unknown committed
861
  VOID(pthread_mutex_lock(&this->LOCK_running));
unknown's avatar
unknown committed
862 863
  if (running) 
  {
unknown's avatar
unknown committed
864
    VOID(pthread_mutex_unlock(&this->LOCK_running));
unknown's avatar
unknown committed
865 866 867
    DBUG_RETURN(-100);
  }
  running= true;
unknown's avatar
unknown committed
868
  VOID(pthread_mutex_unlock(&this->LOCK_running));
unknown's avatar
unknown committed
869 870 871 872

  // TODO Andrey : make this as member variable and delete in destructor
  empty_item_list.empty();
  
unknown's avatar
unknown committed
873
  if (!sphead && (ret= compile(thd, mem_root)))
unknown's avatar
unknown committed
874 875
    goto done;
  
unknown's avatar
unknown committed
876
  ret= sphead->execute_procedure(thd, &empty_item_list);
unknown's avatar
unknown committed
877

unknown's avatar
unknown committed
878
  VOID(pthread_mutex_lock(&this->LOCK_running));
unknown's avatar
unknown committed
879
  running= false;
unknown's avatar
unknown committed
880
  VOID(pthread_mutex_unlock(&this->LOCK_running));
unknown's avatar
unknown committed
881 882

done:
unknown's avatar
unknown committed
883 884
  // Don't cache sphead if allocated on another mem_root
  if (mem_root && sphead)
unknown's avatar
unknown committed
885
  {
unknown's avatar
unknown committed
886 887
    delete sphead;
    sphead= 0;
unknown's avatar
unknown committed
888 889 890 891 892 893
  }

  DBUG_RETURN(ret);
}


894 895 896 897 898 899 900 901
/*
  Returns
                     0 - Success
    EVEX_COMPILE_ERROR - Error during compilation

*/


unknown's avatar
unknown committed
902 903 904
int
event_timed::compile(THD *thd, MEM_ROOT *mem_root)
{
unknown's avatar
unknown committed
905
  int ret= 0;
unknown's avatar
unknown committed
906 907 908 909 910 911 912 913
  MEM_ROOT *tmp_mem_root= 0;
  LEX *old_lex= thd->lex, lex;
  char *old_db;
  event_timed *ett;
  sp_name *spn;
  char *old_query;
  uint old_query_len;
  st_sp_chistics *p;
unknown's avatar
unknown committed
914 915 916 917 918 919 920 921 922 923 924 925 926
  CHARSET_INFO *old_character_set_client, *old_collation_connection,
               *old_character_set_results;

  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;
  
  thd->variables.character_set_client= 
    thd->variables.character_set_results=
      thd->variables.collation_connection=
           get_charset_by_csname("utf8", MY_CS_PRIMARY, MYF(MY_WME));

  thd->update_charset();
unknown's avatar
unknown committed
927 928 929 930 931 932 933 934 935 936 937
  
  DBUG_ENTER("event_timed::compile");
  // change the memory root for the execution time
  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;
unknown's avatar
unknown committed
938
  thd->db= dbname.str;
unknown's avatar
unknown committed
939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958
  thd->query= get_show_create_event(thd, &thd->query_length);
  DBUG_PRINT("event_timed::compile", ("query:%s",thd->query));

  thd->lex= &lex;
  lex_start(thd, (uchar*)thd->query, thd->query_length);
  lex.et_compile_phase= TRUE;
  if (yyparse((void *)thd) || thd->is_fatal_error)
  {
    //  Free lex associated resources
    //  QQ: Do we really need all this stuff here ?
    if (lex.sphead)
    {
      if (&lex != thd->lex)
        thd->lex->sphead->restore_lex(thd);
      delete lex.sphead;
      lex.sphead= 0;
    }
    // QQ: anything else ?
    lex_end(&lex);
    thd->lex= old_lex;
unknown's avatar
unknown committed
959 960 961

    ret= EVEX_COMPILE_ERROR;
    goto done;
unknown's avatar
unknown committed
962 963
  }
  
unknown's avatar
unknown committed
964
  sphead= lex.et->sphead;
unknown's avatar
unknown committed
965
  sphead->m_db= dbname;
unknown's avatar
unknown committed
966 967
  //copy also chistics since they will vanish otherwise we get 0x0 pointer
  // Todo : Handle sql_mode !!
unknown's avatar
unknown committed
968 969 970
  sphead->set_definer(definer.str, definer.length);
  sphead->set_info(0, 0, &lex.sp_chistics, 0/*sql_mode*/);
  sphead->optimize();
unknown's avatar
unknown committed
971 972
  ret= 0;
done:
unknown's avatar
unknown committed
973
  lex.et->free_sphead_on_delete= false;
unknown's avatar
unknown committed
974
  delete lex.et;
unknown's avatar
unknown committed
975 976 977 978 979
  lex_end(&lex);
  thd->lex= old_lex;
  thd->query= old_query;
  thd->query_length= old_query_len;
  thd->db= old_db;
unknown's avatar
unknown committed
980 981 982 983 984 985

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

unknown's avatar
unknown committed
986 987 988 989 990 991
  /*
    Change the memory root for the execution time.
  */
  if (mem_root)
    thd->mem_root= tmp_mem_root;

unknown's avatar
unknown committed
992
  DBUG_RETURN(ret);
unknown's avatar
unknown committed
993 994
}