Commit bc3708a5 authored by unknown's avatar unknown
Browse files

WL#1034 ongoing updates after review


sql/event.cc:
  -my_error() as close as possible to the place where the error
   occurs.
  -a thought how to replicate events
  -use close_thread_tables() in some cases and for others rely on
   this call being done in sql_parse.cc::do_command()
sql/event.h:
  remove redundant defines
sql/event_executor.cc:
  - reenable the compilation again
  - don't backup the open_tables_state, it's not needed
sql/event_timed.cc:
  - inline a bit
  - comment added
sql/mysqld.cc:
  - start mysqld with --event-scheduler=0 by default
sql/share/errmsg.txt:
  3 new messages
sql/sql_parse.cc:
  remove now obsolete error checking - the errors are reported
  as closer as possible to the place where they are detected
sql/sql_yacc.yy:
  add WARNING message. fix a bug that was corrupting 
  thd->client_capabilites ->
  select count(*) from mysql.event
  was reporting : "Unknown table test.event"!!!
  Using temporal variable is nice but IMO quite error-prone.
parent a581a4a1
Loading
Loading
Loading
Loading
+94 −65
Original line number Diff line number Diff line
@@ -31,6 +31,18 @@

 - CREATE EVENT should not go into binary log! Does it now? The SQL statements
   issued by the EVENT are replicated.
   I have an idea how to solve the problem at failover. So the status field
   will be ENUM('DISABLED', 'ENABLED', 'SLAVESIDE_DISABLED').
   In this case when CREATE EVENT is replicated it should go into the binary
   as SLAVESIDE_DISABLED if it is ENABLED, when it's created as DISABLEd it
   should be replicated as disabled. If an event is ALTERed as DISABLED the
   query should go untouched into the binary log, when ALTERed as enable then
   it should go as SLAVESIDE_DISABLED. This is regarding the SQL interface.
   TT routines however modify mysql.event internally and this does not go the log
   so in this case queries has to be injected into the log...somehow... or
   maybe a solution is RBR for this case, because the event may go only from
   ENABLED to DISABLED status change and this is safe for replicating. As well
   an event may be deleted which is also safe for RBR.

 - Add a lock and use it for guarding access to events_array dynamic array.

@@ -225,13 +237,13 @@ evex_db_find_routine_aux(THD *thd, const LEX_STRING dbname,
  */
  if (rname.length > table->field[1]->field_length)
    DBUG_RETURN(SP_KEY_NOT_FOUND);

  table->field[0]->store(dbname.str, dbname.length, &my_charset_bin);
  table->field[1]->store(rname.str, rname.length, &my_charset_bin);
  key_copy(key, table->record[0], table->key_info, table->key_info->key_length);

  if (table->file->index_read_idx(table->record[0], 0,
				  key, table->key_info->key_length,
				  HA_READ_KEY_EXACT))
  if (table->file->index_read_idx(table->record[0], 0, key,
                                  table->key_info->key_length,HA_READ_KEY_EXACT))
    DBUG_RETURN(SP_KEY_NOT_FOUND);

  DBUG_RETURN(0);
@@ -256,10 +268,12 @@ static int
evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update)
{
  DBUG_ENTER("evex_fill_row");
  int ret=0;

  if (table->s->fields != EVEX_FIELD_COUNT)
  {
    my_error(ER_EVENT_COL_COUNT_DOESNT_MATCH, MYF(0), "mysql", "event");
    DBUG_RETURN(EVEX_GET_FIELD_FAILED);
  }
  
  DBUG_PRINT("info", ("m_db.len=%d",et->m_db.length));  
  DBUG_PRINT("info", ("m_name.len=%d",et->m_name.length));  
@@ -361,27 +375,24 @@ db_create_event(THD *thd, event_timed *et)
  if (!(table= evex_open_event_table(thd, TL_WRITE)))
  {
    my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
    goto done;
    goto err;
  }

  DBUG_PRINT("info", ("check existance of an event with the same name"));
  if (!evex_db_find_routine_aux(thd, et->m_db, et->m_name, table))
  {
    my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), et->m_name.str);
    goto done;    
    goto err;    
  }

  DBUG_PRINT("info", ("non-existant, go forward"));
  if ((ret= sp_use_new_db(thd, et->m_db.str, olddb, sizeof(olddb),
			  0, &dbchanged)))
  if ((ret= sp_use_new_db(thd, et->m_db.str,olddb, sizeof(olddb),0, &dbchanged)))
  {
    DBUG_PRINT("info", ("cannot use_new_db. code=%d", ret));
    my_error(ER_BAD_DB_ERROR, MYF(0));
    DBUG_RETURN(0);
    goto err;
  }
  
  restore_record(table, s->default_values); // Get default values for fields
  strxmov(definer, et->m_definer_user.str, "@", et->m_definer_host.str, NullS);

/* TODO : Uncomment these and add handling in sql_parse.cc or here

@@ -400,23 +411,29 @@ db_create_event(THD *thd, event_timed *et)
  {
    DBUG_PRINT("error", ("neither m_expr nor m_execute_as are set!"));
    my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0));
    goto done;
    
    goto err;
  }

  strxmov(definer, et->m_definer_user.str, "@", et->m_definer_host.str, NullS);
  if (table->field[EVEX_FIELD_DEFINER]->
       store(definer, (uint)strlen(definer), system_charset_info))
       store(definer, et->m_definer_user.length + 1 + et->m_definer_host.length,
             system_charset_info))
  {
    my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str);
    goto done;
    goto err;
  }

  ((Field_timestamp *)table->field[EVEX_FIELD_CREATED])->set_time();
  if ((ret= evex_fill_row(thd, table, et, false)))
    goto done; 
    goto err; 

  ret= EVEX_OK;
  if (table->file->write_row(table->record[0]))
  {
    my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str);
    goto err;
  }
  else if (mysql_bin_log.is_open())
  {
    thd->clear_error();
@@ -425,12 +442,15 @@ db_create_event(THD *thd, event_timed *et)
    mysql_bin_log.write(&qinfo);
  }

done:
  // No need to close the table, it will be closed in sql_parse::do_command
  if (dbchanged)
    (void) mysql_change_db(thd, olddb, 1);
  DBUG_RETURN(EVEX_OK);

err:
  if (dbchanged)
    (void) mysql_change_db(thd, olddb, 1);
  DBUG_RETURN(ret);
  DBUG_RETURN(EVEX_GENERAL_ERROR);
}


@@ -462,20 +482,23 @@ db_update_event(THD *thd, sp_name *name, event_timed *et)
  if (!(table= evex_open_event_table(thd, TL_WRITE)))
  {
    my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
    goto done;
    goto err;
  }

  if (evex_db_find_routine_aux(thd, et->m_db, et->m_name, table) == SP_KEY_NOT_FOUND)
  {
    my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->m_name.str);
    goto done;    
    goto err;    
  }
  
  store_record(table,record[1]);
  
  table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; // Don't update create on row update.
  // Don't update create on row update.
  table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;

  // evex_fill_row() calls my_error() in case of error so no need to handle it here
  if ((ret= evex_fill_row(thd, table, et, true)))
    goto done;
    goto err;
   
  if (name)
  {    
@@ -488,11 +511,17 @@ db_update_event(THD *thd, sp_name *name, event_timed *et)
  if ((ret= table->file->update_row(table->record[1], table->record[0])))
  {
    my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str);
    goto done;
    goto err;
  }
done:

  // close mysql.event or we crash later when loading the event from disk
  close_thread_tables(thd);
  DBUG_RETURN(ret);
  DBUG_RETURN(0);

err:
  if (table)
    close_thread_tables(thd);
  DBUG_RETURN(EVEX_GENERAL_ERROR);
}


@@ -500,7 +529,7 @@ db_update_event(THD *thd, sp_name *name, event_timed *et)
    Use sp_name for look up, return in **ett if found
*/
static int
db_find_event(THD *thd, sp_name *name, event_timed **ett)
db_find_event(THD *thd, sp_name *name, event_timed **ett, TABLE *tbl)
{
  TABLE *table;
  int ret;
@@ -508,27 +537,35 @@ db_find_event(THD *thd, sp_name *name, event_timed **ett)
  char *ptr;
  event_timed *et;  
  DBUG_ENTER("db_find_event");
  DBUG_PRINT("enter", ("name: %*s",
		       name->m_name.length, name->m_name.str));

  DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));

  if (!(table= evex_open_event_table(thd, TL_READ)))
  if (tbl)
    table= tbl;
  else if (!(table= evex_open_event_table(thd, TL_READ)))
  {
    my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
    ret= EVEX_GENERAL_ERROR;
    goto done;
  }

  if ((ret= evex_db_find_routine_aux(thd, name->m_db, name->m_name, table)))
  {
    my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->m_name.str);
    goto done;    

  }
  et= new event_timed;
  
  /*
    The table should not be closed beforehand. 
    ::load_from_row only loads and does not compile
    1)The table should not be closed beforehand.  ::load_from_row() only loads
      and does not compile

    2)::load_from_row() is silent on error therefore we emit error msg here
  */
  if ((ret= et->load_from_row(&evex_mem_root, table)))
  {
    my_error(ER_EVENT_CANNOT_LOAD_FROM_TABLE, MYF(0));
    goto done;
  }

done:
  if (ret && et)
@@ -536,6 +573,8 @@ db_find_event(THD *thd, sp_name *name, event_timed **ett)
    delete et;
    et= 0;
  }
  // don't close the table if we haven't opened it ourselves
  if (!tbl)
    close_thread_tables(thd);
  *ett= et;
  DBUG_RETURN(ret);
@@ -555,22 +594,16 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock)
  tmp_mem_root= thd->mem_root;
  thd->mem_root= &evex_mem_root;

  if (db_find_event(thd, spn, &ett))
  {
    ret= EVEX_GENERAL_ERROR;
  // no need to use my_error() here because db_find_event() has done it
  if ((ret= db_find_event(thd, spn, &ett, NULL)))
    goto done;
  }

  /*
    allocate on evex_mem_root. call without evex_mem_root and
    and m_sphead will not be cleared!
    allocate on evex_mem_root. if you call without evex_mem_root
    then m_sphead will not be cleared!
  */

  if ((ret= ett->compile(thd, &evex_mem_root)))
  {
    thd->mem_root= tmp_mem_root;
    goto done;
  }
  
  ett->compute_next_execution_time();
  if (use_lock)
@@ -589,7 +622,7 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock)
  delete ett;

  /*
    We find where the first element resides in the arraay. And then do a
    We find where the first element resides in the array. And then do a
    qsort of events_array.elements (the current number of elements).
    We know that the elements are stored in a contiguous block w/o holes.
  */
@@ -613,7 +646,6 @@ static int
evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock)
{
  uint i;
  int ret= 0;
  bool need_second_pass= true;

  DBUG_ENTER("evex_remove_from_cache");
@@ -690,7 +722,7 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock)
  if (use_lock)
    VOID(pthread_mutex_unlock(&LOCK_event_arrays));

  DBUG_RETURN(ret);
  DBUG_RETURN(0);
}


@@ -711,8 +743,6 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock)
   NOTES
     - in case there is an event with the same name (db) and 
       IF NOT EXISTS is specified, an warning is put into the W stack.
   TODO
     - Add code for in-memory structures - caching & uncaching.
*/

int
@@ -779,8 +809,6 @@ evex_create_event(THD *thd, event_timed *et, uint create_options)
     et contains data about dbname and event name. 
     name is the new name of the event, if not null this means
     that RENAME TO was specified in the query.
   TODO
     - Add code for in-memory structures - caching & uncaching.
*/

int
@@ -800,16 +828,19 @@ evex_update_event(THD *thd, sp_name *name, event_timed *et)
  {}  
  VOID(pthread_mutex_unlock(&LOCK_evex_running));
*/

  /*
    db_update_event() opens & closes the table to prevent
    crash later in the code when loading and compiling the new definition
  */
  if ((ret= db_update_event(thd, name, et)))
    goto done_no_evex;
    goto done;

  VOID(pthread_mutex_lock(&LOCK_evex_running));
  if (!evex_is_running)
  {
    // not running - therefore no memory structures
    VOID(pthread_mutex_unlock(&LOCK_evex_running));
    goto done_no_evex;
    goto done;
  }
  VOID(pthread_mutex_unlock(&LOCK_evex_running));

@@ -828,12 +859,9 @@ evex_update_event(THD *thd, sp_name *name, event_timed *et)
      ret= evex_load_and_compile_event(thd, spn, false);
      delete spn;
    }

done:
  VOID(pthread_mutex_unlock(&LOCK_event_arrays));

done_no_evex:
  // No need to close the table, it will be closed in sql_parse::do_command
done:

  DBUG_RETURN(ret);
}
@@ -848,8 +876,6 @@ evex_update_event(THD *thd, sp_name *name, event_timed *et)
     et              event's name
     drop_if_exists  if set and the event not existing => warning onto the stack
          
 TODO
   Update in-memory structures
*/

int
@@ -875,9 +901,9 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists)

  if (ret == EVEX_OK)
  {
    if (table->file->delete_row(table->record[0]))
    if (ret= table->file->delete_row(table->record[0]))
    { 	
      ret= EVEX_DELETE_ROW_FAILED;
      my_error(ER_EVENT_CANNOT_DELETE, MYF(0));
      goto done;
    }
  }
@@ -897,7 +923,10 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists)
  VOID(pthread_mutex_unlock(&LOCK_evex_running));

done:  
  // No need to close the table, it will be closed in sql_parse::do_command
  /*
     No need to close the table, it will be closed in sql_parse::do_command()
     and evex_remove_from_cache does not try to open a table
  */

  DBUG_RETURN(ret);
}
+0 −11
Original line number Diff line number Diff line
@@ -40,17 +40,6 @@ extern ulong opt_event_executor;
#define EVENT_EXEC_NO_MORE      (1L << 0)
#define EVENT_NOT_USED          (1L << 1)

#define SP_OK                 0
#define SP_KEY_NOT_FOUND     -1
#define SP_OPEN_TABLE_FAILED -2
#define SP_WRITE_ROW_FAILED  -3
#define SP_DELETE_ROW_FAILED -4
#define SP_GET_FIELD_FAILED  -5
#define SP_PARSE_ERROR       -6
#define SP_INTERNAL_ERROR    -7
#define SP_NO_DB_ERROR       -8
#define SP_BAD_IDENTIFIER    -9
#define SP_BODY_TOO_LONG    -10

extern ulong opt_event_executor;

+3 −11
Original line number Diff line number Diff line
@@ -80,8 +80,6 @@ init_events()
  event_executor_running_global_var= false;
  VOID(pthread_mutex_unlock(&LOCK_evex_running));

  DBUG_RETURN(0);
/*
#ifndef DBUG_FAULTY_THR
  //TODO Andrey: Change the error code returned!
  if (pthread_create(&th, NULL, event_executor_main, (void*)NULL))
@@ -91,7 +89,6 @@ init_events()
#endif

  DBUG_RETURN(0);
*/
}


@@ -109,7 +106,6 @@ shutdown_events()
  DBUG_VOID_RETURN;
}

#ifdef ANDREY_0

static int
init_event_thread(THD* thd)
@@ -479,13 +475,11 @@ evex_load_events_from_db(THD *thd)
  TABLE *table;
  READ_RECORD read_record_info;
  MYSQL_LOCK *lock;
  Open_tables_state open_tables_state_backup;
  int ret= -1;
  
  DBUG_ENTER("evex_load_events_from_db");  

  if (!(table= open_proc_type_table_for_read(thd, &open_tables_state_backup,
                                             "event", &mysql_event_table_exists)))
  if (!(table= evex_open_event_table(thd, TL_READ)))
    DBUG_RETURN(SP_OPEN_TABLE_FAILED);

  VOID(pthread_mutex_lock(&LOCK_event_arrays));
@@ -541,18 +535,16 @@ evex_load_events_from_db(THD *thd)

end:
  close_thread_tables(thd);
  thd->restore_backup_open_tables_state(&open_tables_state_backup);

  DBUG_PRINT("evex_load_events_from_db",
                    ("Events loaded from DB. Status code %d", ret));
  DBUG_RETURN(ret);
}

#endif


bool sys_var_event_executor::update(THD *thd, set_var *var)
{
#ifdef ANDREY_0
  // here start the thread if not running.
  VOID(pthread_mutex_lock(&LOCK_evex_running));
  if ((my_bool) var->save_result.ulong_value && !evex_is_running) {
@@ -560,7 +552,7 @@ bool sys_var_event_executor::update(THD *thd, set_var *var)
    init_events();
  } else 
    VOID(pthread_mutex_unlock(&LOCK_evex_running));
#endif  

  return sys_var_bool_ptr::update(thd, var);
}
+7 −4
Original line number Diff line number Diff line
@@ -322,10 +322,8 @@ event_timed::init_comment(THD *thd, LEX_STRING *comment)
{
  DBUG_ENTER("event_timed::init_comment");

  MEM_ROOT *root= thd->mem_root;
  m_comment.length= comment->length;
  m_comment.str= strmake_root(root, comment->str, comment->length);
  DBUG_PRINT("m_comment", ("len=%d",m_comment.length));
  m_comment.str= strmake_root(thd->mem_root, comment->str,
                              m_comment.length= comment->length);

  DBUG_VOID_RETURN;
}
@@ -359,6 +357,11 @@ event_timed::init_definer(THD *thd)
 
 SYNOPSIS
   event_timed::load_from_row()
   
 REMARKS
   This method is silent on errors and should behave like that. Callers
   should handle throwing of error messages. The reason is that the class
   should not know about how to deal with communication.
*/

int
+1 −1
Original line number Diff line number Diff line
@@ -4810,7 +4810,7 @@ Disable with --skip-bdb (will save memory).",
   0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"event-scheduler", OPT_EVENT_EXECUTOR, "Enable/disable the event scheduler.",
   (gptr*) &opt_event_executor, (gptr*) &opt_event_executor, 0, GET_BOOL, NO_ARG,
   1/*default*/, 0/*min-value*/, 1/*max-value*/, 0, 0, 0},
   0/*default*/, 0/*min-value*/, 1/*max-value*/, 0, 0, 0},
  {"exit-info", 'T', "Used for debugging;  Use at your own risk!", 0, 0, 0,
   GET_LONG, OPT_ARG, 0, 0, 0, 0, 0, 0},
  {"external-locking", OPT_USE_LOCKING, "Use system (external) locking.  With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running.",
Loading