List:Commits« Previous MessageNext Message »
From:konstantin Date:April 5 2007 1:24pm
Subject:bk commit into 5.1 tree (kostja:1.2524) BUG#23631
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of kostja. When kostja does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-04-05 15:24:34+04:00, kostja@vajra.(none) +37 -0
  A set of changes aiming to make the Event Scheduler more user-friendly
  when there are no up-to-date system tables to support it:
   - initialize the scheduler before reporting "Ready for connections".
     This ensures that warnings, if any, are printed before "Ready for
     connections", and this message is not mangled.
   - do not abort the scheduler if there are no system tables
   - check the tables once at start up, remember the status and disable
     the scheduler if the tables are not up to date.
     If one attempts to use the scheduler with bad tables,
     issue an error message.
   - clean up the behaviour of the module under LOCK TABLES and pre-locking
     mode
   - make sure implicit commit of Events DDL works as expected.
   - add more tests
  
  
  Collateral clean ups in the events code.
  
  This patch fixes Bug#23631 Events: SHOW VARIABLES doesn't work 
  when mysql.event is damaged

  mysql-test/r/events.result@stripped, 2007-04-05 15:24:28+04:00, kostja@vajra.(none) +354 -50
    Update results.

  mysql-test/r/events_bugs.result@stripped, 2007-04-05 15:24:28+04:00, kostja@vajra.(none)
+149 -1
    Update results.

  mysql-test/r/events_restart_phase1.result@stripped, 2007-04-05 15:24:28+04:00,
kostja@vajra.(none) +13 -9
    Update results.

  mysql-test/r/events_restart_phase2.result@stripped, 2007-04-05 15:24:28+04:00,
kostja@vajra.(none) +42 -6
    Update results.

  mysql-test/r/events_restart_phase3.result@stripped, 2007-04-05 15:24:28+04:00,
kostja@vajra.(none) +5 -5
    Update results.

  mysql-test/r/events_scheduling.result@stripped, 2007-04-05 15:24:29+04:00,
kostja@vajra.(none) +4 -0
    Update results.

  mysql-test/r/events_time_zone.result@stripped, 2007-04-05 15:24:29+04:00, kostja@vajra.(none)
+0 -142
    Update results.

  mysql-test/r/events_trans.result@stripped, 2007-04-05 15:24:31+04:00, kostja@vajra.(none)
+159 -0
    New BitKeeper file ``mysql-test/r/events_trans.result''

  mysql-test/r/events_trans.result@stripped, 2007-04-05 15:24:31+04:00, kostja@vajra.(none) +0
-0

  mysql-test/t/events.test@stripped, 2007-04-05 15:24:29+04:00, kostja@vajra.(none) +337 -37
    Add new tests for tampering with mysql.event and some more
    tests for sub-statements, LOCK TABLES mode and pre-locking.

  mysql-test/t/events_bugs.test@stripped, 2007-04-05 15:24:29+04:00, kostja@vajra.(none) +184
-1
    Move the non-concurrent part of test for Bug 16420 to this file.

  mysql-test/t/events_restart_phase1.test@stripped, 2007-04-05 15:24:29+04:00,
kostja@vajra.(none) +35 -11
    Rewrite events_restart_* tests to take into account that now
    we check mysql.event table only once, at server startup.

  mysql-test/t/events_restart_phase2.test@stripped, 2007-04-05 15:24:29+04:00,
kostja@vajra.(none) +38 -6
    Rewrite events_restart_* tests to take into account that now
    we check mysql.event table only once, at server startup.

  mysql-test/t/events_restart_phase3.test@stripped, 2007-04-05 15:24:29+04:00,
kostja@vajra.(none) +13 -9
    Rewrite events_restart_* tests to take into account that now
    we check mysql.event table only once, at server startup.

  mysql-test/t/events_scheduling.test@stripped, 2007-04-05 15:24:29+04:00, kostja@vajra.(none)
+4 -0
    Add more coverage for event_scheduler global variable.

  mysql-test/t/events_time_zone.test@stripped, 2007-04-05 15:24:29+04:00, kostja@vajra.(none)
+1 -170
    Move the non-concurrent part of the tests for Bug 16420 to 
    events_bugs.test

  mysql-test/t/events_trans.test@stripped, 2007-04-05 15:24:31+04:00, kostja@vajra.(none) +159
-0
    New BitKeeper file ``mysql-test/t/events_trans.test'':
    test cases for Event Scheduler that require a transactional
    storage engine.

  mysql-test/t/events_trans.test@stripped, 2007-04-05 15:24:31+04:00, kostja@vajra.(none) +0 -0

  sql/event_data_objects.cc@stripped, 2007-04-05 15:24:29+04:00, kostja@vajra.(none) +36 -58
    Move update_timing_fields functionality to Event_db_repository.
    Make loading of events from a table record more robust to tampering
    with the table - now we do not check mysql.event on every table open.

  sql/event_data_objects.h@stripped, 2007-04-05 15:24:29+04:00, kostja@vajra.(none) +0 -3
    Cleanup.

  sql/event_db_repository.cc@stripped, 2007-04-05 15:24:29+04:00, kostja@vajra.(none) +338
-321
    Now Event_db_repository is responsible for table I/O only.
    All the logic of events DDL is handled outside, in Events class please
    refer to the added test coverage to see how this change affected
    the behavior of Event Scheduler.
    Dependency on sp_head.h and sp.h removed.
    Make this module robust to tweaks with mysql.event table.
    Move check_system_tables from events.cc to this file

  sql/event_db_repository.h@stripped, 2007-04-05 15:24:29+04:00, kostja@vajra.(none) +21 -7
    Cleanup declarations (remove unused ones, change return type to bool
    from int).

  sql/event_queue.cc@stripped, 2007-04-05 15:24:29+04:00, kostja@vajra.(none) +36 -40
    Update to adapt to the new start up scheme of the Event Scheduler.

  sql/event_queue.h@stripped, 2007-04-05 15:24:29+04:00, kostja@vajra.(none) +19 -22
    Cleanup declarations.

  sql/event_scheduler.cc@stripped, 2007-04-05 15:24:29+04:00, kostja@vajra.(none) +39 -60
    Make all the error messages uniform:
    [SEVERITY] Event Scheduler: [user][schema.event] message
    Using append_identifier for error logging was an overkill - we may 
    need it only if the system character set may have NUL (null character)
    as part of a valid identifier, this is currently never the case, 
    whereas additional quoting did not look nice in the log.

  sql/event_scheduler.h@stripped, 2007-04-05 15:24:29+04:00, kostja@vajra.(none) +7 -18
    Cleanup the headers.

  sql/events.cc@stripped, 2007-04-05 15:24:29+04:00, kostja@vajra.(none) +527 -419
    Use a different start up procedure of Event Scheduler:
    - at start up, try to check the system tables first.
    If they are not up-to-date, disable the scheduler.
    - try to load all the active events. In case of a load error, abort
    start up.
    - do not parse an event on start up. Parsing only gives some information
    about event validity, but far not all.
    Consolidate the business logic of Events DDL in this module.
    Now opt_event_scheduler may change after start up and thus is protected
    by LOCK_event_metadata mutex.

  sql/events.h@stripped, 2007-04-05 15:24:29+04:00, kostja@vajra.(none) +62 -44
    Use all-static-data-members approach to implement Singleton pattern.

  sql/mysqld.cc@stripped, 2007-04-05 15:24:29+04:00, kostja@vajra.(none) +8 -38
    New invocation scheme of Events. Move some logic to events.cc.
    Initialize the scheduler before reporting "Ready for connections".

  sql/set_var.cc@stripped, 2007-04-05 15:24:30+04:00, kostja@vajra.(none) +24 -41
    Clean up sys_var_thd_sql_mode::symbolic_mode_representation
    to work with a LEX_STRING.
    Move more logic related to @@events_scheduler global variable to Events
    module.

  sql/set_var.h@stripped, 2007-04-05 15:24:30+04:00, kostja@vajra.(none) +3 -3
    Update declarations.

  sql/share/errmsg.txt@stripped, 2007-04-05 15:24:30+04:00, kostja@vajra.(none) +2 -2
    If someone tampered with mysql.event table after the server has
    started we no longer give him/her a complete report what was actually 
    broken. Do not send the user to look at the error log in such case,
    as there is nothing there (check_table_intact is not executed).

  sql/sp_head.cc@stripped, 2007-04-05 15:24:30+04:00, kostja@vajra.(none) +10 -18
    Update to a new declaration of 
    sys_var_thd_sql_mode::symbolic_mode_representation

  sql/sql_db.cc@stripped, 2007-04-05 15:24:30+04:00, kostja@vajra.(none) +1 -1
    New invocation scheme of Events module.

  sql/sql_parse.cc@stripped, 2007-04-05 15:24:30+04:00, kostja@vajra.(none) +16 -37
    Move more logic to Events module. Make sure that we are consistent
    in the way access rights are checked for Events DDL: always
    after committing the current transaction and checking the system tables.

  sql/sql_show.cc@stripped, 2007-04-05 15:24:30+04:00, kostja@vajra.(none) +10 -15
    Update to the new declarations of 
    sys_var_thd_sql_mode::symbolic_mode_representation

  sql/sql_test.cc@stripped, 2007-04-05 15:24:30+04:00, kostja@vajra.(none) +1 -1
    New invocation scheme of events.

  sql/table.cc@stripped, 2007-04-05 15:24:30+04:00, kostja@vajra.(none) +125 -128
    mysql.event is a system table.
    Update check_table_intact to be concurrent, more verbose, and less smart. 

  sql/table.h@stripped, 2007-04-05 15:24:30+04:00, kostja@vajra.(none) +16 -2
    Add a helper method.

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	kostja
# Host:	vajra.(none)
# Root:	/opt/local/work/mysql-5.1-c1

--- 1.622/sql/mysqld.cc	2007-03-20 00:37:31 +03:00
+++ 1.623/sql/mysqld.cc	2007-04-05 15:24:29 +04:00
@@ -890,7 +890,7 @@
   }
   (void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list
 
-  Events::get_instance()->deinit();
+  Events::deinit();
   end_slave();
 
   if (thread_count)
@@ -1335,7 +1335,7 @@
   (void) pthread_mutex_destroy(&LOCK_bytes_sent);
   (void) pthread_mutex_destroy(&LOCK_bytes_received);
   (void) pthread_mutex_destroy(&LOCK_user_conn);
-  Events::get_instance()->destroy_mutexes();
+  Events::destroy_mutexes();
 #ifdef HAVE_OPENSSL
   (void) pthread_mutex_destroy(&LOCK_des_key_file);
 #ifndef HAVE_YASSL
@@ -3062,7 +3062,7 @@
   (void) pthread_mutex_init(&LOCK_server_started, MY_MUTEX_INIT_FAST);
   (void) pthread_cond_init(&COND_server_started,NULL);
   sp_cache_init();
-  Events::get_instance()->init_mutexes();
+  Events::init_mutexes();
   /* Parameter for threads created for connections */
   (void) pthread_attr_init(&connection_attrib);
   (void) pthread_attr_setdetachstate(&connection_attrib,
@@ -3841,21 +3841,15 @@
   create_shutdown_thread();
   create_maintenance_thread();
 
+  if (Events::init(opt_noacl))
+    unireg_abort(1);
+
   sql_print_information(ER(ER_STARTUP),my_progname,server_version,
                         ((unix_sock == INVALID_SOCKET) ? (char*) ""
                                                        : mysqld_unix_port),
                          mysqld_port,
                          MYSQL_COMPILATION_COMMENT);
 
-  if (!opt_noacl)
-  {
-    if (Events::get_instance()->init())
-      unireg_abort(1);
-  }
-  else
-  {
-    Events::opt_event_scheduler = Events::EVENTS_DISABLED; 
-  }
 
   /* Signal threads waiting for server to be started */
   pthread_mutex_lock(&LOCK_server_started);
@@ -7568,32 +7562,8 @@
   }
 #endif
   case OPT_EVENT_SCHEDULER:
-    if (!argument)
-      Events::opt_event_scheduler= Events::EVENTS_DISABLED;
-    else
-    {
-      int type;
-      /* 
-        type=     5          1   2      3   4
-             (DISABLE ) - (OFF | ON) - (0 | 1)
-      */
-      switch ((type=find_type(argument, &Events::opt_typelib, 1))) {
-      case 0:
-	fprintf(stderr, "Unknown option to event-scheduler: %s\n",argument);
-	exit(1);
-      case 5: /* OPT_DISABLED */
-        Events::opt_event_scheduler= Events::EVENTS_DISABLED;
-        break;
-      case 2: /* OPT_ON  */
-      case 4: /* 1   */
-        Events::opt_event_scheduler= Events::EVENTS_ON;
-        break;
-      case 1: /* OPT_OFF */
-      case 3: /*  0  */
-        Events::opt_event_scheduler= Events::EVENTS_OFF;
-        break;
-      }
-    }
+    if (Events::set_opt_event_scheduler(argument))
+      exit(1);
     break;
   case (int) OPT_SKIP_NEW:
     opt_specialflag|= SPECIAL_NO_NEW_FUNC;

--- 1.147/sql/sql_db.cc	2006-12-23 22:19:53 +03:00
+++ 1.148/sql/sql_db.cc	2007-04-05 15:24:30 +04:00
@@ -953,7 +953,7 @@
 
 exit:
   (void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now  */
-  Events::get_instance()->drop_schema_events(thd, db);
+  Events::drop_schema_events(thd, db);
   /*
     If this database was the client's selected database, we silently
     change the client's selected database to nothing (to have an empty

--- 1.649/sql/sql_parse.cc	2007-03-27 21:09:51 +04:00
+++ 1.650/sql/sql_parse.cc	2007-04-05 15:24:30 +04:00
@@ -26,7 +26,6 @@
 #include "sp.h"
 #include "sp_cache.h"
 #include "events.h"
-#include "event_data_objects.h"
 #include "sql_trigger.h"
 
 /* Used in error handling only */
@@ -3151,13 +3150,16 @@
 
     switch (lex->sql_command) {
     case SQLCOM_CREATE_EVENT:
-      res= Events::get_instance()->
-            create_event(thd, lex->event_parse_data,
-                         lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS);
+    {
+      bool if_not_exists= (lex->create_info.options &
+                           HA_LEX_CREATE_IF_NOT_EXISTS);
+      res= Events::create_event(thd, lex->event_parse_data, if_not_exists);
       break;
+    }
     case SQLCOM_ALTER_EVENT:
-      res= Events::get_instance()->update_event(thd, lex->event_parse_data,
-                                                lex->spname);
+      res= Events::update_event(thd, lex->event_parse_data,
+                                lex->spname ? &lex->spname->m_db : NULL,
+                                lex->spname ? &lex->spname->m_name : NULL);
       break;
     default:
       DBUG_ASSERT(0);
@@ -3175,39 +3177,16 @@
   }
   /* lex->unit.cleanup() is called outside, no need to call it here */
   break;
-  case SQLCOM_DROP_EVENT:
   case SQLCOM_SHOW_CREATE_EVENT:
-  {
-    DBUG_ASSERT(lex->spname);
-    if (! lex->spname->m_db.str)
-    {
-      my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
-      goto error;
-    }
-    if (check_access(thd, EVENT_ACL, lex->spname->m_db.str, 0, 0, 0,
-                     is_schema_db(lex->spname->m_db.str)))
-      break;
-
-    if (lex->spname->m_name.length > NAME_LEN)
-    {
-      my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
-      /* this jumps to the end of the function and skips own messaging */
-      goto error;
-    }
-
-    if (lex->sql_command == SQLCOM_SHOW_CREATE_EVENT)
-      res= Events::get_instance()->show_create_event(thd, lex->spname->m_db,
-                                                     lex->spname->m_name);
-    else
-    {
-      if (!(res= Events::get_instance()->drop_event(thd,
-                                                    lex->spname->m_db,
-                                                    lex->spname->m_name,
-                                                    lex->drop_if_exists)))
-        send_ok(thd);
-    }
+    res= Events::show_create_event(thd, lex->spname->m_db,
+                                   lex->spname->m_name);
+    break;
+  case SQLCOM_DROP_EVENT:
+    if (!(res= Events::drop_event(thd,
+                                  lex->spname->m_db, lex->spname->m_name,
+                                  lex->drop_if_exists)))
+      send_ok(thd);
     break;
-  }
   case SQLCOM_CREATE_FUNCTION:                  // UDF function
   {
     if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0))

--- 1.400/sql/sql_show.cc	2007-03-20 00:44:15 +03:00
+++ 1.401/sql/sql_show.cc	2007-04-05 15:24:30 +04:00
@@ -3760,8 +3760,7 @@
                           LEX_STRING *definer_buffer)
 {
   CHARSET_INFO *cs= system_charset_info;
-  byte *sql_mode_str;
-  ulong sql_mode_len;
+  LEX_STRING sql_mode_rep;
 
   restore_record(table, s->default_values);
   table->field[1]->store(db, strlen(db), cs);
@@ -3777,11 +3776,9 @@
   table->field[14]->store(STRING_WITH_LEN("OLD"), cs);
   table->field[15]->store(STRING_WITH_LEN("NEW"), cs);
 
-  sql_mode_str=
-    sys_var_thd_sql_mode::symbolic_mode_representation(thd,
-                                                       sql_mode,
-                                                       &sql_mode_len);
-  table->field[17]->store((const char*)sql_mode_str, sql_mode_len, cs);
+  sys_var_thd_sql_mode::symbolic_mode_representation(thd, sql_mode,
+                                                     &sql_mode_rep);
+  table->field[17]->store(sql_mode_rep.str, sql_mode_rep.length, cs);
   table->field[18]->store((const char *)definer_buffer->str,
definer_buffer->length, cs);
   return schema_table_store_record(thd, table);
 }
@@ -4307,13 +4304,13 @@
   CHARSET_INFO *scs= system_charset_info;
   TIME time;
   Event_timed et;    
-  DBUG_ENTER("fill_events_copy_to_schema_tab");
+  DBUG_ENTER("copy_event_to_schema_table");
 
   restore_record(sch_table, s->default_values);
 
   if (et.load_from_row(thd, event_table))
   {
-    my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0));
+    my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), event_table->alias);
     DBUG_RETURN(1);
   }
 
@@ -4348,13 +4345,11 @@
 
   /* SQL_MODE */
   {
-    byte *sql_mode_str;
-    ulong sql_mode_len= 0;
-    sql_mode_str=
-           sys_var_thd_sql_mode::symbolic_mode_representation(thd, et.sql_mode,
-                                                              &sql_mode_len);
+    LEX_STRING sql_mode;
+    sys_var_thd_sql_mode::symbolic_mode_representation(thd, et.sql_mode,
+                                                       &sql_mode);
     sch_table->field[ISE_SQL_MODE]->
-                                store((const char*)sql_mode_str, sql_mode_len, scs);
+                                store(sql_mode.str, sql_mode.length, scs);
   }
 
   int not_used=0;

--- 1.51/sql/sql_test.cc	2007-02-23 14:13:50 +03:00
+++ 1.52/sql/sql_test.cc	2007-04-05 15:24:30 +04:00
@@ -537,6 +537,6 @@
 	 (long) (thread_count * thread_stack + info.hblkhd + info.arena));
 #endif
 
-  Events::get_instance()->dump_internal_status();
+  Events::dump_internal_status();
   puts("");
 }

--- 1.282/sql/table.cc	2007-03-27 21:09:52 +04:00
+++ 1.283/sql/table.cc	2007-04-05 15:24:30 +04:00
@@ -250,7 +250,7 @@
   Currently these are:
 
   help_category, help_keyword, help_relation, help_topic,
-  proc,
+  proc, event
   time_zone, time_zone_leap_second, time_zone_name, time_zone_transition,
   time_zone_transition_type
 
@@ -283,7 +283,14 @@
            my_tolower(ci, name[0]) == 't' &&
            my_tolower(ci, name[1]) == 'i' &&
            my_tolower(ci, name[2]) == 'm' &&
-           my_tolower(ci, name[3]) == 'e'
+           my_tolower(ci, name[3]) == 'e' ||
+
+           /* mysql.event table */
+           my_tolower(ci, name[0]) == 'e' &&
+           my_tolower(ci, name[1]) == 'v' &&
+           my_tolower(ci, name[2]) == 'e' &&
+           my_tolower(ci, name[3]) == 'n' &&
+           my_tolower(ci, name[4]) == 't'
           )
          );
 }
@@ -2432,153 +2439,143 @@
 }
 
 
-/*
+/**
   Checks whether a table is intact. Should be done *just* after the table has
   been opened.
-  
-  SYNOPSIS
-    table_check_intact()
-      table             The table to check
-      table_f_count     Expected number of columns in the table
-      table_def         Expected structure of the table (column name and type)
-      last_create_time  The table->file->create_time of the table in memory
-                        we have checked last time
-      error_num         ER_XXXX from the error messages file. When 0 no error
-                        is sent to the client in case types does not match.
-                        If different col number either 
-                        ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE or 
-                        ER_COL_COUNT_DOESNT_MATCH_CORRUPTED is used
-
-  RETURNS
-    FALSE  OK
-    TRUE   There was an error
+
+  @param[in] table             The table to check
+  @param[in] table_f_count     Expected number of columns in the table
+  @param[in] table_def         Expected structure of the table (column name
+                               and type)
+
+  @retval  FALSE  OK
+  @retval  TRUE   There was an error. An error message is output
+                  to the error log.  We do not push an error
+                  message into the error stack because this
+                  function is currently only called at start up,
+                  and such errors never reach the user.
 */
 
 my_bool
 table_check_intact(TABLE *table, const uint table_f_count,
-                   const TABLE_FIELD_W_TYPE *table_def,
-                   time_t *last_create_time, int error_num)
+                   const TABLE_FIELD_W_TYPE *table_def)
 {
   uint i;
   my_bool error= FALSE;
   my_bool fields_diff_count;
   DBUG_ENTER("table_check_intact");
-  DBUG_PRINT("info",("table: %s  expected_count: %d  last_create_time: %ld",
-                     table->alias, table_f_count, *last_create_time));
-  
-  if ((fields_diff_count= (table->s->fields != table_f_count)) ||
-      (*last_create_time != table->file->stats.create_time))
+  DBUG_PRINT("info",("table: %s  expected_count: %d",
+                     table->alias, table_f_count));
+
+  fields_diff_count= (table->s->fields != table_f_count);
+  if (fields_diff_count)
   {
-    DBUG_PRINT("info", ("I am suspecting, checking table"));
-    if (fields_diff_count)
+    DBUG_PRINT("info", ("Column count has changed, checking the definition"));
+
+    /* previous MySQL version */
+    if (MYSQL_VERSION_ID > table->s->mysql_version)
     {
-      /* previous MySQL version */
-      error= TRUE;
-      if (MYSQL_VERSION_ID > table->s->mysql_version)
-      {
-        my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0), table->alias,
-                 table_f_count, table->s->fields, table->s->mysql_version,
-                 MYSQL_VERSION_ID);
-        sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE),
-                        table->alias, table_f_count, table->s->fields,
-                        table->s->mysql_version, MYSQL_VERSION_ID);
-        DBUG_RETURN(error);
-
-      }
-      else if (MYSQL_VERSION_ID == table->s->mysql_version)
-      {
-        my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED,MYF(0), table->alias,
-                 table_f_count, table->s->fields);
-        sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED), table->alias,
-                        table_f_count, table->s->fields);
-      }
-      else
+      sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE),
+                      table->alias, table_f_count, table->s->fields,
+                      table->s->mysql_version, MYSQL_VERSION_ID);
+      DBUG_RETURN(TRUE);
+    }
+    else if (MYSQL_VERSION_ID == table->s->mysql_version)
+    {
+      sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED), table->alias,
+                      table_f_count, table->s->fields);
+      DBUG_RETURN(TRUE);
+    }
+    /*
+      Something has definitely changed, but we're running an older
+      version of MySQL with new system tables.
+      Let's check column definitions. If a column was added at
+      the end of the table, then we don't care much since such change
+      is backward compatible.
+    */
+  }
+  char buffer[STRING_BUFFER_USUAL_SIZE];
+  for (i=0 ; i < table_f_count; i++, table_def++)
+  {
+    String sql_type(buffer, sizeof(buffer), system_charset_info);
+    sql_type.length(0);
+    if (i < table->s->fields)
+    {
+      Field *field= table->field[i];
+
+      if (strncmp(field->field_name, table_def->name.str,
+                  table_def->name.length))
       {
         /*
-          Moving from newer mysql to older one -> let's say not an error but
-          will check the definition afterwards. If a column was added at the
-          end then we don't care much since it's not in the middle.
+          Name changes are not fatal, we use ordinal numbers to access columns.
+          Still this can be a sign of a tampered table, output an error
+          to the error log.
         */
-        error= FALSE;
+        sql_print_error("Incorrect definition of table %s.%s: "
+                        "expected column '%s' at position %d, found '%s'.",
+                        table->s->db.str, table->alias, table_def->name.str,
i,
+                        field->field_name);
       }
-    }
-    /* definitely something has changed */
-    char buffer[255];
-    for (i=0 ; i < table_f_count; i++, table_def++)
-    {      
-      String sql_type(buffer, sizeof(buffer), system_charset_info);
-      sql_type.length(0);
+      field->sql_type(sql_type);
       /*
-        Name changes are not fatal, we use sequence numbers => no problem
-        for us but this can show tampered table or broken table.
-      */
-      if (i < table->s->fields)
-      {
-        Field *field= table->field[i];
-        if (strncmp(field->field_name, table_def->name.str,
-                                       table_def->name.length))
-        {
-          sql_print_error("(%s) Expected field %s at position %d, found %s",
-                          table->alias, table_def->name.str, i,
-                          field->field_name);
-        }
-                        
-        /*
-          If the type does not match than something is really wrong
-          Check up to length - 1. Why?
-          1. datetime -> datetim -> the same
-          2. int(11) -> int(11  -> the same
-          3. set('one','two') -> set('one','two'  
-             so for sets if the same prefix is there it's ok if more are
-             added as part of the set. The same is valid for enum. So a new
-             table running on a old server will be valid.
-        */ 
-        field->sql_type(sql_type);
-        if (strncmp(sql_type.c_ptr_safe(), table_def->type.str,
-                    table_def->type.length - 1))
-        {
-          sql_print_error("(%s) Expected field %s at position %d to have type "
-                          "%s, found %s", table->alias, table_def->name.str,
-                          i, table_def->type.str, sql_type.c_ptr_safe()); 
-          error= TRUE;
-        }
-        else if (table_def->cset.str && !field->has_charset())
-        {
-          sql_print_error("(%s) Expected field %s at position %d to have "
-                          "character set '%s' but found no such", table->alias,
-                          table_def->name.str, i, table_def->cset.str);        
-          error= TRUE;
-        }
-        else if (table_def->cset.str && 
-                 strcmp(field->charset()->csname, table_def->cset.str))
-        {
-          sql_print_error("(%s) Expected field %s at position %d to have "
-                          "character set '%s' but found '%s'", table->alias,
-                          table_def->name.str, i, table_def->cset.str,
-                          field->charset()->csname);
-          error= TRUE;
-        }
-      }
-      else
-      {
-        sql_print_error("(%s) Expected field %s at position %d to have type %s "
-                        " but no field found.", table->alias,
-                        table_def->name.str, i, table_def->type.str);
-        error= TRUE;        
+        Generally, if column types don't match, then something is
+        wrong.
+
+        However, we only compare column definitions up to the
+        length of the original definition, since we consider the
+        following definitions compatible:
+
+        1. DATETIME and DATETIM
+        2. INT(11) and INT(11
+        3. SET('one', 'two') and SET('one', 'two', 'more')
+
+        For SETs or ENUMs, if the same prefix is there it's OK to
+        add more elements - they will get higher ordinal numbers and
+        the new table definition is backward compatible with the
+        original one.
+       */
+      if (strncmp(sql_type.c_ptr_safe(), table_def->type.str,
+                  table_def->type.length - 1))
+      {
+        sql_print_error("Incorrect definition of table %s.%s: "
+                        "expected column '%s' at position %d to have type "
+                        "%s, found type %s.", table->s->db.str, table->alias,
+                        table_def->name.str, i, table_def->type.str,
+                        sql_type.c_ptr_safe());
+        error= TRUE;
+      }
+      else if (table_def->cset.str && !field->has_charset())
+      {
+        sql_print_error("Incorrect definition of table %s.%s: "
+                        "expected the type of column '%s' at position %d "
+                        "to have character set '%s' but the type has no "
+                        "character set.", table->s->db.str, table->alias,
+                        table_def->name.str, i, table_def->cset.str);
+        error= TRUE;
+      }
+      else if (table_def->cset.str &&
+               strcmp(field->charset()->csname, table_def->cset.str))
+      {
+        sql_print_error("Incorrect definition of table %s.%s: "
+                        "expected the type of column '%s' at position %d "
+                        "to have character set '%s' but found "
+                        "character set '%s'.", table->s->db.str, table->alias,
+                        table_def->name.str, i, table_def->cset.str,
+                        field->charset()->csname);
+        error= TRUE;
       }
     }
-    if (!error)
-      *last_create_time= table->file->stats.create_time;
-    else if (!fields_diff_count && error_num)
-      my_error(error_num,MYF(0), table->alias, table_f_count, table->s->fields);
-  }
-  else
-  {
-    DBUG_PRINT("info", ("Table seems ok without thorough checking."));
-    *last_create_time= table->file->stats.create_time;
+    else
+    {
+      sql_print_error("Incorrect definition of table %s.%s: "
+                      "expected column '%s' at position %d to have type %s "
+                      " but the column is not found.",
+                      table->s->db.str, table->alias,
+                      table_def->name.str, i, table_def->type.str);
+      error= TRUE;
+    }
   }
-   
-  DBUG_RETURN(error);  
+  DBUG_RETURN(error);
 }
 
 

--- 1.164/sql/table.h	2007-03-27 21:09:52 +04:00
+++ 1.165/sql/table.h	2007-04-05 15:24:30 +04:00
@@ -684,6 +684,21 @@
 typedef struct st_table_list
 {
   st_table_list() {}                          /* Remove gcc warning */
+
+  /**
+    Prepare TABLE_LIST that consists of one table instance to use in
+    simple_open_and_lock_tables
+  */
+  inline void init_one_table(const char *db_name_arg,
+                             const char *table_name_arg,
+                             enum thr_lock_type lock_type_arg)
+  {
+    bzero((char*) this, sizeof(*this));
+    db= (char*) db_name_arg;
+    table_name= alias= (char*) table_name_arg;
+    lock_type= lock_type_arg;
+  }
+
   /*
     List of tables local to a subquery (used by SQL_LIST). Considers
     views as leaves (unlike 'next_leaf' below). Created at parse time
@@ -1092,8 +1107,7 @@
 
 my_bool
 table_check_intact(TABLE *table, const uint table_f_count,
-                   const TABLE_FIELD_W_TYPE *table_def,
-                   time_t *last_create_time, int error_num);
+                   const TABLE_FIELD_W_TYPE *table_def);
 
 static inline my_bitmap_map *tmp_use_all_columns(TABLE *table,
                                                  MY_BITMAP *bitmap)

--- 1.49/mysql-test/r/events.result	2007-03-27 22:15:46 +04:00
+++ 1.50/mysql-test/r/events.result	2007-04-05 15:24:28 +04:00
@@ -1,4 +1,8 @@
-create database if not exists events_test;
+drop database if exists events_test;
+drop database if exists db_x;
+drop database if exists mysqltest_db2;
+drop database if exists mysqltest_no_such_database;
+create database events_test;
 use events_test;
 CREATE USER pauline@localhost;
 CREATE DATABASE db_x;
@@ -223,72 +227,180 @@
 drop event root20;
 drop event šóóņ21;
 set names latin1;
+Create a test event. Only event metadata is relevant, 
+the actual schedule and body are not.
 CREATE EVENT intact_check ON SCHEDULE EVERY 10 HOUR DO SELECT "nothing";
 SHOW EVENTS;
 Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
 events_test	intact_check	root@localhost	SYSTEM	RECURRING	NULL	10	#	#	NULL	ENABLED
-ALTER TABLE mysql.event ADD dummy INT FIRST;
-SHOW EVENTS;
-ERROR HY000: Column count of mysql.event is wrong. Expected 17, found 18. The table is
probably corrupted
-ALTER TABLE mysql.event DROP dummy, ADD dummy2 VARCHAR(64) FIRST;
-SHOW EVENTS;
-ERROR HY000: Column count of mysql.event is wrong. Expected 17, found 18. The table is
probably corrupted
-ALTER TABLE mysql.event DROP dummy2;
+Try to alter mysql.event: the server should fail to load
+event information after mysql.event was tampered with.
+
+First, let's add a column to the end and make sure everything
+works as before
+
+ALTER TABLE mysql.event ADD dummy INT;
 SHOW EVENTS;
 Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
 events_test	intact_check	root@localhost	SYSTEM	RECURRING	NULL	10	#	#	NULL	ENABLED
+SELECT event_name FROM INFORMATION_SCHEMA.events;
+event_name
+intact_check
+SHOW CREATE EVENT intact_check;
+Event	sql_mode	time_zone	Create Event
+intact_check		SYSTEM	CREATE EVENT `intact_check` ON SCHEDULE EVERY 10 HOUR STARTS '#' ON
COMPLETION NOT PRESERVE ENABLE DO SELECT "nothing"
+DROP EVENT no_such_event;
+ERROR HY000: Unknown event 'no_such_event'
+CREATE EVENT intact_check_1 ON SCHEDULE EVERY 5 HOUR DO SELECT 5;
+ALTER EVENT intact_check_1 ON SCHEDULE EVERY 8 HOUR DO SELECT 8;
+ALTER EVENT intact_check_1 RENAME TO intact_check_2;
+DROP EVENT intact_check_1;
+ERROR HY000: Unknown event 'intact_check_1'
+DROP EVENT intact_check_2;
+DROP EVENT intact_check;
+DROP DATABASE IF EXISTS mysqltest_no_such_database;
+Warnings:
+Note	1008	Can't drop database 'mysqltest_no_such_database'; database doesn't exist
+CREATE DATABASE mysqltest_db2;
+DROP DATABASE mysqltest_db2;
+SELECT @@event_scheduler;
+@@event_scheduler
+OFF
+SHOW VARIABLES LIKE 'event_scheduler';
+Variable_name	Value
+event_scheduler	OFF
+SET GLOBAL event_scheduler=OFF;
+ALTER TABLE mysql.event DROP dummy;
+CREATE EVENT intact_check ON SCHEDULE EVERY 10 HOUR DO SELECT "nothing";
+
+Now let's add a column to the first position: the server
+expects to see event schema name there
+
+ALTER TABLE mysql.event ADD dummy INT FIRST;
+SHOW EVENTS;
+ERROR HY000: Cannot load from mysql.event. The table is probably corrupted
+SELECT event_name FROM INFORMATION_SCHEMA.events;
+ERROR HY000: Cannot load from mysql.event. The table is probably corrupted
+SHOW CREATE EVENT intact_check;
+ERROR HY000: Unknown event 'intact_check'
+DROP EVENT no_such_event;
+ERROR HY000: Unknown event 'no_such_event'
+CREATE EVENT intact_check_1 ON SCHEDULE EVERY 5 HOUR DO SELECT 5;
+ERROR HY000: Cannot load from mysql.event. The table is probably corrupted
+ALTER EVENT intact_check_1 ON SCHEDULE EVERY 8 HOUR DO SELECT 8;
+ERROR HY000: Unknown event 'intact_check_1'
+ALTER EVENT intact_check_1 RENAME TO intact_check_2;
+ERROR HY000: Unknown event 'intact_check_1'
+DROP EVENT intact_check_1;
+ERROR HY000: Unknown event 'intact_check_1'
+DROP EVENT intact_check_2;
+ERROR HY000: Unknown event 'intact_check_2'
+DROP EVENT intact_check;
+ERROR HY000: Unknown event 'intact_check'
+DROP DATABASE IF EXISTS mysqltest_no_such_database;
+Warnings:
+Note	1008	Can't drop database 'mysqltest_no_such_database'; database doesn't exist
+CREATE DATABASE mysqltest_db2;
+DROP DATABASE mysqltest_db2;
+SELECT @@event_scheduler;
+@@event_scheduler
+OFF
+SHOW VARIABLES LIKE 'event_scheduler';
+Variable_name	Value
+event_scheduler	OFF
+SET GLOBAL event_scheduler=OFF;
+Clean up
+ALTER TABLE mysql.event DROP dummy;
+DELETE FROM mysql.event;
+CREATE EVENT intact_check ON SCHEDULE EVERY 10 HOUR DO SELECT "nothing";
+Back up the table, further changes are not reversible
 CREATE TABLE event_like LIKE mysql.event;
 INSERT INTO event_like SELECT * FROM mysql.event;
-ALTER TABLE mysql.event MODIFY db char(64) character set cp1251 default '';
-SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
-ERROR HY000: Cannot load from mysql.event. The table is probably corrupted. Please see
the error log for details
-ALTER TABLE mysql.event MODIFY db char(20) character set utf8 collate utf8_bin default
'';
-SHOW CREATE TABLE mysql.event;
-Table	Create Table
-event	CREATE TABLE `event` (
-  `db` char(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
-  `name` char(64) NOT NULL DEFAULT '',
-  `body` longblob NOT NULL,
-  `definer` char(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
-  `execute_at` datetime DEFAULT NULL,
-  `interval_value` int(11) DEFAULT NULL,
-  `interval_field`
enum('YEAR','QUARTER','MONTH','DAY','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','SECOND_MICROSECOND')
DEFAULT NULL,
-  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-  `modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
-  `last_executed` datetime DEFAULT NULL,
-  `starts` datetime DEFAULT NULL,
-  `ends` datetime DEFAULT NULL,
-  `status` enum('ENABLED','DISABLED') NOT NULL DEFAULT 'ENABLED',
-  `on_completion` enum('DROP','PRESERVE') NOT NULL DEFAULT 'DROP',
-  `sql_mode`
set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','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','HIGH_NOT_PRECEDENCE')
NOT NULL DEFAULT '',
-  `comment` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
-  `time_zone` char(64) CHARACTER SET latin1 NOT NULL DEFAULT 'SYSTEM',
-  PRIMARY KEY (`db`,`name`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Events'
-SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
-ERROR HY000: Cannot load from mysql.event. The table is probably corrupted. Please see
the error log for details
-ALTER TABLE mysql.event MODIFY db char(64) character set utf8 collate utf8_bin default
'';
-"This should work"
-SHOW EVENTS;
-Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
-events_test	intact_check	root@localhost	SYSTEM	RECURRING	NULL	10	#	#	NULL	ENABLED
-ALTER TABLE mysql.event MODIFY db char(64) character set cp1251 default '';
-SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
-ERROR HY000: Cannot load from mysql.event. The table is probably corrupted. Please see
the error log for details
-ALTER TABLE mysql.event MODIFY db varchar(64) character set utf8 collate utf8_bin default
'';
-SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
-ERROR HY000: Cannot load from mysql.event. The table is probably corrupted. Please see
the error log for details
+
+Drop some columns and try more checks. 
+
+
 ALTER TABLE mysql.event DROP comment, DROP starts;
+SHOW EVENTS;
+ERROR HY000: Cannot load from mysql.event. The table is probably corrupted
 SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
+ERROR HY000: Cannot load from mysql.event. The table is probably corrupted
+SHOW CREATE EVENT intact_check;
+ERROR HY000: Cannot load from mysql.event. The table is probably corrupted
+DROP EVENT no_such_event;
+ERROR HY000: Unknown event 'no_such_event'
+CREATE EVENT intact_check_1 ON SCHEDULE EVERY 5 HOUR DO SELECT 5;
 ERROR HY000: Column count of mysql.event is wrong. Expected 17, found 15. The table is
probably corrupted
+ALTER EVENT intact_check_1 ON SCHEDULE EVERY 8 HOUR DO SELECT 8;
+ERROR HY000: Unknown event 'intact_check_1'
+ALTER EVENT intact_check_1 RENAME TO intact_check_2;
+ERROR HY000: Unknown event 'intact_check_1'
+DROP EVENT intact_check_1;
+ERROR HY000: Unknown event 'intact_check_1'
+DROP EVENT intact_check_2;
+ERROR HY000: Unknown event 'intact_check_2'
+DROP EVENT intact_check;
+DROP DATABASE IF EXISTS mysqltest_no_such_database;
+Warnings:
+Note	1008	Can't drop database 'mysqltest_no_such_database'; database doesn't exist
+CREATE DATABASE mysqltest_db2;
+DROP DATABASE mysqltest_db2;
+SELECT @@event_scheduler;
+@@event_scheduler
+OFF
+SHOW VARIABLES LIKE 'event_scheduler';
+Variable_name	Value
+event_scheduler	OFF
+SET GLOBAL event_scheduler=OFF;
+
+Now drop the table, and test again
+
+
 DROP TABLE mysql.event;
+SHOW EVENTS;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+SELECT event_name FROM INFORMATION_SCHEMA.events;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+SHOW CREATE EVENT intact_check;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+DROP EVENT no_such_event;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+CREATE EVENT intact_check_1 ON SCHEDULE EVERY 5 HOUR DO SELECT 5;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+ALTER EVENT intact_check_1 ON SCHEDULE EVERY 8 HOUR DO SELECT 8;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+ALTER EVENT intact_check_1 RENAME  TO intact_check_2;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+DROP EVENT intact_check_1;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+DROP EVENT intact_check_2;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+DROP EVENT intact_check;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+DROP DATABASE IF EXISTS mysqltest_no_such_database;
+Warnings:
+Note	1008	Can't drop database 'mysqltest_no_such_database'; database doesn't exist
+Error	1146	Table 'mysql.event' doesn't exist
+CREATE DATABASE mysqltest_db2;
+DROP DATABASE mysqltest_db2;
+OK, there is an unnecessary warning about the non-existent table 
+but it's not easy to fix and no one complained about it.
+A similar warning is printed if mysql.proc is missing.
+SHOW WARNINGS;
+Level	Code	Message
+Error	1146	Table 'mysql.event' doesn't exist
+SELECT @@event_scheduler;
+@@event_scheduler
+OFF
+SHOW VARIABLES LIKE 'event_scheduler';
+Variable_name	Value
+event_scheduler	OFF
+SET GLOBAL event_scheduler=OFF;
+Restore the original table.
 CREATE TABLE mysql.event like event_like;
-INSERT INTO  mysql.event SELECT * FROM event_like;
 DROP TABLE event_like;
 SHOW EVENTS;
 Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
-events_test	intact_check	root@localhost	SYSTEM	RECURRING	NULL	10	#	#	NULL	ENABLED
-DROP EVENT intact_check;
 create event e_26 on schedule at '2017-01-01 00:00:00' disable do set @a = 5;
 select db, name, body, definer, convert_tz(execute_at, 'UTC', 'SYSTEM'), on_completion
from mysql.event;
 db	name	body	definer	convert_tz(execute_at, 'UTC', 'SYSTEM')	on_completion
@@ -401,4 +513,196 @@
 ERROR 42000: Incorrect database name ''
 SHOW EVENTS FROM `events\\test`;
 Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
+
+LOCK TABLES mode.
+
+create table t1 (a int);
+create event e1 on schedule every 10 hour do select 1;
+lock table t1 read;
+show create event e1;
+Event	sql_mode	time_zone	Create Event
+e1		SYSTEM	CREATE EVENT `e1` ON SCHEDULE EVERY 10 HOUR STARTS '#' ON COMPLETION NOT
PRESERVE ENABLE DO select 1
+select event_name from information_schema.events;
+event_name
+e1
+create event e2 on schedule every 10 hour do select 1;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+alter event e2 disable;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+alter event e2 rename to e3;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+drop event e2;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+drop event e1;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+unlock tables;
+lock table t1 write;
+show create event e1;
+Event	sql_mode	time_zone	Create Event
+e1		SYSTEM	CREATE EVENT `e1` ON SCHEDULE EVERY 10 HOUR STARTS '#' ON COMPLETION NOT
PRESERVE ENABLE DO select 1
+select event_name from information_schema.events;
+event_name
+e1
+create event e2 on schedule every 10 hour do select 1;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+alter event e2 disable;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+alter event e2 rename to e3;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+drop event e2;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+drop event e1;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+unlock tables;
+lock table t1 read, mysql.event read;
+show create event e1;
+Event	sql_mode	time_zone	Create Event
+e1		SYSTEM	CREATE EVENT `e1` ON SCHEDULE EVERY 10 HOUR STARTS '#' ON COMPLETION NOT
PRESERVE ENABLE DO select 1
+select event_name from information_schema.events;
+event_name
+e1
+create event e2 on schedule every 10 hour do select 1;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+alter event e2 disable;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+alter event e2 rename to e3;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+drop event e2;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+drop event e1;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+unlock tables;
+lock table t1 write, mysql.event read;
+show create event e1;
+Event	sql_mode	time_zone	Create Event
+e1		SYSTEM	CREATE EVENT `e1` ON SCHEDULE EVERY 10 HOUR STARTS '#' ON COMPLETION NOT
PRESERVE ENABLE DO select 1
+select event_name from information_schema.events;
+event_name
+e1
+create event e2 on schedule every 10 hour do select 1;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+alter event e2 disable;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+alter event e2 rename to e3;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+drop event e2;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+drop event e1;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+unlock tables;
+lock table t1 read, mysql.event write;
+ERROR HY000: You can't combine write-locking of system tables with other tables or lock
types
+lock table t1 write, mysql.event write;
+ERROR HY000: You can't combine write-locking of system tables with other tables or lock
types
+lock table mysql.event write;
+show create event e1;
+Event	sql_mode	time_zone	Create Event
+e1		SYSTEM	CREATE EVENT `e1` ON SCHEDULE EVERY 10 HOUR STARTS '#' ON COMPLETION NOT
PRESERVE ENABLE DO select 1
+select event_name from information_schema.events;
+event_name
+e1
+create event e2 on schedule every 10 hour do select 1;
+alter event e2 disable;
+alter event e2 rename to e3;
+drop event e3;
+drop event e1;
+unlock tables;
+Make sure we have left no events 
+select event_name from information_schema.events;
+event_name
+
+Events in sub-statements, events and prelocking
+
+
+create event e1 on schedule every 10 hour do select 1;
+create function f1() returns int
+begin
+show create event e1;
+return 1;
+end|
+ERROR 0A000: Not allowed to return a result set from a function
+create trigger trg before insert on t1 for each row
+begin
+show create event e1;
+end|
+ERROR 0A000: Not allowed to return a result set from a trigger
+create function f1() returns int
+begin
+select event_name from information_schema.events;
+return 1;
+end|
+ERROR 0A000: Not allowed to return a result set from a function
+create trigger trg before insert on t1 for each row
+begin
+select event_name from information_schema.events;
+end|
+ERROR 0A000: Not allowed to return a result set from a trigger
+create function f1() returns int
+begin
+create event e2 on schedule every 10 hour do select 1;
+return 1;
+end|
+ERROR HY000: Recursion of EVENT DDL statements is forbidden when body is present
+create function f1() returns int
+begin
+alter event e1 rename to e2;
+return 1;
+end|
+ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger.
+create function f1() returns int
+begin
+drop event e2;
+return 1;
+end|
+ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger.
+----------------------------------------------------------------------
+create trigger trg before insert on t1 for each row
+begin
+set new.a= f1();
+end|
+create function f1() returns int
+begin
+call p1();
+return 0;
+end|
+create procedure p1()
+begin
+select event_name from information_schema.events;
+end|
+insert into t1 (a) values (1)|
+ERROR 0A000: Not allowed to return a result set from a trigger
+drop procedure p1|
+create procedure p1()
+begin
+show create event e1; 
+end|
+insert into t1 (a) values (1)|
+ERROR 0A000: Not allowed to return a result set from a trigger
+drop procedure p1|
+create procedure p1()
+begin
+create temporary table tmp select event_name from information_schema.events;
+end|
+expected to work, since we redirect the output into a tmp table
+insert into t1 (a) values (1)|
+select * from tmp|
+event_name
+e1
+drop temporary table tmp|
+drop procedure p1|
+create procedure p1()
+begin
+alter event e1 rename to e2;
+end|
+insert into t1 (a) values (1)|
+ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger.
+drop procedure p1|
+create procedure p1()
+begin
+drop event e1;
+end|
+insert into t1 (a) values (1)|
+ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger.
+drop table t1|
+drop event e1|
 drop database events_test;

--- 1.27/mysql-test/r/events_bugs.result	2007-03-27 22:15:46 +04:00
+++ 1.28/mysql-test/r/events_bugs.result	2007-04-05 15:24:28 +04:00
@@ -1,4 +1,7 @@
-create database if not exists events_test;
+drop database if exists events_test;
+drop database if exists mysqltest_db1;
+drop database if exists mysqltest_db2;
+create database events_test;
 use events_test;
 CREATE EVENT lower_case ON SCHEDULE EVERY 1 MINUTE DO SELECT 1;
 CREATE EVENT Lower_case ON SCHEDULE EVERY 2 MINUTE DO SELECT 2;
@@ -381,4 +384,149 @@
 DROP EVENT e1;
 ERROR HY000: Unknown event 'e1'
 DROP USER mysqltest_u1@localhost;
+SET GLOBAL EVENT_SCHEDULER= OFF;
+SET @save_time_zone= @@TIME_ZONE;
+SET TIME_ZONE= '+00:00';
+SET TIMESTAMP= UNIX_TIMESTAMP('2005-12-31 23:58:59');
+CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+SHOW EVENTS;
+Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
+events_test	e1	root@localhost	+00:00	RECURRING	NULL	1	DAY	2005-12-31
23:58:59	NULL	ENABLED
+SET TIME_ZONE= '-01:00';
+ALTER EVENT e1 ON SCHEDULE EVERY 1 DAY STARTS '2000-01-01 00:00:00';
+SHOW EVENTS;
+Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
+events_test	e1	root@localhost	-01:00	RECURRING	NULL	1	DAY	2000-01-01
00:00:00	NULL	ENABLED
+SET TIME_ZONE= '+02:00';
+ALTER EVENT e1 ON SCHEDULE AT '2000-01-02 00:00:00'
+  ON COMPLETION PRESERVE DISABLE;
+SHOW EVENTS;
+Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
+events_test	e1	root@localhost	+02:00	ONE TIME	2000-01-02
00:00:00	NULL	NULL	NULL	NULL	DISABLED
+SET TIME_ZONE= '-03:00';
+ALTER EVENT e1 ON SCHEDULE EVERY 1 DAY ENDS '2030-01-03 00:00:00'
+  ON COMPLETION PRESERVE DISABLE;
+SHOW EVENTS;
+Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
+events_test	e1	root@localhost	-03:00	RECURRING	NULL	1	DAY	2005-12-31 20:58:59	2030-01-03
00:00:00	DISABLED
+SET TIME_ZONE= '+04:00';
+ALTER EVENT e1 DO SELECT 2;
+SHOW EVENTS;
+Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
+events_test	e1	root@localhost	-03:00	RECURRING	NULL	1	DAY	2005-12-31 20:58:59	2030-01-03
00:00:00	ENABLED
+DROP EVENT e1;
+SET TIME_ZONE='+05:00';
+CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
+SELECT 1;
+SET TIMESTAMP= @@TIMESTAMP + 1;
+SET TIME_ZONE='-05:00';
+CREATE EVENT e2 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
+SELECT 1;
+SET TIMESTAMP= @@TIMESTAMP + 1;
+SET TIME_ZONE='+00:00';
+CREATE EVENT e3 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
+SELECT 1;
+SELECT * FROM INFORMATION_SCHEMA.EVENTS ORDER BY event_name;
+EVENT_CATALOG	EVENT_SCHEMA	EVENT_NAME	DEFINER	TIME_ZONE	EVENT_BODY	EVENT_DEFINITION	EVENT_TYPE	EXECUTE_AT	INTERVAL_VALUE	INTERVAL_FIELD	SQL_MODE	STARTS	ENDS	STATUS	ON_COMPLETION	CREATED	LAST_ALTERED	LAST_EXECUTED	EVENT_COMMENT
+NULL	events_test	e1	root@localhost	+05:00	SQL	SELECT 1	RECURRING	NULL	1	DAY		2006-01-01
00:00:00	NULL	ENABLED	NOT PRESERVE	2005-12-31 23:58:59	2005-12-31 23:58:59	NULL	
+NULL	events_test	e2	root@localhost	-05:00	SQL	SELECT 1	RECURRING	NULL	1	DAY		2006-01-01
00:00:00	NULL	ENABLED	NOT PRESERVE	2005-12-31 23:59:00	2005-12-31 23:59:00	NULL	
+NULL	events_test	e3	root@localhost	+00:00	SQL	SELECT 1	RECURRING	NULL	1	DAY		2006-01-01
00:00:00	NULL	ENABLED	NOT PRESERVE	2005-12-31 23:59:01	2005-12-31 23:59:01	NULL	
+SHOW EVENTS;
+Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
+events_test	e1	root@localhost	+05:00	RECURRING	NULL	1	DAY	2006-01-01
00:00:00	NULL	ENABLED
+events_test	e2	root@localhost	-05:00	RECURRING	NULL	1	DAY	2006-01-01
00:00:00	NULL	ENABLED
+events_test	e3	root@localhost	+00:00	RECURRING	NULL	1	DAY	2006-01-01
00:00:00	NULL	ENABLED
+SHOW CREATE EVENT e1;
+Event	sql_mode	time_zone	Create Event
+e1		+05:00	CREATE EVENT `e1` ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' ON
COMPLETION NOT PRESERVE ENABLE DO SELECT 1
+SHOW CREATE EVENT e2;
+Event	sql_mode	time_zone	Create Event
+e2		-05:00	CREATE EVENT `e2` ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' ON
COMPLETION NOT PRESERVE ENABLE DO SELECT 1
+SHOW CREATE EVENT e3;
+Event	sql_mode	time_zone	Create Event
+e3		+00:00	CREATE EVENT `e3` ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' ON
COMPLETION NOT PRESERVE ENABLE DO SELECT 1
+The following should fail, and nothing should be altered.
+ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+  ENDS '1999-01-02 00:00:00';
+ERROR HY000: Event execution time is in the past and ON COMPLETION NOT PRESERVE is set.
Event has not been altered
+ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+  ENDS '1999-01-02 00:00:00' DISABLE;
+ERROR HY000: Event execution time is in the past and ON COMPLETION NOT PRESERVE is set.
Event has not been altered
+The following should give warnings, and nothing should be created.
+CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+  ENDS '1999-01-02 00:00:00'
+DO
+SELECT 1;
+Warnings:
+Note	1584	Event execution time is in the past and ON COMPLETION NOT PRESERVE is set.
Event has not been created
+CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+  ENDS '1999-01-02 00:00:00' DISABLE
+DO
+SELECT 1;
+Warnings:
+Note	1584	Event execution time is in the past and ON COMPLETION NOT PRESERVE is set.
Event has not been created
+CREATE EVENT e4 ON SCHEDULE AT '1999-01-01 00:00:00' DO
+SELECT 1;
+Warnings:
+Note	1584	Event execution time is in the past and ON COMPLETION NOT PRESERVE is set.
Event has not been created
+CREATE EVENT e4 ON SCHEDULE AT '1999-01-01 00:00:00' DISABLE
+DO
+SELECT 1;
+Warnings:
+Note	1584	Event execution time is in the past and ON COMPLETION NOT PRESERVE is set.
Event has not been created
+SHOW EVENTS;
+Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
+events_test	e1	root@localhost	+05:00	RECURRING	NULL	1	DAY	2006-01-01
00:00:00	NULL	ENABLED
+events_test	e2	root@localhost	-05:00	RECURRING	NULL	1	DAY	2006-01-01
00:00:00	NULL	ENABLED
+events_test	e3	root@localhost	+00:00	RECURRING	NULL	1	DAY	2006-01-01
00:00:00	NULL	ENABLED
+The following should succeed giving a warning.
+ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+  ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE;
+Warnings:
+Note	1533	Event execution time is in the past. Event has been disabled
+CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+  ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE
+DO
+SELECT 1;
+Warnings:
+Note	1533	Event execution time is in the past. Event has been disabled
+CREATE EVENT e5 ON SCHEDULE AT '1999-01-01 00:00:00'
+  ON COMPLETION PRESERVE
+DO
+SELECT 1;
+Warnings:
+Note	1533	Event execution time is in the past. Event has been disabled
+The following should succeed without warnings.
+ALTER EVENT e2 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00';
+ALTER EVENT e3 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+  ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE DISABLE;
+CREATE EVENT e6 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00' DO
+SELECT 1;
+CREATE EVENT e7 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+  ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE DISABLE
+DO
+SELECT 1;
+CREATE EVENT e8 ON SCHEDULE AT '1999-01-01 00:00:00'
+  ON COMPLETION PRESERVE DISABLE
+DO
+SELECT 1;
+SHOW EVENTS;
+Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
+events_test	e1	root@localhost	+00:00	RECURRING	NULL	1	HOUR	1999-01-01 00:00:00	1999-01-02
00:00:00	DISABLED
+events_test	e2	root@localhost	+00:00	RECURRING	NULL	1	HOUR	1999-01-01
00:00:00	NULL	ENABLED
+events_test	e3	root@localhost	+00:00	RECURRING	NULL	1	HOUR	1999-01-01 00:00:00	1999-01-02
00:00:00	DISABLED
+events_test	e4	root@localhost	+00:00	RECURRING	NULL	1	HOUR	1999-01-01 00:00:00	1999-01-02
00:00:00	DISABLED
+events_test	e5	root@localhost	+00:00	ONE TIME	1999-01-01
00:00:00	NULL	NULL	NULL	NULL	DISABLED
+events_test	e6	root@localhost	+00:00	RECURRING	NULL	1	HOUR	1999-01-01
00:00:00	NULL	ENABLED
+events_test	e7	root@localhost	+00:00	RECURRING	NULL	1	HOUR	1999-01-01 00:00:00	1999-01-02
00:00:00	DISABLED
+events_test	e8	root@localhost	+00:00	ONE TIME	1999-01-01
00:00:00	NULL	NULL	NULL	NULL	DISABLED
+DROP EVENT e8;
+DROP EVENT e7;
+DROP EVENT e6;
+DROP EVENT e5;
+DROP EVENT e4;
+DROP EVENT e3;
+DROP EVENT e2;
+DROP EVENT e1;
+SET TIME_ZONE=@save_time_zone;
 drop database events_test;

--- 1.3/mysql-test/r/events_restart_phase1.result	2007-03-16 17:31:03 +03:00
+++ 1.4/mysql-test/r/events_restart_phase1.result	2007-04-05 15:24:28 +04:00
@@ -1,12 +1,16 @@
-create database if not exists mysqltest_events_test;
-use mysqltest_events_test;
 set global event_scheduler=off;
+drop database if exists events_test;
+create database events_test;
+use events_test;
 create table execution_log(name char(10));
-create event abc1 on schedule every 1 second do insert into execution_log value('abc1');
-create event abc2 on schedule every 1 second do insert into execution_log value('abc2');
-create event abc3 on schedule every 1 second do insert into execution_log value('abc3');
-select name from execution_log;
-name
-insert into mysql.event values ('db1','bad','select
42','root@localhost',NULL,1000,'MICROSECOND','2006-05-05 17:39:11','2006-05-05
17:39:20','2016-05-05 15:39:24','2016-05-05
15:39:11',NULL,'ENABLED','DROP','','comment1','SYSTEM');
-insert into mysql.event values
('db1','bad2','sect','root@localhost',NULL,1000,'SECOND','2006-05-05
17:39:11','2006-05-05 17:39:20','2016-05-05 15:39:24','2016-05-05
15:39:11',NULL,'ENABLED','DROP','','comment2','SYSTEM');
+create event abc1 on schedule every 1 second do
+insert into execution_log value('abc1');
+create event abc2 on schedule every 1 second do
+insert into execution_log value('abc2');
+create event abc3 on schedule every 1 second do 
+insert into execution_log value('abc3');
+create table event_like like mysql.event;
+insert into event_like select * from mysql.event;
+alter table mysql.event
+change column body  body longtext character set utf8 collate utf8_bin;
 "Now we restart the server"

--- 1.1/mysql-test/r/events_restart_phase2.result	2006-07-13 12:59:44 +04:00
+++ 1.2/mysql-test/r/events_restart_phase2.result	2007-04-05 15:24:28 +04:00
@@ -1,6 +1,42 @@
-use mysqltest_events_test;
-"Should get 0 rows because the queue aborted run
-select distinct name from execution_log order by name;
-name
-delete from mysql.event where name like 'bad%';
-"Now restart the server again"
+use events_test;
+select @@event_scheduler;
+@@event_scheduler
+DISABLED
+show events;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found
damaged at server start
+select event_name from information_schema.events;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found
damaged at server start
+show create event intact_check;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found
damaged at server start
+drop event no_such_event;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found
damaged at server start
+create event intact_check_1 on schedule every 5 hour do select 5;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found
damaged at server start
+alter event intact_check_1 on schedule every 8 hour do select 8;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found
damaged at server start
+alter event intact_check_1 rename to intact_check_2;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found
damaged at server start
+drop event intact_check_1;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found
damaged at server start
+drop event intact_check_2;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found
damaged at server start
+drop event intact_check;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found
damaged at server start
+set global event_scheduler=on;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found
damaged at server start
+set global event_scheduler=off;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found
damaged at server start
+show variables like 'event_scheduler';
+Variable_name	Value
+event_scheduler	DISABLED
+Make sure that we still can create and drop databases,
+and no warnings are produced.
+drop database if exists mysqltest_database_not_exists;
+Warnings:
+Note	1008	Can't drop database 'mysqltest_database_not_exists'; database doesn't exist
+create database mysqltest_db1;
+drop database mysqltest_db1;
+Restore the original mysql.event table
+drop table mysql.event;
+rename table event_like to mysql.event;
+Now let's restart the server again

--- 1.2/mysql-test/r/events_restart_phase3.result	2006-09-01 15:08:37 +04:00
+++ 1.3/mysql-test/r/events_restart_phase3.result	2007-04-05 15:24:28 +04:00
@@ -1,12 +1,12 @@
-use mysqltest_events_test;
+use events_test;
+select @@event_scheduler;
+@@event_scheduler
+ON
 "Should get 3 rows : abc1, abc2, abc3
 select distinct name from execution_log order by name;
 name
 abc1
 abc2
 abc3
-drop event abc1;
-drop event abc2;
-drop event abc3;
 drop table execution_log;
-drop database mysqltest_events_test;
+drop database events_test;

--- 1.9/mysql-test/r/events_scheduling.result	2007-03-20 00:37:24 +03:00
+++ 1.10/mysql-test/r/events_scheduling.result	2007-04-05 15:24:29 +04:00
@@ -1,6 +1,8 @@
 CREATE DATABASE IF NOT EXISTS events_test;
 USE events_test;
 SET GLOBAL event_scheduler=OFF;
+Try agian to make sure it's allowed
+SET GLOBAL event_scheduler=OFF;
 SHOW VARIABLES LIKE 'event_scheduler';
 Variable_name	Value
 event_scheduler	OFF
@@ -12,6 +14,8 @@
 SHOW VARIABLES LIKE 'event_scheduler';
 Variable_name	Value
 event_scheduler	OFF
+SET GLOBAL event_scheduler=ON;
+Try again to make sure it's allowed
 SET GLOBAL event_scheduler=ON;
 SHOW VARIABLES LIKE 'event_scheduler';
 Variable_name	Value

--- 1.45/mysql-test/t/events.test	2007-03-27 22:15:46 +04:00
+++ 1.46/mysql-test/t/events.test	2007-04-05 15:24:29 +04:00
@@ -1,7 +1,13 @@
 # Can't test with embedded server that doesn't support grants
 -- source include/not_embedded.inc
 
-create database if not exists events_test;
+--disable_warnings
+drop database if exists events_test;
+drop database if exists db_x;
+drop database if exists mysqltest_db2;
+drop database if exists mysqltest_no_such_database;
+--enable_warnings
+create database events_test;
 use events_test;
 
 #
@@ -215,62 +221,161 @@
 #
 
 #
-# mysql.event intact checking start
+# mysql.event intact checking
+# Check that the server does not crash if
+# one has destroyed or tampered with the event table. 
+# Please see see for events_restart_phase* tests to 
+# see the server behavior at start up with bad mysql.event
+# table.
 #
-# There should be at least 1 second between the ALTERs or we can't catch the change of
create_time!!
+#
+--echo Create a test event. Only event metadata is relevant, 
+--echo the actual schedule and body are not.
 #
 CREATE EVENT intact_check ON SCHEDULE EVERY 10 HOUR DO SELECT "nothing";
 --replace_column 8 # 9 #
 SHOW EVENTS;
-ALTER TABLE mysql.event ADD dummy INT FIRST;
---error ER_COL_COUNT_DOESNT_MATCH_CORRUPTED
-SHOW EVENTS;
-ALTER TABLE mysql.event DROP dummy, ADD dummy2 VARCHAR(64) FIRST;
---error ER_COL_COUNT_DOESNT_MATCH_CORRUPTED
-SHOW EVENTS;
-ALTER TABLE mysql.event DROP dummy2;
+#
+--echo Try to alter mysql.event: the server should fail to load
+--echo event information after mysql.event was tampered with.
+--echo 
+--echo First, let's add a column to the end and make sure everything
+--echo works as before
+--echo 
+ALTER TABLE mysql.event ADD dummy INT;
 --replace_column 8 # 9 #
 SHOW EVENTS;
+SELECT event_name FROM INFORMATION_SCHEMA.events;
+--replace_regex /STARTS '[^']+'/STARTS '#'/
+SHOW CREATE EVENT intact_check;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT no_such_event;
+CREATE EVENT intact_check_1 ON SCHEDULE EVERY 5 HOUR DO SELECT 5;
+ALTER EVENT intact_check_1 ON SCHEDULE EVERY 8 HOUR DO SELECT 8;
+ALTER EVENT intact_check_1 RENAME TO intact_check_2;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT intact_check_1;
+DROP EVENT intact_check_2;
+DROP EVENT intact_check;
+DROP DATABASE IF EXISTS mysqltest_no_such_database;
+CREATE DATABASE mysqltest_db2;
+DROP DATABASE mysqltest_db2;
+SELECT @@event_scheduler;
+SHOW VARIABLES LIKE 'event_scheduler';
+SET GLOBAL event_scheduler=OFF;
+# Clean up
+ALTER TABLE mysql.event DROP dummy;
+CREATE EVENT intact_check ON SCHEDULE EVERY 10 HOUR DO SELECT "nothing";
+--echo 
+--echo Now let's add a column to the first position: the server
+--echo expects to see event schema name there
+--echo 
+ALTER TABLE mysql.event ADD dummy INT FIRST;
+--error ER_CANNOT_LOAD_FROM_TABLE
+SHOW EVENTS;
+--error ER_CANNOT_LOAD_FROM_TABLE
+SELECT event_name FROM INFORMATION_SCHEMA.events;
+--error ER_EVENT_DOES_NOT_EXIST
+SHOW CREATE EVENT intact_check;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT no_such_event;
+--error ER_CANNOT_LOAD_FROM_TABLE
+CREATE EVENT intact_check_1 ON SCHEDULE EVERY 5 HOUR DO SELECT 5;
+--error ER_EVENT_DOES_NOT_EXIST
+ALTER EVENT intact_check_1 ON SCHEDULE EVERY 8 HOUR DO SELECT 8;
+--error ER_EVENT_DOES_NOT_EXIST
+ALTER EVENT intact_check_1 RENAME TO intact_check_2;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT intact_check_1;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT intact_check_2;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT intact_check;
+# Should work OK
+DROP DATABASE IF EXISTS mysqltest_no_such_database;
+CREATE DATABASE mysqltest_db2;
+DROP DATABASE mysqltest_db2;
+SELECT @@event_scheduler;
+SHOW VARIABLES LIKE 'event_scheduler';
+SET GLOBAL event_scheduler=OFF;
+--echo Clean up
+ALTER TABLE mysql.event DROP dummy;
+DELETE FROM mysql.event;
+CREATE EVENT intact_check ON SCHEDULE EVERY 10 HOUR DO SELECT "nothing";
+--echo Back up the table, further changes are not reversible
 CREATE TABLE event_like LIKE mysql.event;
 INSERT INTO event_like SELECT * FROM mysql.event;
-#sleep a bit or we won't catch the change of time
---sleep 1.1
-ALTER TABLE mysql.event MODIFY db char(64) character set cp1251 default '';
---error ER_CANNOT_LOAD_FROM_TABLE
-SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
-ALTER TABLE mysql.event MODIFY db char(20) character set utf8 collate utf8_bin default
'';
-#wait a bit or we won't see the difference because of seconds resolution
---sleep 1.1
-SHOW CREATE TABLE mysql.event;
+--echo 
+--echo Drop some columns and try more checks. 
+--echo
+--echo 
+ALTER TABLE mysql.event DROP comment, DROP starts;
 --error ER_CANNOT_LOAD_FROM_TABLE
-SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
---sleep 1.1
-ALTER TABLE mysql.event MODIFY db char(64) character set utf8 collate utf8_bin default
'';
---sleep 1.1
---echo "This should work"
---replace_column 8 # 9 #
 SHOW EVENTS;
---sleep 1.1
-ALTER TABLE mysql.event MODIFY db char(64) character set cp1251 default '';
 --error ER_CANNOT_LOAD_FROM_TABLE
 SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
---sleep 1.1
-ALTER TABLE mysql.event MODIFY db varchar(64) character set utf8 collate utf8_bin default
'';
---sleep 1.1
 --error ER_CANNOT_LOAD_FROM_TABLE
-SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
---sleep 1.1
-ALTER TABLE mysql.event DROP comment, DROP starts;
---sleep 1.1
+SHOW CREATE EVENT intact_check;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT no_such_event;
 --error ER_COL_COUNT_DOESNT_MATCH_CORRUPTED
-SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
+CREATE EVENT intact_check_1 ON SCHEDULE EVERY 5 HOUR DO SELECT 5;
+--error ER_EVENT_DOES_NOT_EXIST
+ALTER EVENT intact_check_1 ON SCHEDULE EVERY 8 HOUR DO SELECT 8;
+--error ER_EVENT_DOES_NOT_EXIST
+ALTER EVENT intact_check_1 RENAME TO intact_check_2;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT intact_check_1;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT intact_check_2;
+# Should succeed
+DROP EVENT intact_check;
+DROP DATABASE IF EXISTS mysqltest_no_such_database;
+CREATE DATABASE mysqltest_db2;
+DROP DATABASE mysqltest_db2;
+SELECT @@event_scheduler;
+SHOW VARIABLES LIKE 'event_scheduler';
+SET GLOBAL event_scheduler=OFF;
+--echo 
+--echo Now drop the table, and test again
+--echo
+--echo 
 DROP TABLE mysql.event;
+--error ER_NO_SUCH_TABLE
+SHOW EVENTS;
+--error ER_NO_SUCH_TABLE
+SELECT event_name FROM INFORMATION_SCHEMA.events;
+--error ER_NO_SUCH_TABLE
+SHOW CREATE EVENT intact_check;
+--error ER_NO_SUCH_TABLE
+DROP EVENT no_such_event;
+--error ER_NO_SUCH_TABLE
+CREATE EVENT intact_check_1 ON SCHEDULE EVERY 5 HOUR DO SELECT 5;
+--error ER_NO_SUCH_TABLE
+ALTER EVENT intact_check_1 ON SCHEDULE EVERY 8 HOUR DO SELECT 8;
+--error ER_NO_SUCH_TABLE
+ALTER EVENT intact_check_1 RENAME  TO intact_check_2;
+--error ER_NO_SUCH_TABLE
+DROP EVENT intact_check_1;
+--error ER_NO_SUCH_TABLE
+DROP EVENT intact_check_2;
+--error ER_NO_SUCH_TABLE
+DROP EVENT intact_check;
+DROP DATABASE IF EXISTS mysqltest_no_such_database;
+CREATE DATABASE mysqltest_db2;
+DROP DATABASE mysqltest_db2;
+--echo OK, there is an unnecessary warning about the non-existent table 
+--echo but it's not easy to fix and no one complained about it.
+--echo A similar warning is printed if mysql.proc is missing.
+SHOW WARNINGS;
+SELECT @@event_scheduler;
+SHOW VARIABLES LIKE 'event_scheduler';
+SET GLOBAL event_scheduler=OFF;
+--echo Restore the original table.
 CREATE TABLE mysql.event like event_like;
-INSERT INTO  mysql.event SELECT * FROM event_like;
 DROP TABLE event_like;
 --replace_column 8 # 9 #
 SHOW EVENTS;
-DROP EVENT intact_check;
 #
 # mysql.event intact checking end
 #
@@ -424,5 +529,200 @@
 SHOW EVENTS FROM ``;
 
 SHOW EVENTS FROM `events\\test`;
+#
+# A check for events SQL under LOCK TABLES and in pre-locked mode.
+#
+--echo
+--echo LOCK TABLES mode.
+--echo 
+#
+# SHOW CREATE EVENT and INFORMATION_SCHEMA.events are available and 
+# cause an implicit lock/unlock of mysql.event table, regardless of the
+# currently locked tables.
+#
+create table t1 (a int);
+create event e1 on schedule every 10 hour do select 1;
+#
+lock table t1 read;
+#
+--replace_regex /STARTS '[^']+'/STARTS '#'/
+show create event e1;
+select event_name from information_schema.events;
+--error ER_TABLE_NOT_LOCKED
+create event e2 on schedule every 10 hour do select 1;
+--error ER_TABLE_NOT_LOCKED
+alter event e2 disable;
+--error ER_TABLE_NOT_LOCKED
+alter event e2 rename to e3;
+--error ER_TABLE_NOT_LOCKED
+drop event e2;
+--error ER_TABLE_NOT_LOCKED
+drop event e1;
+unlock tables;
+#
+lock table t1 write;
+#
+--replace_regex /STARTS '[^']+'/STARTS '#'/
+show create event e1;
+select event_name from information_schema.events;
+--error ER_TABLE_NOT_LOCKED
+create event e2 on schedule every 10 hour do select 1;
+--error ER_TABLE_NOT_LOCKED
+alter event e2 disable;
+--error ER_TABLE_NOT_LOCKED
+alter event e2 rename to e3;
+--error ER_TABLE_NOT_LOCKED
+drop event e2;
+--error ER_TABLE_NOT_LOCKED
+drop event e1;
+unlock tables;
+#
+lock table t1 read, mysql.event read;
+#
+--replace_regex /STARTS '[^']+'/STARTS '#'/
+show create event e1;
+select event_name from information_schema.events;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+create event e2 on schedule every 10 hour do select 1;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+alter event e2 disable;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+alter event e2 rename to e3;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+drop event e2;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+drop event e1;
+unlock tables;
+#
+lock table t1 write, mysql.event read;
+#
+--replace_regex /STARTS '[^']+'/STARTS '#'/
+show create event e1;
+select event_name from information_schema.events;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+create event e2 on schedule every 10 hour do select 1;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+alter event e2 disable;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+alter event e2 rename to e3;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+drop event e2;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+drop event e1;
+unlock tables;
+#
+--error ER_WRONG_LOCK_OF_SYSTEM_TABLE
+lock table t1 read, mysql.event write;
+#
+--error ER_WRONG_LOCK_OF_SYSTEM_TABLE
+lock table t1 write, mysql.event write;
+#
+lock table mysql.event write;
+--replace_regex /STARTS '[^']+'/STARTS '#'/
+show create event e1;
+select event_name from information_schema.events;
+create event e2 on schedule every 10 hour do select 1;
+alter event e2 disable;
+alter event e2 rename to e3;
+drop event e3;
+drop event e1;
+unlock tables;
+--echo Make sure we have left no events 
+select event_name from information_schema.events;
+--echo
+--echo Events in sub-statements, events and prelocking
+--echo 
+--echo 
+create event e1 on schedule every 10 hour do select 1;
+delimiter |;
+--error ER_SP_NO_RETSET
+create function f1() returns int
+begin
+  show create event e1;
+  return 1;
+end|
+--error ER_SP_NO_RETSET
+create trigger trg before insert on t1 for each row
+begin
+  show create event e1;
+end|
+--error ER_SP_NO_RETSET
+create function f1() returns int
+begin
+  select event_name from information_schema.events;
+  return 1;
+end|
+--error ER_SP_NO_RETSET
+create trigger trg before insert on t1 for each row
+begin
+  select event_name from information_schema.events;
+end|
+--error ER_EVENT_RECURSION_FORBIDDEN
+create function f1() returns int
+begin
+  create event e2 on schedule every 10 hour do select 1;
+  return 1;
+end|
+--error ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG
+create function f1() returns int
+begin
+  alter event e1 rename to e2;
+  return 1;
+end|
+--error ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG
+create function f1() returns int
+begin
+  drop event e2;
+  return 1;
+end|
+--echo ----------------------------------------------------------------------
+create trigger trg before insert on t1 for each row
+begin
+  set new.a= f1();
+end|
+create function f1() returns int
+begin
+  call p1();
+  return 0;
+end|
+create procedure p1()
+begin
+  select event_name from information_schema.events;
+end|
+--error ER_SP_NO_RETSET
+insert into t1 (a) values (1)|
+drop procedure p1|
+create procedure p1()
+begin
+  show create event e1; 
+end|
+--error ER_SP_NO_RETSET
+insert into t1 (a) values (1)|
+drop procedure p1|
+create procedure p1()
+begin
+  create temporary table tmp select event_name from information_schema.events;
+end|
+--echo expected to work, since we redirect the output into a tmp table
+insert into t1 (a) values (1)|
+select * from tmp|
+drop temporary table tmp|
+drop procedure p1|
+create procedure p1()
+begin
+  alter event e1 rename to e2;
+end|
+--error ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG
+insert into t1 (a) values (1)|
+drop procedure p1|
+create procedure p1()
+begin
+  drop event e1;
+end|
+--error ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG
+insert into t1 (a) values (1)|
+drop table t1|
+drop event e1|
+delimiter ;|
 
 drop database events_test;

--- 1.26/mysql-test/t/events_bugs.test	2007-03-27 22:15:46 +04:00
+++ 1.27/mysql-test/t/events_bugs.test	2007-04-05 15:24:29 +04:00
@@ -1,7 +1,12 @@
 # Can't test with embedded server that doesn't support grants
 -- source include/not_embedded.inc
 
-create database if not exists events_test;
+--disable_warnings
+drop database if exists events_test;
+drop database if exists mysqltest_db1;
+drop database if exists mysqltest_db2;
+--enable_warnings
+create database events_test;
 use events_test;
 
 #
@@ -429,6 +434,184 @@
 
 
 #
+# BUG#16420: Events: timestamps become UTC
+# BUG#26429: SHOW CREATE EVENT is incorrect for an event that
+#            STARTS NOW()
+# BUG#26431: Impossible to re-create an event from backup if its
+#            STARTS clause is in the past
+# WL#3698: Events: execution in local time zone
+#
+# Here we only check non-concurrent aspects of the patch.
+# For the actual tests of time zones please see events_time_zone.test
+#
+SET GLOBAL EVENT_SCHEDULER= OFF;
+SET @save_time_zone= @@TIME_ZONE;
+
+#----------------------------------------------------------------------
+
+# We will use a separate connection because SET TIMESTAMP will stop
+# the clock in that connection.
+
+SET TIME_ZONE= '+00:00';
+SET TIMESTAMP= UNIX_TIMESTAMP('2005-12-31 23:58:59');
+
+
+# Test when event time zone is updated on ALTER EVENT.
+#
+
+CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+SHOW EVENTS;
+
+# Test storing and updating of the event time zone.
+#
+SET TIME_ZONE= '-01:00';
+ALTER EVENT e1 ON SCHEDULE EVERY 1 DAY STARTS '2000-01-01 00:00:00';
+SHOW EVENTS;
+
+# This will update event time zone.
+SET TIME_ZONE= '+02:00';
+ALTER EVENT e1 ON SCHEDULE AT '2000-01-02 00:00:00'
+  ON COMPLETION PRESERVE DISABLE;
+SHOW EVENTS;
+
+# This will update event time zone.
+SET TIME_ZONE= '-03:00';
+ALTER EVENT e1 ON SCHEDULE EVERY 1 DAY ENDS '2030-01-03 00:00:00'
+  ON COMPLETION PRESERVE DISABLE;
+SHOW EVENTS;
+
+# This will not update event time zone, as no time is being adjusted.
+SET TIME_ZONE= '+04:00';
+ALTER EVENT e1 DO SELECT 2;
+SHOW EVENTS;
+
+DROP EVENT e1;
+
+#----------------------------------------------------------------------
+
+# Create some events.
+SET TIME_ZONE='+05:00';
+CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
+  SELECT 1;
+
+SET TIMESTAMP= @@TIMESTAMP + 1;
+
+SET TIME_ZONE='-05:00';
+CREATE EVENT e2 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
+  SELECT 1;
+
+SET TIMESTAMP= @@TIMESTAMP + 1;
+
+SET TIME_ZONE='+00:00';
+CREATE EVENT e3 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
+  SELECT 1;
+
+
+# Test INFORMATION_SCHEMA.EVENTS.
+#
+
+SELECT * FROM INFORMATION_SCHEMA.EVENTS ORDER BY event_name;
+
+
+# Test SHOW EVENTS.
+#
+
+SHOW EVENTS;
+
+
+# Test SHOW CREATE EVENT.
+#
+
+SHOW CREATE EVENT e1;
+SHOW CREATE EVENT e2;
+SHOW CREATE EVENT e3;
+
+#----------------------------------------------------------------------
+
+# Test times in the past.
+#
+
+--echo The following should fail, and nothing should be altered.
+
+--error ER_EVENT_CANNOT_ALTER_IN_THE_PAST
+ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+  ENDS '1999-01-02 00:00:00';
+
+--error ER_EVENT_CANNOT_ALTER_IN_THE_PAST
+ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+  ENDS '1999-01-02 00:00:00' DISABLE;
+
+--echo The following should give warnings, and nothing should be created.
+
+CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+  ENDS '1999-01-02 00:00:00'
+DO
+  SELECT 1;
+
+CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+  ENDS '1999-01-02 00:00:00' DISABLE
+DO
+  SELECT 1;
+
+CREATE EVENT e4 ON SCHEDULE AT '1999-01-01 00:00:00' DO
+  SELECT 1;
+
+CREATE EVENT e4 ON SCHEDULE AT '1999-01-01 00:00:00' DISABLE
+DO
+  SELECT 1;
+
+SHOW EVENTS;
+
+--echo The following should succeed giving a warning.
+
+ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+  ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE;
+
+CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+  ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE
+DO
+  SELECT 1;
+
+CREATE EVENT e5 ON SCHEDULE AT '1999-01-01 00:00:00'
+  ON COMPLETION PRESERVE
+DO
+  SELECT 1;
+
+--echo The following should succeed without warnings.
+
+ALTER EVENT e2 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00';
+
+ALTER EVENT e3 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+  ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE DISABLE;
+
+CREATE EVENT e6 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00' DO
+  SELECT 1;
+
+CREATE EVENT e7 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+  ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE DISABLE
+DO
+  SELECT 1;
+
+CREATE EVENT e8 ON SCHEDULE AT '1999-01-01 00:00:00'
+  ON COMPLETION PRESERVE DISABLE
+DO
+  SELECT 1;
+
+SHOW EVENTS;
+
+
+DROP EVENT e8;
+DROP EVENT e7;
+DROP EVENT e6;
+DROP EVENT e5;
+DROP EVENT e4;
+DROP EVENT e3;
+DROP EVENT e2;
+DROP EVENT e1;
+
+SET TIME_ZONE=@save_time_zone;
+
+# 
 # End of tests
 #
 drop database events_test;

--- 1.3/mysql-test/t/events_restart_phase1.test	2007-03-16 17:31:03 +03:00
+++ 1.4/mysql-test/t/events_restart_phase1.test	2007-04-05 15:24:29 +04:00
@@ -1,19 +1,43 @@
 # Can't test with embedded server that doesn't support grants
 -- source include/not_embedded.inc
+#
+# Test that when the server is restarted, it checks mysql.event table,
+# and disables the scheduler if it's not up to date.
+#
+
+# Switch off the scheduler for now.
+set global event_scheduler=off;
 
 --disable_warnings
-create database if not exists mysqltest_events_test;
+drop database if exists events_test;
 --enable_warnings
-
-use mysqltest_events_test;
-set global event_scheduler=off;
+create database events_test;
+use events_test;
 create table execution_log(name char(10));
-create event abc1 on schedule every 1 second do insert into execution_log value('abc1');
-create event abc2 on schedule every 1 second do insert into execution_log value('abc2');
-create event abc3 on schedule every 1 second do insert into execution_log value('abc3');
---sleep 1.5
-select name from execution_log;
 
-insert into mysql.event values ('db1','bad','select
42','root@localhost',NULL,1000,'MICROSECOND','2006-05-05 17:39:11','2006-05-05
17:39:20','2016-05-05 15:39:24','2016-05-05
15:39:11',NULL,'ENABLED','DROP','','comment1','SYSTEM');
-insert into mysql.event values
('db1','bad2','sect','root@localhost',NULL,1000,'SECOND','2006-05-05
17:39:11','2006-05-05 17:39:20','2016-05-05 15:39:24','2016-05-05
15:39:11',NULL,'ENABLED','DROP','','comment2','SYSTEM');
+create event abc1 on schedule every 1 second do
+  insert into execution_log value('abc1');
+create event abc2 on schedule every 1 second do
+  insert into execution_log value('abc2');
+create event abc3 on schedule every 1 second do 
+  insert into execution_log value('abc3');
+#
+# There are various conditions when a server would regard mysql.event
+# table as damaged:
+# - the table has more column than specified in the compiled in value, but
+# the version of the server which created the table is the same
+# - the column count in the table is less than the compiled in value
+# - some column has an incompatible type specification (for what is an
+# incompatible type specification please refer to the comments in the source
+#
+# Unfortunately, in order to test a condition, we need to restart the
+# server. Therefore, here we test only one simple case: changing the data
+# type of the 'body' field to blob.
+#
+# First, let's do a backup to not depend on actual definition of mysql.event
+create table event_like like mysql.event;
+insert into event_like select * from mysql.event;
+# Now let's alter the table and restart the server
+alter table mysql.event
+  change column body  body longtext character set utf8 collate utf8_bin;
 --echo "Now we restart the server"

--- 1.1/mysql-test/t/events_restart_phase2.test	2006-07-13 12:59:46 +04:00
+++ 1.2/mysql-test/t/events_restart_phase2.test	2007-04-05 15:24:29 +04:00
@@ -1,9 +1,41 @@
 # Can't test with embedded server that doesn't support grants
 -- source include/not_embedded.inc
 
-use mysqltest_events_test;
---sleep 1.5
---echo "Should get 0 rows because the queue aborted run
-select distinct name from execution_log order by name;
-delete from mysql.event where name like 'bad%';
---echo "Now restart the server again"
+use events_test;
+# Event scheduler should be disabled: the system tables are damaged
+select @@event_scheduler;
+# Try various Event Scheduler operation and check the output.
+--error ER_EVENTS_DB_ERROR
+show events;
+--error ER_EVENTS_DB_ERROR
+select event_name from information_schema.events;
+--error ER_EVENTS_DB_ERROR
+show create event intact_check;
+--error ER_EVENTS_DB_ERROR
+drop event no_such_event;
+--error ER_EVENTS_DB_ERROR
+create event intact_check_1 on schedule every 5 hour do select 5;
+--error ER_EVENTS_DB_ERROR
+alter event intact_check_1 on schedule every 8 hour do select 8;
+--error ER_EVENTS_DB_ERROR
+alter event intact_check_1 rename to intact_check_2;
+--error ER_EVENTS_DB_ERROR
+drop event intact_check_1;
+--error ER_EVENTS_DB_ERROR
+drop event intact_check_2;
+--error ER_EVENTS_DB_ERROR
+drop event intact_check;
+--error ER_EVENTS_DB_ERROR
+set global event_scheduler=on;
+--error ER_EVENTS_DB_ERROR
+set global event_scheduler=off;
+show variables like 'event_scheduler';
+--echo Make sure that we still can create and drop databases,
+--echo and no warnings are produced.
+drop database if exists mysqltest_database_not_exists;
+create database mysqltest_db1;
+drop database mysqltest_db1;
+--echo Restore the original mysql.event table
+drop table mysql.event;
+rename table event_like to mysql.event;
+--echo Now let's restart the server again

--- 1.1/mysql-test/t/events_restart_phase3.test	2006-07-13 12:59:47 +04:00
+++ 1.2/mysql-test/t/events_restart_phase3.test	2007-04-05 15:24:29 +04:00
@@ -1,14 +1,18 @@
 # Can't test with embedded server that doesn't support grants
 -- source include/not_embedded.inc
-
-use mysqltest_events_test;
---sleep 2
+#
+# We need this file primarily to make sure that the scheduler is restarted
+# and enabled after we have restored mysql.event table.
+# This is the final step of the "cleanup".
+#
+# Make sure also that events are executed OK after restart, just in case.
+use events_test;
+# Make sure the scheduler was started successfully
+select @@event_scheduler;
+let $wait_condition=select count(distinct name)=3 from execution_log;
+--source include/wait_condition.inc
 --echo "Should get 3 rows : abc1, abc2, abc3
 select distinct name from execution_log order by name;
-
-drop event abc1;
-drop event abc2;
-drop event abc3;
 drop table execution_log;
-
-drop database mysqltest_events_test;
+# Will drop all events
+drop database events_test;

--- 1.11/mysql-test/t/events_scheduling.test	2007-03-20 00:37:25 +03:00
+++ 1.12/mysql-test/t/events_scheduling.test	2007-04-05 15:24:29 +04:00
@@ -6,11 +6,15 @@
 USE events_test;
 
 SET GLOBAL event_scheduler=OFF;
+--echo Try agian to make sure it's allowed
+SET GLOBAL event_scheduler=OFF;
 SHOW VARIABLES LIKE 'event_scheduler';
 SET GLOBAL event_scheduler=1;
 SHOW VARIABLES LIKE 'event_scheduler';
 SET GLOBAL event_scheduler=0;
 SHOW VARIABLES LIKE 'event_scheduler';
+SET GLOBAL event_scheduler=ON;
+--echo Try again to make sure it's allowed
 SET GLOBAL event_scheduler=ON;
 SHOW VARIABLES LIKE 'event_scheduler';
 --error ER_WRONG_VALUE_FOR_VAR

--- 1.73/sql/events.cc	2007-03-23 19:14:06 +03:00
+++ 1.74/sql/events.cc	2007-04-05 15:24:29 +04:00
@@ -19,7 +19,6 @@
 #include "event_db_repository.h"
 #include "event_queue.h"
 #include "event_scheduler.h"
-#include "sp_head.h"
 
 /*
  TODO list :
@@ -66,7 +65,7 @@
 static const char *opt_event_scheduler_state_names[]=
     { "OFF", "ON", "0", "1", "DISABLED", NullS };
 
-TYPELIB Events::opt_typelib=
+const TYPELIB Events::opt_typelib=
 {
   array_elements(opt_event_scheduler_state_names)-1,
   "",
@@ -82,7 +81,7 @@
 */
 static const char *var_event_scheduler_state_names[]= { "OFF", "ON", NullS };
 
-TYPELIB Events::var_typelib=
+const TYPELIB Events::var_typelib=
 {
   array_elements(var_event_scheduler_state_names)-1,
   "",
@@ -90,20 +89,13 @@
   NULL
 };
 
-
-static
-Event_queue events_event_queue;
-
-static
-Event_scheduler events_event_scheduler;
-
-
-Event_db_repository events_event_db_repository;
-
-Events Events::singleton;
-
-enum Events::enum_opt_event_scheduler Events::opt_event_scheduler=
-     Events::EVENTS_OFF;
+Event_queue *Events::event_queue;
+Event_scheduler *Events::scheduler;
+Event_db_repository *Events::db_repository;
+enum Events::enum_opt_event_scheduler
+Events::opt_event_scheduler= Events::EVENTS_OFF;
+pthread_mutex_t Events::LOCK_event_metadata;
+bool Events::check_system_tables_error= FALSE;
 
 
 /*
@@ -128,25 +120,89 @@
 }
 
 
-/*
-  Accessor for the singleton instance.
+/**
+  @brief Initialize the start up option of the Events scheduler.
 
-  SYNOPSIS
-    Events::get_instance()
+  Do not initialize the scheduler subsystem yet - the initialization
+  is split into steps as it has to fit into the common MySQL
+  initialization framework.
+  No locking as this is called only at start up.
+
+  @param[in,out]  argument  The value of the argument. If this value
+                            is found in the typelib, the argument is
+                            updated.
 
-  RETURN VALUE
-    address
+  @retval TRUE  unknown option value
+  @retval FALSE success
 */
 
-Events *
-Events::get_instance()
+bool
+Events::set_opt_event_scheduler(char *argument)
 {
-  DBUG_ENTER("Events::get_instance");
-  DBUG_RETURN(&singleton);
+  if (argument == NULL)
+    opt_event_scheduler= Events::EVENTS_DISABLED;
+  else
+  {
+    int type;
+    /*
+      type=   1   2      3   4      5
+           (OFF | ON) - (0 | 1) (DISABLE )
+    */
+    const static enum enum_opt_event_scheduler type2state[]=
+    { EVENTS_OFF, EVENTS_ON, EVENTS_OFF, EVENTS_ON, EVENTS_DISABLED };
+
+    type= find_type(argument, &opt_typelib, 1);
+
+    DBUG_ASSERT(type >= 0 && type <= 5); /* guaranteed by find_type */
+
+    if (type == 0)
+    {
+      fprintf(stderr, "Unknown option to event-scheduler: %s\n", argument);
+      return TRUE;
+    }
+    opt_event_scheduler= type2state[type-1];
+  }
+  return FALSE;
 }
 
 
-/*
+/**
+  Return a string representation of the current scheduler mode.
+*/
+
+const char *
+Events::get_opt_event_scheduler_str()
+{
+  const char *str;
+
+  pthread_mutex_lock(&LOCK_event_metadata);
+  str= opt_typelib.type_names[(int) opt_event_scheduler];
+  pthread_mutex_unlock(&LOCK_event_metadata);
+
+  return str;
+}
+
+
+/**
+  Push an error into the error stack if the system tables are
+  not up to date.
+*/
+
+bool Events::check_if_system_tables_error()
+{
+  DBUG_ENTER("Events::check_if_system_tables_error");
+
+  if (check_system_tables_error)
+  {
+    my_error(ER_EVENTS_DB_ERROR, MYF(0));
+    DBUG_RETURN(TRUE);
+  }
+
+  DBUG_RETURN(FALSE);
+}
+
+
+/**
   Reconstructs interval expression from interval type and expression
   value that is in form of a value of the smalles entity:
   For
@@ -278,56 +334,70 @@
   return 0;
 }
 
-/*
-  Constructor of Events class. It's called when events.o
-  is loaded. Assigning addressed of static variables in this
-  object file.
 
-  SYNOPSIS
-    Events::Events()
+/**
+  Create a new event.
+
+  @param[in,out]  thd            THD
+  @param[in]      parse_data     Event's data from parsing stage
+  @param[in]      if_not_exists  Whether IF NOT EXISTS was
+                                 specified
+  In case there is an event with the same name (db) and
+  IF NOT EXISTS is specified, an warning is put into the stack.
+  @sa Events::drop_event for the notes about locking, pre-locking
+  and Events DDL.
+
+  @retval  FALSE  OK
+  @retval  TRUE   Error (reported)
 */
 
-Events::Events()
+bool
+Events::create_event(THD *thd, Event_parse_data *parse_data,
+                     bool if_not_exists)
 {
-  scheduler=     &events_event_scheduler;
-  event_queue=   &events_event_queue;
-  db_repository= &events_event_db_repository;
-}
+  int ret;
+  DBUG_ENTER("Events::create_event");
 
+  /*
+    Let's commit the transaction first - MySQL manual specifies
+    that a DDL issues an implicit commit, and it doesn't say "successful
+    DDL", so that an implicit commit is a property of any successfully
+    parsed DDL statement.
+  */
+  if (end_active_trans(thd))
+    DBUG_RETURN(TRUE);
 
-/*
-  The function exported to the world for creating of events.
+  if (check_if_system_tables_error())
+    DBUG_RETURN(TRUE);
 
-  SYNOPSIS
-    Events::create_event()
-      thd            [in]  THD
-      parse_data     [in]  Event's data from parsing stage
-      if_not_exists  [in]  Whether IF NOT EXISTS was specified in the DDL
+  /*
+    Perform semantic checks outside of Event_db_repository:
+    once CREATE EVENT is supported in prepared statements, the
+    checks will be moved to PREPARE phase.
+  */
+  if (parse_data->check_parse_data(thd))
+    DBUG_RETURN(TRUE);
 
-  RETURN VALUE
-    FALSE  OK
-    TRUE   Error (Reported)
+  /* At create, one of them must be set */
+  DBUG_ASSERT(parse_data->expression || parse_data->execute_at);
 
-  NOTES
-    In case there is an event with the same name (db) and
-    IF NOT EXISTS is specified, an warning is put into the stack.
-*/
+  if (check_access(thd, EVENT_ACL, parse_data->dbname.str, 0, 0, 0,
+                   is_schema_db(parse_data->dbname.str)))
+    DBUG_RETURN(TRUE);
 
-bool
-Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists)
-{
-  int ret;
-  DBUG_ENTER("Events::create_event");
-  if (unlikely(check_system_tables_error))
+  if (check_db_dir_existence(parse_data->dbname.str))
   {
-    my_error(ER_EVENTS_DB_ERROR, MYF(0));
+    my_error(ER_BAD_DB_ERROR, MYF(0));
     DBUG_RETURN(TRUE);
   }
 
+  if (parse_data->do_not_create)
+    DBUG_RETURN(FALSE);
+
   pthread_mutex_lock(&LOCK_event_metadata);
+
   /* On error conditions my_error() is called so no need to handle here */
-  if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists)) &&
-      !parse_data->do_not_create)
+  if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists)))
   {
     Event_queue_element *new_element;
 
@@ -337,11 +407,16 @@
                                                    parse_data->name,
                                                    new_element)))
     {
-      DBUG_ASSERT(ret == OP_LOAD_ERROR);
+      db_repository->drop_event(thd, parse_data->dbname, parse_data->name,
+                                TRUE);
       delete new_element;
     }
-    else
-      event_queue->create_event(thd, new_element);
+    else if (event_queue)
+    {
+      /* TODO: do not ignore the out parameter and a possible OOM error! */
+      bool created;
+      event_queue->create_event(thd, new_element, &created);
+    }
   }
   pthread_mutex_unlock(&LOCK_event_metadata);
 
@@ -349,42 +424,85 @@
 }
 
 
-/*
-  The function exported to the world for alteration of events.
-
-  SYNOPSIS
-    Events::update_event()
-      thd           [in]  THD
-      parse_data    [in]  Event's data from parsing stage
-      rename_to     [in]  Set in case of RENAME TO.
+/**
+  Alter an event.
 
-  RETURN VALUE
-    FALSE  OK
-    TRUE   Error
+  @param[in,out] thd         THD
+  @param[in]     parse_data  Event's data from parsing stage
+  @param[in]     new_dbname  A new schema name for the event. Set in the case of
+                             ALTER EVENT RENAME, otherwise is NULL.
+  @param[in]     new_name    A new name for the event. Set in the case of
+                             ALTER EVENT RENAME
+
+  Parameter 'et' contains data about dbname and event name.
+  Parameter 'new_name' is the new name of the event, if not null
+  this means that RENAME TO was specified in the query
+  @sa Events::drop_event for the locking notes.
 
-  NOTES
-    et contains data about dbname and event name.
-    new_name is the new name of the event, if not null this means
-    that RENAME TO was specified in the query
+  @retval  FALSE  OK
+  @retval  TRUE   error (reported)
 */
 
 bool
-Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to)
+Events::update_event(THD *thd, Event_parse_data *parse_data,
+                     LEX_STRING *new_dbname, LEX_STRING *new_name)
 {
   int ret;
   Event_queue_element *new_element;
+
   DBUG_ENTER("Events::update_event");
-  LEX_STRING *new_dbname= rename_to ? &rename_to->m_db : NULL;
-  LEX_STRING *new_name= rename_to ? &rename_to->m_name : NULL;
-  if (unlikely(check_system_tables_error))
-  {
-    my_error(ER_EVENTS_DB_ERROR, MYF(0));
+
+  /*
+    For consistency, implicit COMMIT should be the first thing in the
+    execution chain.
+  */
+  if (end_active_trans(thd))
+    DBUG_RETURN(TRUE);
+
+  if (check_if_system_tables_error())
+    DBUG_RETURN(TRUE);
+
+  if (parse_data->check_parse_data(thd) || parse_data->do_not_create)
+    DBUG_RETURN(TRUE);
+
+  if (check_access(thd, EVENT_ACL, parse_data->dbname.str, 0, 0, 0,
+                   is_schema_db(parse_data->dbname.str)))
     DBUG_RETURN(TRUE);
+
+  if (new_dbname)                               /* It's a rename */
+  {
+    /* Check that the new and the old names differ. */
+    if ( !sortcmp_lex_string(parse_data->dbname, *new_dbname,
+                             system_charset_info) &&
+         !sortcmp_lex_string(parse_data->name, *new_name,
+                             system_charset_info))
+    {
+      my_error(ER_EVENT_SAME_NAME, MYF(0), parse_data->name.str);
+      DBUG_RETURN(TRUE);
+    }
+
+    /*
+      And the user has sufficient privileges to use the target database.
+      Do it before checking whether the database exists: we don't want
+      to tell the user that a database doesn't exist if they can not
+      access it.
+    */
+    if (check_access(thd, EVENT_ACL, new_dbname->str, 0, 0, 0,
+                     is_schema_db(new_dbname->str)))
+      DBUG_RETURN(TRUE);
+
+    /* Check that the target database exists */
+    if (check_db_dir_existence(new_dbname->str))
+    {
+      my_error(ER_BAD_DB_ERROR, MYF(0));
+      DBUG_RETURN(TRUE);
+    }
   }
 
   pthread_mutex_lock(&LOCK_event_metadata);
   /* On error conditions my_error() is called so no need to handle here */
-  if (!(ret= db_repository->update_event(thd, parse_data, new_dbname, new_name)))
+  if (!(ret= db_repository->update_event(thd, parse_data,
+                                         new_dbname, new_name)))
   {
     LEX_STRING dbname= new_dbname ? *new_dbname : parse_data->dbname;
     LEX_STRING name= new_name ? *new_name : parse_data->name;
@@ -397,9 +515,17 @@
       DBUG_ASSERT(ret == OP_LOAD_ERROR);
       delete new_element;
     }
-    else
+    else if (event_queue)
+    {
+      /*
+        TODO: check if an update actually has inserted an entry
+        into the queue.
+        If not, and the element is ON COMPLETION NOT PRESERVE, delete
+        it right away.
+      */
       event_queue->update_event(thd, parse_data->dbname, parse_data->name,
                                 new_element);
+    }
   }
   pthread_mutex_unlock(&LOCK_event_metadata);
 
@@ -407,20 +533,28 @@
 }
 
 
-/*
+/**
   Drops an event
 
-  SYNOPSIS
-    Events::drop_event()
-      thd             [in]  THD
-      dbname          [in]  Event's schema
-      name            [in]  Event's name
-      if_exists       [in]  When set and the event does not exist =>
-                            warning onto the stack
+  @param[in,out]  thd        THD
+  @param[in]      dbname     Event's schema
+  @param[in]      name       Event's name
+  @param[in]      if_exists  When this is set and the event does not exist
+                             a warning is pushed into the warning stack.
+                             Otherwise the operation produces an error.
+
+  @note Similarly to DROP PROCEDURE, we do not allow DROP EVENT
+  under LOCK TABLES mode, unless table mysql.event is locked.  To
+  ensure that, we do not reset & backup the open tables state in
+  this function - if in LOCK TABLES or pre-locking mode, this will
+  lead to an error 'Table mysql.event is not locked with LOCK
+  TABLES' unless it _is_ locked. In pre-locked mode there is
+  another barrier - DROP EVENT commits the current transaction,
+  and COMMIT/ROLLBACK is not allowed in stored functions and
+  triggers.
 
-  RETURN VALUE
-    FALSE  OK
-    TRUE   Error (reported)
+  @retval  FALSE  OK
+  @retval  TRUE   Error (reported)
 */
 
 bool
@@ -428,16 +562,35 @@
 {
   int ret;
   DBUG_ENTER("Events::drop_event");
-  if (unlikely(check_system_tables_error))
-  {
-    my_error(ER_EVENTS_DB_ERROR, MYF(0));
+
+  /*
+    In MySQL, DDL must always commit: since mysql.* tables are
+    non-transactional, we must modify them outside a transaction
+    to not break atomicity.
+    But the second and more important reason to commit here
+    regardless whether we're actually changing mysql.event table
+    or not is replication: end_active_trans syncs the binary log,
+    and unless we run DDL in it's own transaction it may simply
+    never appear on the slave in case the outside transaction
+    rolls back.
+  */
+  if (end_active_trans(thd))
+    DBUG_RETURN(TRUE);
+
+  if (check_if_system_tables_error())
+    DBUG_RETURN(TRUE);
+
+  if (check_access(thd, EVENT_ACL, dbname.str, 0, 0, 0,
+                   is_schema_db(dbname.str)))
     DBUG_RETURN(TRUE);
-  }
 
   pthread_mutex_lock(&LOCK_event_metadata);
   /* On error conditions my_error() is called so no need to handle here */
   if (!(ret= db_repository->drop_event(thd, dbname, name, if_exists)))
-    event_queue->drop_event(thd, dbname, name);
+  {
+    if (event_queue)
+      event_queue->drop_event(thd, dbname, name);
+  }
   pthread_mutex_unlock(&LOCK_event_metadata);
   DBUG_RETURN(ret);
 }
@@ -446,10 +599,12 @@
 /**
   Drops all events from a schema
 
-  SYNOPSIS
-    Events::drop_schema_events()
-      thd  Thread
-      db   ASCIIZ schema name
+  @note We allow to drop all events in a schema even if the
+  scheduler is disabled. This is to not produce any warnings
+  in case of DROP DATABASE and a disabled scheduler.
+
+  @param[in,out]  thd  Thread
+  @param[in]      db   ASCIIZ schema name
 */
 
 void
@@ -459,14 +614,15 @@
 
   DBUG_ENTER("Events::drop_schema_events");
   DBUG_PRINT("enter", ("dropping events from %s", db));
-  if (unlikely(check_system_tables_error))
-  {
-    my_error(ER_EVENTS_DB_ERROR, MYF(0));
-    DBUG_VOID_RETURN;
-  }
+
+  /*
+    sic: no check if the scheduler is disabled or system tables
+    are damaged, as intended.
+  */
 
   pthread_mutex_lock(&LOCK_event_metadata);
-  event_queue->drop_schema_events(thd, db_lex);
+  if (event_queue)
+    event_queue->drop_schema_events(thd, db_lex);
   db_repository->drop_schema_events(thd, db_lex);
   pthread_mutex_unlock(&LOCK_event_metadata);
 
@@ -474,115 +630,137 @@
 }
 
 
-/*
-  SHOW CREATE EVENT
-
-  SYNOPSIS
-    Events::show_create_event()
-      thd   Thread context
-      spn   The name of the event (db, name)
-
-  RETURN VALUE
-    FALSE  OK
-    TRUE   Error during writing to the wire
+/**
+  A helper function to generate SHOW CREATE EVENT output from
+  a named event
 */
 
-bool
-Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
+static bool
+send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol)
 {
-  CHARSET_INFO *scs= system_charset_info;
-  int ret;
-  Event_timed *et= new Event_timed();
+  char show_str_buf[10 * STRING_BUFFER_USUAL_SIZE];
+  String show_str(show_str_buf, sizeof(show_str_buf), system_charset_info);
+  List<Item> field_list;
+  LEX_STRING sql_mode;
+  const String *tz_name;
 
-  DBUG_ENTER("Events::show_create_event");
-  DBUG_PRINT("enter", ("name: %s@%s", dbname.str, name.str));
-  if (unlikely(check_system_tables_error))
-  {
-    my_error(ER_EVENTS_DB_ERROR, MYF(0));
+  DBUG_ENTER("send_show_create_event");
+
+  show_str.length(0);
+  if (et->get_create_event(thd, &show_str))
     DBUG_RETURN(TRUE);
-  }
 
-  ret= db_repository->load_named_event(thd, dbname, name, et);
+  field_list.push_back(new Item_empty_string("Event", NAME_LEN));
 
-  if (!ret)
-  {
-    Protocol *protocol= thd->protocol;
-    char show_str_buf[10 * STRING_BUFFER_USUAL_SIZE];
-    String show_str(show_str_buf, sizeof(show_str_buf), scs);
-    List<Item> field_list;
-    byte *sql_mode_str;
-    ulong sql_mode_len=0;
+  if (sys_var_thd_sql_mode::symbolic_mode_representation(thd, et->sql_mode,
+                                                         &sql_mode))
+    DBUG_RETURN(TRUE);
 
-    show_str.length(0);
-    show_str.set_charset(system_charset_info);
+  field_list.push_back(new Item_empty_string("sql_mode", sql_mode.length));
 
-    if (et->get_create_event(thd, &show_str))
-      goto err;
+  tz_name= et->time_zone->get_name();
 
-    field_list.push_back(new Item_empty_string("Event", NAME_LEN));
+  field_list.push_back(new Item_empty_string("time_zone",
+                                             tz_name->length()));
 
-    sql_mode_str=
-      sys_var_thd_sql_mode::symbolic_mode_representation(thd, et->sql_mode,
-                                                         &sql_mode_len);
+  field_list.push_back(new Item_empty_string("Create Event",
+                                             show_str.length()));
 
-    field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));
+  if (protocol->send_fields(&field_list,
+                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+    DBUG_RETURN(TRUE);
 
-    const String *tz_name= et->time_zone->get_name();
-    field_list.push_back(new Item_empty_string("time_zone",
-                                               tz_name->length()));
+  protocol->prepare_for_resend();
 
-    field_list.push_back(new Item_empty_string("Create Event",
-                                               show_str.length()));
+  protocol->store(et->name.str, et->name.length, system_charset_info);
+  protocol->store(sql_mode.str, sql_mode.length, system_charset_info);
+  protocol->store(tz_name->ptr(), tz_name->length(), system_charset_info);
+  protocol->store(show_str.c_ptr(), show_str.length(), system_charset_info);
 
-    if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
-                                           Protocol::SEND_EOF))
-      goto err;
+  if (protocol->write())
+    DBUG_RETURN(TRUE);
 
-    protocol->prepare_for_resend();
-    protocol->store(et->name.str, et->name.length, scs);
+  send_eof(thd);
 
-    protocol->store((char*) sql_mode_str, sql_mode_len, scs);
+  DBUG_RETURN(FALSE);
+}
 
-    protocol->store((char*) tz_name->ptr(), tz_name->length(), scs);
 
-    protocol->store(show_str.c_ptr(), show_str.length(), scs);
-    ret= protocol->write();
-    send_eof(thd);
-  }
-  delete et;
+/**
+  Implement SHOW CREATE EVENT statement
+
+      thd   Thread context
+      spn   The name of the event (db, name)
+
+  @retval  FALSE  OK
+  @retval  TRUE   error (reported)
+*/
+
+bool
+Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
+{
+  Open_tables_state open_tables_backup;
+  Event_timed et;
+  bool ret;
+
+  DBUG_ENTER("Events::show_create_event");
+  DBUG_PRINT("enter", ("name: %s@%s", dbname.str, name.str));
+
+  if (check_if_system_tables_error())
+    DBUG_RETURN(TRUE);
+
+  if (check_access(thd, EVENT_ACL, dbname.str, 0, 0, 0,
+                   is_schema_db(dbname.str)))
+    DBUG_RETURN(TRUE);
+
+  /*
+    We would like to allow SHOW CREATE EVENT under LOCK TABLES and
+    in pre-locked mode. mysql.event table is marked as a system table.
+    This flag reduces the set of its participation scenarios in LOCK TABLES
+    operation, and therefore an out-of-bound open of this table
+    for reading like the one below (sic, only for reading) is
+    more or less deadlock-free. For additional information about when a
+    deadlock can occur please refer to the description of 'system table'
+    flag.
+  */
+  thd->reset_n_backup_open_tables_state(&open_tables_backup);
+  ret= db_repository->load_named_event(thd, dbname, name, &et);
+  thd->restore_backup_open_tables_state(&open_tables_backup);
+
+  if (!ret)
+    ret= send_show_create_event(thd, &et, thd->protocol);
+
   DBUG_RETURN(ret);
-err:
-  delete et;
-  DBUG_RETURN(TRUE);
 }
 
 
-/*
-  Proxy for Event_db_repository::fill_schema_events.
-  Callback for I_S from sql_show.cc
+/**
+  Check access rights and fill INFORMATION_SCHEMA.events table.
 
-  SYNOPSIS
-    Events::fill_schema_events()
-      thd     Thread context
-      tables  The schema table
+  @param[in,out]  thd     Thread context
+  @param[in]      table   The temporary table to fill.
       cond    Unused
 
-  RETURN VALUE
-    0  OK
-    !0 Error
+  In MySQL INFORMATION_SCHEMA tables are temporary tables that are
+  created and filled on demand. In this function, we fill
+  INFORMATION_SCHEMA.events. It is a callback for I_S module, invoked from
+  sql_show.cc
+
+  @return Has to be integer, as such is the requirement of the I_S API
+  @retval  0  success
+  @retval  1  an error, pushed into the error stack
 */
 
 int
 Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
 {
   char *db= NULL;
+  int ret;
+  Open_tables_state open_tables_backup;
   DBUG_ENTER("Events::fill_schema_events");
-  Events *myself= get_instance();
-  if (unlikely(myself->check_system_tables_error))
-  {
-    my_error(ER_EVENTS_DB_ERROR, MYF(0));
-    DBUG_RETURN(TRUE);
-  }
+
+  if (check_if_system_tables_error())
+    DBUG_RETURN(1);
 
   /*
     If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to
@@ -596,7 +774,17 @@
       DBUG_RETURN(1);
     db= thd->lex->select_lex.db;
   }
-  DBUG_RETURN(myself->db_repository->fill_schema_events(thd, tables, db));
+  /*
+    Reset and backup of the currently open tables in this thread
+    is a way to allow SELECTs from INFORMATION_SCHEMA.events under
+    LOCK TABLES and in pre-locked mode. See also
+    Events::show_create_event for additional comments.
+  */
+  thd->reset_n_backup_open_tables_state(&open_tables_backup);
+  ret= db_repository->fill_schema_events(thd, tables, db);
+  thd->restore_backup_open_tables_state(&open_tables_backup);
+
+  DBUG_RETURN(ret);
 }
 
 
@@ -615,14 +803,17 @@
 */
 
 bool
-Events::init()
+Events::init(my_bool opt_noacl)
 {
   THD *thd;
   bool res= FALSE;
+
   DBUG_ENTER("Events::init");
 
-  if (opt_event_scheduler == Events::EVENTS_DISABLED)
-    DBUG_RETURN(FALSE);
+  /* Disable the scheduler if running with --skip-grant-tables */
+  if (opt_noacl)
+    opt_event_scheduler= EVENTS_DISABLED;
+
 
   /* We need a temporary THD during boot */
   if (!(thd= new THD()))
@@ -638,30 +829,67 @@
   thd->thread_stack= (char*) &thd;
   thd->store_globals();
 
-  if (check_system_tables(thd))
+  /*
+    We will need Event_db_repository anyway, even if the scheduler is
+    disabled - to perform events DDL.
+  */
+  if (!(db_repository= new Event_db_repository))
   {
-    check_system_tables_error= TRUE;
-    sql_print_error("Event Scheduler: The system tables are damaged. "
-                    "The scheduler subsystem will be unusable during this run.");
+    res= TRUE; /* fatal error: request unireg_abort */
     goto end;
   }
-  check_system_tables_error= FALSE;
 
-  if (event_queue->init_queue(thd) || load_events_from_db(thd))
+  /*
+    Since we allow event DDL even if the scheduler is disabled,
+    check the system tables, as we might need them.
+  */
+  if (Event_db_repository::check_system_tables(thd))
   {
-    sql_print_error("Event Scheduler: Error while loading from disk.");
+    sql_print_error("Event Scheduler: An error occurred when initializing "
+                    "system tables.%s",
+                    opt_event_scheduler == EVENTS_DISABLED ?
+                    "" : " Disabling the Event Scheduler.");
+
+    /* Disable the scheduler since the system tables are not up to date */
+    opt_event_scheduler= EVENTS_DISABLED;
+    check_system_tables_error= TRUE;
     goto end;
   }
 
-  scheduler->init_scheduler(event_queue);
+  /*
+    Was disabled explicitly from the command line, or because we're running
+    with --skip-grant-tables, or because we have no system tables.
+  */
+  if (opt_event_scheduler == Events::EVENTS_DISABLED)
+    goto end;
+
 
   DBUG_ASSERT(opt_event_scheduler == Events::EVENTS_ON ||
               opt_event_scheduler == Events::EVENTS_OFF);
-  if (opt_event_scheduler == Events::EVENTS_ON)
-    res= scheduler->start();
 
-  Event_worker_thread::init(this, db_repository);
+  if (!(event_queue= new Event_queue) ||
+      !(scheduler= new Event_scheduler(event_queue)))
+  {
+    res= TRUE; /* fatal error: request unireg_abort */
+    goto end;
+  }
+
+  if (event_queue->init_queue(thd) || load_events_from_db(thd) ||
+      opt_event_scheduler == EVENTS_ON && scheduler->start())
+  {
+    sql_print_error("Event Scheduler: Error while loading from disk.");
+    res= TRUE; /* fatal error: request unireg_abort */
+    goto end;
+  }
+  Event_worker_thread::init(db_repository);
+
 end:
+  if (res)
+  {
+    delete db_repository;
+    delete event_queue;
+    delete scheduler;
+  }
   delete thd;
   /* Remember that we don't have a THD */
   my_pthread_setspecific_ptr(THR_THD,  NULL);
@@ -684,14 +912,18 @@
 Events::deinit()
 {
   DBUG_ENTER("Events::deinit");
-  if (likely(!check_system_tables_error))
-  {
-    scheduler->stop();
-    scheduler->deinit_scheduler();
 
-    event_queue->deinit_queue();
+  if (opt_event_scheduler != EVENTS_DISABLED)
+  {
+    delete scheduler;
+    scheduler= NULL;                            /* safety */
+    delete event_queue;
+    event_queue= NULL;                          /* safety */
   }
 
+  delete db_repository;
+  db_repository= NULL;                          /* safety */
+
   DBUG_VOID_RETURN;
 }
 
@@ -708,8 +940,6 @@
 Events::init_mutexes()
 {
   pthread_mutex_init(&LOCK_event_metadata, MY_MUTEX_INIT_FAST);
-  event_queue->init_mutexes();
-  scheduler->init_mutexes();
 }
 
 
@@ -723,8 +953,6 @@
 void
 Events::destroy_mutexes()
 {
-  event_queue->deinit_mutexes();
-  scheduler->deinit_mutexes();
   pthread_mutex_destroy(&LOCK_event_metadata);
 }
 
@@ -747,283 +975,163 @@
   puts("LLA = Last Locked At  LUA = Last Unlocked At");
   puts("WOC = Waiting On Condition  DL = Data Locked");
 
-  scheduler->dump_internal_status();
-  event_queue->dump_internal_status();
+  pthread_mutex_lock(&LOCK_event_metadata);
+  if (opt_event_scheduler == EVENTS_DISABLED)
+    puts("The Event Scheduler is disabled");
+  else
+  {
+    scheduler->dump_internal_status();
+    event_queue->dump_internal_status();
+  }
 
+  pthread_mutex_unlock(&LOCK_event_metadata);
   DBUG_VOID_RETURN;
 }
 
 
 /**
-  Starts execution of events by the scheduler
-
-  SYNOPSIS
-    Events::start_execution_of_events()
+  Starts or stops the event scheduler thread.
 
-  RETURN VALUE
-    FALSE  OK
-    TRUE   Error
+  @retval FALSE success
+  @retval TRUE  error
 */
 
 bool
-Events::start_execution_of_events()
+Events::start_or_stop_event_scheduler(enum_opt_event_scheduler start_or_stop)
 {
-  DBUG_ENTER("Events::start_execution_of_events");
-  if (unlikely(check_system_tables_error))
-  {
-    my_error(ER_EVENTS_DB_ERROR, MYF(0));
-    DBUG_RETURN(TRUE);
-  }
-  DBUG_RETURN(scheduler->start());
-}
+  bool ret= FALSE;
 
+  DBUG_ENTER("Events::start_or_stop_event_scheduler");
 
-/*
-  Stops execution of events by the scheduler.
-  Already running events will not be stopped. If the user needs
-  them stopped manual intervention is needed.
-
-  SYNOPSIS
-    Events::stop_execution_of_events()
+  DBUG_ASSERT(start_or_stop == Events::EVENTS_ON ||
+              start_or_stop == Events::EVENTS_OFF);
 
-  RETURN VALUE
-    FALSE  OK
-    TRUE   Error
-*/
-
-bool
-Events::stop_execution_of_events()
-{
-  DBUG_ENTER("Events::stop_execution_of_events");
-  if (unlikely(check_system_tables_error))
-  {
-    my_error(ER_EVENTS_DB_ERROR, MYF(0));
+  /*
+    If the scheduler was disabled because there are no/bad
+    system tables, produce a more meaningful error message
+    than ER_OPTION_PREVENTS_STATEMENT
+  */
+  if (check_if_system_tables_error())
     DBUG_RETURN(TRUE);
-  }
-  DBUG_RETURN(scheduler->stop());
-}
-
-
-/*
-  Checks whether the scheduler is running or not.
-
-  SYNOPSIS
-    Events::is_started()
-
-  RETURN VALUE
-    TRUE   Yes
-    FALSE  No
-*/
-
-bool
-Events::is_execution_of_events_started()
-{
-  DBUG_ENTER("Events::is_execution_of_events_started");
-  if (unlikely(check_system_tables_error))
-  {
-    my_error(ER_EVENTS_DB_ERROR, MYF(0));
-    DBUG_RETURN(FALSE);
-  }
-  DBUG_RETURN(scheduler->is_running());
-}
-
-
-
-/*
-  Opens mysql.db and mysql.user and checks whether:
-    1. mysql.db has column Event_priv at column 20 (0 based);
-    2. mysql.user has column Event_priv at column 29 (0 based);
 
-  SYNOPSIS
-    Events::check_system_tables()
-      thd  Thread
-
-  RETURN VALUE
-    FALSE  OK
-    TRUE   Error
-*/
-
-bool
-Events::check_system_tables(THD *thd)
-{
-  TABLE_LIST tables;
-  Open_tables_state backup;
-  bool ret= FALSE;
-
-  DBUG_ENTER("Events::check_system_tables");
-  DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
-
-  thd->reset_n_backup_open_tables_state(&backup);
-
-  bzero((char*) &tables, sizeof(tables));
-  tables.db= (char*) "mysql";
-  tables.table_name= tables.alias= (char*) "db";
-  tables.lock_type= TL_READ;
+  pthread_mutex_lock(&LOCK_event_metadata);
 
-  if ((ret= simple_open_n_lock_tables(thd, &tables)))
+  if (opt_event_scheduler == EVENTS_DISABLED)
   {
-    sql_print_error("Event Scheduler: Cannot open mysql.db");
+    my_error(ER_OPTION_PREVENTS_STATEMENT,
+             MYF(0), "--event-scheduler=DISABLED or --skip-grant-tables");
     ret= TRUE;
+    goto end;
   }
-  ret= table_check_intact(tables.table, MYSQL_DB_FIELD_COUNT,
-                          mysql_db_table_fields, &mysql_db_table_last_check,
-                          ER_CANNOT_LOAD_FROM_TABLE);
-  close_thread_tables(thd);
-
-  bzero((char*) &tables, sizeof(tables));
-  tables.db= (char*) "mysql";
-  tables.table_name= tables.alias= (char*) "user";
-  tables.lock_type= TL_READ;
 
-  if (simple_open_n_lock_tables(thd, &tables))
-  {
-    sql_print_error("Event Scheduler: Cannot open mysql.user");
-    ret= TRUE;
-  }
+  if (start_or_stop == EVENTS_ON)
+    ret= scheduler->start();
   else
+    ret= scheduler->stop();
+
+  if (ret)
   {
-    if (tables.table->s->fields < 29 ||
-        strncmp(tables.table->field[29]->field_name,
-                STRING_WITH_LEN("Event_priv")))
-    {
-      sql_print_error("mysql.user has no `Event_priv` column at position %d",
-                      29);
-      ret= TRUE;
-    }
-    close_thread_tables(thd);
+    my_error(ER_EVENT_SET_VAR_ERROR, MYF(0));
+    goto end;
   }
 
-  thd->restore_backup_open_tables_state(&backup);
+  opt_event_scheduler= start_or_stop;
 
+end:
+  pthread_mutex_unlock(&LOCK_event_metadata);
   DBUG_RETURN(ret);
 }
 
 
-/*
-  Loads all ENABLED events from mysql.event into the prioritized
-  queue. Called during scheduler main thread initialization. Compiles
-  the events. Creates Event_queue_element instances for every ENABLED event
-  from mysql.event.
+/**
+  Loads all ENABLED events from mysql.event into a prioritized
+  queue.
 
-  SYNOPSIS
-    Events::load_events_from_db()
-      thd  Thread context. Used for memory allocation in some cases.
+  This function is called during the server start up. It reads
+  every event, computes the next execution time, and if the event
+  needs execution, adds it to a prioritized queue. Otherwise, if
+  ON COMPLETION DROP is specified, the event is automatically
+  removed from the table.
 
-  RETURN VALUE
-    0  OK
-   !0  Error (EVEX_OPEN_TABLE_FAILED, EVEX_MICROSECOND_UNSUP,
-              EVEX_COMPILE_ERROR) - in all these cases mysql.event was
-              tampered.
+  @param[in,out] thd Thread context. Used for memory allocation in some cases.
 
-  NOTES
-    Reports the error to the console
+  @retval  FALSE  success
+  @retval  TRUE   error, the load is aborted
+
+  @note Reports the error to the console
 */
 
-int
+bool
 Events::load_events_from_db(THD *thd)
 {
   TABLE *table;
   READ_RECORD read_record_info;
-  int ret= -1;
+  bool ret= TRUE;
   uint count= 0;
-  bool clean_the_queue= TRUE;
 
   DBUG_ENTER("Events::load_events_from_db");
   DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
 
-  if ((ret= db_repository->open_event_table(thd, TL_READ, &table)))
+  if ((ret= db_repository->open_event_table(thd, TL_WRITE, &table)))
   {
-    sql_print_error("Event Scheduler: Table mysql.event is damaged. Can not open");
-    DBUG_RETURN(EVEX_OPEN_TABLE_FAILED);
+    sql_print_error("Event Scheduler: Failed to open table mysql.event");
+    DBUG_RETURN(TRUE);
   }
 
-  init_read_record(&read_record_info, thd, table ,NULL,1,0);
+  init_read_record(&read_record_info, thd, table, NULL, 0, 1);
   while (!(read_record_info.read_record(&read_record_info)))
   {
     Event_queue_element *et;
+    bool created;
+    bool drop_on_completion;
+
     if (!(et= new Event_queue_element))
-    {
-      DBUG_PRINT("info", ("Out of memory"));
-      break;
-    }
+      goto end;
+
     DBUG_PRINT("info", ("Loading event from row."));
 
-    if ((ret= et->load_from_row(thd, table)))
+    if (et->load_from_row(thd, table))
     {
       sql_print_error("Event Scheduler: "
                       "Error while reading from mysql.event. "
                       "The table is probably corrupted");
-      break;
-    }
-    if (et->status != Event_queue_element::ENABLED)
-    {
-      DBUG_PRINT("info",("%s is disabled",et->name.str));
       delete et;
-      continue;
+      goto end;
     }
+    drop_on_completion= (et->on_completion ==
+                         Event_queue_element::ON_COMPLETION_DROP);
 
-    /* let's find when to be executed */
-    if (et->compute_next_execution_time())
+
+    if (event_queue->create_event(thd, et, &created))
     {
-      sql_print_error("Event Scheduler: Error while computing execution time of %s.%s."
-                      " Skipping", et->dbname.str, et->name.str);
-      continue;
+      /* Out of memory */
+      delete et;
+      goto end;
     }
-
+    if (created)
+      count++;
+    else if (drop_on_completion)
     {
-      Event_job_data temp_job_data;
-      DBUG_PRINT("info", ("Event %s loaded from row. ", et->name.str));
-
-      temp_job_data.load_from_row(thd, table);
-
       /*
-        We load only on scheduler root just to check whether the body
-        compiles.
+        If not created, a stale event - drop if immediately if
+        ON COMPLETION NOT PRESERVE
       */
-      switch (ret= temp_job_data.compile(thd, thd->mem_root)) {
-      case EVEX_MICROSECOND_UNSUP:
-        sql_print_error("Event Scheduler: mysql.event is tampered. MICROSECOND is not "
-                        "supported but found in mysql.event");
-        break;
-      case EVEX_COMPILE_ERROR:
-        sql_print_error("Event Scheduler: Error while compiling %s.%s. Aborting load",
-                        et->dbname.str, et->name.str);
-        break;
-      default:
-        break;
+      int rc= table->file->ha_delete_row(table->record[0]);
+      if (rc)
+      {
+        table->file->print_error(rc, MYF(0));
+        goto end;
       }
-      thd->end_statement();
-      thd->cleanup_after_query();
     }
-    if (ret)
-    {
-      delete et;
-      goto end;
-    }
-
-    DBUG_PRINT("load_events_from_db", ("Adding 0x%lx to the exec list.",
-                                       (long) et));
-    event_queue->create_event(thd, et);
-    count++;
   }
-  clean_the_queue= FALSE;
+  sql_print_information("Event Scheduler: Loaded %d event%s",
+                        count, (count == 1) ? "" : "s");
+  ret= FALSE;
+
 end:
   end_read_record(&read_record_info);
 
-  if (clean_the_queue)
-  {
-    event_queue->empty_queue();
-    ret= -1;
-  }
-  else
-  {
-    ret= 0;
-    sql_print_information("Event Scheduler: Loaded %d event%s",
-                          count, (count == 1)?"":"s");
-  }
-
   close_thread_tables(thd);
 
-  DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count));
   DBUG_RETURN(ret);
 }

--- 1.53/sql/events.h	2007-03-23 18:14:01 +03:00
+++ 1.54/sql/events.h	2007-04-05 15:24:29 +04:00
@@ -15,7 +15,11 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
-class sp_name;
+/*
+  @file
+  A public interface of Events Scheduler module.
+*/
+
 class Event_parse_data;
 class Event_db_repository;
 class Event_queue;
@@ -40,6 +44,22 @@
 /**
   @class Events -- a facade to the functionality of the Event Scheduler.
 
+  Every public operation against the scheduler has to be executed via the
+  interface provided by a static method of this class. No instance of this
+  class is ever created and it has no non-static data members.
+
+  The life cycle of the Events module is the following:
+
+  At server start up:
+     set_opt_event_scheduler() -> init_mutexes() -> init()
+  When the server is running:
+     create_event(), drop_event(), start_or_stop_event_scheduler(), etc
+  At shutdown:
+     deinit(), destroy_mutexes().
+
+  The peculiar initialization and shutdown cycle is an adaptation to the
+  outside server startup/shutdown framework and mimics the rest of MySQL
+  subsystems (ACL, time zone tables, etc).
 */
 
 class Events
@@ -53,47 +73,48 @@
     EVENTS_DISABLED= 4
   };
 
-  static enum_opt_event_scheduler opt_event_scheduler;
-  static TYPELIB opt_typelib;
-  static TYPELIB var_typelib;
+  /* Possible values of @@event_scheduler variable */
+  static const TYPELIB var_typelib;
 
-  bool
-  init();
+  static bool
+  set_opt_event_scheduler(char *argument);
 
-  void
-  deinit();
+  static const char *
+  get_opt_event_scheduler_str();
 
-  void
-  init_mutexes();
+  /* A hack needed for Event_queue_element */
+  static Event_db_repository *
+  get_db_repository() { return db_repository; }
 
-  void
-  destroy_mutexes();
+  static bool
+  init(my_bool opt_noacl);
 
-  bool
-  start_execution_of_events();
+  static void
+  deinit();
 
-  bool
-  stop_execution_of_events();
+  static void
+  init_mutexes();
 
-  bool
-  is_execution_of_events_started();
+  static void
+  destroy_mutexes();
 
-  static Events *
-  get_instance();
+  static bool
+  start_or_stop_event_scheduler(enum enum_opt_event_scheduler start_or_stop);
 
-  bool
+  static bool
   create_event(THD *thd, Event_parse_data *parse_data, bool if_exists);
 
-  bool
-  update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to);
+  static bool
+  update_event(THD *thd, Event_parse_data *parse_data,
+               LEX_STRING *new_dbname, LEX_STRING *new_name);
 
-  bool
+  static bool
   drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists);
 
-  void
+  static void
   drop_schema_events(THD *thd, char *db);
 
-  bool
+  static bool
   show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name);
 
   /* Needed for both SHOW CREATE EVENT and INFORMATION_SCHEMA */
@@ -104,31 +125,28 @@
   static int
   fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */);
 
-  void
+  static void
   dump_internal_status();
 
 private:
-  bool
-  check_system_tables(THD *thd);
+  static bool check_if_system_tables_error();
 
-  int
+  static bool
   load_events_from_db(THD *thd);
 
-  /* Singleton DP is used */
-  Events();
-  ~Events(){}
-
-  /* Singleton instance */
-  static Events singleton;
-
-  Event_queue         *event_queue;
-  Event_scheduler     *scheduler;
-  Event_db_repository *db_repository;
-
-  pthread_mutex_t LOCK_event_metadata;
-
-  bool check_system_tables_error;
+private:
+  /* Command line option names */
+  static const TYPELIB opt_typelib;
+  static pthread_mutex_t LOCK_event_metadata;
+  static Event_queue         *event_queue;
+  static Event_scheduler     *scheduler;
+  static Event_db_repository *db_repository;
+  /* Current state of Event Scheduler */
+  static enum enum_opt_event_scheduler opt_event_scheduler;
+  /* Set to TRUE if an error at start up */
+  static bool check_system_tables_error;
 
+private:
   /* Prevent use of these */
   Events(const Events &);
   void operator=(Events &);

--- 1.25/sql/event_db_repository.cc	2007-03-23 18:14:01 +03:00
+++ 1.26/sql/event_db_repository.cc	2007-04-05 15:24:29 +04:00
@@ -18,12 +18,6 @@
 #include "event_data_objects.h"
 #include "events.h"
 #include "sql_show.h"
-#include "sp.h"
-#include "sp_head.h"
-
-
-static
-time_t mysql_event_last_create_time= 0L;
 
 static
 const TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] =
@@ -127,26 +121,21 @@
 };
 
 
-/*
+/**
   Puts some data common to CREATE and ALTER EVENT into a row.
 
-  SYNOPSIS
-    mysql_event_fill_row()
-      thd        THD
-      table      The row to fill out
-      et         Event's data
-      is_update  CREATE EVENT or ALTER EVENT
+  Used both when an event is created and when it is altered.
 
-  RETURN VALUE
-    0                       OK
-    EVEX_GENERAL_ERROR      Bad data
-    EVEX_GET_FIELD_FAILED   Field count does not match. table corrupted?
+  @param   thd        THD
+  @param   table      The row to fill out
+  @param   et         Event's data
+  @param   is_update  CREATE EVENT or ALTER EVENT
 
-  DESCRIPTION
-    Used both when an event is created and when it is altered.
+  @retval  FALSE success
+  @retval  TRUE error
 */
 
-static int
+static bool
 mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et,
                      my_bool is_update)
 {
@@ -160,6 +149,17 @@
   DBUG_PRINT("info", ("name  =[%s]", et->name.str));
   DBUG_PRINT("info", ("body  =[%s]", et->body.str));
 
+  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);
+  }
+
   if (fields[f_num= ET_FIELD_DEFINER]->
                               store(et->definer.str, et->definer.length, scs))
     goto err_truncate;
@@ -263,11 +263,11 @@
       goto err_truncate;
   }
 
-  DBUG_RETURN(0);
+  DBUG_RETURN(FALSE);
 
 err_truncate:
   my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), fields[f_num]->field_name);
-  DBUG_RETURN(EVEX_GENERAL_ERROR);
+  DBUG_RETURN(TRUE);
 }
 
 
@@ -381,40 +381,33 @@
 }
 
 
-/*
+/**
   Fills I_S.EVENTS with data loaded from mysql.event. Also used by
   SHOW EVENTS
 
-  SYNOPSIS
-    Event_db_repository::fill_schema_events()
-      thd     Thread
-      tables  The schema table
-      db      If not NULL then get events only from this schema
+  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.
 
-  RETURN VALUE
-    FALSE  OK
-    TRUE   Error
+  @retval FALSE  success
+  @retval TRUE   error
 */
 
-int
+bool
 Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables,
                                         const char *db)
 {
   TABLE *schema_table= tables->table;
   TABLE *event_table= NULL;
-  Open_tables_state backup;
   int ret= 0;
 
   DBUG_ENTER("Event_db_repository::fill_schema_events");
   DBUG_PRINT("info",("db=%s", db? db:"(null)"));
 
-  thd->reset_n_backup_open_tables_state(&backup);
   if (open_event_table(thd, TL_READ, &event_table))
-  {
-    sql_print_error("Table mysql.event is damaged.");
-    thd->restore_backup_open_tables_state(&backup);
-    DBUG_RETURN(1);
-  }
+    DBUG_RETURN(TRUE);
 
   /*
     1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order
@@ -431,164 +424,101 @@
     ret= table_scan_all_for_i_s(thd, schema_table, event_table);
 
   close_thread_tables(thd);
-  thd->restore_backup_open_tables_state(&backup);
 
   DBUG_PRINT("info", ("Return code=%d", ret));
   DBUG_RETURN(ret);
 }
 
 
-/*
-  Open mysql.event table for read
-
-  SYNOPSIS
-    Events::open_event_table()
-    thd         [in]  Thread context
-    lock_type   [in]  How to lock the table
-    table       [out] We will store the open table here
+/**
+  Open mysql.event table for read.
 
-  RETURN VALUE
-    1   Cannot lock table
-    2   The table is corrupted - different number of fields
-    0   OK
+  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.
+
+  @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
 */
 
-int
+bool
 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");
 
-  bzero((char*) &tables, sizeof(tables));
-  tables.db= (char*) "mysql";
-  tables.table_name= tables.alias= (char*) "event";
-  tables.lock_type= lock_type;
+  tables.init_one_table("mysql", "event", lock_type);
 
   if (simple_open_n_lock_tables(thd, &tables))
-    DBUG_RETURN(1);
+    DBUG_RETURN(TRUE);
 
-  if (table_check_intact(tables.table, ET_FIELD_COUNT,
-                         event_table_fields,
-                         &mysql_event_last_create_time,
-                         ER_CANNOT_LOAD_FROM_TABLE))
-  {
-    close_thread_tables(thd);
-    DBUG_RETURN(2);
-  }
   *table= tables.table;
   tables.table->use_all_columns();
-  DBUG_RETURN(0);
-}
-
-
-/*
-  Checks parameters which we got from the parsing phase.
-
-  SYNOPSIS
-    check_parse_params()
-      thd         Thread context
-      parse_data  Event's data
-
-  RETURN VALUE
-    FALSE  OK
-    TRUE   Error (reported)
-*/
-
-static int
-check_parse_params(THD *thd, Event_parse_data *parse_data)
-{
-  DBUG_ENTER("check_parse_params");
-
-  if (parse_data->check_parse_data(thd))
-    DBUG_RETURN(EVEX_BAD_PARAMS);
-
-  if (!parse_data->dbname.str ||
-      (thd->lex->sql_command == SQLCOM_ALTER_EVENT &&
thd->lex->spname &&
-       !thd->lex->spname->m_db.str))
-  {
-    my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
-    DBUG_RETURN(EVEX_BAD_PARAMS);
-  }
-
-  if (check_access(thd, EVENT_ACL, parse_data->dbname.str, 0, 0, 0,
-                   is_schema_db(parse_data->dbname.str)) ||
-      (thd->lex->sql_command == SQLCOM_ALTER_EVENT &&
thd->lex->spname &&
-       (check_access(thd, EVENT_ACL, thd->lex->spname->m_db.str, 0, 0, 0,
-                     is_schema_db(thd->lex->spname->m_db.str)))))
-    DBUG_RETURN(EVEX_BAD_PARAMS);
-
-  DBUG_RETURN(0);
+  DBUG_RETURN(FALSE);
 }
 
 
-/*
-  Creates an event in mysql.event
+/**
+  Creates an event record in mysql.event table.
 
-  SYNOPSIS
-    Event_db_repository::create_event()
-      thd             [in]  THD
-      parse_data      [in]  Object containing info about the event
-      create_if_not   [in]  Whether to generate anwarning in case event exists
+  Creates an event. Relies on mysql_event_fill_row which is shared with
+  ::update_event.
 
-  RETURN VALUE
-    0                   OK
-    EVEX_GENERAL_ERROR  Failure
+  @pre All semantic checks must be performed outside. This function
+  only creates a record on disk.
+  @pre The thread handle has no open tables.
+
+  @param[in,out]               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
 
-  DESCRIPTION
-    Creates an event. Relies on mysql_event_fill_row which is shared with
-    ::update_event. The name of the event is inside "et".
+  @retval FALSE  success
+  @retval TRUE   error
 */
 
 bool
 Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
                                   my_bool create_if_not)
 {
-  int ret= 0;
+  int ret= 1;
   TABLE *table= NULL;
-  char old_db_buf[NAME_LEN+1];
-  LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
-  bool dbchanged= FALSE;
 
   DBUG_ENTER("Event_db_repository::create_event");
 
-  if (check_parse_params(thd, parse_data))
-    goto err;
-  if (parse_data->do_not_create)
-    goto ok;
-
   DBUG_PRINT("info", ("open mysql.event for update"));
+
   if (open_event_table(thd, TL_WRITE, &table))
-  {
-    my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
-    goto err;
-  }
+    goto end;
 
 
   DBUG_PRINT("info", ("name: %.*s", parse_data->name.length,
              parse_data->name.str));
 
   DBUG_PRINT("info", ("check existance of an event with the same name"));
-  if (!find_named_event(thd, parse_data->dbname, parse_data->name, table))
+  if (!find_named_event(parse_data->dbname, parse_data->name, table))
   {
     if (create_if_not)
     {
       push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                           ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
                           parse_data->name.str);
-      goto ok;
+      ret= 0;
     }
-    my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str);
-    goto err;
+    else
+      my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str);
+    goto end;
   }
 
-  DBUG_PRINT("info", ("non-existant, go forward"));
-
-  if ((ret= sp_use_new_db(thd, parse_data->dbname, &old_db, 0, &dbchanged)))
-  {
-    my_error(ER_BAD_DB_ERROR, MYF(0));
-    goto err;
-  }
+  DBUG_PRINT("info", ("non-existent, go forward"));
 
   restore_record(table, s->default_values);     // Get default values for fields
 
@@ -598,7 +528,7 @@
       table->field[ET_FIELD_DB]->char_length())
   {
     my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->dbname.str);
-    goto err;
+    goto end;
   }
 
   if (system_charset_info->cset->
@@ -607,20 +537,13 @@
       table->field[ET_FIELD_NAME]->char_length())
   {
     my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->name.str);
-    goto err;
+    goto end;
   }
 
   if (parse_data->body.length > table->field[ET_FIELD_BODY]->field_length)
   {
     my_error(ER_TOO_LONG_BODY, MYF(0), parse_data->name.str);
-    goto err;
-  }
-
-  if (!(parse_data->expression) && !(parse_data->execute_at))
-  {
-    DBUG_PRINT("error", ("neither expression nor execute_at are set!"));
-    my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0));
-    goto err;
+    goto end;
   }
 
   ((Field_timestamp *)table->field[ET_FIELD_CREATED])->set_time();
@@ -629,93 +552,70 @@
     mysql_event_fill_row() calls my_error() in case of error so no need to
     handle it here
   */
-  if ((ret= mysql_event_fill_row(thd, table, parse_data, FALSE)))
-    goto err;
+  if (mysql_event_fill_row(thd, table, parse_data, FALSE))
+    goto end;
 
-  /* Close active transaction only if We are going to modify disk */
-  if (end_active_trans(thd))
-    goto err;
 
-  if (table->file->ha_write_row(table->record[0]))
+  if ((ret= table->file->ha_write_row(table->record[0])))
   {
-    my_error(ER_EVENT_STORE_FAILED, MYF(0), parse_data->name.str, ret);
-    goto err;
+    table->file->print_error(ret, MYF(0));
+    goto end;
   }
+  ret= 0;
 
-ok:
-  if (dbchanged)
-    (void) mysql_change_db(thd, old_db.str, 1);
-  /*
-    This statement may cause a spooky valgrind warning at startup
-    inside init_key_cache on my system (ahristov, 2006/08/10)
-  */
-  close_thread_tables(thd);
-  DBUG_RETURN(FALSE);
-
-err:
-  if (dbchanged)
-    (void) mysql_change_db(thd, old_db.str, 1);
+end:
   if (table)
     close_thread_tables(thd);
-  DBUG_RETURN(TRUE);
+  DBUG_RETURN(test(ret));
 }
 
 
-/*
+/**
   Used to execute ALTER EVENT. Pendant to Events::update_event().
 
-  SYNOPSIS
-    Event_db_repository::update_event()
-      thd      THD
-      sp_name  the name of the event to alter
-      et       event's data
-
-  RETURN VALUE
-    FALSE  OK
-    TRUE   Error (reported)
+  @param[in,out]  thd         thread handle
+  @param[in]      parse_data  parsed event definition
+  @paran[in[      new_dbname  not NULL if ALTER EVENT RENAME
+                              points at a new database name
+  @param[in]      new_name    not NULL if ALTER EVENT RENAME
+                              points at a new event name
+
+  @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.
 
-  NOTES
-    sp_name is passed since this is the name of the event to
-    alter in case of RENAME TO.
+  @retval FALSE success
+  @retval TRUE error (reported)
 */
 
 bool
 Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
-                                  LEX_STRING *new_dbname, LEX_STRING *new_name)
+                                  LEX_STRING *new_dbname,
+                                  LEX_STRING *new_name)
 {
   CHARSET_INFO *scs= system_charset_info;
   TABLE *table= NULL;
+  int ret= 1;
   DBUG_ENTER("Event_db_repository::update_event");
 
-  if (open_event_table(thd, TL_WRITE, &table))
-  {
-    my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
-    goto err;
-  }
+  /* None or both must be set */
+  DBUG_ASSERT(new_dbname && new_name || new_dbname == new_name);
 
-  if (check_parse_params(thd, parse_data) || parse_data->do_not_create)
-    goto err;
+  if (open_event_table(thd, TL_WRITE, &table))
+    goto end;
 
   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));
-  if (new_dbname)
-    DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str));
 
   /* first look whether we overwrite */
   if (new_name)
   {
-    if (!sortcmp_lex_string(parse_data->name, *new_name, scs) &&
-        !sortcmp_lex_string(parse_data->dbname, *new_dbname, scs))
-    {
-      my_error(ER_EVENT_SAME_NAME, MYF(0), parse_data->name.str);
-      goto err;
-    }
-
-    if (!find_named_event(thd, *new_dbname, *new_name, table))
+    DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str));
+    if (!find_named_event(*new_dbname, *new_name, table))
     {
       my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str);
-      goto err;
+      goto end;
     }
   }
   /*
@@ -724,10 +624,10 @@
     overwrite the key and SE will tell us that it cannot find the already found
     row (copied into record[1] later
   */
-  if (find_named_event(thd, parse_data->dbname, parse_data->name, table))
+  if (find_named_event(parse_data->dbname, parse_data->name, table))
   {
     my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), parse_data->name.str);
-    goto err;
+    goto end;
   }
 
   store_record(table,record[1]);
@@ -740,7 +640,7 @@
     handle it here
   */
   if (mysql_event_fill_row(thd, table, parse_data, TRUE))
-    goto err;
+    goto end;
 
   if (new_dbname)
   {
@@ -748,42 +648,32 @@
     table->field[ET_FIELD_NAME]->store(new_name->str, new_name->length, scs);
   }
 
-  /* Close active transaction only if We are going to modify disk */
-  if (end_active_trans(thd))
-    goto err;
-
-  int res;
-  if ((res= table->file->ha_update_row(table->record[1], table->record[0])))
+  if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
   {
-    my_error(ER_EVENT_STORE_FAILED, MYF(0), parse_data->name.str, res);
-    goto err;
+    table->file->print_error(ret, MYF(0));
+    goto end;
   }
+  ret= 0;
 
-  /* close mysql.event or we crash later when loading the event from disk */
-  close_thread_tables(thd);
-  DBUG_RETURN(FALSE);
-
-err:
+end:
   if (table)
     close_thread_tables(thd);
-  DBUG_RETURN(TRUE);
+  DBUG_RETURN(test(ret));
 }
 
 
-/*
-  Drops an event
+/**
+  Delete event record from mysql.event table.
 
-  SYNOPSIS
-    Event_db_repository::drop_event()
-      thd             [in]  THD
-      db              [in]  Database name
-      name            [in]  Event's name
-      drop_if_exists  [in]  If set and the event not existing => warning
-                            onto the stack
+  @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.
 
-  RETURN VALUE
-    FALSE  OK
-    TRUE   Error (reported)
+  @retval FALSE success
+  @retval TRUE error (reported)
 */
 
 bool
@@ -791,66 +681,59 @@
                                 bool drop_if_exists)
 {
   TABLE *table= NULL;
-  Open_tables_state backup;
-  int ret;
+  int ret= 1;
 
   DBUG_ENTER("Event_db_repository::drop_event");
   DBUG_PRINT("enter", ("%s@%s", db.str, name.str));
 
-  thd->reset_n_backup_open_tables_state(&backup);
-  if ((ret= open_event_table(thd, TL_WRITE, &table)))
-  {
-    my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
-    goto done;
-  }
+  if (open_event_table(thd, TL_WRITE, &table))
+    goto end;
 
-  if (!(ret= find_named_event(thd, db, name, table)))
+  if (!find_named_event(db, name, table))
   {
-    /* Close active transaction only if we are actually going to modify disk */
-    if (!(ret= end_active_trans(thd)) &&
-        (ret= table->file->ha_delete_row(table->record[0])))
-      my_error(ER_EVENT_CANNOT_DELETE, MYF(0));
+    if ((ret= table->file->ha_delete_row(table->record[0])))
+      table->file->print_error(ret, MYF(0));
+    goto end;
   }
-  else
+
+  /* Event not found */
+  if (!drop_if_exists)
   {
-    if (drop_if_exists)
-    {
-      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;
-    } else
-      my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
+    my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
+    goto end;
   }
 
-done:
+  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:
   if (table)
     close_thread_tables(thd);
-  thd->restore_backup_open_tables_state(&backup);
 
-  DBUG_RETURN(ret);
+  DBUG_RETURN(test(ret));
 }
 
 
-/*
+/**
   Positions the internal pointer of `table` to the place where (db, name)
   is stored.
 
-  SYNOPSIS
-    Event_db_repository::find_named_event()
-      thd    Thread
-      db     Schema
-      name   Event name
-      table  Opened mysql.event
+  In case search succeeded, the table cursor points at the found row.
 
-  RETURN VALUE
-    FALSE  OK
-    TRUE   No such event
+  @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
+  @reval  TRUE   no record found or an error occured.
 */
 
 bool
-Event_db_repository::find_named_event(THD *thd, LEX_STRING db, LEX_STRING name,
-                                     TABLE *table)
+Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name,
+                                      TABLE *table)
 {
   byte key[MAX_KEY_LENGTH];
   DBUG_ENTER("Event_db_repository::find_named_event");
@@ -902,15 +785,15 @@
 }
 
 
-/*
-  Drops all events by field which has specific value of the field
+/**
+  Drops all events which have a specific value of a field.
 
-  SYNOPSIS
-    Event_db_repository::drop_events_by_field()
-      thd         Thread
-      table       mysql.event TABLE
-      field       Which field of the row to use for matching
-      field_value The value that should match
+  @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
 */
 
 void
@@ -925,16 +808,7 @@
   DBUG_PRINT("enter", ("field=%d field_value=%s", field, field_value.str));
 
   if (open_event_table(thd, TL_WRITE, &table))
-  {
-    /*
-      Currently being used only for DROP DATABASE - In this case we don't need
-      error message since the OK packet has been sent. But for DROP USER we
-      could need it.
-
-      my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
-    */
     DBUG_VOID_RETURN;
-  }
 
   /* only enabled events are in memory, so we go now and delete the rest */
   init_read_record(&read_record_info, thd, table, NULL, 1, 0);
@@ -942,14 +816,20 @@
   {
     char *et_field= get_field(thd->mem_root, table->field[field]);
 
-    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))
+    /* et_field may be NULL if the table is corrupted or out of memory */
+    if (et_field)
     {
-      DBUG_PRINT("info", ("Dropping"));
-      ret= table->file->ha_delete_row(table->record[0]);
+      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));
+      }
     }
   }
   end_read_record(&read_record_info);
@@ -959,20 +839,14 @@
 }
 
 
-/*
+/**
   Looks for a named event in mysql.event and then loads it from
-  the table, compiles and inserts it into the cache.
+  the table.
 
-  SYNOPSIS
-    Event_db_repository::load_named_event()
-      thd      [in]  Thread context
-      dbname   [in]  Event's db name
-      name     [in]  Event's name
-      etn      [out] The loaded event
+  @pre The given thread does not have open tables.
 
-  RETURN VALUE
-    FALSE  OK
-    TRUE   Error (reported)
+  @retval FALSE  success
+  @retval TRUE   error
 */
 
 bool
@@ -980,26 +854,169 @@
                                       LEX_STRING name, Event_basic *etn)
 {
   TABLE *table= NULL;
-  int ret= 0;
-  Open_tables_state backup;
+  bool ret;
 
   DBUG_ENTER("Event_db_repository::load_named_event");
   DBUG_PRINT("enter",("thd: 0x%lx  name: %*s", (long) thd, name.length, name.str));
 
-  thd->reset_n_backup_open_tables_state(&backup);
+  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");
 
-  if ((ret= open_event_table(thd, TL_READ, &table)))
-    my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
-  else if ((ret= find_named_event(thd, 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");
+    close_thread_tables(thd);
+  }
+
+
+  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");
+
+  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)
+  {
+    TIME time;
+    my_tz_UTC->gmt_sec_to_TIME(&time, last_executed);
+
+    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:
   if (table)
     close_thread_tables(thd);
 
-  thd->restore_backup_open_tables_state(&backup);
-  /* In this case no memory was allocated so we don't need to clean */
+  DBUG_RETURN(test(ret));
+}
 
-  DBUG_RETURN(ret);
+
+/**
+  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 */
+  tables.init_one_table("mysql", "db", TL_READ);
+
+  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 */
+  tables.init_one_table("mysql", "user", TL_READ);
+
+  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 */
+  tables.init_one_table("mysql", "event", TL_READ);
+
+  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));
 }

--- 1.14/sql/event_db_repository.h	2007-03-23 18:14:01 +03:00
+++ 1.15/sql/event_db_repository.h	2007-04-05 15:24:29 +04:00
@@ -15,7 +15,12 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
-#define EVEX_OPEN_TABLE_FAILED  -1
+/*
+  @file
+  This is a private header file of Events module. Please do not include it
+  directly. All public declarations of Events module should be stored in
+  events.h and event_data_objects.h.
+*/
 
 enum enum_events_table_field
 {
@@ -69,17 +74,28 @@
   drop_schema_events(THD *thd, LEX_STRING schema);
 
   bool
-  find_named_event(THD *thd, LEX_STRING db, LEX_STRING name, TABLE *table);
+  find_named_event(LEX_STRING db, LEX_STRING name, TABLE *table);
 
   bool
   load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_basic *et);
 
-  int
+  bool
   open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
 
-  int
+  bool
   fill_schema_events(THD *thd, TABLE_LIST *tables, const char *db);
 
+  bool
+  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);
+public:
+  static bool
+  check_system_tables(THD *thd);
 private:
   void
   drop_events_by_field(THD *thd, enum enum_events_table_field field,
@@ -91,9 +107,7 @@
   bool
   table_scan_all_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table);
 
-  static bool
-  check_system_tables(THD *thd);
-
+private:
   /* Prevent use of these */
   Event_db_repository(const Event_db_repository &);
   void operator=(Event_db_repository &);

--- 1.29/sql/event_queue.cc	2007-03-23 19:14:06 +03:00
+++ 1.30/sql/event_queue.cc	2007-04-05 15:24:29 +04:00
@@ -72,39 +72,21 @@
 Event_queue::Event_queue()
   :mutex_last_unlocked_at_line(0), mutex_last_locked_at_line(0),
    mutex_last_attempted_lock_at_line(0),
-   mutex_queue_data_locked(FALSE), mutex_queue_data_attempting_lock(FALSE)
+   mutex_queue_data_locked(FALSE),
+   mutex_queue_data_attempting_lock(FALSE),
+   next_activation_at(0)
 {
   mutex_last_unlocked_in_func= mutex_last_locked_in_func=
     mutex_last_attempted_lock_in_func= "";
-  next_activation_at= 0;
-}
-
-
-/*
-  Inits mutexes.
-
-  SYNOPSIS
-    Event_queue::init_mutexes()
-*/
 
-void
-Event_queue::init_mutexes()
-{
   pthread_mutex_init(&LOCK_event_queue, MY_MUTEX_INIT_FAST);
   pthread_cond_init(&COND_queue_state, NULL);
 }
 
 
-/*
-  Destroys mutexes.
-
-  SYNOPSIS
-    Event_queue::deinit_mutexes()
-*/
-
-void
-Event_queue::deinit_mutexes()
+Event_queue::~Event_queue()
 {
+  deinit_queue();
   pthread_mutex_destroy(&LOCK_event_queue);
   pthread_cond_destroy(&COND_queue_state);
 }
@@ -176,33 +158,47 @@
 /**
   Adds an event to the queue.
 
-  SYNOPSIS
-    Event_queue::create_event()
-      dbname  The schema of the new event
-      name    The name of the new event
+  Compute the next execution time for an event, and if it is still
+  active, add it to the queue. Otherwise delete it.
+  The object is left intact in case of an error. Otherwise
+  the queue container assumes ownership of it.
+
+  @param[in]  thd      thread handle
+  @param[in]  new_element a new element to add to the queue
+  @param[out] created  set to TRUE if no error and the element is
+                       added to the queue, FALSE otherwise
+
+  @retval TRUE  an error occured. The value of created is undefined,
+                the element was not deleted.
+  @retval FALSE success
 */
 
-void
-Event_queue::create_event(THD *thd, Event_queue_element *new_element)
+bool
+Event_queue::create_event(THD *thd, Event_queue_element *new_element,
+                          bool *created)
 {
   DBUG_ENTER("Event_queue::create_event");
   DBUG_PRINT("enter", ("thd: 0x%lx et=%s.%s", (long) thd,
              new_element->dbname.str, new_element->name.str));
 
+  /* Will do nothing if the event is disabled */
+  new_element->compute_next_execution_time();
   if (new_element->status == Event_queue_element::DISABLED)
-    delete new_element;
-  else
   {
-    new_element->compute_next_execution_time();
-    DBUG_PRINT("info", ("new event in the queue: 0x%lx", (long) new_element));
-
-    LOCK_QUEUE_DATA();
-    queue_insert_safe(&queue, (byte *) new_element);
-    dbug_dump_queue(thd->query_start());
-    pthread_cond_broadcast(&COND_queue_state);
-    UNLOCK_QUEUE_DATA();
+    delete new_element;
+    *created= FALSE;
+    DBUG_RETURN(FALSE);
   }
-  DBUG_VOID_RETURN;
+
+  DBUG_PRINT("info", ("new event in the queue: 0x%lx", (long) new_element));
+
+  LOCK_QUEUE_DATA();
+  *created= (queue_insert_safe(&queue, (byte *) new_element) == FALSE);
+  dbug_dump_queue(thd->query_start());
+  pthread_cond_broadcast(&COND_queue_state);
+  UNLOCK_QUEUE_DATA();
+
+  DBUG_RETURN(!*created);
 }
 
 

--- 1.16/sql/event_queue.h	2007-03-23 18:14:01 +03:00
+++ 1.17/sql/event_queue.h	2007-04-05 15:24:29 +04:00
@@ -25,23 +25,16 @@
 {
 public:
   Event_queue();
-
-  void
-  init_mutexes();
-
-  void
-  deinit_mutexes();
+  ~Event_queue();
 
   bool
   init_queue(THD *thd);
 
-  void
-  deinit_queue();
-
   /* Methods for queue management follow */
 
-  void
-  create_event(THD *thd, Event_queue_element *new_element);
+  bool
+  create_event(THD *thd, Event_queue_element *new_element,
+               bool *created);
 
   void
   update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
@@ -64,9 +57,23 @@
   void
   dump_internal_status();
 
+private:
   void
   empty_queue();
-protected:
+
+  void
+  deinit_queue();
+  /* helper functions for working with mutexes & conditionals */
+  void
+  lock_data(const char *func, uint line);
+
+  void
+  unlock_data(const char *func, uint line);
+
+  void
+  cond_wait(THD *thd, struct timespec *abstime, const char* msg,
+            const char *func, uint line);
+
   void
   find_n_remove_event(LEX_STRING db, LEX_STRING name);
 
@@ -98,16 +105,6 @@
   bool mutex_queue_data_attempting_lock;
   bool waiting_on_cond;
 
-  /* helper functions for working with mutexes & conditionals */
-  void
-  lock_data(const char *func, uint line);
-
-  void
-  unlock_data(const char *func, uint line);
-
-  void
-  cond_wait(THD *thd, struct timespec *abstime, const char* msg,
-            const char *func, uint line);
 };
 
 #endif /* _EVENT_QUEUE_H_ */

--- 1.39/sql/event_scheduler.cc	2007-03-23 19:14:06 +03:00
+++ 1.40/sql/event_scheduler.cc	2007-04-05 15:24:29 +04:00
@@ -37,7 +37,6 @@
 
 
 Event_db_repository *Event_worker_thread::db_repository;
-Events *Event_worker_thread::events_facade;
 
 
 static
@@ -80,11 +79,11 @@
   prefix.length(0);
   prefix.append("Event Scheduler: [");
 
-  append_identifier(thd, &prefix, et->definer.str, et->definer.length);
+  prefix.append(et->definer.str, et->definer.length, system_charset_info);
   prefix.append("][", 2);
-  append_identifier(thd,&prefix, et->dbname.str, et->dbname.length);
+  prefix.append(et->dbname.str, et->dbname.length, system_charset_info);
   prefix.append('.');
-  append_identifier(thd,&prefix, et->name.str, et->name.length);
+  prefix.append(et->name.str, et->name.length, system_charset_info);
   prefix.append("] ", 2);
 
   List_iterator_fast<MYSQL_ERROR> it(thd->warn_list);
@@ -95,7 +94,6 @@
     err_msg.length(0);
     err_msg.append(prefix);
     err_msg.append(err->msg, strlen(err->msg), system_charset_info);
-    err_msg.append("]");
     DBUG_ASSERT(err->level < 3);
     (sql_print_message_handlers[err->level])("%*s", err_msg.length(),
                                               err_msg.c_ptr());
@@ -239,7 +237,7 @@
 }
 
 
-/*
+/**
   Function that executes an event in a child thread. Setups the
   environment for the event execution and cleans after that.
 
@@ -266,7 +264,7 @@
 }
 
 
-/*
+/**
   Function that executes an event in a child thread. Setups the
   environment for the event execution and cleans after that.
 
@@ -315,17 +313,27 @@
 
   print_warnings(thd, job_data);
 
-  sql_print_information("Event Scheduler: "
-                        "[%s.%s of %s] executed in thread %lu. "
-                        "RetCode=%d", job_data->dbname.str, job_data->name.str,
-                        job_data->definer.str, thd->thread_id, ret);
-  if (ret == EVEX_COMPILE_ERROR)
+  switch (ret) {
+  case 0:
     sql_print_information("Event Scheduler: "
-                          "COMPILE ERROR for event %s.%s of %s",
+                          "[%s].[%s.%s] executed successfully in thread %lu.",
+                          job_data->definer.str,
                           job_data->dbname.str, job_data->name.str,
-                          job_data->definer.str);
-  else if (ret == EVEX_MICROSECOND_UNSUP)
-    sql_print_information("Event Scheduler: MICROSECOND is not supported");
+                          thd->thread_id);
+    break;
+  case EVEX_COMPILE_ERROR:
+    sql_print_information("Event Scheduler: "
+                          "[%s].[%s.%s] event compilation failed.",
+                          job_data->definer.str,
+                          job_data->dbname.str, job_data->name.str);
+    break;
+  default:
+    sql_print_information("Event Scheduler: "
+                          "[%s].[%s.%s] event execution failed.",
+                          job_data->definer.str,
+                          job_data->dbname.str, job_data->name.str);
+    break;
+  }
 
 end:
   delete job_data;
@@ -349,66 +357,34 @@
       problem. However, this comes at the price of introduction bi-directional
       association between class Events and class Event_worker_thread.
     */
-    events_facade->drop_event(thd, event->dbname, event->name, FALSE);
+    Events::drop_event(thd, event->dbname, event->name, FALSE);
   }
   DBUG_PRINT("info", ("Done with Event %s.%s", event->dbname.str,
              event->name.str));
 
   delete event;
   deinit_event_thread(thd);
-  pthread_exit(0);
-}
-
-
-/*
-  Performs initialization of the scheduler data, outside of the
-  threading primitives.
-
-  SYNOPSIS
-    Event_scheduler::init_scheduler()
-*/
-
-void
-Event_scheduler::init_scheduler(Event_queue *q)
-{
-  LOCK_DATA();
-  queue= q;
-  started_events= 0;
-  scheduler_thd= NULL;
-  state= INITIALIZED;
-  UNLOCK_DATA();
+  /*
+    Do not pthread_exit since we want local destructors for stack objects
+    to be invoked.
+  */
 }
 
 
-void
-Event_scheduler::deinit_scheduler() {}
-
-
-/*
-  Inits scheduler's threading primitives.
-
-  SYNOPSIS
-    Event_scheduler::init_mutexes()
-*/
-
-void
-Event_scheduler::init_mutexes()
+Event_scheduler::Event_scheduler(Event_queue *queue_arg)
+  :state(UNINITIALIZED),
+  scheduler_thd(NULL),
+  queue(queue_arg),
+  started_events(0)
 {
   pthread_mutex_init(&LOCK_scheduler_state, MY_MUTEX_INIT_FAST);
   pthread_cond_init(&COND_state, NULL);
 }
 
 
-/*
-  Deinits scheduler's threading primitives.
-
-  SYNOPSIS
-    Event_scheduler::deinit_mutexes()
-*/
-
-void
-Event_scheduler::deinit_mutexes()
+Event_scheduler::~Event_scheduler()
 {
+  stop();                                    /* does nothing if not running */
   pthread_mutex_destroy(&LOCK_scheduler_state);
   pthread_cond_destroy(&COND_state);
 }
@@ -638,6 +614,9 @@
 /**
   Stops the scheduler (again). Waits for acknowledgement from the
   scheduler that it has stopped - synchronous stopping.
+
+  Already running events will not be stopped. If the user needs
+  them stopped manual intervention is needed.
 
   SYNOPSIS
     Event_scheduler::stop()

--- 1.21/sql/event_scheduler.h	2007-03-23 18:14:01 +03:00
+++ 1.22/sql/event_scheduler.h	2007-04-05 15:24:29 +04:00
@@ -15,7 +15,8 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
-/*
+/**
+  @file
   This file is internal to Events module. Please do not include it directly.
   All public declarations of Events module are in events.h and
   event_data_objects.h.
@@ -41,10 +42,9 @@
 {
 public:
   static void
-  init(Events *events, Event_db_repository *db_repo)
+  init(Event_db_repository *db_repository_arg)
   {
-    db_repository= db_repo;
-    events_facade= events;
+    db_repository= db_repository_arg;
   }
 
   void
@@ -55,15 +55,15 @@
   print_warnings(THD *thd, Event_job_data *et);
 
   static Event_db_repository *db_repository;
-  static Events *events_facade;
 };
 
 
 class Event_scheduler
 {
 public:
-  Event_scheduler():state(UNINITIALIZED){}
-  ~Event_scheduler(){}
+  Event_scheduler(Event_queue *event_queue_arg);
+  ~Event_scheduler();
+
 
   /* State changing methods follow */
 
@@ -80,17 +80,6 @@
   bool
   run(THD *thd);
 
-  void
-  init_scheduler(Event_queue *queue);
-
-  void
-  deinit_scheduler();
-
-  void
-  init_mutexes();
-
-  void
-  deinit_mutexes();
 
   /* Information retrieving methods follow */
   bool

--- 1.92/sql/event_data_objects.cc	2007-03-27 21:09:51 +04:00
+++ 1.93/sql/event_data_objects.cc	2007-04-05 15:24:29 +04:00
@@ -20,8 +20,6 @@
 #include "event_db_repository.h"
 #include "sp_head.h"
 
-/* That's a provisional solution */
-extern Event_db_repository events_event_db_repository;
 
 #define EVEX_MAX_INTERVAL_VALUE 1000000000L
 
@@ -215,7 +213,7 @@
     ++body_begin;
     --body.length;
   }
-  body.str= thd->strmake((char *)body_begin, body.length);
+  body.str= thd->strmake(body_begin, body.length);
 
   DBUG_VOID_RETURN;
 }
@@ -694,7 +692,7 @@
 
   va_start(args, fields);
   field_name= (enum enum_events_table_field) va_arg(args, int);
-  while (field_name != ET_FIELD_COUNT)
+  while (field_name < ET_FIELD_COUNT)
   {
     field_value= va_arg(args, LEX_STRING *);
     if ((field_value->str= get_field(&mem_root, fields[field_name])) == NullS)
@@ -858,13 +856,19 @@
   if (!table)
     goto error;
 
-  if (table->s->fields != ET_FIELD_COUNT)
+  if (table->s->fields < ET_FIELD_COUNT)
     goto error;
 
   LEX_STRING tz_name;
-  load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name,
-                     ET_FIELD_BODY, &body, ET_FIELD_DEFINER, &definer,
-                     ET_FIELD_TIME_ZONE, &tz_name, ET_FIELD_COUNT);
+  if (load_string_fields(table->field,
+                         ET_FIELD_DB, &dbname,
+                         ET_FIELD_NAME, &name,
+                         ET_FIELD_BODY, &body,
+                         ET_FIELD_DEFINER, &definer,
+                         ET_FIELD_TIME_ZONE, &tz_name,
+                         ET_FIELD_COUNT))
+    goto error;
+
   if (load_time_zone(thd, tz_name))
     goto error;
 
@@ -910,19 +914,24 @@
 {
   char *ptr;
   TIME time;
+  LEX_STRING tz_name;
 
   DBUG_ENTER("Event_queue_element::load_from_row");
 
   if (!table)
     goto error;
 
-  if (table->s->fields != ET_FIELD_COUNT)
+  if (table->s->fields < ET_FIELD_COUNT)
+    goto error;
+
+  if (load_string_fields(table->field,
+                         ET_FIELD_DB, &dbname,
+                         ET_FIELD_NAME, &name,
+                         ET_FIELD_DEFINER, &definer,
+                         ET_FIELD_TIME_ZONE, &tz_name,
+                         ET_FIELD_COUNT))
     goto error;
 
-  LEX_STRING tz_name;
-  load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name,
-                     ET_FIELD_DEFINER, &definer,
-                     ET_FIELD_TIME_ZONE, &tz_name, ET_FIELD_COUNT);
   if (load_time_zone(thd, tz_name))
     goto error;
 
@@ -1039,7 +1048,11 @@
   if (Event_queue_element::load_from_row(thd, table))
     goto error;
 
-  load_string_fields(table->field, ET_FIELD_BODY, &body, ET_FIELD_COUNT);
+  if (load_string_fields(table->field,
+                         ET_FIELD_BODY, &body,
+                         ET_FIELD_COUNT))
+    goto error;
+
 
   ptr= strchr(definer.str, '@');
 
@@ -1594,10 +1607,8 @@
 bool
 Event_queue_element::update_timing_fields(THD *thd)
 {
-  TABLE *table;
-  Field **fields;
-  Open_tables_state backup;
-  int ret= FALSE;
+  Event_db_repository *db_repository= Events::get_db_repository();
+  int ret;
 
   DBUG_ENTER("Event_queue_element::update_timing_fields");
 
@@ -1607,46 +1618,13 @@
   if (!(status_changed || last_executed_changed))
     DBUG_RETURN(0);
 
-  thd->reset_n_backup_open_tables_state(&backup);
-
-  if (events_event_db_repository.open_event_table(thd, TL_WRITE, &table))
-  {
-    ret= TRUE;
-    goto done;
-  }
-  fields= table->field;
-  if ((ret= events_event_db_repository.
-                                 find_named_event(thd, dbname, name, table)))
-    goto done;
-
-  store_record(table,record[1]);
-  /* Don't update create on row update. */
-  table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
-
-  if (last_executed_changed)
-  {
-    TIME time;
-    my_tz_UTC->gmt_sec_to_TIME(&time, last_executed);
-
-    fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
-    fields[ET_FIELD_LAST_EXECUTED]->store_time(&time,
-                                               MYSQL_TIMESTAMP_DATETIME);
-    last_executed_changed= FALSE;
-  }
-  if (status_changed)
-  {
-    fields[ET_FIELD_STATUS]->set_notnull();
-    fields[ET_FIELD_STATUS]->store((longlong)status, TRUE);
-    status_changed= FALSE;
-  }
-
-  if ((table->file->ha_update_row(table->record[1], table->record[0])))
-    ret= TRUE;
-
-done:
-  close_thread_tables(thd);
-  thd->restore_backup_open_tables_state(&backup);
-
+  ret= db_repository->update_timing_fields_for_event(thd,
+                                                     dbname, name,
+                                                     last_executed_changed,
+                                                     last_executed,
+                                                     status_changed,
+                                                     (ulonglong) status);
+  last_executed_changed= status_changed= FALSE;
   DBUG_RETURN(ret);
 }
 

--- 1.19/sql/event_data_objects.h	2007-03-27 21:09:51 +04:00
+++ 1.20/sql/event_data_objects.h	2007-04-05 15:24:29 +04:00
@@ -18,7 +18,6 @@
 
 #define EVEX_GET_FIELD_FAILED   -2
 #define EVEX_COMPILE_ERROR      -3
-#define EVEX_GENERAL_ERROR      -4
 #define EVEX_BAD_PARAMS         -5
 #define EVEX_MICROSECOND_UNSUP  -6
 
@@ -172,8 +171,6 @@
   LEX_STRING definer_host;
 
   ulong sql_mode;
-
-  uint execution_count;
 
   Event_job_data();
   virtual ~Event_job_data();

--- 1.151/sql/share/errmsg.txt	2007-03-27 22:15:47 +04:00
+++ 1.152/sql/share/errmsg.txt	2007-04-05 15:24:30 +04:00
@@ -5884,8 +5884,8 @@
         eng "Column count of mysql.%s is wrong. Expected %d, found %d. The table is
probably corrupted"
         ger "Spaltenanzahl von mysql.%s falsch. %d erwartet, aber %d gefunden. Tabelle
ist wahrscheinlich beschädigt"
 ER_CANNOT_LOAD_FROM_TABLE
-        eng "Cannot load from mysql.%s. The table is probably corrupted. Please see the
error log for details"
-        ger "Kann mysql.%s nicht einlesen. Tabelle ist wahrscheinlich beschädigt, siehe
Fehlerlog"
+        eng "Cannot load from mysql.%s. The table is probably corrupted"
+        ger "Kann mysql.%s nicht einlesen. Tabelle ist wahrscheinlich beschädigt"
 ER_EVENT_CANNOT_DELETE
         eng "Failed to delete the event from mysql.event"
         ger "Löschen des Events aus mysql.event fehlgeschlagen"
--- New file ---
+++ mysql-test/r/events_trans.result	07/04/05 15:24:31
drop database if exists events_test;
drop database if exists mysqltest_db2;
drop database if exists mysqltest_no_such_database;
create database events_test;
use events_test;

Test that Events DDL issue an implicit COMMIT


set autocommit=off;
select @@autocommit;
@@autocommit
0
create table t1 (a varchar(255)) engine=innodb;
begin work;
insert into t1 (a) values ("OK: create event");
create event e1 on schedule every 1 day do select 1;
rollback work;
select * from t1;
a
OK: create event
delete from t1;
commit work;
begin work;
insert into t1 (a) values ("OK: alter event");
alter event e1 on schedule every 2 day do select 2;
rollback work;
select * from t1;
a
OK: alter event
delete from t1;
commit work;
begin work;
insert into t1 (a) values ("OK: alter event rename");
alter event e1 rename to e2;
rollback work;
select * from t1;
a
OK: alter event rename
delete from t1;
commit work;
begin work;
insert into t1 (a) values ("OK: drop event");
drop event e2;
rollback work;
select * from t1;
a
OK: drop event
delete from t1;
commit work;
begin work;
insert into t1 (a) values ("OK: drop event if exists");
drop event if exists e2;
Warnings:
Note	1305	Event e2 does not exist
rollback work;
select * from t1;
a
OK: drop event if exists
delete from t1;
commit work;
create event e1 on schedule every 1 day do select 1;
begin work;
insert into t1 (a) values ("OK: create event if not exists");
create event if not exists e1 on schedule every 2 day do select 2;
Warnings:
Note	1526	Event 'e1' already exists
rollback work;
select * from t1;
a
OK: create event if not exists
delete from t1;
commit work;

Now check various error conditions: make sure we issue an
implicit commit anyway

begin work;
insert into t1 (a) values ("OK: create event: event already exists");
create event e1 on schedule every 2 day do select 2;
ERROR HY000: Event 'e1' already exists
rollback work;
select * from t1;
a
OK: create event: event already exists
delete from t1;
commit work;
begin work;
insert into t1 (a) values ("OK: alter event rename: rename to same name");
alter event e1 rename to e1;
ERROR HY000: Same old and new event name
rollback work;
select * from t1;
a
OK: alter event rename: rename to same name
delete from t1;
commit work;
create event e2 on schedule every 3 day do select 3;
begin work;
insert into t1 (a) values ("OK: alter event rename: destination exists");
alter event e2 rename to e1;
ERROR HY000: Event 'e1' already exists
rollback work;
select * from t1;
a
OK: alter event rename: destination exists
delete from t1;
commit work;
begin work;
insert into t1 (a) values ("OK: create event: database does not exist");
create event mysqltest_no_such_database.e1 on schedule every 1 day do select 1;
ERROR 42000: Unknown database 'mysqltest_no_such_database'
rollback work;
select * from t1;
a
OK: create event: database does not exist
delete from t1;
commit work;
grant create, insert, select, delete on mysqltest_db2.*
to mysqltest_user1@localhost;
create database mysqltest_db2;
set autocommit=off;
select @@autocommit;
@@autocommit
0
create table t1 (a varchar(255)) engine=innodb;
begin work;
insert into t1 (a) values ("OK: create event: insufficient privileges");
create event e1 on schedule every 1 day do select 1;
ERROR 42000: Access denied for user 'mysqltest_user1'@'localhost' to database
'mysqltest_db2'
rollback work;
select * from t1;
a
OK: create event: insufficient privileges
delete from t1;
commit work;
begin work;
insert into t1 (a) values ("OK: alter event: insufficient privileges");
alter event e1 on schedule every 1 day do select 1;
ERROR 42000: Access denied for user 'mysqltest_user1'@'localhost' to database
'mysqltest_db2'
rollback work;
select * from t1;
a
OK: alter event: insufficient privileges
delete from t1;
commit work;
begin work;
insert into t1 (a) values ("OK: drop event: insufficient privileges");
drop event e1;
ERROR 42000: Access denied for user 'mysqltest_user1'@'localhost' to database
'mysqltest_db2'
rollback work;
select * from t1;
a
OK: drop event: insufficient privileges
delete from t1;
commit work;
drop user mysqltest_user1@localhost;
drop database mysqltest_db2;
drop database events_test;

--- New file ---
+++ mysql-test/t/events_trans.test	07/04/05 15:24:31
#
# Tests that require transactions
#
-- source include/have_innodb.inc
--disable_warnings
drop database if exists events_test;
drop database if exists mysqltest_db2;
drop database if exists mysqltest_no_such_database;
--enable_warnings
create database events_test;
use events_test;

--echo
--echo Test that Events DDL issue an implicit COMMIT
--echo
--echo
set autocommit=off;
# Sanity check
select @@autocommit;
create table t1 (a varchar(255)) engine=innodb;
# Basic: check that successful Events DDL commits pending transaction
begin work;
insert into t1 (a) values ("OK: create event");
create event e1 on schedule every 1 day do select 1;
rollback work;
select * from t1;
delete from t1;
commit work;
#
begin work;
insert into t1 (a) values ("OK: alter event");
alter event e1 on schedule every 2 day do select 2;
rollback work;
select * from t1;
delete from t1;
commit work;
#
begin work;
insert into t1 (a) values ("OK: alter event rename");
alter event e1 rename to e2;
rollback work;
select * from t1;
delete from t1;
commit work;
#
begin work;
insert into t1 (a) values ("OK: drop event");
drop event e2;
rollback work;
select * from t1;
delete from t1;
commit work;
#
begin work;
insert into t1 (a) values ("OK: drop event if exists");
drop event if exists e2;
rollback work;
select * from t1;
delete from t1;
commit work;
#
create event e1 on schedule every 1 day do select 1;
begin work;
insert into t1 (a) values ("OK: create event if not exists");
create event if not exists e1 on schedule every 2 day do select 2;
rollback work;
select * from t1;
delete from t1;
commit work;
--echo
--echo Now check various error conditions: make sure we issue an
--echo implicit commit anyway
--echo
#
begin work;
insert into t1 (a) values ("OK: create event: event already exists");
--error ER_EVENT_ALREADY_EXISTS
create event e1 on schedule every 2 day do select 2;
rollback work;
select * from t1;
delete from t1;
commit work;
#
begin work;
insert into t1 (a) values ("OK: alter event rename: rename to same name");
--error ER_EVENT_SAME_NAME
alter event e1 rename to e1;
rollback work;
select * from t1;
delete from t1;
commit work;
#
create event e2 on schedule every 3 day do select 3;
begin work;
insert into t1 (a) values ("OK: alter event rename: destination exists");
--error ER_EVENT_ALREADY_EXISTS
alter event e2 rename to e1;
rollback work;
select * from t1;
delete from t1;
commit work;
#
begin work;
insert into t1 (a) values ("OK: create event: database does not exist");
--error ER_BAD_DB_ERROR
create event mysqltest_no_such_database.e1 on schedule every 1 day do select 1;
rollback work;
select * from t1;
delete from t1;
commit work;
#
# Privilege checks
#
grant create, insert, select, delete on mysqltest_db2.*
  to mysqltest_user1@localhost;
create database mysqltest_db2;
connect (conn1,localhost,mysqltest_user1,,mysqltest_db2);
set autocommit=off;
# Sanity check
select @@autocommit;
create table t1 (a varchar(255)) engine=innodb;
# Not enough privileges to CREATE EVENT
begin work;
insert into t1 (a) values ("OK: create event: insufficient privileges");
--error ER_DBACCESS_DENIED_ERROR
create event e1 on schedule every 1 day do select 1;
rollback work;
select * from t1;
delete from t1;
commit work;
# Not enough privileges to ALTER EVENT
begin work;
insert into t1 (a) values ("OK: alter event: insufficient privileges");
--error ER_DBACCESS_DENIED_ERROR
alter event e1 on schedule every 1 day do select 1;
rollback work;
select * from t1;
delete from t1;
commit work;
# Not enough privileges to DROP EVENT
begin work;
insert into t1 (a) values ("OK: drop event: insufficient privileges");
--error ER_DBACCESS_DENIED_ERROR
drop event e1;
rollback work;
select * from t1;
delete from t1;
commit work;
# Cleanup
disconnect conn1;
connection default;
drop user mysqltest_user1@localhost;
drop database mysqltest_db2;

#
# Cleanup
#
drop database events_test;



--- 1.1/mysql-test/r/events_time_zone.result	2007-03-16 17:31:04 +03:00
+++ 1.2/mysql-test/r/events_time_zone.result	2007-04-05 15:24:29 +04:00
@@ -3,148 +3,6 @@
 USE mysqltest_db1;
 SET GLOBAL EVENT_SCHEDULER= OFF;
 SET @save_time_zone= @@TIME_ZONE;
-SET TIME_ZONE= '+00:00';
-SET TIMESTAMP= UNIX_TIMESTAMP('2005-12-31 23:58:59');
-CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
-SHOW EVENTS;
-Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
-mysqltest_db1	e1	root@localhost	+00:00	RECURRING	NULL	1	DAY	2005-12-31
23:58:59	NULL	ENABLED
-SET TIME_ZONE= '-01:00';
-ALTER EVENT e1 ON SCHEDULE EVERY 1 DAY STARTS '2000-01-01 00:00:00';
-SHOW EVENTS;
-Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
-mysqltest_db1	e1	root@localhost	-01:00	RECURRING	NULL	1	DAY	2000-01-01
00:00:00	NULL	ENABLED
-SET TIME_ZONE= '+02:00';
-ALTER EVENT e1 ON SCHEDULE AT '2000-01-02 00:00:00'
-  ON COMPLETION PRESERVE DISABLE;
-SHOW EVENTS;
-Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
-mysqltest_db1	e1	root@localhost	+02:00	ONE TIME	2000-01-02
00:00:00	NULL	NULL	NULL	NULL	DISABLED
-SET TIME_ZONE= '-03:00';
-ALTER EVENT e1 ON SCHEDULE EVERY 1 DAY ENDS '2030-01-03 00:00:00'
-  ON COMPLETION PRESERVE DISABLE;
-SHOW EVENTS;
-Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
-mysqltest_db1	e1	root@localhost	-03:00	RECURRING	NULL	1	DAY	2005-12-31
20:58:59	2030-01-03 00:00:00	DISABLED
-SET TIME_ZONE= '+04:00';
-ALTER EVENT e1 DO SELECT 2;
-SHOW EVENTS;
-Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
-mysqltest_db1	e1	root@localhost	-03:00	RECURRING	NULL	1	DAY	2005-12-31
20:58:59	2030-01-03 00:00:00	ENABLED
-DROP EVENT e1;
-SET TIME_ZONE='+05:00';
-CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
-SELECT 1;
-SET TIMESTAMP= @@TIMESTAMP + 1;
-SET TIME_ZONE='-05:00';
-CREATE EVENT e2 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
-SELECT 1;
-SET TIMESTAMP= @@TIMESTAMP + 1;
-SET TIME_ZONE='+00:00';
-CREATE EVENT e3 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
-SELECT 1;
-SELECT * FROM INFORMATION_SCHEMA.EVENTS;
-EVENT_CATALOG	EVENT_SCHEMA	EVENT_NAME	DEFINER	TIME_ZONE	EVENT_BODY	EVENT_DEFINITION	EVENT_TYPE	EXECUTE_AT	INTERVAL_VALUE	INTERVAL_FIELD	SQL_MODE	STARTS	ENDS	STATUS	ON_COMPLETION	CREATED	LAST_ALTERED	LAST_EXECUTED	EVENT_COMMENT
-NULL	mysqltest_db1	e1	root@localhost	+05:00	SQL	SELECT 1	RECURRING	NULL	1	DAY		2006-01-01
00:00:00	NULL	ENABLED	NOT PRESERVE	2005-12-31 23:58:59	2005-12-31 23:58:59	NULL	
-NULL	mysqltest_db1	e2	root@localhost	-05:00	SQL	SELECT 1	RECURRING	NULL	1	DAY		2006-01-01
00:00:00	NULL	ENABLED	NOT PRESERVE	2005-12-31 23:59:00	2005-12-31 23:59:00	NULL	
-NULL	mysqltest_db1	e3	root@localhost	+00:00	SQL	SELECT 1	RECURRING	NULL	1	DAY		2006-01-01
00:00:00	NULL	ENABLED	NOT PRESERVE	2005-12-31 23:59:01	2005-12-31 23:59:01	NULL	
-SHOW EVENTS;
-Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
-mysqltest_db1	e1	root@localhost	+05:00	RECURRING	NULL	1	DAY	2006-01-01
00:00:00	NULL	ENABLED
-mysqltest_db1	e2	root@localhost	-05:00	RECURRING	NULL	1	DAY	2006-01-01
00:00:00	NULL	ENABLED
-mysqltest_db1	e3	root@localhost	+00:00	RECURRING	NULL	1	DAY	2006-01-01
00:00:00	NULL	ENABLED
-SHOW CREATE EVENT e1;
-Event	sql_mode	time_zone	Create Event
-e1		+05:00	CREATE EVENT `e1` ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' ON
COMPLETION NOT PRESERVE ENABLE DO SELECT 1
-SHOW CREATE EVENT e2;
-Event	sql_mode	time_zone	Create Event
-e2		-05:00	CREATE EVENT `e2` ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' ON
COMPLETION NOT PRESERVE ENABLE DO SELECT 1
-SHOW CREATE EVENT e3;
-Event	sql_mode	time_zone	Create Event
-e3		+00:00	CREATE EVENT `e3` ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' ON
COMPLETION NOT PRESERVE ENABLE DO SELECT 1
-The following should fail, and nothing should be altered.
-ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
-  ENDS '1999-01-02 00:00:00';
-ERROR HY000: Event execution time is in the past and ON COMPLETION NOT PRESERVE is set.
Event has not been altered
-ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
-  ENDS '1999-01-02 00:00:00' DISABLE;
-ERROR HY000: Event execution time is in the past and ON COMPLETION NOT PRESERVE is set.
Event has not been altered
-The following should give warnings, and nothing should be created.
-CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
-  ENDS '1999-01-02 00:00:00'
-DO
-SELECT 1;
-Warnings:
-Note	1584	Event execution time is in the past and ON COMPLETION NOT PRESERVE is set.
Event has not been created
-CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
-  ENDS '1999-01-02 00:00:00' DISABLE
-DO
-SELECT 1;
-Warnings:
-Note	1584	Event execution time is in the past and ON COMPLETION NOT PRESERVE is set.
Event has not been created
-CREATE EVENT e4 ON SCHEDULE AT '1999-01-01 00:00:00' DO
-SELECT 1;
-Warnings:
-Note	1584	Event execution time is in the past and ON COMPLETION NOT PRESERVE is set.
Event has not been created
-CREATE EVENT e4 ON SCHEDULE AT '1999-01-01 00:00:00' DISABLE
-DO
-SELECT 1;
-Warnings:
-Note	1584	Event execution time is in the past and ON COMPLETION NOT PRESERVE is set.
Event has not been created
-SHOW EVENTS;
-Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
-mysqltest_db1	e1	root@localhost	+05:00	RECURRING	NULL	1	DAY	2006-01-01
00:00:00	NULL	ENABLED
-mysqltest_db1	e2	root@localhost	-05:00	RECURRING	NULL	1	DAY	2006-01-01
00:00:00	NULL	ENABLED
-mysqltest_db1	e3	root@localhost	+00:00	RECURRING	NULL	1	DAY	2006-01-01
00:00:00	NULL	ENABLED
-The following should succeed giving a warning.
-ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
-  ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE;
-Warnings:
-Note	1533	Event execution time is in the past. Event has been disabled
-CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
-  ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE
-DO
-SELECT 1;
-Warnings:
-Note	1533	Event execution time is in the past. Event has been disabled
-CREATE EVENT e5 ON SCHEDULE AT '1999-01-01 00:00:00'
-  ON COMPLETION PRESERVE
-DO
-SELECT 1;
-Warnings:
-Note	1533	Event execution time is in the past. Event has been disabled
-The following should succeed without warnings.
-ALTER EVENT e2 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00';
-ALTER EVENT e3 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
-  ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE DISABLE;
-CREATE EVENT e6 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00' DO
-SELECT 1;
-CREATE EVENT e7 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
-  ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE DISABLE
-DO
-SELECT 1;
-CREATE EVENT e8 ON SCHEDULE AT '1999-01-01 00:00:00'
-  ON COMPLETION PRESERVE DISABLE
-DO
-SELECT 1;
-SHOW EVENTS;
-Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status
-mysqltest_db1	e1	root@localhost	+00:00	RECURRING	NULL	1	HOUR	1999-01-01
00:00:00	1999-01-02 00:00:00	DISABLED
-mysqltest_db1	e2	root@localhost	+00:00	RECURRING	NULL	1	HOUR	1999-01-01
00:00:00	NULL	ENABLED
-mysqltest_db1	e3	root@localhost	+00:00	RECURRING	NULL	1	HOUR	1999-01-01
00:00:00	1999-01-02 00:00:00	DISABLED
-mysqltest_db1	e4	root@localhost	+00:00	RECURRING	NULL	1	HOUR	1999-01-01
00:00:00	1999-01-02 00:00:00	DISABLED
-mysqltest_db1	e5	root@localhost	+00:00	ONE TIME	1999-01-01
00:00:00	NULL	NULL	NULL	NULL	DISABLED
-mysqltest_db1	e6	root@localhost	+00:00	RECURRING	NULL	1	HOUR	1999-01-01
00:00:00	NULL	ENABLED
-mysqltest_db1	e7	root@localhost	+00:00	RECURRING	NULL	1	HOUR	1999-01-01
00:00:00	1999-01-02 00:00:00	DISABLED
-mysqltest_db1	e8	root@localhost	+00:00	ONE TIME	1999-01-01
00:00:00	NULL	NULL	NULL	NULL	DISABLED
-DROP EVENT e8;
-DROP EVENT e7;
-DROP EVENT e6;
-DROP EVENT e5;
-DROP EVENT e4;
-DROP EVENT e3;
-DROP EVENT e2;
-DROP EVENT e1;
 CREATE TABLE t_step (step INT);
 INSERT INTO t_step VALUES (@step);
 CREATE FUNCTION round_to_step(i INT, n INT) RETURNS INT

--- 1.1/mysql-test/t/events_time_zone.test	2007-03-16 17:31:04 +03:00
+++ 1.2/mysql-test/t/events_time_zone.test	2007-04-05 15:24:29 +04:00
@@ -20,6 +20,7 @@
 USE mysqltest_db1;
 
 SET GLOBAL EVENT_SCHEDULER= OFF;
+SET @save_time_zone= @@TIME_ZONE;
 
 
 #
@@ -30,176 +31,6 @@
 #            STARTS clause is in the past
 # WL#3698: Events: execution in local time zone
 #
-
-SET @save_time_zone= @@TIME_ZONE;
-
-#----------------------------------------------------------------------
-
-# We will use a separate connection because SET TIMESTAMP will stop
-# the clock in that connection.
-
-connect (conn1, localhost, root, , mysqltest_db1);
-
-SET TIME_ZONE= '+00:00';
-SET TIMESTAMP= UNIX_TIMESTAMP('2005-12-31 23:58:59');
-
-
-# Test when event time zone is updated on ALTER EVENT.
-#
-
-CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
-SHOW EVENTS;
-
-# Test storing and updating of the event time zone.
-#
-SET TIME_ZONE= '-01:00';
-ALTER EVENT e1 ON SCHEDULE EVERY 1 DAY STARTS '2000-01-01 00:00:00';
-SHOW EVENTS;
-
-# This will update event time zone.
-SET TIME_ZONE= '+02:00';
-ALTER EVENT e1 ON SCHEDULE AT '2000-01-02 00:00:00'
-  ON COMPLETION PRESERVE DISABLE;
-SHOW EVENTS;
-
-# This will update event time zone.
-SET TIME_ZONE= '-03:00';
-ALTER EVENT e1 ON SCHEDULE EVERY 1 DAY ENDS '2030-01-03 00:00:00'
-  ON COMPLETION PRESERVE DISABLE;
-SHOW EVENTS;
-
-# This will not update event time zone, as no time is being adjusted.
-SET TIME_ZONE= '+04:00';
-ALTER EVENT e1 DO SELECT 2;
-SHOW EVENTS;
-
-DROP EVENT e1;
-
-#----------------------------------------------------------------------
-
-# Create some events.
-SET TIME_ZONE='+05:00';
-CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
-  SELECT 1;
-
-SET TIMESTAMP= @@TIMESTAMP + 1;
-
-SET TIME_ZONE='-05:00';
-CREATE EVENT e2 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
-  SELECT 1;
-
-SET TIMESTAMP= @@TIMESTAMP + 1;
-
-SET TIME_ZONE='+00:00';
-CREATE EVENT e3 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
-  SELECT 1;
-
-
-# Test INFORMATION_SCHEMA.EVENTS.
-#
-
-SELECT * FROM INFORMATION_SCHEMA.EVENTS;
-
-
-# Test SHOW EVENTS.
-#
-
-SHOW EVENTS;
-
-
-# Test SHOW CREATE EVENT.
-#
-
-SHOW CREATE EVENT e1;
-SHOW CREATE EVENT e2;
-SHOW CREATE EVENT e3;
-
-#----------------------------------------------------------------------
-
-# Test times in the past.
-#
-
---echo The following should fail, and nothing should be altered.
-
---error ER_EVENT_CANNOT_ALTER_IN_THE_PAST
-ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
-  ENDS '1999-01-02 00:00:00';
-
---error ER_EVENT_CANNOT_ALTER_IN_THE_PAST
-ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
-  ENDS '1999-01-02 00:00:00' DISABLE;
-
---echo The following should give warnings, and nothing should be created.
-
-CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
-  ENDS '1999-01-02 00:00:00'
-DO
-  SELECT 1;
-
-CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
-  ENDS '1999-01-02 00:00:00' DISABLE
-DO
-  SELECT 1;
-
-CREATE EVENT e4 ON SCHEDULE AT '1999-01-01 00:00:00' DO
-  SELECT 1;
-
-CREATE EVENT e4 ON SCHEDULE AT '1999-01-01 00:00:00' DISABLE
-DO
-  SELECT 1;
-
-SHOW EVENTS;
-
---echo The following should succeed giving a warning.
-
-ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
-  ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE;
-
-CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
-  ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE
-DO
-  SELECT 1;
-
-CREATE EVENT e5 ON SCHEDULE AT '1999-01-01 00:00:00'
-  ON COMPLETION PRESERVE
-DO
-  SELECT 1;
-
---echo The following should succeed without warnings.
-
-ALTER EVENT e2 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00';
-
-ALTER EVENT e3 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
-  ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE DISABLE;
-
-CREATE EVENT e6 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00' DO
-  SELECT 1;
-
-CREATE EVENT e7 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
-  ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE DISABLE
-DO
-  SELECT 1;
-
-CREATE EVENT e8 ON SCHEDULE AT '1999-01-01 00:00:00'
-  ON COMPLETION PRESERVE DISABLE
-DO
-  SELECT 1;
-
-SHOW EVENTS;
-
-
-DROP EVENT e8;
-DROP EVENT e7;
-DROP EVENT e6;
-DROP EVENT e5;
-DROP EVENT e4;
-DROP EVENT e3;
-DROP EVENT e2;
-DROP EVENT e1;
-
-
-disconnect conn1;
-connection default;
 
 #----------------------------------------------------------------------
 

--- 1.222/sql/set_var.cc	2007-03-20 00:37:31 +03:00
+++ 1.223/sql/set_var.cc	2007-04-05 15:24:30 +04:00
@@ -1683,7 +1683,7 @@
 }
 
 
-bool sys_var::check_enum(THD *thd, set_var *var, TYPELIB *enum_names)
+bool sys_var::check_enum(THD *thd, set_var *var, const TYPELIB *enum_names)
 {
   char buff[STRING_BUFFER_USUAL_SIZE];
   const char *value;
@@ -3640,21 +3640,18 @@
   SYNOPSIS
     thd   in  thread handler
     val   in  sql_mode value
-    len   out pointer on length of string
-
-  RETURN
-    pointer to string with sql_mode representation
+    rep   out pointer pointer to string with sql_mode representation
 */
 
-byte *sys_var_thd_sql_mode::symbolic_mode_representation(THD *thd,
-                                                         ulonglong val,
-                                                         ulong *len)
+bool
+sys_var_thd_sql_mode::
+symbolic_mode_representation(THD *thd, ulonglong val, LEX_STRING *rep)
 {
-  char buff[256];
+  char buff[STRING_BUFFER_USUAL_SIZE*8];
   String tmp(buff, sizeof(buff), &my_charset_latin1);
-  ulong length;
 
   tmp.length(0);
+
   for (uint i= 0; val; val>>= 1, i++)
   {
     if (val & 1)
@@ -3665,20 +3662,25 @@
     }
   }
 
-  if ((length= tmp.length()))
-    length--;
-  *len= length;
-  return (byte*) thd->strmake(tmp.ptr(), length);
+  if (tmp.length())
+    tmp.length(tmp.length() - 1); /* trim the trailing comma */
+
+  rep->str= thd->strmake(tmp.ptr(), tmp.length());
+
+  rep->length= rep->str ? tmp.length() : 0;
+
+  return rep->length != tmp.length();
 }
 
 
 byte *sys_var_thd_sql_mode::value_ptr(THD *thd, enum_var_type type,
 				      LEX_STRING *base)
 {
+  LEX_STRING sql_mode;
   ulonglong val= ((type == OPT_GLOBAL) ? global_system_variables.*offset :
                   thd->variables.*offset);
-  ulong length_unused;
-  return symbolic_mode_representation(thd, val, &length_unused);
+  (void) symbolic_mode_representation(thd, val, &sql_mode);
+  return sql_mode.str;
 }
 
 
@@ -4009,24 +4011,13 @@
   int res;
   /* here start the thread if not running. */
   DBUG_ENTER("sys_var_event_scheduler::update");
-  if (Events::opt_event_scheduler == Events::EVENTS_DISABLED)
-  {
-    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--event-scheduler=DISABLED or
--skip-grant-tables");
-    DBUG_RETURN(TRUE);
-  }
-
   DBUG_PRINT("info", ("new_value: %d", (int) var->save_result.ulong_value));
 
-  if (var->save_result.ulong_value == Events::EVENTS_ON)
-    res= Events::get_instance()->start_execution_of_events();
-  else if (var->save_result.ulong_value == Events::EVENTS_OFF)
-    res= Events::get_instance()->stop_execution_of_events();
-  else
-  {
-    assert(0);                                  // Impossible
-  }
-  if (res)
-    my_error(ER_EVENT_SET_VAR_ERROR, MYF(0));
+  enum Events::enum_opt_event_scheduler
+    new_state=
+    (enum Events::enum_opt_event_scheduler) var->save_result.ulong_value;
+
+  res= Events::start_or_stop_event_scheduler(new_state);
 
   DBUG_RETURN((bool) res);
 }
@@ -4035,15 +4026,7 @@
 byte *sys_var_event_scheduler::value_ptr(THD *thd, enum_var_type type,
                                          LEX_STRING *base)
 {
-  int state;
-  if (Events::opt_event_scheduler == Events::EVENTS_DISABLED)
-    state= Events::EVENTS_DISABLED;              // This should be DISABLED
-  else if (Events::get_instance()->is_execution_of_events_started())
-    state= Events::EVENTS_ON;                    // This should be ON
-  else
-    state= Events::EVENTS_OFF;                   // This should be OFF
-
-  return (byte*) Events::opt_typelib.type_names[state];
+  return (byte *) Events::get_opt_event_scheduler_str();
 }
 
 

--- 1.102/sql/set_var.h	2007-02-23 14:23:40 +03:00
+++ 1.103/sql/set_var.h	2007-04-05 15:24:30 +04:00
@@ -61,7 +61,7 @@
     sys_vars++;
   }
   virtual bool check(THD *thd, set_var *var);
-  bool check_enum(THD *thd, set_var *var, TYPELIB *enum_names);
+  bool check_enum(THD *thd, set_var *var, const TYPELIB *enum_names);
   bool check_set(THD *thd, set_var *var, TYPELIB *enum_names);
   virtual bool update(THD *thd, set_var *var)=0;
   virtual void set_default(THD *thd_arg, enum_var_type type) {}
@@ -440,8 +440,8 @@
   }
   void set_default(THD *thd, enum_var_type type);
   byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
-  static byte *symbolic_mode_representation(THD *thd, ulonglong sql_mode,
-                                            ulong *length);
+  static bool symbolic_mode_representation(THD *thd, ulonglong sql_mode,
+                                           LEX_STRING *rep);
 };
 
 

--- 1.262/sql/sp_head.cc	2007-03-27 21:09:51 +04:00
+++ 1.263/sql/sp_head.cc	2007-04-05 15:24:30 +04:00
@@ -2104,8 +2104,7 @@
   String buffer(buff, sizeof(buff), system_charset_info);
   int res;
   List<Item> field_list;
-  byte *sql_mode_str;
-  ulong sql_mode_len;
+  LEX_STRING sql_mode;
   bool full_access;
   DBUG_ENTER("sp_head::show_create_procedure");
   DBUG_PRINT("info", ("procedure %s", m_name.str));
@@ -2116,12 +2115,10 @@
   if (check_show_routine_access(thd, this, &full_access))
     DBUG_RETURN(1);
 
-  sql_mode_str=
-    sys_var_thd_sql_mode::symbolic_mode_representation(thd,
-                                                       m_sql_mode,
-                                                       &sql_mode_len);
+  sys_var_thd_sql_mode::symbolic_mode_representation(thd, m_sql_mode,
+                                                     &sql_mode);
   field_list.push_back(new Item_empty_string("Procedure", NAME_LEN));
-  field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));
+  field_list.push_back(new Item_empty_string("sql_mode", sql_mode.length));
   // 1024 is for not to confuse old clients
   Item_empty_string *definition=
     new Item_empty_string("Create Procedure", max(buffer.length(),1024));
@@ -2133,7 +2130,7 @@
     DBUG_RETURN(1);
   protocol->prepare_for_resend();
   protocol->store(m_name.str, m_name.length, system_charset_info);
-  protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info);
+  protocol->store((char*) sql_mode.str, sql_mode.length, system_charset_info);
   if (full_access)
     protocol->store(m_defstr.str, m_defstr.length, system_charset_info);
   else
@@ -2176,23 +2173,18 @@
   String buffer(buff, sizeof(buff), system_charset_info);
   int res;
   List<Item> field_list;
-  byte *sql_mode_str;
-  ulong sql_mode_len;
+  LEX_STRING sql_mode;
   bool full_access;
   DBUG_ENTER("sp_head::show_create_function");
   DBUG_PRINT("info", ("procedure %s", m_name.str));
-  LINT_INIT(sql_mode_str);
-  LINT_INIT(sql_mode_len);
 
   if (check_show_routine_access(thd, this, &full_access))
     DBUG_RETURN(1);
 
-  sql_mode_str=
-    sys_var_thd_sql_mode::symbolic_mode_representation(thd,
-                                                       m_sql_mode,
-                                                       &sql_mode_len);
+  sys_var_thd_sql_mode::symbolic_mode_representation(thd, m_sql_mode,
+                                                     &sql_mode);
   field_list.push_back(new Item_empty_string("Function",NAME_LEN));
-  field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));
+  field_list.push_back(new Item_empty_string("sql_mode", sql_mode.length));
   Item_empty_string *definition=
     new Item_empty_string("Create Function", max(buffer.length(),1024));
   definition->maybe_null= TRUE;
@@ -2203,7 +2195,7 @@
     DBUG_RETURN(1);
   protocol->prepare_for_resend();
   protocol->store(m_name.str, m_name.length, system_charset_info);
-  protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info);
+  protocol->store(sql_mode.str, sql_mode.length, system_charset_info);
   if (full_access)
     protocol->store(m_defstr.str, m_defstr.length, system_charset_info);
   else
Thread
bk commit into 5.1 tree (kostja:1.2524) BUG#23631konstantin5 Apr