Commit 70856a0d authored by unknown's avatar unknown
Browse files

WL#1034

updated sources


sql/event.cc:
  update
  put some error calls to the places they occur
sql/event.h:
  - change the default (does not work in STRICT mode)
sql/event_executor.cc:
  move mutex initialization to evex_init_mutexes so init_events() can be reused when the
  main thread does not work and set global event_scheduler=1; (this will start the thread)
  The main thread is now visible with show processlist and can be killed.
sql/event_priv.h:
  don't use anymore SP for opening table
sql/event_timed.cc:
  don't use anymore SP routines for opening mysql.event
sql/mysqld.cc:
  shutdown_events() should be maximal at the end of the server because
  it destroys mutexes of EVEX. The call should not be in the main thread.
sql/set_var.cc:
  make sys_var_event_executor subclass sys_var_bool_ptr
  to overload ::update() method - needed to start a
  killed (non-running) evex main thread
sql/set_var.h:
  declare class sys_var_event_executor
sql/share/errmsg.txt:
  2 new messages
parent 7ff79771
Loading
Loading
Loading
Loading
+193 −108
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@

/*
 TODO list :
 - The default value of created/modified should not be 0000-00-00 because of
   STRICT mode restricions.
 - Remove m_ prefixes of member variables.

 - Use timestamps instead of datetime.
@@ -150,6 +152,93 @@ event_timed_compare(event_timed **a, event_timed **b)
}



/*
  Open mysql.event table for read

  SYNOPSIS
    evex_open_event_table_for_read()
      thd         Thread context
      lock_type   How to lock the table
  RETURN
    0	Error
    #	Pointer to TABLE object
*/

TABLE *evex_open_event_table(THD *thd, enum thr_lock_type lock_type)
{
  TABLE_LIST tables;
  bool not_used;
  DBUG_ENTER("open_proc_table");

  /*
    Speed up things if the table doesn't exists. *table_exists
    is set when we create or read stored procedure or on flush privileges.
  */
  if (!mysql_event_table_exists)
    DBUG_RETURN(0);

  bzero((char*) &tables, sizeof(tables));
  tables.db= (char*) "mysql";
  tables.table_name= tables.alias= (char*) "event";
  tables.lock_type= lock_type;

  if (simple_open_n_lock_tables(thd, &tables))
  {
    mysql_event_table_exists= 0;
    DBUG_RETURN(0);
  }

  DBUG_RETURN(tables.table);
}


/*
  Find row in open mysql.event table representing event

  SYNOPSIS
    evex_db_find_routine_aux()
      thd    Thread context
      dbname Name of event's database
      rname  Name of the event inside the db  
      table  TABLE object for open mysql.event table.

  RETURN VALUE
    SP_OK           - Routine found
    SP_KEY_NOT_FOUND- No routine with given name
*/

static int
evex_db_find_routine_aux(THD *thd, const LEX_STRING dbname,
                       const LEX_STRING rname, TABLE *table)
{
  byte key[MAX_KEY_LENGTH];	// db, name, optional key length type
  DBUG_ENTER("evex_db_find_routine_aux");
  DBUG_PRINT("enter", ("name: %.*s", rname.length, rname.str));

  /*
    Create key to find row. We have to use field->store() to be able to
    handle VARCHAR and CHAR fields.
    Assumption here is that the two first fields in the table are
    'db' and 'name' and the first key is the primary key over the
    same fields.
  */
  if (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))
    DBUG_RETURN(SP_KEY_NOT_FOUND);

  DBUG_RETURN(0);
}



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

@@ -164,13 +253,13 @@ event_timed_compare(event_timed **a, event_timed **b)
*/

static int
evex_fill_row(THD *thd, TABLE *table, event_timed *et)
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)
    goto get_field_failed;
    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));  
@@ -220,13 +309,15 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et)
  {
    // fix_fields already called in init_execute_at
    table->field[EVEX_FIELD_EXECUTE_AT]->set_notnull();
    table->field[EVEX_FIELD_EXECUTE_AT]->store_time(&et->m_execute_at, MYSQL_TIMESTAMP_DATETIME);    
    table->field[EVEX_FIELD_EXECUTE_AT]->store_time(&et->m_execute_at,
                                                    MYSQL_TIMESTAMP_DATETIME);    
    
	//this will make it NULL because we don't call set_notnull
    table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong) 0);  
  }
  else
  {
    DBUG_ASSERT(is_update);
    // it is normal to be here when the action is update
    // this is an error if the action is create. something is borked
  }
@@ -238,8 +329,6 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et)
	store((et->m_comment).str, (et->m_comment).length, system_charset_info);

  DBUG_RETURN(0);  
get_field_failed:
  DBUG_RETURN(EVEX_GET_FIELD_FAILED);
}


@@ -259,32 +348,36 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et)
static int
db_create_event(THD *thd, event_timed *et)
{
  int ret;
  int ret= EVEX_OK;
  TABLE *table;
  TABLE_LIST tables;
  char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
  char olddb[128];
  bool dbchanged;
  bool dbchanged= false;
  DBUG_ENTER("db_create_event");
  DBUG_PRINT("enter", ("name: %.*s", et->m_name.length, et->m_name.str));

  dbchanged= false;
  if ((ret= sp_use_new_db(thd, et->m_db.str, olddb, sizeof(olddb),
			  0, &dbchanged)))

  DBUG_PRINT("info", ("open mysql.event for update"));
  if (!(table= evex_open_event_table(thd, TL_WRITE)))
  {
    DBUG_PRINT("info", ("cannot use_new_db. code=%d", ret));
    DBUG_RETURN(EVEX_NO_DB_ERROR);
    my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
    goto done;
  }

  bzero(&tables, sizeof(tables));
  tables.db= (char*)"mysql";
  tables.table_name= tables.alias= (char*)"event";
  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;    
  }

  if (!(table= EVEX_OPEN_TABLE_FOR_UPDATE()))
  DBUG_PRINT("info", ("non-existant, go forward"));
  if ((ret= sp_use_new_db(thd, et->m_db.str, olddb, sizeof(olddb),
			  0, &dbchanged)))
  {
    if (dbchanged)
      (void)mysql_change_db(thd, olddb, 1);
    DBUG_RETURN(SP_OPEN_TABLE_FAILED);
    DBUG_PRINT("info", ("cannot use_new_db. code=%d", ret));
    my_error(ER_BAD_DB_ERROR, MYF(0));
    DBUG_RETURN(0);
  }
  
  restore_record(table, s->default_values); // Get default values for fields
@@ -305,25 +398,25 @@ db_create_event(THD *thd, event_timed *et)
*/
  if (!(et->m_expr) && !(et->m_execute_at.year))
  {
    DBUG_PRINT("error", ("neither m_expr nor m_execute_as is set!"));
    ret= EVEX_WRITE_ROW_FAILED;
    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;
  }
  ret= table->field[EVEX_FIELD_DEFINER]->
       store(definer, (uint)strlen(definer), system_charset_info);
  if (ret)

  if (table->field[EVEX_FIELD_DEFINER]->
       store(definer, (uint)strlen(definer), system_charset_info))
  {
    ret= EVEX_PARSE_ERROR;
    my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str);
    goto done;
  }

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

  ret= EVEX_OK;
  if (table->file->write_row(table->record[0]))
    ret= EVEX_WRITE_ROW_FAILED;
    my_error(ER_EVENT_STORE_FAILED, MYF(0), et->m_name.str);
  else if (mysql_bin_log.is_open())
  {
    thd->clear_error();
@@ -333,7 +426,8 @@ db_create_event(THD *thd, event_timed *et)
  }

done:
  close_thread_tables(thd);
  // 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(ret);
@@ -365,16 +459,22 @@ db_update_event(THD *thd, sp_name *name, event_timed *et)
    DBUG_PRINT("enter", ("rename to: %.*s", name->m_name.length, name->m_name.str));

  // Todo: Handle in sql_prepare.cc SP_OPEN_TABLE_FAILED
  if (!(table= EVEX_OPEN_TABLE_FOR_UPDATE()))
    DBUG_RETURN(SP_OPEN_TABLE_FAILED);
  if (!(table= evex_open_event_table(thd, TL_WRITE)))
  {
    my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
    goto done;
  }

  ret= sp_db_find_routine_aux(thd, 0/*notype*/, et->m_db, et->m_name, table);
  if (ret == EVEX_OK)
  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;    
  }
  
  store_record(table,record[1]);
  
  table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; // Don't update create on row update.
    ret= evex_fill_row(thd, table, et);
    if (ret)
  if ((ret= evex_fill_row(thd, table, et, true)))
    goto done;
   
  if (name)
@@ -385,14 +485,17 @@ db_update_event(THD *thd, sp_name *name, event_timed *et)
      store(name->m_name.str, name->m_name.length, system_charset_info);
  }

    if ((table->file->update_row(table->record[1],table->record[0])))
      ret= EVEX_WRITE_ROW_FAILED;
  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;
  }
done:
  close_thread_tables(thd);
  DBUG_RETURN(ret);
}


/*
    Use sp_name for look up, return in **ett if found
*/
@@ -404,18 +507,18 @@ db_find_event(THD *thd, sp_name *name, event_timed **ett)
  const char *definer;
  char *ptr;
  event_timed *et;  
  Open_tables_state open_tables_state_backup;
  DBUG_ENTER("db_find_event");
  DBUG_PRINT("enter", ("name: %*s",
		       name->m_name.length, name->m_name.str));


  if (!(table= open_proc_type_table_for_read(thd, &open_tables_state_backup,
                                             "event", &mysql_event_table_exists)))
    DBUG_RETURN(SP_OPEN_TABLE_FAILED);
  if (!(table= evex_open_event_table(thd, TL_READ)))
  {
    my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
    goto done;
  }

  if ((ret= sp_db_find_routine_aux(thd, 0/*notype*/, name->m_db, name->m_name,
                                   table)) != SP_OK)
  if ((ret= evex_db_find_routine_aux(thd, name->m_db, name->m_name, table)))
    goto done;

  et= new event_timed;
@@ -434,7 +537,6 @@ db_find_event(THD *thd, sp_name *name, event_timed **ett)
    et= 0;
  }
  close_thread_tables(thd);
  thd->restore_backup_open_tables_state(&open_tables_state_backup);
  *ett= et;
  DBUG_RETURN(ret);
}
@@ -503,8 +605,6 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock)
  if (thd->mem_root != tmp_mem_root)
    thd->mem_root= tmp_mem_root;  

  if (spn) 
    delete spn;
  DBUG_RETURN(ret);
}

@@ -619,19 +719,19 @@ int
evex_create_event(THD *thd, event_timed *et, uint create_options)
{
  int ret = 0;
  sp_name *spn= 0;

  DBUG_ENTER("evex_create_event");
  DBUG_PRINT("enter", ("name: %*s options:%d", et->m_name.length,
                et->m_name.str, create_options));

/*
  VOID(pthread_mutex_lock(&LOCK_evex_running));
  if (!evex_is_running)
  // TODO: put an warning to the user here.
  //       Is it needed? (Andrey, 051129)
  {}  
  VOID(pthread_mutex_unlock(&LOCK_evex_running));

*/

  if ((ret = db_create_event(thd, et)) == EVEX_WRITE_ROW_FAILED && 
        (create_options & HA_LEX_CREATE_IF_NOT_EXISTS))
@@ -652,24 +752,16 @@ evex_create_event(THD *thd, event_timed *et, uint create_options)
    goto done;

  VOID(pthread_mutex_lock(&LOCK_evex_running));
  if (!evex_is_running)
  if (evex_is_running && et->m_status == MYSQL_EVENT_ENABLED)
  {
    VOID(pthread_mutex_unlock(&LOCK_evex_running));
    goto done;
    sp_name spn(et->m_db, et->m_name);
    ret= evex_load_and_compile_event(thd, &spn, true);
  }
  VOID(pthread_mutex_unlock(&LOCK_evex_running));

  //cache only if the event is ENABLED
  if (et->m_status == MYSQL_EVENT_ENABLED)
  {
    spn= new sp_name(et->m_db, et->m_name);
    if ((ret= evex_load_and_compile_event(thd, spn, true)))
      goto done;
  }

done:
  if (spn) 
    delete spn;
  // No need to close the table, it will be closed in sql_parse::do_command

  DBUG_RETURN(ret);
}

@@ -685,7 +777,7 @@ evex_create_event(THD *thd, event_timed *et, uint create_options)
          
   NOTES
     et contains data about dbname and event name. 
     name is the new name of the event. if not null this means
     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.
@@ -701,19 +793,24 @@ evex_update_event(THD *thd, sp_name *name, event_timed *et)
  DBUG_ENTER("evex_update_event");
  DBUG_PRINT("enter", ("name: %*s", et->m_name.length, et->m_name.str));

/*
  VOID(pthread_mutex_lock(&LOCK_evex_running));
  if (!evex_is_running)
  // put an warning to the user here
  {}  
  VOID(pthread_mutex_unlock(&LOCK_evex_running));
*/

  if ((ret= db_update_event(thd, name, et)))
    goto done_no_evex;

  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;
  }
  VOID(pthread_mutex_unlock(&LOCK_evex_running));

  /*
@@ -721,31 +818,23 @@ evex_update_event(THD *thd, sp_name *name, event_timed *et)
    The reason is that DISABLED events are not cached.
  */
  VOID(pthread_mutex_lock(&LOCK_event_arrays));
  evex_remove_from_cache(&et->m_db, &et->m_name, false);
  if (et->m_status == MYSQL_EVENT_ENABLED)
    if (name)
  {
    evex_remove_from_cache(&name->m_db, &name->m_name, false);
    if (et->m_status == MYSQL_EVENT_ENABLED &&
        (ret= evex_load_and_compile_event(thd, name, false))
       )
      goto done;
  }
      ret= evex_load_and_compile_event(thd, name, false);
    else
    {
    evex_remove_from_cache(&et->m_db, &et->m_name, false);
      spn= new sp_name(et->m_db, et->m_name);
    if (et->m_status == MYSQL_EVENT_ENABLED &&
        (ret= evex_load_and_compile_event(thd, spn, false))
        )
    {
      ret= evex_load_and_compile_event(thd, spn, false);
      delete spn;
      goto done;
    } 
    }

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

  DBUG_RETURN(ret);
}

@@ -771,13 +860,15 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists)
  bool opened;
  DBUG_ENTER("evex_drop_event");

/*
  VOID(pthread_mutex_lock(&LOCK_evex_running));
  if (!evex_is_running)
  // put an warning to the user here
  {}  
  VOID(pthread_mutex_unlock(&LOCK_evex_running));

  if (!(table= EVEX_OPEN_TABLE_FOR_UPDATE()))
*/
////
  if (!(table= evex_open_event_table(thd, TL_WRITE)))
    DBUG_RETURN(SP_OPEN_TABLE_FAILED);

  ret= sp_db_find_routine_aux(thd, 0/*notype*/, et->m_db, et->m_name, table);
@@ -806,14 +897,8 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists)
  VOID(pthread_mutex_unlock(&LOCK_evex_running));

done:  
  /* 
    "opened" is switched to TRUE when we open mysql.event for checking.
    In this case we have to close the table after finishing working with it.
  */
  close_thread_tables(thd);
  // No need to close the table, it will be closed in sql_parse::do_command

  DBUG_RETURN(ret);
}

+2 −2
Original line number Diff line number Diff line
@@ -214,8 +214,8 @@ CREATE TABLE `event` (
  `execute_at` datetime default NULL,
  `transient_expression` int(11) default NULL,
  `interval_type` 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 '0000-00-00 00:00:00',
  `modified` timestamp NOT NULL default '0000-00-00 00:00:00',
  `created` timestamp NOT NULL,
  `modified` timestamp NOT NULL,
  `last_executed` datetime default NULL,
  `starts` datetime default NULL,
  `ends` datetime default NULL,
+40 −7
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ my_bool event_executor_running_global_var= false;

extern ulong thread_created;

static my_bool evex_mutexes_initted= false;

static int
evex_load_events_from_db(THD *thd);
@@ -50,6 +51,19 @@ evex_load_events_from_db(THD *thd);
pthread_handler_t event_executor_worker(void *arg);
pthread_handler_t event_executor_main(void *arg);

static
void evex_init_mutexes()
{
  if (evex_mutexes_initted)
  {
    evex_mutexes_initted= true;
    return;
  }
  pthread_mutex_init(&LOCK_event_arrays, MY_MUTEX_INIT_FAST);
  pthread_mutex_init(&LOCK_workers_count, MY_MUTEX_INIT_FAST);
  pthread_mutex_init(&LOCK_evex_running, MY_MUTEX_INIT_FAST);
}

int
init_events()
{
@@ -59,15 +73,15 @@ init_events()

  DBUG_PRINT("info",("Starting events main thread"));

  pthread_mutex_init(&LOCK_event_arrays, MY_MUTEX_INIT_FAST);
  pthread_mutex_init(&LOCK_workers_count, MY_MUTEX_INIT_FAST);
  pthread_mutex_init(&LOCK_evex_running, MY_MUTEX_INIT_FAST);
  evex_init_mutexes();
  
  VOID(pthread_mutex_lock(&LOCK_evex_running));
  evex_is_running= false;  
  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))
@@ -77,6 +91,7 @@ init_events()
#endif

  DBUG_RETURN(0);
*/
}


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

#ifdef ANDREY_0

static int
init_event_thread(THD* thd)
@@ -165,7 +181,7 @@ pthread_handler_t event_executor_main(void *arg)
    goto err;

  // make this thread invisible it has no vio -> show processlist won't see
  thd->system_thread= 0;
  thd->system_thread= 1;

  VOID(pthread_mutex_lock(&LOCK_thread_count));
  threads.append(thd);
@@ -350,7 +366,7 @@ pthread_handler_t event_executor_main(void *arg)
  free_root(&evex_mem_root, MYF(0));
  sql_print_information("Event executor stopped");

  shutdown_events();
//  shutdown_events();

  my_thread_end();
  pthread_exit(0);
@@ -391,7 +407,7 @@ pthread_handler_t event_executor_worker(void *event_void)
  thd->init_for_queries();

  // make this thread visible it has no vio -> show processlist needs this flag
  thd->system_thread= 0;
  thd->system_thread= 1;

  VOID(pthread_mutex_lock(&LOCK_thread_count));
  threads.append(thd);
@@ -531,3 +547,20 @@ evex_load_events_from_db(THD *thd)
                    ("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) {
    VOID(pthread_mutex_unlock(&LOCK_evex_running));
    init_events();
  } else 
    VOID(pthread_mutex_unlock(&LOCK_evex_running));
#endif  
  return sys_var_bool_ptr::update(thd, var);
}
+3 −4
Original line number Diff line number Diff line
@@ -17,9 +17,6 @@
#ifndef _EVENT_PRIV_H_
#define _EVENT_PRIV_H_

#define EVEX_OPEN_TABLE_FOR_UPDATE() \
       open_proc_type_table_for_update(thd, "event", &mysql_event_table_exists)


enum
{
@@ -54,4 +51,6 @@ extern pthread_mutex_t LOCK_event_arrays,
int
my_time_compare(TIME *a, TIME *b);


TABLE *evex_open_event_table(THD *thd, enum thr_lock_type lock_type);             
#endif /* _EVENT_PRIV_H_ */
+1 −1
Original line number Diff line number Diff line
@@ -719,7 +719,7 @@ event_timed::update_fields(THD *thd)
  if (!(m_status_changed || m_last_executed_changed))
    goto done;
  
  if (!(table= EVEX_OPEN_TABLE_FOR_UPDATE()))
  if (!(table= evex_open_event_table(thd, TL_WRITE)))
    DBUG_RETURN(SP_OPEN_TABLE_FAILED);

  if ((ret= sp_db_find_routine_aux(thd, 0/*notype*/, m_db, m_name, table)))
Loading