event_db_repository.cc 36 KB
Newer Older
1
/*
2
   Copyright (c) 2006, 2011, Oracle and/or its affiliates.
3 4 5

   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
unknown's avatar
unknown committed
6
   the Free Software Foundation; version 2 of the License.
7 8 9 10 11 12 13 14

   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
15
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
16

17 18 19
#include "sql_priv.h"
#include "unireg.h"
#include "sql_base.h"                           // close_thread_tables
20
#include "event_db_repository.h"
21 22 23 24 25 26
#include "key.h"                                // key_copy
#include "sql_db.h"                        // get_default_db_collation
#include "sql_time.h"                      // interval_type_to_name
#include "tztime.h"                             // struct Time_zone
#include "sql_acl.h" // SUPER_ACL, MYSQL_DB_FIELD_COUNT, mysql_db_table_fields
#include "records.h"          // init_read_record, end_read_record
27
#include "sp_head.h"
28
#include "event_data_objects.h"
unknown's avatar
unknown committed
29
#include "events.h"
unknown's avatar
unknown committed
30
#include "sql_show.h"
31
#include "lock.h"                               // MYSQL_LOCK_IGNORE_TIMEOUT
unknown's avatar
unknown committed
32

33 34 35 36 37
/**
  @addtogroup Event_Scheduler
  @{
*/

unknown's avatar
unknown committed
38
static
39
const TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] =
40
{
unknown's avatar
unknown committed
41
  {
42 43 44
    { C_STRING_WITH_LEN("db") },
    { C_STRING_WITH_LEN("char(64)") },
    { C_STRING_WITH_LEN("utf8") }
unknown's avatar
unknown committed
45
  },
unknown's avatar
unknown committed
46
  {
47 48 49
    { C_STRING_WITH_LEN("name") },
    { C_STRING_WITH_LEN("char(64)") },
    { C_STRING_WITH_LEN("utf8") }
unknown's avatar
unknown committed
50 51
  },
  {
52 53
    { C_STRING_WITH_LEN("body") },
    { C_STRING_WITH_LEN("longblob") },
unknown's avatar
unknown committed
54
    {NULL, 0}
unknown's avatar
unknown committed
55
  },
unknown's avatar
unknown committed
56
  {
57 58 59
    { C_STRING_WITH_LEN("definer") },
    { C_STRING_WITH_LEN("char(77)") },
    { C_STRING_WITH_LEN("utf8") }
unknown's avatar
unknown committed
60 61
  },
  {
62 63
    { C_STRING_WITH_LEN("execute_at") },
    { C_STRING_WITH_LEN("datetime") },
unknown's avatar
unknown committed
64
    {NULL, 0}
unknown's avatar
unknown committed
65
  },
unknown's avatar
unknown committed
66
  {
67 68
    { C_STRING_WITH_LEN("interval_value") },
    { C_STRING_WITH_LEN("int(11)") },
unknown's avatar
unknown committed
69 70 71
    {NULL, 0}
  },
  {
72 73
    { C_STRING_WITH_LEN("interval_field") },
    { C_STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY',"
unknown's avatar
unknown committed
74 75 76
    "'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',"
77
    "'SECOND_MICROSECOND')") },
unknown's avatar
unknown committed
78
    {NULL, 0}
unknown's avatar
unknown committed
79
  },
unknown's avatar
unknown committed
80
  {
81 82
    { C_STRING_WITH_LEN("created") },
    { C_STRING_WITH_LEN("timestamp") },
unknown's avatar
unknown committed
83 84 85
    {NULL, 0}
  },
  {
86 87
    { C_STRING_WITH_LEN("modified") },
    { C_STRING_WITH_LEN("timestamp") },
unknown's avatar
unknown committed
88
    {NULL, 0}
unknown's avatar
unknown committed
89
  },
unknown's avatar
unknown committed
90
  {
91 92
    { C_STRING_WITH_LEN("last_executed") },
    { C_STRING_WITH_LEN("datetime") },
unknown's avatar
unknown committed
93 94 95
    {NULL, 0}
  },
  {
96 97
    { C_STRING_WITH_LEN("starts") },
    { C_STRING_WITH_LEN("datetime") },
unknown's avatar
unknown committed
98
    {NULL, 0}
unknown's avatar
unknown committed
99
  },
unknown's avatar
unknown committed
100
  {
101 102
    { C_STRING_WITH_LEN("ends") },
    { C_STRING_WITH_LEN("datetime") },
unknown's avatar
unknown committed
103 104 105
    {NULL, 0}
  },
  {
106
    { C_STRING_WITH_LEN("status") },
107
    { C_STRING_WITH_LEN("enum('ENABLED','DISABLED','SLAVESIDE_DISABLED')") },
unknown's avatar
unknown committed
108
    {NULL, 0}
unknown's avatar
unknown committed
109
  },
unknown's avatar
unknown committed
110
  {
111 112
    { C_STRING_WITH_LEN("on_completion") },
    { C_STRING_WITH_LEN("enum('DROP','PRESERVE')") },
unknown's avatar
unknown committed
113 114 115
    {NULL, 0}
  },
  {
116 117
    { C_STRING_WITH_LEN("sql_mode") },
    { C_STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES',"
118 119
    "'IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY',"
    "'NO_UNSIGNED_SUBTRACTION',"
unknown's avatar
unknown committed
120 121 122 123 124
    "'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',"
125
    "'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH')") },
unknown's avatar
unknown committed
126 127 128
    {NULL, 0}
  },
  {
129 130 131
    { C_STRING_WITH_LEN("comment") },
    { C_STRING_WITH_LEN("char(64)") },
    { C_STRING_WITH_LEN("utf8") }
132
  },
133 134 135 136
  {
    { C_STRING_WITH_LEN("originator") },
    { C_STRING_WITH_LEN("int(10)") },
    {NULL, 0}
137
  },
138 139 140 141
  {
    { C_STRING_WITH_LEN("time_zone") },
    { C_STRING_WITH_LEN("char(64)") },
    { C_STRING_WITH_LEN("latin1") }
unknown's avatar
unknown committed
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
  },
  {
    { 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 }
unknown's avatar
unknown committed
162 163 164
  }
};

165
static const TABLE_FIELD_DEF
166
event_table_def= {ET_FIELD_COUNT, event_table_fields, 0, (uint*) 0};
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182

class Event_db_intact : public Table_check_intact
{
protected:
  void report_error(uint, const char *fmt, ...)
  {
    va_list args;
    va_start(args, fmt);
    error_log_print(ERROR_LEVEL, fmt, args);
    va_end(args);
  }
};

/** In case of an error, a message is printed to the error log. */
static Event_db_intact table_intact;

unknown's avatar
unknown committed
183

184
/**
unknown's avatar
unknown committed
185 186
  Puts some data common to CREATE and ALTER EVENT into a row.

187
  Used both when an event is created and when it is altered.
unknown's avatar
unknown committed
188

189 190 191
  @param   thd        THD
  @param   table      The row to fill out
  @param   et         Event's data
unknown's avatar
unknown committed
192
  @param   sp         Event stored routine
193
  @param   is_update  CREATE EVENT or ALTER EVENT
unknown's avatar
unknown committed
194

195 196
  @retval  FALSE success
  @retval  TRUE error
unknown's avatar
unknown committed
197 198
*/

199
static bool
200 201 202 203
mysql_event_fill_row(THD *thd,
                     TABLE *table,
                     Event_parse_data *et,
                     sp_head *sp,
204
                     ulonglong sql_mode,
205
                     my_bool is_update)
unknown's avatar
unknown committed
206 207
{
  CHARSET_INFO *scs= system_charset_info;
208 209
  enum enum_events_table_field f_num;
  Field **fields= table->field;
210
  int rs= FALSE;
unknown's avatar
unknown committed
211

212
  DBUG_ENTER("mysql_event_fill_row");
unknown's avatar
unknown committed
213 214 215 216

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

217 218
  DBUG_ASSERT(et->on_completion != Event_parse_data::ON_COMPLETION_DEFAULT);

219 220 221 222 223 224
  if (table->s->fields < ET_FIELD_COUNT)
  {
    /*
      Safety: this can only happen if someone started the server
      and then altered mysql.event.
    */
225
    my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED, MYF(0), table->alias.c_ptr(),
226 227 228 229
             (int) ET_FIELD_COUNT, table->s->fields);
    DBUG_RETURN(TRUE);
  }

230 231
  if (fields[f_num= ET_FIELD_DEFINER]->
                              store(et->definer.str, et->definer.length, scs))
unknown's avatar
unknown committed
232 233
    goto err_truncate;

234
  if (fields[f_num= ET_FIELD_DB]->store(et->dbname.str, et->dbname.length, scs))
unknown's avatar
unknown committed
235 236
    goto err_truncate;

237
  if (fields[f_num= ET_FIELD_NAME]->store(et->name.str, et->name.length, scs))
unknown's avatar
unknown committed
238 239
    goto err_truncate;

240
  /* ON_COMPLETION field is NOT NULL thus not calling set_notnull()*/
241
  rs|= fields[ET_FIELD_ON_COMPLETION]->store((longlong)et->on_completion, TRUE);
242 243 244 245 246 247 248 249

  /*
    Set STATUS value unconditionally in case of CREATE EVENT.
    For ALTER EVENT set it only if value of this field was changed.
    Since STATUS field is NOT NULL call to set_notnull() is not needed.
  */
  if (!is_update || et->status_changed)
    rs|= fields[ET_FIELD_STATUS]->store((longlong)et->status, TRUE);
250
  rs|= fields[ET_FIELD_ORIGINATOR]->store((longlong)et->originator, TRUE);
251

252 253 254
  if (!is_update)
    rs|= fields[ET_FIELD_CREATED]->set_time();

unknown's avatar
unknown committed
255 256 257
  /*
    Change the SQL_MODE only if body was present in an ALTER EVENT and of course
    always during CREATE EVENT.
258
  */
259
  if (et->body_changed)
unknown's avatar
unknown committed
260
  {
261 262
    DBUG_ASSERT(sp->m_body.str);

263
    rs|= fields[ET_FIELD_SQL_MODE]->store((longlong)sql_mode, TRUE);
264 265 266 267 268

    if (fields[f_num= ET_FIELD_BODY]->store(sp->m_body.str,
                                            sp->m_body.length,
                                            scs))
    {
unknown's avatar
unknown committed
269
      goto err_truncate;
270
    }
unknown's avatar
unknown committed
271 272
  }

unknown's avatar
unknown committed
273 274
  if (et->expression)
  {
275 276 277 278
    const String *tz_name= thd->variables.time_zone->get_name();
    if (!is_update || !et->starts_null)
    {
      fields[ET_FIELD_TIME_ZONE]->set_notnull();
279 280
      rs|= fields[ET_FIELD_TIME_ZONE]->store(tz_name->ptr(), tz_name->length(),
                                             tz_name->charset());
281 282
    }

283
    fields[ET_FIELD_INTERVAL_EXPR]->set_notnull();
284
    rs|= fields[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, TRUE);
unknown's avatar
unknown committed
285

286
    fields[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull();
287

288
    rs|= fields[ET_FIELD_TRANSIENT_INTERVAL]->
289 290 291
                            store(interval_type_to_name[et->interval].str,
                                  interval_type_to_name[et->interval].length,
                                  scs);
unknown's avatar
unknown committed
292

293
    fields[ET_FIELD_EXECUTE_AT]->set_null();
unknown's avatar
unknown committed
294 295 296

    if (!et->starts_null)
    {
297
      MYSQL_TIME time;
298
      my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->starts);
299

300
      fields[ET_FIELD_STARTS]->set_notnull();
301
      fields[ET_FIELD_STARTS]->store_time(&time);
302
    }
unknown's avatar
unknown committed
303 304 305

    if (!et->ends_null)
    {
306
      MYSQL_TIME time;
307
      my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->ends);
308

309
      fields[ET_FIELD_ENDS]->set_notnull();
310
      fields[ET_FIELD_ENDS]->store_time(&time);
unknown's avatar
unknown committed
311 312
    }
  }
313
  else if (et->execute_at)
unknown's avatar
unknown committed
314
  {
315 316
    const String *tz_name= thd->variables.time_zone->get_name();
    fields[ET_FIELD_TIME_ZONE]->set_notnull();
317 318
    rs|= fields[ET_FIELD_TIME_ZONE]->store(tz_name->ptr(), tz_name->length(),
                                           tz_name->charset());
319

320 321 322 323
    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();
324

325
    MYSQL_TIME time;
326
    my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->execute_at);
327

328
    fields[ET_FIELD_EXECUTE_AT]->set_notnull();
329
    fields[ET_FIELD_EXECUTE_AT]->store_time(&time);
unknown's avatar
unknown committed
330 331 332 333 334 335 336 337 338
  }
  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
    */
  }
339

340
  rs|= fields[ET_FIELD_MODIFIED]->set_time();
unknown's avatar
unknown committed
341 342 343

  if (et->comment.str)
  {
344 345
    if (fields[f_num= ET_FIELD_COMMENT]->
                          store(et->comment.str, et->comment.length, scs))
unknown's avatar
unknown committed
346 347 348
      goto err_truncate;
  }

unknown's avatar
unknown committed
349
  fields[ET_FIELD_CHARACTER_SET_CLIENT]->set_notnull();
350
  rs|= fields[ET_FIELD_CHARACTER_SET_CLIENT]->store(
unknown's avatar
unknown committed
351 352 353 354 355
    thd->variables.character_set_client->csname,
    strlen(thd->variables.character_set_client->csname),
    system_charset_info);

  fields[ET_FIELD_COLLATION_CONNECTION]->set_notnull();
356
  rs|= fields[ET_FIELD_COLLATION_CONNECTION]->store(
unknown's avatar
unknown committed
357 358 359 360 361 362 363 364
    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();
365 366 367
    rs|= fields[ET_FIELD_DB_COLLATION]->store(db_cl->name,
                                              strlen(db_cl->name),
                                              system_charset_info);
unknown's avatar
unknown committed
368 369 370 371 372
  }

  if (et->body_changed)
  {
    fields[ET_FIELD_BODY_UTF8]->set_notnull();
373 374 375 376 377 378 379 380 381
    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);
unknown's avatar
unknown committed
382 383
  }

384
  DBUG_RETURN(FALSE);
385

unknown's avatar
unknown committed
386
err_truncate:
387
  my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), fields[f_num]->field_name);
388
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
389 390 391
}


unknown's avatar
unknown committed
392 393 394
/*
  Performs an index scan of event_table (mysql.event) and fills schema_table.

395
  SYNOPSIS
unknown's avatar
unknown committed
396 397 398 399
    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)
400
      db           For which schema to do an index scan.
unknown's avatar
unknown committed
401

402
  RETURN VALUE
unknown's avatar
unknown committed
403 404 405 406
    0  OK
    1  Error
*/

407
bool
unknown's avatar
unknown committed
408
Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
409 410
                                               TABLE *event_table,
                                               const char *db)
unknown's avatar
unknown committed
411 412 413 414
{
  CHARSET_INFO *scs= system_charset_info;
  KEY *key_info;
  uint key_len;
415
  uchar *key_buf= NULL;
unknown's avatar
unknown committed
416 417 418 419 420
  LINT_INIT(key_buf);

  DBUG_ENTER("Event_db_repository::index_read_for_db_for_i_s");

  DBUG_PRINT("info", ("Using prefix scanning on PK"));
421 422 423 424 425 426 427 428

  int ret= event_table->file->ha_index_init(0, 1);
  if (ret)
  {
    event_table->file->print_error(ret, MYF(0));
    DBUG_RETURN(true);
  }

unknown's avatar
unknown committed
429
  key_info= event_table->key_info;
430 431 432 433 434 435 436 437 438 439 440

  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);
unknown's avatar
unknown committed
441 442
  key_len= key_info->key_part[0].store_length;

443
  if (!(key_buf= (uchar *)alloc_root(thd->mem_root, key_len)))
unknown's avatar
unknown committed
444
  {
445
    /* Don't send error, it would be done by sql_alloc_error_handler() */
446 447
    ret= 1;
    goto end;
unknown's avatar
unknown committed
448
  }
449 450

  key_copy(key_buf, event_table->record[0], key_info, key_len);
451 452 453
  if (!(ret= event_table->file->ha_index_read_map(event_table->record[0],
                                                  key_buf,
                                                  (key_part_map)1,
Sergei Golubchik's avatar
Sergei Golubchik committed
454
                                                  HA_READ_KEY_EXACT)))
unknown's avatar
unknown committed
455
  {
456 457
    DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret));
    do
unknown's avatar
unknown committed
458
    {
459 460
      ret= copy_event_to_schema_table(thd, schema_table, event_table);
      if (ret == 0)
461 462
        ret= event_table->file->ha_index_next_same(event_table->record[0],
                                                   key_buf, key_len);
463
    } while (ret == 0);
unknown's avatar
unknown committed
464
  }
465 466
  DBUG_PRINT("info", ("Scan finished. ret=%d", ret));

unknown's avatar
unknown committed
467 468
  /*  ret is guaranteed to be != 0 */
  if (ret == HA_ERR_END_OF_FILE || ret == HA_ERR_KEY_NOT_FOUND)
469 470 471
    ret= 0;
  else
    event_table->file->print_error(ret, MYF(0));
472

473 474 475 476
end:
  event_table->file->ha_index_end();

  DBUG_RETURN(test(ret));
unknown's avatar
unknown committed
477 478 479 480 481 482
}


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

483
  SYNOPSIS
unknown's avatar
unknown committed
484 485 486 487 488
    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.

489 490 491
  RETURN VALUE
    FALSE  OK
    TRUE   Error
unknown's avatar
unknown committed
492 493
*/

494
bool
unknown's avatar
unknown committed
495 496 497 498 499 500
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");
501

502 503
  if (init_read_record(&read_record_info, thd, event_table, NULL, 1, 0, FALSE))
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
504 505 506 507 508 509 510 511 512 513 514

  /*
    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);
515
  } while (ret == 0);
unknown's avatar
unknown committed
516 517 518 519 520

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

  /*  ret is guaranteed to be != 0 */
521
  DBUG_RETURN(ret == -1? FALSE:TRUE);
unknown's avatar
unknown committed
522 523 524
}


525
/**
unknown's avatar
unknown committed
526 527 528
  Fills I_S.EVENTS with data loaded from mysql.event. Also used by
  SHOW EVENTS

529 530 531 532 533
  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.
unknown's avatar
unknown committed
534

535 536
  @retval FALSE  success
  @retval TRUE   error
unknown's avatar
unknown committed
537 538
*/

539
bool
540
Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *i_s_table,
541
                                        const char *db)
unknown's avatar
unknown committed
542
{
543 544 545
  TABLE *schema_table= i_s_table->table;
  Open_tables_backup open_tables_backup;
  TABLE_LIST event_table;
unknown's avatar
unknown committed
546 547 548 549 550
  int ret= 0;

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

551 552 553
  event_table.init_one_table("mysql", 5, "event", 5, "event", TL_READ);

  if (open_system_tables_for_read(thd, &event_table, &open_tables_backup))
554
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
555

556 557 558 559
  if (table_intact.check(event_table.table, &event_table_def))
  {
    close_system_tables(thd, &open_tables_backup);
    my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
560
    DBUG_RETURN(TRUE);
561
  }
unknown's avatar
unknown committed
562 563 564 565 566 567 568 569 570 571 572

  /*
    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)
573
    ret= index_read_for_db_for_i_s(thd, schema_table, event_table.table, db);
unknown's avatar
unknown committed
574
  else
575
    ret= table_scan_all_for_i_s(thd, schema_table, event_table.table);
unknown's avatar
unknown committed
576

577
  close_system_tables(thd, &open_tables_backup);
unknown's avatar
unknown committed
578 579 580 581 582 583

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


584 585
/**
  Open mysql.event table for read.
unknown's avatar
unknown committed
586

587 588 589 590 591
  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.
unknown's avatar
unknown committed
592

593 594 595 596
  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.

597 598 599 600 601 602 603
  @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
unknown's avatar
unknown committed
604 605
*/

606
bool
unknown's avatar
unknown committed
607 608 609 610 611 612
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
613
  tables.init_one_table("mysql", 5, "event", 5, "event", lock_type);
unknown's avatar
unknown committed
614

615
  if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
616
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
617

unknown's avatar
unknown committed
618 619
  *table= tables.table;
  tables.table->use_all_columns();
620 621 622 623 624 625 626 627

  if (table_intact.check(*table, &event_table_def))
  {
    close_thread_tables(thd);
    my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
    DBUG_RETURN(TRUE);
  }

628
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
629 630 631
}


632 633
/**
  Creates an event record in mysql.event table.
unknown's avatar
unknown committed
634

635 636
  Creates an event. Relies on mysql_event_fill_row which is shared with
  ::update_event.
unknown's avatar
unknown committed
637

638 639 640
  @pre All semantic checks must be performed outside. This function
  only creates a record on disk.
  @pre The thread handle has no open tables.
unknown's avatar
unknown committed
641

642 643 644 645 646 647 648
  @param[in,out] thd                   THD
  @param[in]     parse_data            Parsed event definition
  @param[in]     create_if_not         TRUE if IF NOT EXISTS clause was provided
                                       to CREATE EVENT statement
  @param[out]    event_already_exists  When method is completed successfully
                                       set to true if event already exists else
                                       set to false
649 650
  @retval FALSE  success
  @retval TRUE   error
unknown's avatar
unknown committed
651 652
*/

653
bool
unknown's avatar
unknown committed
654
Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
655 656
                                  bool create_if_not,
                                  bool *event_already_exists)
unknown's avatar
unknown committed
657
{
658
  int ret= 1;
unknown's avatar
unknown committed
659
  TABLE *table= NULL;
660
  sp_head *sp= thd->lex->sphead;
661
  ulonglong saved_mode= thd->variables.sql_mode;
662 663 664 665 666 667
  /*
    Take a savepoint to release only the lock on mysql.event
    table at the end but keep the global read lock and
    possible other locks taken by the caller.
  */
  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
unknown's avatar
unknown committed
668

unknown's avatar
unknown committed
669 670 671
  DBUG_ENTER("Event_db_repository::create_event");

  DBUG_PRINT("info", ("open mysql.event for update"));
672
  DBUG_ASSERT(sp);
673

674 675 676
  /* Reset sql_mode during data dictionary operations. */
  thd->variables.sql_mode= 0;

unknown's avatar
unknown committed
677
  if (open_event_table(thd, TL_WRITE, &table))
678
    goto end;
unknown's avatar
unknown committed
679

680
  DBUG_PRINT("info", ("name: %.*s", (int) parse_data->name.length,
unknown's avatar
unknown committed
681 682
             parse_data->name.str));

unknown's avatar
unknown committed
683
  DBUG_PRINT("info", ("check existance of an event with the same name"));
684
  if (!find_named_event(parse_data->dbname, parse_data->name, table))
unknown's avatar
unknown committed
685 686 687
  {
    if (create_if_not)
    {
688
      *event_already_exists= true;
unknown's avatar
unknown committed
689 690
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
unknown's avatar
unknown committed
691
                          parse_data->name.str);
692
      ret= 0;
unknown's avatar
unknown committed
693
    }
694 695
    else
      my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str);
696

697
    goto end;
698 699
  } else
    *event_already_exists= false;
unknown's avatar
unknown committed
700

701
  DBUG_PRINT("info", ("non-existent, go forward"));
unknown's avatar
unknown committed
702 703 704

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

unknown's avatar
unknown committed
705 706
  if (system_charset_info->cset->
        numchars(system_charset_info, parse_data->dbname.str,
707 708
                 parse_data->dbname.str + parse_data->dbname.length) >
      table->field[ET_FIELD_DB]->char_length())
unknown's avatar
unknown committed
709
  {
unknown's avatar
unknown committed
710
    my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->dbname.str);
711
    goto end;
unknown's avatar
unknown committed
712
  }
713

unknown's avatar
unknown committed
714 715
  if (system_charset_info->cset->
        numchars(system_charset_info, parse_data->name.str,
716 717
                 parse_data->name.str + parse_data->name.length) >
      table->field[ET_FIELD_NAME]->char_length())
unknown's avatar
unknown committed
718
  {
unknown's avatar
unknown committed
719
    my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->name.str);
720
    goto end;
unknown's avatar
unknown committed
721 722
  }

723
  if (sp->m_body.length > table->field[ET_FIELD_BODY]->field_length)
unknown's avatar
unknown committed
724
  {
unknown's avatar
unknown committed
725
    my_error(ER_TOO_LONG_BODY, MYF(0), parse_data->name.str);
726
    goto end;
unknown's avatar
unknown committed
727 728 729
  }

  /*
730
    mysql_event_fill_row() calls my_error() in case of error so no need to
unknown's avatar
unknown committed
731 732
    handle it here
  */
733
  if (mysql_event_fill_row(thd, table, parse_data, sp, saved_mode, FALSE))
734
    goto end;
unknown's avatar
unknown committed
735

736
  if ((ret= table->file->ha_write_row(table->record[0])))
unknown's avatar
unknown committed
737
  {
738 739
    table->file->print_error(ret, MYF(0));
    goto end;
unknown's avatar
unknown committed
740
  }
741
  ret= 0;
unknown's avatar
unknown committed
742

743
end:
744 745
  close_thread_tables(thd);
  thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
746

747
  thd->variables.sql_mode= saved_mode;
748
  DBUG_RETURN(test(ret));
unknown's avatar
unknown committed
749 750 751
}


752
/**
unknown's avatar
unknown committed
753 754
  Used to execute ALTER EVENT. Pendant to Events::update_event().

755 756
  @param[in,out]  thd         thread handle
  @param[in]      parse_data  parsed event definition
unknown's avatar
unknown committed
757
  @param[in]      new_dbname  not NULL if ALTER EVENT RENAME
758 759 760
                              points at a new database name
  @param[in]      new_name    not NULL if ALTER EVENT RENAME
                              points at a new event name
unknown's avatar
unknown committed
761

762 763 764
  @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.
unknown's avatar
unknown committed
765

766 767
  @retval FALSE success
  @retval TRUE error (reported)
unknown's avatar
unknown committed
768 769
*/

770
bool
unknown's avatar
unknown committed
771
Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
772 773
                                  LEX_STRING *new_dbname,
                                  LEX_STRING *new_name)
unknown's avatar
unknown committed
774 775
{
  CHARSET_INFO *scs= system_charset_info;
unknown's avatar
unknown committed
776
  TABLE *table= NULL;
777
  sp_head *sp= thd->lex->sphead;
778
  ulonglong saved_mode= thd->variables.sql_mode;
779 780 781 782 783 784
  /*
    Take a savepoint to release only the lock on mysql.event
    table at the end but keep the global read lock and
    possible other locks taken by the caller.
  */
  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
785
  int ret= 1;
786

unknown's avatar
unknown committed
787 788
  DBUG_ENTER("Event_db_repository::update_event");

789
  /* None or both must be set */
790
  DBUG_ASSERT((new_dbname && new_name) || new_dbname == new_name);
unknown's avatar
unknown committed
791

792 793 794
  /* Reset sql_mode during data dictionary operations. */
  thd->variables.sql_mode= 0;

795 796
  if (open_event_table(thd, TL_WRITE, &table))
    goto end;
unknown's avatar
unknown committed
797 798 799 800 801

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

unknown's avatar
unknown committed
802
  /* first look whether we overwrite */
803
  if (new_name)
unknown's avatar
unknown committed
804
  {
805 806
    DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str));
    if (!find_named_event(*new_dbname, *new_name, table))
unknown's avatar
unknown committed
807
    {
808
      my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str);
809
      goto end;
unknown's avatar
unknown committed
810
    }
unknown's avatar
unknown committed
811 812 813 814 815 816 817
  }
  /*
    ...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
  */
818
  if (find_named_event(parse_data->dbname, parse_data->name, table))
unknown's avatar
unknown committed
819
  {
unknown's avatar
unknown committed
820
    my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), parse_data->name.str);
821
    goto end;
unknown's avatar
unknown committed
822 823 824 825
  }

  store_record(table,record[1]);

826 827 828 829 830 831 832 833 834
  /*
    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,
835
                              (int) table->field[ET_FIELD_ON_COMPLETION]->val_int()))
836 837
    goto end;

unknown's avatar
unknown committed
838
  /*
839
    mysql_event_fill_row() calls my_error() in case of error so no need to
unknown's avatar
unknown committed
840 841
    handle it here
  */
842
  if (mysql_event_fill_row(thd, table, parse_data, sp, saved_mode, TRUE))
843
    goto end;
unknown's avatar
unknown committed
844

845
  if (new_dbname)
unknown's avatar
unknown committed
846
  {
847 848
    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);
unknown's avatar
unknown committed
849 850
  }

851
  if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
unknown's avatar
unknown committed
852
  {
853 854
    table->file->print_error(ret, MYF(0));
    goto end;
unknown's avatar
unknown committed
855
  }
856
  ret= 0;
unknown's avatar
unknown committed
857

858
end:
859 860
  close_thread_tables(thd);
  thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
861

862
  thd->variables.sql_mode= saved_mode;
863
  DBUG_RETURN(test(ret));
unknown's avatar
unknown committed
864 865 866
}


867 868
/**
  Delete event record from mysql.event table.
unknown's avatar
unknown committed
869

870 871 872 873 874 875
  @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.
unknown's avatar
unknown committed
876

877 878
  @retval FALSE success
  @retval TRUE error (reported)
unknown's avatar
unknown committed
879 880
*/

881
bool
unknown's avatar
unknown committed
882
Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
883
                                bool drop_if_exists)
unknown's avatar
unknown committed
884
{
unknown's avatar
unknown committed
885
  TABLE *table= NULL;
886 887 888 889 890 891
  /*
    Take a savepoint to release only the lock on mysql.event
    table at the end but keep the global read lock and
    possible other locks taken by the caller.
  */
  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
892
  int ret= 1;
unknown's avatar
unknown committed
893 894

  DBUG_ENTER("Event_db_repository::drop_event");
895
  DBUG_PRINT("enter", ("%s@%s", db.str, name.str));
unknown's avatar
unknown committed
896

897 898
  if (open_event_table(thd, TL_WRITE, &table))
    goto end;
unknown's avatar
unknown committed
899

900
  if (!find_named_event(db, name, table))
901
  {
902 903 904
    if ((ret= table->file->ha_delete_row(table->record[0])))
      table->file->print_error(ret, MYF(0));
    goto end;
905
  }
906 907 908

  /* Event not found */
  if (!drop_if_exists)
909
  {
910 911
    my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
    goto end;
unknown's avatar
unknown committed
912 913
  }

914 915 916 917 918 919
  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:
920 921
  close_thread_tables(thd);
  thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
922

923
  DBUG_RETURN(test(ret));
unknown's avatar
unknown committed
924 925 926
}


927
/**
928 929 930
  Positions the internal pointer of `table` to the place where (db, name)
  is stored.

931
  In case search succeeded, the table cursor points at the found row.
932

933 934 935 936 937 938
  @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
unknown's avatar
unknown committed
939
  @retval  TRUE   no record found or an error occured.
940 941
*/

942
bool
943 944
Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name,
                                      TABLE *table)
unknown's avatar
unknown committed
945
{
946
  uchar key[MAX_KEY_LENGTH];
947
  DBUG_ENTER("Event_db_repository::find_named_event");
948
  DBUG_PRINT("enter", ("name: %.*s", (int) name.length, name.str));
unknown's avatar
unknown committed
949 950 951 952 953 954 955 956 957

  /*
    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 ||
958 959 960 961 962
      name.length > table->field[ET_FIELD_NAME]->field_length ||
      table->s->keys == 0 ||
      table->key_info[0].key_parts != 2 ||
      table->key_info[0].key_part[0].fieldnr != ET_FIELD_DB+1 ||
      table->key_info[0].key_part[1].fieldnr != ET_FIELD_NAME+1)
963
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
964 965 966 967

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

968
  key_copy(key, table->record[0], table->key_info, table->key_info->key_length);
unknown's avatar
unknown committed
969

970 971 972
  if (table->file->ha_index_read_idx_map(table->record[0], 0, key,
                                         HA_WHOLE_KEY,
                                         HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
973 974
  {
    DBUG_PRINT("info", ("Row not found"));
975
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
976 977 978
  }

  DBUG_PRINT("info", ("Row found!"));
979
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
980 981 982
}


983 984
/*
  Drops all events in the selected database, from mysql.event.
unknown's avatar
unknown committed
985

986 987 988 989 990
  SYNOPSIS
    Event_db_repository::drop_schema_events()
      thd     Thread
      schema  The database to clean from events
*/
unknown's avatar
unknown committed
991

992
void
993
Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema)
unknown's avatar
unknown committed
994 995
{
  int ret= 0;
unknown's avatar
unknown committed
996
  TABLE *table= NULL;
unknown's avatar
unknown committed
997
  READ_RECORD read_record_info;
998
  enum enum_events_table_field field= ET_FIELD_DB;
999
  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
1000 1001
  DBUG_ENTER("Event_db_repository::drop_schema_events");
  DBUG_PRINT("enter", ("field=%d schema=%s", field, schema.str));
unknown's avatar
unknown committed
1002 1003

  if (open_event_table(thd, TL_WRITE, &table))
1004
    DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1005 1006

  /* only enabled events are in memory, so we go now and delete the rest */
1007 1008 1009
  if (init_read_record(&read_record_info, thd, table, NULL, 1, 0, FALSE))
    goto end;

unknown's avatar
unknown committed
1010 1011 1012 1013
  while (!ret && !(read_record_info.read_record(&read_record_info)) )
  {
    char *et_field= get_field(thd->mem_root, table->field[field]);

1014 1015
    /* et_field may be NULL if the table is corrupted or out of memory */
    if (et_field)
unknown's avatar
unknown committed
1016
    {
1017 1018 1019 1020 1021
      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])));

1022
      if (!sortcmp_lex_string(et_field_lex, schema, system_charset_info))
1023 1024 1025 1026 1027
      {
        DBUG_PRINT("info", ("Dropping"));
        if ((ret= table->file->ha_delete_row(table->record[0])))
          table->file->print_error(ret, MYF(0));
      }
unknown's avatar
unknown committed
1028 1029 1030 1031
    }
  }
  end_read_record(&read_record_info);

1032
end:
1033
  close_thread_tables(thd);
1034 1035 1036 1037 1038
  /*
    Make sure to only release the MDL lock on mysql.event, not other
    metadata locks DROP DATABASE might have acquired.
  */
  thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
unknown's avatar
unknown committed
1039

1040
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1041 1042 1043
}


1044
/**
1045
  Looks for a named event in mysql.event and then loads it from
1046
  the table.
1047

1048
  @pre The given thread does not have open tables.
1049

1050 1051
  @retval FALSE  success
  @retval TRUE   error
1052 1053
*/

1054
bool
1055 1056
Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname,
                                      LEX_STRING name, Event_basic *etn)
1057
{
1058
  bool ret;
1059
  ulonglong saved_mode= thd->variables.sql_mode;
1060 1061
  Open_tables_backup open_tables_backup;
  TABLE_LIST event_table;
1062

1063
  DBUG_ENTER("Event_db_repository::load_named_event");
1064 1065
  DBUG_PRINT("enter",("thd: 0x%lx  name: %*s", (long) thd,
                      (int) name.length, name.str));
1066

1067 1068
  event_table.init_one_table("mysql", 5, "event", 5, "event", TL_READ);

1069 1070 1071
  /* Reset sql_mode during data dictionary operations. */
  thd->variables.sql_mode= 0;

1072 1073 1074 1075 1076 1077 1078
  /*
    We don't use open_event_table() here to make sure that SHOW
    CREATE EVENT works properly in transactional context, and
    does not release transactional metadata locks when the
    event table is closed.
  */
  if (!(ret= open_system_tables_for_read(thd, &event_table, &open_tables_backup)))
1079
  {
1080 1081 1082 1083 1084 1085 1086
    if (table_intact.check(event_table.table, &event_table_def))
    {
      close_system_tables(thd, &open_tables_backup);
      my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
      DBUG_RETURN(TRUE);
    }

1087
    if ((ret= find_named_event(dbname, name, event_table.table)))
1088
      my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
1089
    else if ((ret= etn->load_from_row(thd, event_table.table)))
1090
      my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event");
1091

1092
    close_system_tables(thd, &open_tables_backup);
1093 1094
  }

1095
  thd->variables.sql_mode= saved_mode;
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
  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,
                               my_time_t last_executed,
                               ulonglong status)
{
  TABLE *table= NULL;
  Field **fields;
  int ret= 1;
1118
  enum_binlog_format save_binlog_format;
1119
  MYSQL_TIME time;
1120 1121
  DBUG_ENTER("Event_db_repository::update_timing_fields_for_event");

1122 1123 1124 1125
  /*
    Turn off row binlogging of event timing updates. These are not used
    for RBR of events replicated to the slave.
  */
1126
  save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
1127

1128 1129
  DBUG_ASSERT(thd->security_ctx->master_access & SUPER_ACL);

1130 1131 1132 1133 1134 1135 1136 1137 1138 1139
  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]);

1140 1141
  my_tz_OFFSET0->gmt_sec_to_TIME(&time, last_executed);
  fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
Sergei Golubchik's avatar
Sergei Golubchik committed
1142
  fields[ET_FIELD_LAST_EXECUTED]->store_time(&time);
1143

1144 1145
  fields[ET_FIELD_STATUS]->set_notnull();
  fields[ET_FIELD_STATUS]->store(status, TRUE);
1146 1147 1148 1149 1150 1151 1152 1153 1154 1155

  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:
1156
  if (table)
1157 1158
    close_mysql_tables(thd);

1159
  thd->restore_stmt_binlog_format(save_binlog_format);
1160

1161 1162
  DBUG_RETURN(test(ret));
}
1163

1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189

/**
  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
1190
  tables.init_one_table("mysql", 5, "db", 2, "db", TL_READ);
1191

1192
  if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
1193 1194 1195 1196 1197 1198
  {
    ret= 1;
    sql_print_error("Cannot open mysql.db");
  }
  else
  {
1199
    if (table_intact.check(tables.table, &mysql_db_table_def))
1200 1201
      ret= 1;

1202
    close_mysql_tables(thd);
1203 1204
  }
  /* Check mysql.user */
Konstantin Osipov's avatar
Konstantin Osipov committed
1205
  tables.init_one_table("mysql", 5, "user", 4, "user", TL_READ);
1206

1207
  if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221
  {
    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;
    }
1222
    close_mysql_tables(thd);
1223 1224
  }
  /* Check mysql.event */
Konstantin Osipov's avatar
Konstantin Osipov committed
1225
  tables.init_one_table("mysql", 5, "event", 5, "event", TL_READ);
1226

1227
  if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
1228 1229 1230 1231 1232 1233
  {
    ret= 1;
    sql_print_error("Cannot open mysql.event");
  }
  else
  {
1234
    if (table_intact.check(tables.table, &event_table_def))
1235
      ret= 1;
1236
    close_mysql_tables(thd);
1237 1238 1239
  }

  DBUG_RETURN(test(ret));
1240
}
1241 1242 1243 1244

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