event_db_repository.cc 33.2 KB
Newer Older
1
/* Copyright 2004-2008 MySQL AB, 2008 Sun Microsystems, Inc.
2 3 4

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

   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 "mysql_priv.h"
#include "event_db_repository.h"
18
#include "sp_head.h"
19
#include "event_data_objects.h"
20
#include "events.h"
21
#include "sql_show.h"
22

23 24 25 26 27
/**
  @addtogroup Event_Scheduler
  @{
*/

28
static
29
const TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] =
30
{
31
  {
32 33 34
    { C_STRING_WITH_LEN("db") },
    { C_STRING_WITH_LEN("char(64)") },
    { C_STRING_WITH_LEN("utf8") }
35
  },
36
  {
37 38 39
    { C_STRING_WITH_LEN("name") },
    { C_STRING_WITH_LEN("char(64)") },
    { C_STRING_WITH_LEN("utf8") }
40 41
  },
  {
42 43
    { C_STRING_WITH_LEN("body") },
    { C_STRING_WITH_LEN("longblob") },
44
    {NULL, 0}
45
  },
46
  {
47 48 49
    { C_STRING_WITH_LEN("definer") },
    { C_STRING_WITH_LEN("char(77)") },
    { C_STRING_WITH_LEN("utf8") }
50 51
  },
  {
52 53
    { C_STRING_WITH_LEN("execute_at") },
    { C_STRING_WITH_LEN("datetime") },
54
    {NULL, 0}
55
  },
56
  {
57 58
    { C_STRING_WITH_LEN("interval_value") },
    { C_STRING_WITH_LEN("int(11)") },
59 60 61
    {NULL, 0}
  },
  {
62 63
    { C_STRING_WITH_LEN("interval_field") },
    { C_STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY',"
64 65 66
    "'HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR',"
    "'DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND',"
    "'DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND',"
67
    "'SECOND_MICROSECOND')") },
68
    {NULL, 0}
69
  },
70
  {
71 72
    { C_STRING_WITH_LEN("created") },
    { C_STRING_WITH_LEN("timestamp") },
73 74 75
    {NULL, 0}
  },
  {
76 77
    { C_STRING_WITH_LEN("modified") },
    { C_STRING_WITH_LEN("timestamp") },
78
    {NULL, 0}
79
  },
80
  {
81 82
    { C_STRING_WITH_LEN("last_executed") },
    { C_STRING_WITH_LEN("datetime") },
83 84 85
    {NULL, 0}
  },
  {
86 87
    { C_STRING_WITH_LEN("starts") },
    { C_STRING_WITH_LEN("datetime") },
88
    {NULL, 0}
89
  },
90
  {
91 92
    { C_STRING_WITH_LEN("ends") },
    { C_STRING_WITH_LEN("datetime") },
93 94 95
    {NULL, 0}
  },
  {
96
    { C_STRING_WITH_LEN("status") },
97
    { C_STRING_WITH_LEN("enum('ENABLED','DISABLED','SLAVESIDE_DISABLED')") },
98
    {NULL, 0}
99
  },
100
  {
101 102
    { C_STRING_WITH_LEN("on_completion") },
    { C_STRING_WITH_LEN("enum('DROP','PRESERVE')") },
103 104 105
    {NULL, 0}
  },
  {
106 107
    { C_STRING_WITH_LEN("sql_mode") },
    { C_STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES',"
108 109 110 111 112 113
    "'IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION',"
    "'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB',"
    "'NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40',"
    "'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES',"
    "'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES',"
    "'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER',"
114
    "'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH')") },
115 116 117
    {NULL, 0}
  },
  {
118 119 120
    { C_STRING_WITH_LEN("comment") },
    { C_STRING_WITH_LEN("char(64)") },
    { C_STRING_WITH_LEN("utf8") }
121
  },
122 123 124 125
  {
    { C_STRING_WITH_LEN("originator") },
    { C_STRING_WITH_LEN("int(10)") },
    {NULL, 0}
126
  },
127 128 129 130
  {
    { C_STRING_WITH_LEN("time_zone") },
    { C_STRING_WITH_LEN("char(64)") },
    { C_STRING_WITH_LEN("latin1") }
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
  },
  {
    { C_STRING_WITH_LEN("character_set_client") },
    { C_STRING_WITH_LEN("char(32)") },
    { C_STRING_WITH_LEN("utf8") }
  },
  {
    { C_STRING_WITH_LEN("collation_connection") },
    { C_STRING_WITH_LEN("char(32)") },
    { C_STRING_WITH_LEN("utf8") }
  },
  {
    { C_STRING_WITH_LEN("db_collation") },
    { C_STRING_WITH_LEN("char(32)") },
    { C_STRING_WITH_LEN("utf8") }
  },
  {
    { C_STRING_WITH_LEN("body_utf8") },
    { C_STRING_WITH_LEN("longblob") },
    { NULL, 0 }
151 152 153 154
  }
};


155
/**
156 157
  Puts some data common to CREATE and ALTER EVENT into a row.

158
  Used both when an event is created and when it is altered.
159

160 161 162
  @param   thd        THD
  @param   table      The row to fill out
  @param   et         Event's data
163
  @param   sp         Event stored routine
164
  @param   is_update  CREATE EVENT or ALTER EVENT
165

166 167
  @retval  FALSE success
  @retval  TRUE error
168 169
*/

170
static bool
171 172 173 174
mysql_event_fill_row(THD *thd,
                     TABLE *table,
                     Event_parse_data *et,
                     sp_head *sp,
175
                     ulong sql_mode,
176
                     my_bool is_update)
177 178
{
  CHARSET_INFO *scs= system_charset_info;
179 180
  enum enum_events_table_field f_num;
  Field **fields= table->field;
181
  int rs= FALSE;
182

183
  DBUG_ENTER("mysql_event_fill_row");
184 185 186 187

  DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str));
  DBUG_PRINT("info", ("name  =[%s]", et->name.str));

188 189
  DBUG_ASSERT(et->on_completion != Event_parse_data::ON_COMPLETION_DEFAULT);

190 191 192 193 194 195 196 197 198 199 200
  if (table->s->fields < ET_FIELD_COUNT)
  {
    /*
      Safety: this can only happen if someone started the server
      and then altered mysql.event.
    */
    my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED, MYF(0), table->alias,
             (int) ET_FIELD_COUNT, table->s->fields);
    DBUG_RETURN(TRUE);
  }

201 202
  if (fields[f_num= ET_FIELD_DEFINER]->
                              store(et->definer.str, et->definer.length, scs))
203 204
    goto err_truncate;

205
  if (fields[f_num= ET_FIELD_DB]->store(et->dbname.str, et->dbname.length, scs))
206 207
    goto err_truncate;

208
  if (fields[f_num= ET_FIELD_NAME]->store(et->name.str, et->name.length, scs))
209 210 211
    goto err_truncate;

  /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/
212 213 214
  rs|= fields[ET_FIELD_ON_COMPLETION]->store((longlong)et->on_completion, TRUE);
  rs|= fields[ET_FIELD_STATUS]->store((longlong)et->status, TRUE);
  rs|= fields[ET_FIELD_ORIGINATOR]->store((longlong)et->originator, TRUE);
215

216 217 218
  /*
    Change the SQL_MODE only if body was present in an ALTER EVENT and of course
    always during CREATE EVENT.
219
  */
220
  if (et->body_changed)
221
  {
222 223
    DBUG_ASSERT(sp->m_body.str);

224
    rs|= fields[ET_FIELD_SQL_MODE]->store((longlong)sql_mode, TRUE);
225 226 227 228 229

    if (fields[f_num= ET_FIELD_BODY]->store(sp->m_body.str,
                                            sp->m_body.length,
                                            scs))
    {
230
      goto err_truncate;
231
    }
232 233
  }

234 235
  if (et->expression)
  {
236 237 238 239
    const String *tz_name= thd->variables.time_zone->get_name();
    if (!is_update || !et->starts_null)
    {
      fields[ET_FIELD_TIME_ZONE]->set_notnull();
240 241
      rs|= fields[ET_FIELD_TIME_ZONE]->store(tz_name->ptr(), tz_name->length(),
                                             tz_name->charset());
242 243
    }

244
    fields[ET_FIELD_INTERVAL_EXPR]->set_notnull();
245
    rs|= fields[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, TRUE);
246

247
    fields[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull();
248

249
    rs|= fields[ET_FIELD_TRANSIENT_INTERVAL]->
250 251 252
                            store(interval_type_to_name[et->interval].str,
                                  interval_type_to_name[et->interval].length,
                                  scs);
253

254
    fields[ET_FIELD_EXECUTE_AT]->set_null();
255 256 257

    if (!et->starts_null)
    {
258
      MYSQL_TIME time;
259
      my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->starts);
260

261
      fields[ET_FIELD_STARTS]->set_notnull();
262
      fields[ET_FIELD_STARTS]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
263
    }
264 265 266

    if (!et->ends_null)
    {
267
      MYSQL_TIME time;
268
      my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->ends);
269

270
      fields[ET_FIELD_ENDS]->set_notnull();
271
      fields[ET_FIELD_ENDS]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
272 273
    }
  }
274
  else if (et->execute_at)
275
  {
276 277
    const String *tz_name= thd->variables.time_zone->get_name();
    fields[ET_FIELD_TIME_ZONE]->set_notnull();
278 279
    rs|= fields[ET_FIELD_TIME_ZONE]->store(tz_name->ptr(), tz_name->length(),
                                           tz_name->charset());
280

281 282 283 284
    fields[ET_FIELD_INTERVAL_EXPR]->set_null();
    fields[ET_FIELD_TRANSIENT_INTERVAL]->set_null();
    fields[ET_FIELD_STARTS]->set_null();
    fields[ET_FIELD_ENDS]->set_null();
285

286
    MYSQL_TIME time;
287
    my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->execute_at);
288

289 290
    fields[ET_FIELD_EXECUTE_AT]->set_notnull();
    fields[ET_FIELD_EXECUTE_AT]->
291
                        store_time(&time, MYSQL_TIMESTAMP_DATETIME);
292 293 294 295 296 297 298 299 300
  }
  else
  {
    DBUG_ASSERT(is_update);
    /*
      it is normal to be here when the action is update
      this is an error if the action is create. something is borked
    */
  }
301

302
  ((Field_timestamp *)fields[ET_FIELD_MODIFIED])->set_time();
303 304 305

  if (et->comment.str)
  {
306 307
    if (fields[f_num= ET_FIELD_COMMENT]->
                          store(et->comment.str, et->comment.length, scs))
308 309 310
      goto err_truncate;
  }

311
  fields[ET_FIELD_CHARACTER_SET_CLIENT]->set_notnull();
312
  rs|= fields[ET_FIELD_CHARACTER_SET_CLIENT]->store(
313 314 315 316 317
    thd->variables.character_set_client->csname,
    strlen(thd->variables.character_set_client->csname),
    system_charset_info);

  fields[ET_FIELD_COLLATION_CONNECTION]->set_notnull();
318
  rs|= fields[ET_FIELD_COLLATION_CONNECTION]->store(
319 320 321 322 323 324 325 326
    thd->variables.collation_connection->name,
    strlen(thd->variables.collation_connection->name),
    system_charset_info);

  {
    CHARSET_INFO *db_cl= get_default_db_collation(thd, et->dbname.str);

    fields[ET_FIELD_DB_COLLATION]->set_notnull();
327 328 329
    rs|= fields[ET_FIELD_DB_COLLATION]->store(db_cl->name,
                                              strlen(db_cl->name),
                                              system_charset_info);
330 331 332 333 334
  }

  if (et->body_changed)
  {
    fields[ET_FIELD_BODY_UTF8]->set_notnull();
335 336 337 338 339 340 341 342 343
    rs|= fields[ET_FIELD_BODY_UTF8]->store(sp->m_body_utf8.str,
                                           sp->m_body_utf8.length,
                                           system_charset_info);
  }

  if (rs)
  {
    my_error(ER_EVENT_STORE_FAILED, MYF(0), fields[f_num]->field_name, rs);
    DBUG_RETURN(TRUE);
344 345
  }

346
  DBUG_RETURN(FALSE);
347

348
err_truncate:
349
  my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), fields[f_num]->field_name);
350
  DBUG_RETURN(TRUE);
351 352 353
}


354 355 356
/*
  Performs an index scan of event_table (mysql.event) and fills schema_table.

357
  SYNOPSIS
358 359 360 361
    Event_db_repository::index_read_for_db_for_i_s()
      thd          Thread
      schema_table The I_S.EVENTS table
      event_table  The event table to use for loading (mysql.event)
362
      db           For which schema to do an index scan.
363

364
  RETURN VALUE
365 366 367 368
    0  OK
    1  Error
*/

369
bool
370
Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
371 372
                                               TABLE *event_table,
                                               const char *db)
373 374 375 376 377
{
  int ret=0;
  CHARSET_INFO *scs= system_charset_info;
  KEY *key_info;
  uint key_len;
378
  uchar *key_buf= NULL;
379 380 381 382 383 384 385
  LINT_INIT(key_buf);

  DBUG_ENTER("Event_db_repository::index_read_for_db_for_i_s");

  DBUG_PRINT("info", ("Using prefix scanning on PK"));
  event_table->file->ha_index_init(0, 1);
  key_info= event_table->key_info;
386 387 388 389 390 391 392 393 394 395 396

  if (key_info->key_parts == 0 ||
      key_info->key_part[0].field != event_table->field[ET_FIELD_DB])
  {
    /* Corrupted table: no index or index on a wrong column */
    my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event");
    ret= 1;
    goto end;
  }

  event_table->field[ET_FIELD_DB]->store(db, strlen(db), scs);
397 398
  key_len= key_info->key_part[0].store_length;

399
  if (!(key_buf= (uchar *)alloc_root(thd->mem_root, key_len)))
400
  {
401
    /* Don't send error, it would be done by sql_alloc_error_handler() */
402 403
    ret= 1;
    goto end;
404
  }
405 406

  key_copy(key_buf, event_table->record[0], key_info, key_len);
407 408 409
  if (!(ret= event_table->file->index_read_map(event_table->record[0], key_buf,
                                               (key_part_map)1,
                                               HA_READ_PREFIX)))
410
  {
411 412
    DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret));
    do
413
    {
414 415 416 417 418
      ret= copy_event_to_schema_table(thd, schema_table, event_table);
      if (ret == 0)
        ret= event_table->file->index_next_same(event_table->record[0],
                                                key_buf, key_len);
    } while (ret == 0);
419
  }
420 421
  DBUG_PRINT("info", ("Scan finished. ret=%d", ret));

422 423
  /*  ret is guaranteed to be != 0 */
  if (ret == HA_ERR_END_OF_FILE || ret == HA_ERR_KEY_NOT_FOUND)
424 425 426
    ret= 0;
  else
    event_table->file->print_error(ret, MYF(0));
427

428 429 430 431
end:
  event_table->file->ha_index_end();

  DBUG_RETURN(test(ret));
432 433 434 435 436 437
}


/*
  Performs a table scan of event_table (mysql.event) and fills schema_table.

438
  SYNOPSIS
439 440 441 442 443
    Events_db_repository::table_scan_all_for_i_s()
      thd          Thread
      schema_table The I_S.EVENTS in memory table
      event_table  The event table to use for loading.

444 445 446
  RETURN VALUE
    FALSE  OK
    TRUE   Error
447 448
*/

449
bool
450 451 452 453 454 455
Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table,
                                            TABLE *event_table)
{
  int ret;
  READ_RECORD read_record_info;
  DBUG_ENTER("Event_db_repository::table_scan_all_for_i_s");
456

457
  init_read_record(&read_record_info, thd, event_table, NULL, 1, 0, FALSE);
458 459 460 461 462 463 464 465 466 467 468

  /*
    rr_sequential, in read_record(), returns 137==HA_ERR_END_OF_FILE,
    but rr_handle_error returns -1 for that reason. Thus, read_record()
    returns -1 eventually.
  */
  do
  {
    ret= read_record_info.read_record(&read_record_info);
    if (ret == 0)
      ret= copy_event_to_schema_table(thd, schema_table, event_table);
469
  } while (ret == 0);
470 471 472 473 474

  DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
  end_read_record(&read_record_info);

  /*  ret is guaranteed to be != 0 */
475
  DBUG_RETURN(ret == -1? FALSE:TRUE);
476 477 478
}


479
/**
480 481 482
  Fills I_S.EVENTS with data loaded from mysql.event. Also used by
  SHOW EVENTS

483 484 485 486 487
  The reason we reset and backup open tables here is that this
  function may be called from any query that accesses
  INFORMATION_SCHEMA - including a query that is issued from
  a pre-locked statement, one that already has open and locked
  tables.
488

489 490
  @retval FALSE  success
  @retval TRUE   error
491 492
*/

493
bool
494 495
Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables,
                                        const char *db)
496 497 498 499 500 501 502 503 504
{
  TABLE *schema_table= tables->table;
  TABLE *event_table= NULL;
  int ret= 0;

  DBUG_ENTER("Event_db_repository::fill_schema_events");
  DBUG_PRINT("info",("db=%s", db? db:"(null)"));

  if (open_event_table(thd, TL_READ, &event_table))
505
    DBUG_RETURN(TRUE);
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527

  /*
    1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order
                     thus we won't order it. OTOH, SHOW EVENTS will be
                     ordered.
    2. SHOW EVENTS => PRIMARY KEY with prefix scanning on (db)
       Reasoning: Events are per schema, therefore a scan over an index
                  will save use from doing a table scan and comparing
                  every single row's `db` with the schema which we show.
  */
  if (db)
    ret= index_read_for_db_for_i_s(thd, schema_table, event_table, db);
  else
    ret= table_scan_all_for_i_s(thd, schema_table, event_table);

  close_thread_tables(thd);

  DBUG_PRINT("info", ("Return code=%d", ret));
  DBUG_RETURN(ret);
}


528 529
/**
  Open mysql.event table for read.
530

531 532 533 534 535
  It's assumed that the caller knows what they are doing:
  - whether it was necessary to reset-and-backup the open tables state
  - whether the requested lock does not lead to a deadlock
  - whether this open mode would work under LOCK TABLES, or inside a
  stored function or trigger.
536

537 538 539 540
  Note that if the table can't be locked successfully this operation will
  close it. Therefore it provides guarantee that it either opens and locks
  table or fails without leaving any tables open.

541 542 543 544 545 546 547
  @param[in]  thd  Thread context
  @param[in]  lock_type  How to lock the table
  @param[out] table  We will store the open table here

  @retval TRUE open and lock failed - an error message is pushed into the
               stack
  @retval FALSE success
548 549
*/

550
bool
551 552 553 554 555 556
Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
                                      TABLE **table)
{
  TABLE_LIST tables;
  DBUG_ENTER("Event_db_repository::open_event_table");

Konstantin Osipov's avatar
Konstantin Osipov committed
557
  tables.init_one_table("mysql", 5, "event", 5, "event", lock_type);
558
  alloc_mdl_locks(&tables, thd->mem_root);
559 560

  if (simple_open_n_lock_tables(thd, &tables))
561
  {
562
    close_thread_tables(thd);
563
    DBUG_RETURN(TRUE);
564
  }
565

566 567
  *table= tables.table;
  tables.table->use_all_columns();
568
  DBUG_RETURN(FALSE);
569 570 571
}


572 573
/**
  Creates an event record in mysql.event table.
574

575 576
  Creates an event. Relies on mysql_event_fill_row which is shared with
  ::update_event.
577

578 579 580
  @pre All semantic checks must be performed outside. This function
  only creates a record on disk.
  @pre The thread handle has no open tables.
581

kostja@bodhi.(none)'s avatar
kostja@bodhi.(none) committed
582
  @param[in,out] thd           THD
583 584 585
  @param[in]     parse_data    Parsed event definition
  @param[in]     create_if_not TRUE if IF NOT EXISTS clause was provided
                               to CREATE EVENT statement
586

587 588
  @retval FALSE  success
  @retval TRUE   error
589 590
*/

591
bool
592
Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
593
                                  my_bool create_if_not)
594
{
595
  int ret= 1;
596
  TABLE *table= NULL;
597
  sp_head *sp= thd->lex->sphead;
598
  ulong saved_mode= thd->variables.sql_mode;
andrey@lmy004's avatar
andrey@lmy004 committed
599

600 601 602
  DBUG_ENTER("Event_db_repository::create_event");

  DBUG_PRINT("info", ("open mysql.event for update"));
603
  DBUG_ASSERT(sp);
604

605 606 607
  /* Reset sql_mode during data dictionary operations. */
  thd->variables.sql_mode= 0;

608
  if (open_event_table(thd, TL_WRITE, &table))
609
    goto end;
610

611
  DBUG_PRINT("info", ("name: %.*s", (int) parse_data->name.length,
612 613
             parse_data->name.str));

614
  DBUG_PRINT("info", ("check existance of an event with the same name"));
615
  if (!find_named_event(parse_data->dbname, parse_data->name, table))
616 617 618 619 620
  {
    if (create_if_not)
    {
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
621
                          parse_data->name.str);
622
      ret= 0;
623
    }
624 625 626
    else
      my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str);
    goto end;
627 628
  }

629
  DBUG_PRINT("info", ("non-existent, go forward"));
630 631 632

  restore_record(table, s->default_values);     // Get default values for fields

633 634
  if (system_charset_info->cset->
        numchars(system_charset_info, parse_data->dbname.str,
635 636
                 parse_data->dbname.str + parse_data->dbname.length) >
      table->field[ET_FIELD_DB]->char_length())
637
  {
638
    my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->dbname.str);
639
    goto end;
640
  }
641

642 643
  if (system_charset_info->cset->
        numchars(system_charset_info, parse_data->name.str,
644 645
                 parse_data->name.str + parse_data->name.length) >
      table->field[ET_FIELD_NAME]->char_length())
646
  {
647
    my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->name.str);
648
    goto end;
649 650
  }

651
  if (sp->m_body.length > table->field[ET_FIELD_BODY]->field_length)
652
  {
653
    my_error(ER_TOO_LONG_BODY, MYF(0), parse_data->name.str);
654
    goto end;
655 656 657 658 659
  }

  ((Field_timestamp *)table->field[ET_FIELD_CREATED])->set_time();

  /*
660
    mysql_event_fill_row() calls my_error() in case of error so no need to
661 662
    handle it here
  */
663
  if (mysql_event_fill_row(thd, table, parse_data, sp, saved_mode, FALSE))
664
    goto end;
665

666 667
  table->field[ET_FIELD_STATUS]->store((longlong)parse_data->status, TRUE);

668
  if ((ret= table->file->ha_write_row(table->record[0])))
669
  {
670 671
    table->file->print_error(ret, MYF(0));
    goto end;
672
  }
673
  ret= 0;
674

675
end:
676 677
  if (table)
    close_thread_tables(thd);
678
  thd->variables.sql_mode= saved_mode;
679
  DBUG_RETURN(test(ret));
680 681 682
}


683
/**
684 685
  Used to execute ALTER EVENT. Pendant to Events::update_event().

686 687
  @param[in,out]  thd         thread handle
  @param[in]      parse_data  parsed event definition
kostja@bodhi.(none)'s avatar
kostja@bodhi.(none) committed
688
  @param[in]      new_dbname  not NULL if ALTER EVENT RENAME
689 690 691
                              points at a new database name
  @param[in]      new_name    not NULL if ALTER EVENT RENAME
                              points at a new event name
692

693 694 695
  @pre All semantic checks are performed outside this function,
  it only updates the event definition on disk.
  @pre We don't have any tables open in the given thread.
696

697 698
  @retval FALSE success
  @retval TRUE error (reported)
699 700
*/

701
bool
702
Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
703 704
                                  LEX_STRING *new_dbname,
                                  LEX_STRING *new_name)
705 706
{
  CHARSET_INFO *scs= system_charset_info;
andrey@lmy004's avatar
andrey@lmy004 committed
707
  TABLE *table= NULL;
708
  sp_head *sp= thd->lex->sphead;
709
  ulong saved_mode= thd->variables.sql_mode;
710
  int ret= 1;
711

712 713
  DBUG_ENTER("Event_db_repository::update_event");

714
  /* None or both must be set */
715
  DBUG_ASSERT((new_dbname && new_name) || new_dbname == new_name);
716

717 718 719
  /* Reset sql_mode during data dictionary operations. */
  thd->variables.sql_mode= 0;

720 721
  if (open_event_table(thd, TL_WRITE, &table))
    goto end;
722 723 724 725 726

  DBUG_PRINT("info", ("dbname: %s", parse_data->dbname.str));
  DBUG_PRINT("info", ("name: %s", parse_data->name.str));
  DBUG_PRINT("info", ("user: %s", parse_data->definer.str));

727
  /* first look whether we overwrite */
728
  if (new_name)
729
  {
730 731
    DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str));
    if (!find_named_event(*new_dbname, *new_name, table))
732
    {
733
      my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str);
734
      goto end;
735
    }
736 737 738 739 740 741 742
  }
  /*
    ...and then if there is such an event. Don't exchange the blocks
    because you will get error 120 from table handler because new_name will
    overwrite the key and SE will tell us that it cannot find the already found
    row (copied into record[1] later
  */
743
  if (find_named_event(parse_data->dbname, parse_data->name, table))
744
  {
745
    my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), parse_data->name.str);
746
    goto end;
747 748 749 750
  }

  store_record(table,record[1]);

751 752 753 754 755 756 757 758 759
  /*
    We check whether ALTER EVENT was given dates that are in the past.
    However to know how to react, we need the ON COMPLETION type. The
    check is deferred to this point because by now we have the previous
    setting (from the event-table) to fall back on if nothing was specified
    in the ALTER EVENT-statement.
  */

  if (parse_data->check_dates(thd,
760
                              (int) table->field[ET_FIELD_ON_COMPLETION]->val_int()))
761 762
    goto end;

763 764 765 766
  /* Don't update create on row update. */
  table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;

  /*
767
    mysql_event_fill_row() calls my_error() in case of error so no need to
768 769
    handle it here
  */
770
  if (mysql_event_fill_row(thd, table, parse_data, sp, saved_mode, TRUE))
771
    goto end;
772

773
  if (new_dbname)
774
  {
775 776
    table->field[ET_FIELD_DB]->store(new_dbname->str, new_dbname->length, scs);
    table->field[ET_FIELD_NAME]->store(new_name->str, new_name->length, scs);
777 778
  }

779
  if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
780
  {
781 782
    table->file->print_error(ret, MYF(0));
    goto end;
783
  }
784
  ret= 0;
785

786
end:
787 788
  if (table)
    close_thread_tables(thd);
789
  thd->variables.sql_mode= saved_mode;
790
  DBUG_RETURN(test(ret));
791 792 793
}


794 795
/**
  Delete event record from mysql.event table.
796

797 798 799 800 801 802
  @param[in,out] thd            thread handle
  @param[in]     db             Database name
  @param[in]     name           Event name
  @param[in]     drop_if_exists DROP IF EXISTS clause was specified.
                                If set, and the event does not exist,
                                the error is downgraded to a warning.
803

804 805
  @retval FALSE success
  @retval TRUE error (reported)
806 807
*/

808
bool
809
Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
810
                                bool drop_if_exists)
811
{
andrey@lmy004's avatar
andrey@lmy004 committed
812
  TABLE *table= NULL;
813
  int ret= 1;
814 815

  DBUG_ENTER("Event_db_repository::drop_event");
816
  DBUG_PRINT("enter", ("%s@%s", db.str, name.str));
817

818 819
  if (open_event_table(thd, TL_WRITE, &table))
    goto end;
820

821
  if (!find_named_event(db, name, table))
822
  {
823 824 825
    if ((ret= table->file->ha_delete_row(table->record[0])))
      table->file->print_error(ret, MYF(0));
    goto end;
826
  }
827 828 829

  /* Event not found */
  if (!drop_if_exists)
830
  {
831 832
    my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
    goto end;
833 834
  }

835 836 837 838 839 840
  push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                      ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
                      "Event", name.str);
  ret= 0;

end:
andrey@lmy004's avatar
andrey@lmy004 committed
841 842
  if (table)
    close_thread_tables(thd);
843

844
  DBUG_RETURN(test(ret));
845 846 847
}


848
/**
849 850 851
  Positions the internal pointer of `table` to the place where (db, name)
  is stored.

852
  In case search succeeded, the table cursor points at the found row.
853

854 855 856 857 858 859
  @param[in]      db     database name
  @param[in]      name   event name
  @param[in,out]  table  mysql.event table


  @retval FALSE  an event with such db/name key exists
kostja@bodhi.(none)'s avatar
kostja@bodhi.(none) committed
860
  @retval  TRUE   no record found or an error occured.
861 862
*/

863
bool
864 865
Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name,
                                      TABLE *table)
866
{
867
  uchar key[MAX_KEY_LENGTH];
868
  DBUG_ENTER("Event_db_repository::find_named_event");
869
  DBUG_PRINT("enter", ("name: %.*s", (int) name.length, name.str));
870 871 872 873 874 875 876 877 878 879

  /*
    Create key to find row. We have to use field->store() to be able to
    handle VARCHAR and CHAR fields.
    Assumption here is that the two first fields in the table are
    'db' and 'name' and the first key is the primary key over the
    same fields.
  */
  if (db.length > table->field[ET_FIELD_DB]->field_length ||
      name.length > table->field[ET_FIELD_NAME]->field_length)
880
    DBUG_RETURN(TRUE);
881 882 883 884

  table->field[ET_FIELD_DB]->store(db.str, db.length, &my_charset_bin);
  table->field[ET_FIELD_NAME]->store(name.str, name.length, &my_charset_bin);

885
  key_copy(key, table->record[0], table->key_info, table->key_info->key_length);
886

887 888
  if (table->file->index_read_idx_map(table->record[0], 0, key, HA_WHOLE_KEY,
                                      HA_READ_KEY_EXACT))
889 890
  {
    DBUG_PRINT("info", ("Row not found"));
891
    DBUG_RETURN(TRUE);
892 893 894
  }

  DBUG_PRINT("info", ("Row found!"));
895
  DBUG_RETURN(FALSE);
896 897 898
}


899 900
/*
  Drops all events in the selected database, from mysql.event.
901

902 903 904 905 906
  SYNOPSIS
    Event_db_repository::drop_schema_events()
      thd     Thread
      schema  The database to clean from events
*/
907

908
void
909
Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema)
910
{
911 912 913
  DBUG_ENTER("Event_db_repository::drop_schema_events");
  drop_events_by_field(thd, ET_FIELD_DB, schema);
  DBUG_VOID_RETURN;
914 915 916
}


917 918
/**
  Drops all events which have a specific value of a field.
919

920 921 922 923 924 925
  @pre The thread handle has no open tables.

  @param[in,out] thd         Thread
  @param[in,out] table       mysql.event TABLE
  @param[in]     field       Which field of the row to use for matching
  @param[in]     field_value The value that should match
926 927
*/

928
void
929
Event_db_repository::drop_events_by_field(THD *thd,
930 931 932 933
                                          enum enum_events_table_field field,
                                          LEX_STRING field_value)
{
  int ret= 0;
934
  TABLE *table= NULL;
935
  READ_RECORD read_record_info;
936
  DBUG_ENTER("Event_db_repository::drop_events_by_field");
937 938 939
  DBUG_PRINT("enter", ("field=%d field_value=%s", field, field_value.str));

  if (open_event_table(thd, TL_WRITE, &table))
940
    DBUG_VOID_RETURN;
941 942

  /* only enabled events are in memory, so we go now and delete the rest */
943
  init_read_record(&read_record_info, thd, table, NULL, 1, 0, FALSE);
944 945 946 947
  while (!ret && !(read_record_info.read_record(&read_record_info)) )
  {
    char *et_field= get_field(thd->mem_root, table->field[field]);

948 949
    /* et_field may be NULL if the table is corrupted or out of memory */
    if (et_field)
950
    {
951 952 953 954 955 956 957 958 959 960 961
      LEX_STRING et_field_lex= { et_field, strlen(et_field) };
      DBUG_PRINT("info", ("Current event %s name=%s", et_field,
                          get_field(thd->mem_root,
                                    table->field[ET_FIELD_NAME])));

      if (!sortcmp_lex_string(et_field_lex, field_value, system_charset_info))
      {
        DBUG_PRINT("info", ("Dropping"));
        if ((ret= table->file->ha_delete_row(table->record[0])))
          table->file->print_error(ret, MYF(0));
      }
962 963 964
    }
  }
  end_read_record(&read_record_info);
965
  close_thread_tables(thd);
966

967
  DBUG_VOID_RETURN;
968 969 970
}


971
/**
972
  Looks for a named event in mysql.event and then loads it from
973
  the table.
974

975
  @pre The given thread does not have open tables.
976

977 978
  @retval FALSE  success
  @retval TRUE   error
979 980
*/

981
bool
982 983
Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname,
                                      LEX_STRING name, Event_basic *etn)
984
{
985
  bool ret;
986 987
  TABLE *table= NULL;
  ulong saved_mode= thd->variables.sql_mode;
988

989
  DBUG_ENTER("Event_db_repository::load_named_event");
990 991
  DBUG_PRINT("enter",("thd: 0x%lx  name: %*s", (long) thd,
                      (int) name.length, name.str));
992

993 994 995
  /* Reset sql_mode during data dictionary operations. */
  thd->variables.sql_mode= 0;

996 997 998 999 1000 1001
  if (!(ret= open_event_table(thd, TL_READ, &table)))
  {
    if ((ret= find_named_event(dbname, name, table)))
      my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
    else if ((ret= etn->load_from_row(thd, table)))
      my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event");
1002

1003 1004 1005
    close_thread_tables(thd);
  }

1006
  thd->variables.sql_mode= saved_mode;
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
  DBUG_RETURN(ret);
}


/**
  Update the event record in mysql.event table with a changed status
  and/or last execution time.

  @pre The thread handle does not have open tables.
*/

bool
Event_db_repository::
update_timing_fields_for_event(THD *thd,
                               LEX_STRING event_db_name,
                               LEX_STRING event_name,
                               bool update_last_executed,
                               my_time_t last_executed,
                               bool update_status,
                               ulonglong status)
{
  TABLE *table= NULL;
  Field **fields;
  int ret= 1;

  DBUG_ENTER("Event_db_repository::update_timing_fields_for_event");

1034 1035 1036 1037 1038 1039 1040
  /*
    Turn off row binlogging of event timing updates. These are not used
    for RBR of events replicated to the slave.
  */
  if (thd->current_stmt_binlog_row_based)
    thd->clear_current_stmt_binlog_row_based();

1041 1042
  DBUG_ASSERT(thd->security_ctx->master_access & SUPER_ACL);

1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056
  if (open_event_table(thd, TL_WRITE, &table))
    goto end;

  fields= table->field;

  if (find_named_event(event_db_name, event_name, table))
    goto end;

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

  if (update_last_executed)
  {
1057
    MYSQL_TIME time;
1058
    my_tz_OFFSET0->gmt_sec_to_TIME(&time, last_executed);
1059

1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
    fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
    fields[ET_FIELD_LAST_EXECUTED]->store_time(&time,
                                               MYSQL_TIMESTAMP_DATETIME);
  }
  if (update_status)
  {
    fields[ET_FIELD_STATUS]->set_notnull();
    fields[ET_FIELD_STATUS]->store(status, TRUE);
  }

  if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
  {
    table->file->print_error(ret, MYF(0));
    goto end;
  }

  ret= 0;

end:
1079 1080 1081
  if (table)
    close_thread_tables(thd);

1082 1083
  DBUG_RETURN(test(ret));
}
1084

1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111

/**
  Open mysql.db, mysql.user and mysql.event and check whether:
    - mysql.db exists and is up to date (or from a newer version of MySQL),
    - mysql.user has column Event_priv at an expected position,
    - mysql.event exists and is up to date (or from a newer version of
      MySQL)

  This function is called only when the server is started.
  @pre The passed in thread handle has no open tables.

  @retval FALSE  OK
  @retval TRUE   Error, an error message is output to the error log.
*/

bool
Event_db_repository::check_system_tables(THD *thd)
{
  TABLE_LIST tables;
  int ret= FALSE;
  const unsigned int event_priv_column_position= 29;

  DBUG_ENTER("Event_db_repository::check_system_tables");
  DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));


  /* Check mysql.db */
Konstantin Osipov's avatar
Konstantin Osipov committed
1112
  tables.init_one_table("mysql", 5, "db", 2, "db", TL_READ);
1113
  alloc_mdl_locks(&tables, thd->mem_root);
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129

  if (simple_open_n_lock_tables(thd, &tables))
  {
    ret= 1;
    sql_print_error("Cannot open mysql.db");
  }
  else
  {
    if (table_check_intact(tables.table, MYSQL_DB_FIELD_COUNT,
                           mysql_db_table_fields))
      ret= 1;
    /* in case of an error, the message is printed inside table_check_intact */

    close_thread_tables(thd);
  }
  /* Check mysql.user */
Konstantin Osipov's avatar
Konstantin Osipov committed
1130
  tables.init_one_table("mysql", 5, "user", 4, "user", TL_READ);
1131
  alloc_mdl_locks(&tables, thd->mem_root);
1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150

  if (simple_open_n_lock_tables(thd, &tables))
  {
    ret= 1;
    sql_print_error("Cannot open mysql.user");
  }
  else
  {
    if (tables.table->s->fields < event_priv_column_position ||
        strncmp(tables.table->field[event_priv_column_position]->field_name,
                STRING_WITH_LEN("Event_priv")))
    {
      sql_print_error("mysql.user has no `Event_priv` column at position %d",
                      event_priv_column_position);
      ret= 1;
    }
    close_thread_tables(thd);
  }
  /* Check mysql.event */
Konstantin Osipov's avatar
Konstantin Osipov committed
1151
  tables.init_one_table("mysql", 5, "event", 5, "event", TL_READ);
1152
  alloc_mdl_locks(&tables, thd->mem_root);
1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167

  if (simple_open_n_lock_tables(thd, &tables))
  {
    ret= 1;
    sql_print_error("Cannot open mysql.event");
  }
  else
  {
    if (table_check_intact(tables.table, ET_FIELD_COUNT, event_table_fields))
      ret= 1;
    /* in case of an error, the message is printed inside table_check_intact */
    close_thread_tables(thd);
  }

  DBUG_RETURN(test(ret));
1168
}
1169 1170 1171 1172

/**
  @} (End of group Event_Scheduler)
*/