WL#3337 (Event scheduler new architecture)

Cleaned up the code a bit. Fixed few leaks.
This code still does not load events on server startup
from disk. The problem is that there is a need for a THD instance, which
does not exist during server boot. This will be solved soon.
Still Event_timed is used both for the memory queue and for exectution.
This will be changed according to WL#3337 probably in the next commit.
This commit is contained in:
andrey@lmy004. 2006-07-05 17:12:50 +02:00
parent 2bdd872e5f
commit 3b840adee7
14 changed files with 242 additions and 2286 deletions

View File

@ -530,18 +530,14 @@ Event_parse_data::init_ends(THD *thd, Item *new_ends)
Event_timed::Event_timed() Event_timed::Event_timed()
*/ */
Event_timed::Event_timed():in_spawned_thread(0),locked_by_thread_id(0), Event_timed::Event_timed():status_changed(false),
running(0), thread_id(0), status_changed(false),
last_executed_changed(false), expression(0), last_executed_changed(false), expression(0),
created(0), modified(0), created(0), modified(0),
on_completion(Event_timed::ON_COMPLETION_DROP), on_completion(Event_timed::ON_COMPLETION_DROP),
status(Event_timed::ENABLED), sphead(0), status(Event_timed::ENABLED), sphead(0),
sql_mode(0), dropped(false), sql_mode(0), dropped(false), flags(0)
free_sphead_on_delete(true), flags(0)
{ {
pthread_mutex_init(&this->LOCK_running, MY_MUTEX_INIT_FAST);
pthread_cond_init(&this->COND_finished, NULL);
init(); init();
} }
@ -555,49 +551,11 @@ Event_timed::Event_timed():in_spawned_thread(0),locked_by_thread_id(0),
Event_timed::~Event_timed() Event_timed::~Event_timed()
{ {
deinit_mutexes();
free_root(&mem_root, MYF(0)); free_root(&mem_root, MYF(0));
if (free_sphead_on_delete)
free_sp(); free_sp();
} }
/*
Destructor
SYNOPSIS
Event_timed::deinit_mutexes()
*/
void
Event_timed::deinit_mutexes()
{
pthread_mutex_destroy(&this->LOCK_running);
pthread_cond_destroy(&this->COND_finished);
}
/*
Checks whether the event is running
SYNOPSIS
Event_timed::is_running()
*/
bool
Event_timed::is_running()
{
bool ret;
VOID(pthread_mutex_lock(&this->LOCK_running));
ret= running;
VOID(pthread_mutex_unlock(&this->LOCK_running));
return ret;
}
/* /*
Init all member variables Init all member variables
@ -1253,7 +1211,7 @@ Event_timed::update_fields(THD *thd)
Open_tables_state backup; Open_tables_state backup;
int ret; int ret;
DBUG_ENTER("Event_timed::update_time_fields"); DBUG_ENTER("Event_timed::update_fields");
DBUG_PRINT("enter", ("name: %*s", name.length, name.str)); DBUG_PRINT("enter", ("name: %*s", name.length, name.str));
@ -1382,7 +1340,7 @@ Event_timed::get_create_event(THD *thd, String *buf)
Executes the event (the underlying sp_head object); Executes the event (the underlying sp_head object);
SYNOPSIS SYNOPSIS
evex_fill_row() Event_timed::execute()
thd THD thd THD
mem_root If != NULL use it to compile the event on it mem_root If != NULL use it to compile the event on it
@ -1607,149 +1565,6 @@ done:
} }
extern pthread_attr_t connection_attrib;
/*
Checks whether is possible and forks a thread. Passes self as argument.
RETURN VALUE
EVENT_EXEC_STARTED OK
EVENT_EXEC_ALREADY_EXEC Thread not forked, already working
EVENT_EXEC_CANT_FORK Unable to spawn thread (error)
*/
int
Event_timed::spawn_now(void * (*thread_func)(void*), void *arg)
{
THD *thd= current_thd;
int ret= EVENT_EXEC_STARTED;
DBUG_ENTER("Event_timed::spawn_now");
DBUG_PRINT("info", ("[%s.%s]", dbname.str, name.str));
VOID(pthread_mutex_lock(&this->LOCK_running));
DBUG_PRINT("info", ("SCHEDULER: execute_at of %s is %lld", name.str,
TIME_to_ulonglong_datetime(&execute_at)));
mark_last_executed(thd);
if (compute_next_execution_time())
{
sql_print_error("SCHEDULER: Error while computing time of %s.%s . "
"Disabling after execution.", dbname.str, name.str);
status= DISABLED;
}
DBUG_PRINT("evex manager", ("[%10s] next exec at [%llu]", name.str,
TIME_to_ulonglong_datetime(&execute_at)));
/*
1. For one-time event : year is > 0 and expression is 0
2. For recurring, expression is != -=> check execute_at_null in this case
*/
if ((execute_at.year && !expression) || execute_at_null)
{
sql_print_information("SCHEDULER: [%s.%s of %s] no more executions "
"after this one", dbname.str, name.str,
definer.str);
flags |= EVENT_EXEC_NO_MORE | EVENT_FREE_WHEN_FINISHED;
}
update_fields(thd);
if (!in_spawned_thread)
{
pthread_t th;
in_spawned_thread= true;
if (pthread_create(&th, &connection_attrib, thread_func, arg))
{
DBUG_PRINT("info", ("problem while spawning thread"));
ret= EVENT_EXEC_CANT_FORK;
in_spawned_thread= false;
}
}
else
{
DBUG_PRINT("info", ("already in spawned thread. skipping"));
ret= EVENT_EXEC_ALREADY_EXEC;
}
VOID(pthread_mutex_unlock(&this->LOCK_running));
DBUG_RETURN(ret);
}
bool
Event_timed::spawn_thread_finish(THD *thd)
{
bool should_free;
DBUG_ENTER("Event_timed::spawn_thread_finish");
VOID(pthread_mutex_lock(&LOCK_running));
in_spawned_thread= false;
DBUG_PRINT("info", ("Sending COND_finished for thread %d", thread_id));
thread_id= 0;
if (dropped)
drop(thd);
pthread_cond_broadcast(&COND_finished);
should_free= flags & EVENT_FREE_WHEN_FINISHED;
VOID(pthread_mutex_unlock(&LOCK_running));
DBUG_RETURN(should_free);
}
/*
Kills a running event
SYNOPSIS
Event_timed::kill_thread()
RETURN VALUE
0 OK
-1 EVEX_CANT_KILL
!0 Error
*/
int
Event_timed::kill_thread(THD *thd)
{
int ret= 0;
DBUG_ENTER("Event_timed::kill_thread");
pthread_mutex_lock(&LOCK_running);
DBUG_PRINT("info", ("thread_id=%lu", thread_id));
if (thread_id == thd->thread_id)
{
/*
We don't kill ourselves in cases like :
alter event e_43 do alter event e_43 do set @a = 4 because
we will never receive COND_finished.
*/
DBUG_PRINT("info", ("It's not safe to kill ourselves in self altering queries"));
ret= EVEX_CANT_KILL;
}
else if (thread_id && !(ret= kill_one_thread(thd, thread_id, false)))
{
thd->enter_cond(&COND_finished, &LOCK_running, "Waiting for finished");
DBUG_PRINT("info", ("Waiting for COND_finished from thread %d", thread_id));
while (thread_id)
pthread_cond_wait(&COND_finished, &LOCK_running);
DBUG_PRINT("info", ("Got COND_finished"));
/* This will implicitly unlock LOCK_running. Hence we return before that */
thd->exit_cond("");
DBUG_RETURN(0);
}
else if (!thread_id && in_spawned_thread)
{
/*
Because the manager thread waits for the forked thread to update thread_id
this situation is impossible.
*/
DBUG_ASSERT(0);
}
pthread_mutex_unlock(&LOCK_running);
DBUG_PRINT("exit", ("%d", ret));
DBUG_RETURN(ret);
}
/* /*
Checks whether two events have the same name Checks whether two events have the same name

View File

@ -63,12 +63,6 @@ class Event_timed
{ {
Event_timed(const Event_timed &); /* Prevent use of these */ Event_timed(const Event_timed &); /* Prevent use of these */
void operator=(Event_timed &); void operator=(Event_timed &);
my_bool in_spawned_thread;
ulong locked_by_thread_id;
my_bool running;
ulong thread_id;
pthread_mutex_t LOCK_running;
pthread_cond_t COND_finished;
bool status_changed; bool status_changed;
bool last_executed_changed; bool last_executed_changed;
@ -118,7 +112,6 @@ public:
ulong sql_mode; ulong sql_mode;
bool dropped; bool dropped;
bool free_sphead_on_delete;
uint flags;//all kind of purposes uint flags;//all kind of purposes
static void *operator new(size_t size) static void *operator new(size_t size)
@ -146,9 +139,6 @@ public:
void void
init(); init();
void
deinit_mutexes();
int int
load_from_row(TABLE *table); load_from_row(TABLE *table);
@ -173,23 +163,8 @@ public:
int int
compile(THD *thd, MEM_ROOT *mem_root); compile(THD *thd, MEM_ROOT *mem_root);
bool
is_running();
int
spawn_now(void * (*thread_func)(void*), void *arg);
bool
spawn_thread_finish(THD *thd);
void void
free_sp(); free_sp();
int
kill_thread(THD *thd);
void
set_thread_id(ulong tid) { thread_id= tid; }
}; };

View File

@ -374,8 +374,7 @@ Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table,
ret= read_record_info.read_record(&read_record_info); ret= read_record_info.read_record(&read_record_info);
if (ret == 0) if (ret == 0)
ret= copy_event_to_schema_table(thd, schema_table, event_table); ret= copy_event_to_schema_table(thd, schema_table, event_table);
} } while (ret == 0);
while (ret == 0);
DBUG_PRINT("info", ("Scan finished. ret=%d", ret)); DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
end_read_record(&read_record_info); end_read_record(&read_record_info);
@ -464,8 +463,7 @@ Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables, char *db)
int int
Event_db_repository::find_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_db_repository::find_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
Event_timed **ett, Event_timed **ett, TABLE *tbl)
TABLE *tbl, MEM_ROOT *root)
{ {
TABLE *table; TABLE *table;
int ret; int ret;
@ -505,7 +503,7 @@ done:
if (ret) if (ret)
{ {
delete et; delete et;
et= 0; et= NULL;
} }
/* don't close the table if we haven't opened it ourselves */ /* don't close the table if we haven't opened it ourselves */
if (!tbl && table) if (!tbl && table)
@ -518,7 +516,6 @@ done:
int int
Event_db_repository::init_repository() Event_db_repository::init_repository()
{ {
init_alloc_root(&repo_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
return 0; return 0;
} }
@ -526,7 +523,6 @@ Event_db_repository::init_repository()
void void
Event_db_repository::deinit_repository() Event_db_repository::deinit_repository()
{ {
free_root(&repo_root, MYF(0));
} }
@ -731,7 +727,8 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
parse_data->name.str)); parse_data->name.str));
DBUG_PRINT("info", ("check existance of an event with the same name")); DBUG_PRINT("info", ("check existance of an event with the same name"));
if (!evex_db_find_event_by_name(thd, parse_data->dbname, parse_data->name, table)) if (!evex_db_find_event_by_name(thd, parse_data->dbname,
parse_data->name, table))
{ {
if (create_if_not) if (create_if_not)
{ {
@ -1026,14 +1023,12 @@ Event_db_repository::find_event_by_name(THD *thd, LEX_STRING db,
*/ */
if (db.length > table->field[ET_FIELD_DB]->field_length || if (db.length > table->field[ET_FIELD_DB]->field_length ||
name.length > table->field[ET_FIELD_NAME]->field_length) name.length > table->field[ET_FIELD_NAME]->field_length)
DBUG_RETURN(EVEX_KEY_NOT_FOUND); DBUG_RETURN(EVEX_KEY_NOT_FOUND);
table->field[ET_FIELD_DB]->store(db.str, db.length, &my_charset_bin); table->field[ET_FIELD_DB]->store(db.str, db.length, &my_charset_bin);
table->field[ET_FIELD_NAME]->store(name.str, name.length, &my_charset_bin); table->field[ET_FIELD_NAME]->store(name.str, name.length, &my_charset_bin);
key_copy(key, table->record[0], table->key_info, key_copy(key, table->record[0], table->key_info, table->key_info->key_length);
table->key_info->key_length);
if (table->file->index_read_idx(table->record[0], 0, key, if (table->file->index_read_idx(table->record[0], 0, key,
table->key_info->key_length, table->key_info->key_length,
@ -1125,7 +1120,7 @@ Event_db_repository::drop_events_by_field(THD *thd,
the table, compiles and inserts it into the cache. the table, compiles and inserts it into the cache.
SYNOPSIS SYNOPSIS
Event_scheduler::load_named_event() Event_db_repository::load_named_event_timed()
thd THD thd THD
etn The name of the event to load and compile on scheduler's root etn The name of the event to load and compile on scheduler's root
etn_new The loaded event etn_new The loaded event
@ -1136,7 +1131,8 @@ Event_db_repository::drop_events_by_field(THD *thd,
*/ */
int int
Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_db_repository::load_named_event_timed(THD *thd, LEX_STRING dbname,
LEX_STRING name,
Event_timed **etn_new) Event_timed **etn_new)
{ {
int ret= 0; int ret= 0;
@ -1144,12 +1140,12 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING na
Event_timed *et_loaded= NULL; Event_timed *et_loaded= NULL;
Open_tables_state backup; Open_tables_state backup;
DBUG_ENTER("Event_db_repository::load_named_event"); DBUG_ENTER("Event_db_repository::load_named_event_timed");
DBUG_PRINT("enter",("thd=%p name:%*s",thd, name.length, name.str)); DBUG_PRINT("enter",("thd=%p name:%*s",thd, name.length, name.str));
thd->reset_n_backup_open_tables_state(&backup); thd->reset_n_backup_open_tables_state(&backup);
/* No need to use my_error() here because db_find_event() has done it */ /* No need to use my_error() here because db_find_event() has done it */
ret= find_event(thd, dbname, name, &et_loaded, NULL, &repo_root); ret= find_event(thd, dbname, name, &et_loaded, NULL);
thd->restore_backup_open_tables_state(&backup); thd->restore_backup_open_tables_state(&backup);
/* In this case no memory was allocated so we don't need to clean */ /* In this case no memory was allocated so we don't need to clean */
if (ret) if (ret)
@ -1171,3 +1167,57 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING na
DBUG_RETURN(OP_OK); DBUG_RETURN(OP_OK);
} }
/*
Looks for a named event in mysql.event and then loads it from
the table, compiles and inserts it into the cache.
SYNOPSIS
Event_db_repository::load_named_event_job()
thd THD
etn The name of the event to load and compile on scheduler's root
etn_new The loaded event
RETURN VALUE
NULL Error during compile or the event is non-enabled.
otherwise Address
*/
int
Event_db_repository::load_named_event_job(THD *thd, LEX_STRING dbname,
LEX_STRING name,
Event_job_data **etn_new)
{
int ret= 0;
MEM_ROOT *tmp_mem_root;
Event_timed *et_loaded= NULL;
Open_tables_state backup;
DBUG_ENTER("Event_db_repository::load_named_event_job");
DBUG_PRINT("enter",("thd=%p name:%*s",thd, name.length, name.str));
#if 0
thd->reset_n_backup_open_tables_state(&backup);
/* No need to use my_error() here because db_find_event() has done it */
ret= find_event(thd, dbname, name, &et_loaded, NULL);
thd->restore_backup_open_tables_state(&backup);
/* In this case no memory was allocated so we don't need to clean */
if (ret)
DBUG_RETURN(OP_LOAD_ERROR);
if (et_loaded->status != Event_timed::ENABLED)
{
/*
We don't load non-enabled events.
In db_find_event() `et_new` was allocated on the heap and not on
scheduler_root therefore we delete it here.
*/
delete et_loaded;
DBUG_RETURN(OP_DISABLED_EVENT);
}
et_loaded->compute_next_execution_time();
*etn_new= et_loaded;
#endif
DBUG_RETURN(OP_OK);
}

View File

@ -56,6 +56,7 @@ fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */);
class Event_timed; class Event_timed;
class Event_parse_data; class Event_parse_data;
class Event_queue_element; class Event_queue_element;
class Event_job_data;
class Event_db_repository class Event_db_repository
{ {
@ -88,10 +89,15 @@ public:
int int
find_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_timed **ett, find_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_timed **ett,
TABLE *tbl, MEM_ROOT *root); TABLE *tbl);
int int
load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_timed **etn_new); load_named_event_timed(THD *thd, LEX_STRING dbname, LEX_STRING name,
Event_timed **etn_new);
int
load_named_event_job(THD *thd, LEX_STRING dbname, LEX_STRING name,
Event_job_data **etn_new);
int int
find_event_by_name(THD *thd, LEX_STRING db, LEX_STRING name, TABLE *table); find_event_by_name(THD *thd, LEX_STRING db, LEX_STRING name, TABLE *table);
@ -116,8 +122,6 @@ private:
static bool static bool
check_system_tables(THD *thd); check_system_tables(THD *thd);
MEM_ROOT repo_root;
/* Prevent use of these */ /* Prevent use of these */
Event_db_repository(const Event_db_repository &); Event_db_repository(const Event_db_repository &);
void operator=(Event_db_repository &); void operator=(Event_db_repository &);

View File

@ -90,34 +90,29 @@ Event_queue::Event_queue()
RETURN VALUE RETURN VALUE
OP_OK OK or scheduler not working OP_OK OK or scheduler not working
OP_LOAD_ERROR Error during loading from disk OP_LOAD_ERROR Error during loading from disk
OP_ALREADY_EXISTS Event already in the queue
*/ */
int int
Event_queue::create_event(THD *thd, Event_parse_data *et, bool check_existence) Event_queue::create_event(THD *thd, Event_parse_data *et)
{ {
int res; int res;
Event_timed *et_new; Event_timed *et_new;
DBUG_ENTER("Event_queue::create_event"); DBUG_ENTER("Event_queue::create_event");
DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et, &LOCK_event_queue)); DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et, &LOCK_event_queue));
res= db_repository->load_named_event_timed(thd, et->dbname, et->name, &et_new);
LOCK_QUEUE_DATA(); LOCK_QUEUE_DATA();
if (check_existence && find_event(et->dbname, et->name, FALSE)) if (!res)
{
res= OP_ALREADY_EXISTS;
goto end;
}
if (!(res= db_repository->
load_named_event(thd, et->dbname, et->name, &et_new)))
{ {
DBUG_PRINT("info", ("new event in the queue %p", et_new)); DBUG_PRINT("info", ("new event in the queue %p", et_new));
queue_insert_safe(&queue, (byte *) et_new); queue_insert_safe(&queue, (byte *) et_new);
on_queue_change(); notify_observers();
} }
else if (res == OP_DISABLED_EVENT) else if (res == OP_DISABLED_EVENT)
res= OP_OK; res= OP_OK;
end:
UNLOCK_QUEUE_DATA(); UNLOCK_QUEUE_DATA();
DBUG_RETURN(res); DBUG_RETURN(res);
} }
@ -129,104 +124,54 @@ end:
Event_queue::update_event() Event_queue::update_event()
thd Thread thd Thread
et The event to replace(add) into the queue et The event to replace(add) into the queue
new_schema New schema new_schema New schema, in case of RENAME TO
new_name New name new_name New name, in case of RENAME TO
RETURN VALUE RETURN VALUE
OP_OK OK or scheduler not working OP_OK OK or scheduler not working
OP_LOAD_ERROR Error during loading from disk OP_LOAD_ERROR Error during loading from disk
OP_ALREADY_EXISTS Event already in the queue
*/ */
int int
Event_queue::update_event(THD *thd, Event_parse_data *et, Event_queue::update_event(THD *thd, Event_parse_data *et,
LEX_STRING *new_schema, LEX_STRING *new_schema, LEX_STRING *new_name)
LEX_STRING *new_name)
{ {
int res= OP_OK; int res;
Event_timed *et_old, *et_new= NULL; Event_timed *et_old= NULL, *et_new= NULL;
LEX_STRING old_schema, old_name;
LINT_INIT(old_schema.str);
LINT_INIT(old_schema.length);
LINT_INIT(old_name.str);
LINT_INIT(old_name.length);
DBUG_ENTER("Event_queue::update_event"); DBUG_ENTER("Event_queue::update_event");
DBUG_PRINT("enter", ("thd=%p et=%p et=[%s.%s] lock=%p", DBUG_PRINT("enter", ("thd=%p et=%p et=[%s.%s] lock=%p",
thd, et, et->dbname.str, et->name.str, &LOCK_event_queue)); thd, et, et->dbname.str, et->name.str, &LOCK_event_queue));
res= db_repository->
load_named_event_timed(thd, new_schema?*new_schema:et->dbname,
new_name? *new_name:et->name, &et_new);
if (res && res != OP_DISABLED_EVENT)
goto end;
LOCK_QUEUE_DATA(); LOCK_QUEUE_DATA();
if (!(et_old= find_event(et->dbname, et->name, TRUE))) if (!(et_old= find_event(et->dbname, et->name, TRUE)))
{
DBUG_PRINT("info", ("%s.%s not found cached, probably was DISABLED", DBUG_PRINT("info", ("%s.%s not found cached, probably was DISABLED",
et->dbname.str, et->name.str)); et->dbname.str, et->name.str));
if (new_schema && new_name)
{
old_schema= et->dbname;
old_name= et->name;
et->dbname= *new_schema;
et->name= *new_name;
} }
if (!(res= db_repository-> if (!res)
load_named_event(thd, et->dbname, et->name, &et_new)))
{ {
DBUG_PRINT("info", ("new event in the queue %p old %p", et_new, et_old)); DBUG_PRINT("info", ("new event in the queue %p old %p", et_new, et_old));
queue_insert_safe(&queue, (byte *) et_new); queue_insert_safe(&queue, (byte *) et_new);
on_queue_change();
} }
else if (res == OP_DISABLED_EVENT) else if (res == OP_DISABLED_EVENT)
res= OP_OK; res= OP_OK;
if (new_schema && new_name)
{
et->dbname= old_schema;
et->name= old_name;
}
DBUG_PRINT("info", ("res=%d", res));
UNLOCK_QUEUE_DATA(); UNLOCK_QUEUE_DATA();
/*
Andrey: Is this comment still truthful ???
We don't move this code above because a potential kill_thread will call notify_observers();
THD::awake(). Which in turn will try to acqure mysys_var->current_mutex,
which is LOCK_event_queue on which the COND_new_work in ::run() locks.
Hence, we try to acquire a lock which we have already acquired and we run
into an assert. Holding LOCK_event_queue however is not needed because
we don't touch any invariant of the scheduler anymore. ::drop_event() does
the same.
*/
if (et_old) if (et_old)
{
switch (et_old->kill_thread(thd)) {
case EVEX_CANT_KILL:
/* Don't delete but continue */
et_old->flags |= EVENT_FREE_WHEN_FINISHED;
break;
case 0:
/*
kill_thread() waits till the spawned thread finishes after it's
killed. Hence, we delete here memory which is no more referenced from
a running thread.
*/
delete et_old; delete et_old;
/* end:
We don't signal COND_new_work here because: DBUG_PRINT("info", ("res=%d", res));
1. Even if the dropped event is on top of the queue this will not
move another one to be executed before the time the one on the
top (but could be at the same second as the dropped one)
2. If this was the last event on the queue, then pthread_cond_timedwait
in ::run() will finish and then see that the queue is empty and
call cond_wait(). Hence, no need to interrupt the blocked
::run() thread.
*/
break;
default:
DBUG_ASSERT(0);
}
}
DBUG_RETURN(res); DBUG_RETURN(res);
} }
@ -256,40 +201,13 @@ Event_queue::drop_event(THD *thd, sp_name *name)
LOCK_QUEUE_DATA(); LOCK_QUEUE_DATA();
if (!(et_old= find_event(name->m_db, name->m_name, TRUE))) if (!(et_old= find_event(name->m_db, name->m_name, TRUE)))
DBUG_PRINT("info", ("No such event found, probably DISABLED")); DBUG_PRINT("info", ("No such event found, probably DISABLED"));
UNLOCK_QUEUE_DATA(); UNLOCK_QUEUE_DATA();
/* See comments in ::replace_event() why this is split in two parts. */
if (et_old) if (et_old)
{
switch ((res= et_old->kill_thread(thd))) {
case EVEX_CANT_KILL:
/* Don't delete but continue */
et_old->flags |= EVENT_FREE_WHEN_FINISHED;
break;
case 0:
/*
kill_thread() waits till the spawned thread finishes after it's
killed. Hence, we delete here memory which is no more referenced from
a running thread.
*/
delete et_old; delete et_old;
/* /*
We don't signal COND_new_work here because: We don't signal here because the scheduler will catch the change
1. Even if the dropped event is on top of the queue this will not next time it wakes up.
move another one to be executed before the time the one on the
top (but could be at the same second as the dropped one)
2. If this was the last event on the queue, then pthread_cond_timedwait
in ::run() will finish and then see that the queue is empty and
call cond_wait(). Hence, no need to interrupt the blocked
::run() thread.
*/ */
break;
default:
sql_print_error("SCHEDULER: Got unexpected error %d", res);
DBUG_ASSERT(0);
}
}
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
} }
@ -361,7 +279,7 @@ Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern,
DBUG_ENTER("Event_queue::drop_matching_events"); DBUG_ENTER("Event_queue::drop_matching_events");
DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern.length, pattern.str)); DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern.length, pattern.str));
uint i= 0, dropped= 0; uint i= 0;
while (i < queue.elements) while (i < queue.elements)
{ {
Event_timed *et= (Event_timed *) queue_element(&queue, i); Event_timed *et= (Event_timed *) queue_element(&queue, i);
@ -375,30 +293,20 @@ Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern,
counter and the (i < queue.elements) condition is ok. counter and the (i < queue.elements) condition is ok.
*/ */
queue_remove(&queue, i); queue_remove(&queue, i);
/* See replace_event() */
switch (et->kill_thread(thd)) {
case EVEX_CANT_KILL:
/* Don't delete but continue */
et->flags |= EVENT_FREE_WHEN_FINISHED;
++dropped;
break;
case 0:
delete et; delete et;
++dropped;
break;
default:
DBUG_ASSERT(0);
}
} }
else else
i++; i++;
} }
DBUG_PRINT("info", ("Dropped %lu", dropped));
/* /*
Don't send COND_new_work because no need to wake up the scheduler thread. We don't call notify_observers() . If we remove the top event:
When it wakes next time up it will recalculate how much more it should 1. The queue is empty. The scheduler will wake up at some time and realize
sleep if the top of the queue has been changed by this method. that the queue is empty. If create_event() comes inbetween it will
signal the scheduler
2. The queue is not empty, but the next event after the previous top, won't
be executed any time sooner than the element we removed. Hence, we may
not notify the scheduler and it will realize the change when it
wakes up from timedwait.
*/ */
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
@ -418,16 +326,14 @@ Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern,
>=0 Number of dropped events >=0 Number of dropped events
*/ */
int void
Event_queue::drop_schema_events(THD *thd, LEX_STRING schema) Event_queue::drop_schema_events(THD *thd, LEX_STRING schema)
{ {
int ret;
DBUG_ENTER("Event_queue::drop_schema_events"); DBUG_ENTER("Event_queue::drop_schema_events");
LOCK_QUEUE_DATA(); LOCK_QUEUE_DATA();
drop_matching_events(thd, schema, event_timed_db_equal); drop_matching_events(thd, schema, event_timed_db_equal);
UNLOCK_QUEUE_DATA(); UNLOCK_QUEUE_DATA();
DBUG_VOID_RETURN;
DBUG_RETURN(ret);
} }
@ -744,13 +650,13 @@ Event_queue::deinit_mutexes()
its state. its state.
SYNOPSIS SYNOPSIS
Event_queue::on_queue_change() Event_queue::notify_observers()
*/ */
void void
Event_queue::on_queue_change() Event_queue::notify_observers()
{ {
DBUG_ENTER("Event_queue::on_queue_change"); DBUG_ENTER("Event_queue::notify_observers");
DBUG_PRINT("info", ("Signalling change of the queue")); DBUG_PRINT("info", ("Signalling change of the queue"));
scheduler->queue_changed(); scheduler->queue_changed();
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
@ -761,7 +667,7 @@ Event_queue::on_queue_change()
The implementation of full-fledged initialization. The implementation of full-fledged initialization.
SYNOPSIS SYNOPSIS
Event_scheduler::init() Event_queue::init()
RETURN VALUE RETURN VALUE
FALSE OK FALSE OK
@ -769,15 +675,16 @@ Event_queue::on_queue_change()
*/ */
bool bool
Event_queue::init(Event_db_repository *db_repo) Event_queue::init_queue(Event_db_repository *db_repo, Event_scheduler_ng *sched)
{ {
int i= 0; int i= 0;
bool ret= FALSE; bool ret= FALSE;
DBUG_ENTER("Event_queue::init"); DBUG_ENTER("Event_queue::init_queue");
DBUG_PRINT("enter", ("this=%p", this)); DBUG_PRINT("enter", ("this=%p", this));
LOCK_QUEUE_DATA(); LOCK_QUEUE_DATA();
db_repository= db_repo; db_repository= db_repo;
scheduler= sched;
if (init_queue_ex(&queue, 30 /*num_el*/, 0 /*offset*/, 0 /*smallest_on_top*/, if (init_queue_ex(&queue, 30 /*num_el*/, 0 /*offset*/, 0 /*smallest_on_top*/,
event_timed_compare_q, NULL, 30 /*auto_extent*/)) event_timed_compare_q, NULL, 30 /*auto_extent*/))
@ -803,9 +710,9 @@ end:
void void
Event_queue::deinit() Event_queue::deinit_queue()
{ {
DBUG_ENTER("Event_queue::deinit"); DBUG_ENTER("Event_queue::deinit_queue");
LOCK_QUEUE_DATA(); LOCK_QUEUE_DATA();
empty_queue(); empty_queue();
@ -833,6 +740,8 @@ void
Event_queue::empty_queue() Event_queue::empty_queue()
{ {
uint i; uint i;
DBUG_ENTER("Event_queue::empty_queue");
DBUG_PRINT("enter", ("Purging the queue. %d element(s)", queue.elements));
/* empty the queue */ /* empty the queue */
for (i= 0; i < events_count_no_lock(); ++i) for (i= 0; i < events_count_no_lock(); ++i)
{ {
@ -840,6 +749,7 @@ Event_queue::empty_queue()
delete et; delete et;
} }
resize_queue(&queue, 0); resize_queue(&queue, 0);
DBUG_VOID_RETURN;
} }
@ -864,25 +774,12 @@ Event_queue::top_changed()
} }
Event_timed * inline void
Event_queue::get_top_for_execution_if_time(THD *thd, time_t now, Event_queue::dbug_dump_queue(time_t now)
struct timespec *abstime)
{ {
struct timespec top_time; #ifndef DBUG_OFF
Event_timed *et_new= NULL; Event_timed *et;
DBUG_ENTER("Event_queue::get_top_for_execution_if_time"); uint i;
DBUG_PRINT("enter", ("thd=%p now=%d", thd, now));
abstime->tv_nsec= 0;
LOCK_QUEUE_DATA();
do {
int res;
Event_timed *et= NULL;
if (!queue.elements)
{
abstime->tv_sec= 0;
break;
}
int i;
DBUG_PRINT("info", ("Dumping queue . Elements=%u", queue.elements)); DBUG_PRINT("info", ("Dumping queue . Elements=%u", queue.elements));
for (i = 0; i < queue.elements; i++) for (i = 0; i < queue.elements; i++)
{ {
@ -897,14 +794,36 @@ Event_queue::get_top_for_execution_if_time(THD *thd, time_t now,
(int)(sec_since_epoch_TIME(&et->execute_at) - now), (int)(sec_since_epoch_TIME(&et->execute_at) - now),
sec_since_epoch_TIME(&et->execute_at) <= now)); sec_since_epoch_TIME(&et->execute_at) <= now));
} }
et= ((Event_timed*)queue_element(&queue, 0)); #endif
}
Event_timed *
Event_queue::get_top_for_execution_if_time(THD *thd, time_t now,
struct timespec *abstime)
{
struct timespec top_time;
Event_timed *et_new= NULL;
DBUG_ENTER("Event_queue::get_top_for_execution_if_time");
DBUG_PRINT("enter", ("thd=%p now=%d", thd, now));
abstime->tv_nsec= 0;
LOCK_QUEUE_DATA();
do {
int res;
if (!queue.elements)
{
abstime->tv_sec= 0;
break;
}
dbug_dump_queue(now);
Event_timed *et= ((Event_timed*)queue_element(&queue, 0));
top_time.tv_sec= sec_since_epoch_TIME(&et->execute_at); top_time.tv_sec= sec_since_epoch_TIME(&et->execute_at);
if (top_time.tv_sec <= now) if (top_time.tv_sec <= now)
{ {
DBUG_PRINT("info", ("Ready for execution")); DBUG_PRINT("info", ("Ready for execution"));
abstime->tv_sec= 0; abstime->tv_sec= 0;
if ((res= db_repository->load_named_event(thd, et->dbname, et->name, if ((res= db_repository->load_named_event_timed(thd, et->dbname, et->name,
&et_new))) &et_new)))
{ {
DBUG_ASSERT(0); DBUG_ASSERT(0);

View File

@ -38,15 +38,15 @@ public:
deinit_mutexes(); deinit_mutexes();
bool bool
init(Event_db_repository *db_repo); init_queue(Event_db_repository *db_repo, Event_scheduler_ng *sched);
void void
deinit(); deinit_queue();
/* Methods for queue management follow */ /* Methods for queue management follow */
int int
create_event(THD *thd, Event_parse_data *et, bool check_existence); create_event(THD *thd, Event_parse_data *et);
int int
update_event(THD *thd, Event_parse_data *et, LEX_STRING *new_schema, update_event(THD *thd, Event_parse_data *et, LEX_STRING *new_schema,
@ -55,13 +55,9 @@ public:
bool bool
drop_event(THD *thd, sp_name *name); drop_event(THD *thd, sp_name *name);
int void
drop_schema_events(THD *thd, LEX_STRING schema); drop_schema_events(THD *thd, LEX_STRING schema);
int
drop_user_events(THD *thd, LEX_STRING *definer)
{ DBUG_ASSERT(0); return 0;}
uint uint
events_count(); events_count();
@ -89,7 +85,7 @@ public:
void void
top_changed(); top_changed();
///////////////protected protected:
Event_timed * Event_timed *
find_event(LEX_STRING db, LEX_STRING name, bool remove_from_q); find_event(LEX_STRING db, LEX_STRING name, bool remove_from_q);
@ -105,8 +101,6 @@ public:
Event_db_repository *db_repository; Event_db_repository *db_repository;
/* The sorted queue with the Event_timed objects */
QUEUE queue;
uint mutex_last_locked_at_line; uint mutex_last_locked_at_line;
uint mutex_last_unlocked_at_line; uint mutex_last_unlocked_at_line;
@ -122,10 +116,16 @@ public:
unlock_data(const char *func, uint line); unlock_data(const char *func, uint line);
void void
on_queue_change(); notify_observers();
void
dbug_dump_queue(time_t now);
Event_scheduler_ng *scheduler; Event_scheduler_ng *scheduler;
protected:
//public:
/* The sorted queue with the Event_timed objects */
QUEUE queue;
}; };

File diff suppressed because it is too large Load Diff

View File

@ -16,191 +16,4 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
class sp_name;
class Event_timed;
class Event_db_repository;
class Event_queue;
class THD;
int
events_init();
void
events_shutdown();
#include "event_queue.h"
#include "event_scheduler.h"
class Event_scheduler
{
public:
enum enum_state
{
UNINITIALIZED= 0,
INITIALIZED,
COMMENCING,
CANTSTART,
RUNNING,
SUSPENDED,
IN_SHUTDOWN
};
enum enum_suspend_or_resume
{
SUSPEND= 1,
RESUME= 2
};
/* This is the current status of the life-cycle of the scheduler. */
enum enum_state state;
static void
create_instance(Event_queue *queue);
static void
init_mutexes();
static void
destroy_mutexes();
/* Singleton access */
static Event_scheduler*
get_instance();
bool
init(Event_db_repository *db_repo);
void
destroy();
/* State changing methods follow */
bool
start();
int
stop();
bool
start_suspended();
/*
Need to be public because has to be called from the function
passed to pthread_create.
*/
bool
run(THD *thd);
int
suspend_or_resume(enum enum_suspend_or_resume action);
/*
static void
init_mutexes();
static void
destroy_mutexes();
*/
void
report_error_during_start();
/* Information retrieving methods follow */
enum enum_state
get_state();
bool
initialized();
static int
dump_internal_status(THD *thd);
/* helper functions for working with mutexes & conditionals */
void
lock_data(const char *func, uint line);
void
unlock_data(const char *func, uint line);
int
cond_wait(int cond, pthread_mutex_t *mutex);
void
queue_changed();
Event_queue *event_queue;
protected:
uint
workers_count();
/* helper functions */
bool
execute_top(THD *thd, Event_timed *et);
void
clean_memory(THD *thd);
void
stop_all_running_events(THD *thd);
bool
check_n_suspend_if_needed(THD *thd);
bool
check_n_wait_for_non_empty_queue(THD *thd);
/* Singleton DP is used */
Event_scheduler();
pthread_mutex_t LOCK_data;
pthread_mutex_t *LOCK_scheduler_data;
/* The MEM_ROOT of the object */
MEM_ROOT scheduler_root;
/* Set to start the scheduler in suspended state */
bool start_scheduler_suspended;
/*
Holds the thread id of the executor thread or 0 if the executor is not
running. It is used by ::shutdown() to know which thread to kill with
kill_one_thread(). The latter wake ups a thread if it is waiting on a
conditional variable and sets thd->killed to non-zero.
*/
ulong thread_id;
enum enum_cond_vars
{
COND_NONE= -1,
COND_new_work= 0,
COND_started_or_stopped,
COND_suspend_or_resume,
/* Must be always last */
COND_LAST
};
uint mutex_last_locked_at_line;
uint mutex_last_unlocked_at_line;
const char* mutex_last_locked_in_func;
const char* mutex_last_unlocked_in_func;
int cond_waiting_on;
bool mutex_scheduler_data_locked;
static const char * const cond_vars_names[COND_LAST];
pthread_cond_t cond_vars[COND_LAST];
/* Singleton instance */
static Event_scheduler *singleton;
private:
/* Prevent use of these */
Event_scheduler(const Event_scheduler &);
void operator=(Event_scheduler &);
};
#endif /* _EVENT_SCHEDULER_H_ */ #endif /* _EVENT_SCHEDULER_H_ */

View File

@ -212,6 +212,7 @@ end:
pthread_mutex_unlock(&LOCK_thread_count); pthread_mutex_unlock(&LOCK_thread_count);
my_thread_end(); my_thread_end();
DBUG_RETURN(0); // Against gcc warnings
} }
@ -296,26 +297,22 @@ end:
delete event; delete event;
my_thread_end(); my_thread_end();
DBUG_RETURN(0); // Against gcc warnings
} }
bool bool
Event_scheduler_ng::init(Event_queue *q) Event_scheduler_ng::init_scheduler(Event_queue *q)
{ {
thread_id= 0; thread_id= 0;
state= INITIALIZED; state= INITIALIZED;
/* init memory root */
queue= q; queue= q;
return FALSE; return FALSE;
} }
void void
Event_scheduler_ng::deinit() Event_scheduler_ng::deinit_scheduler() {}
{
}
void void
@ -477,7 +474,6 @@ Event_scheduler_ng::run(THD *thd)
pthread_cond_signal(&COND_state); pthread_cond_signal(&COND_state);
error: error:
state= INITIALIZED; state= INITIALIZED;
stop_all_running_events(thd);
UNLOCK_SCHEDULER_DATA(); UNLOCK_SCHEDULER_DATA();
sql_print_information("SCHEDULER: Stopped"); sql_print_information("SCHEDULER: Stopped");
@ -560,98 +556,18 @@ Event_scheduler_ng::workers_count()
} }
/*
Stops all running events
SYNOPSIS
Event_scheduler::stop_all_running_events()
thd Thread
NOTE
LOCK_scheduler data must be acquired prior to call to this method
*/
void
Event_scheduler_ng::stop_all_running_events(THD *thd)
{
CHARSET_INFO *scs= system_charset_info;
uint i;
DYNAMIC_ARRAY running_threads;
THD *tmp;
DBUG_ENTER("Event_scheduler::stop_all_running_events");
DBUG_PRINT("enter", ("workers_count=%d", workers_count()));
my_init_dynamic_array(&running_threads, sizeof(ulong), 10, 10);
bool had_super= FALSE;
VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
I_List_iterator<THD> it(threads);
while ((tmp=it++))
{
if (tmp->command == COM_DAEMON)
continue;
if (tmp->system_thread == SYSTEM_THREAD_EVENT_WORKER)
push_dynamic(&running_threads, (gptr) &tmp->thread_id);
}
VOID(pthread_mutex_unlock(&LOCK_thread_count));
/* We need temporarily SUPER_ACL to be able to kill our offsprings */
if (!(thd->security_ctx->master_access & SUPER_ACL))
thd->security_ctx->master_access|= SUPER_ACL;
else
had_super= TRUE;
char tmp_buff[10*STRING_BUFFER_USUAL_SIZE];
char int_buff[STRING_BUFFER_USUAL_SIZE];
String tmp_string(tmp_buff, sizeof(tmp_buff), scs);
String int_string(int_buff, sizeof(int_buff), scs);
tmp_string.length(0);
for (i= 0; i < running_threads.elements; ++i)
{
int ret;
ulong thd_id= *dynamic_element(&running_threads, i, ulong*);
int_string.set((longlong) thd_id,scs);
tmp_string.append(int_string);
if (i < running_threads.elements - 1)
tmp_string.append(' ');
if ((ret= kill_one_thread(thd, thd_id, FALSE)))
{
sql_print_error("SCHEDULER: Error killing %lu code=%d", thd_id, ret);
break;
}
}
if (running_threads.elements)
sql_print_information("SCHEDULER: Killing workers :%s", tmp_string.c_ptr());
if (!had_super)
thd->security_ctx->master_access &= ~SUPER_ACL;
delete_dynamic(&running_threads);
sql_print_information("SCHEDULER: Waiting for worker threads to finish");
while (workers_count())
my_sleep(100000);
DBUG_VOID_RETURN;
}
/* /*
Signals the main scheduler thread that the queue has changed Signals the main scheduler thread that the queue has changed
its state. its state.
SYNOPSIS SYNOPSIS
Event_scheduler::queue_changed() Event_scheduler_ng::queue_changed()
*/ */
void void
Event_scheduler_ng::queue_changed() Event_scheduler_ng::queue_changed()
{ {
DBUG_ENTER("Event_scheduler::queue_changed"); DBUG_ENTER("Event_scheduler_ng::queue_changed");
DBUG_PRINT("info", ("Sending COND_state")); DBUG_PRINT("info", ("Sending COND_state"));
pthread_cond_signal(&COND_state); pthread_cond_signal(&COND_state);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
@ -662,8 +578,7 @@ void
Event_scheduler_ng::lock_data(const char *func, uint line) Event_scheduler_ng::lock_data(const char *func, uint line)
{ {
DBUG_ENTER("Event_scheduler_ng::lock_mutex"); DBUG_ENTER("Event_scheduler_ng::lock_mutex");
DBUG_PRINT("enter", ("mutex_lock=%p func=%s line=%u", DBUG_PRINT("enter", ("func=%s line=%u", func, line));
&LOCK_scheduler_state, func, line));
pthread_mutex_lock(&LOCK_scheduler_state); pthread_mutex_lock(&LOCK_scheduler_state);
mutex_last_locked_in_func= func; mutex_last_locked_in_func= func;
mutex_last_locked_at_line= line; mutex_last_locked_at_line= line;
@ -675,9 +590,8 @@ Event_scheduler_ng::lock_data(const char *func, uint line)
void void
Event_scheduler_ng::unlock_data(const char *func, uint line) Event_scheduler_ng::unlock_data(const char *func, uint line)
{ {
DBUG_ENTER("Event_scheduler_ng::UNLOCK_mutex"); DBUG_ENTER("Event_scheduler_ng::unlock_mutex");
DBUG_PRINT("enter", ("mutex_unlock=%p func=%s line=%u", DBUG_PRINT("enter", ("func=%s line=%u", func, line));
&LOCK_scheduler_state, func, line));
mutex_last_unlocked_at_line= line; mutex_last_unlocked_at_line= line;
mutex_scheduler_data_locked= FALSE; mutex_scheduler_data_locked= FALSE;
mutex_last_unlocked_in_func= func; mutex_last_unlocked_in_func= func;

View File

@ -48,10 +48,10 @@ public:
run(THD *thd); run(THD *thd);
bool bool
init(Event_queue *queue); init_scheduler(Event_queue *queue);
void void
deinit(); deinit_scheduler();
void void
init_mutexes(); init_mutexes();
@ -78,9 +78,6 @@ private:
bool bool
execute_top(THD *thd, Event_timed *job_data); execute_top(THD *thd, Event_timed *job_data);
void
stop_all_running_events(THD *thd);
/* helper functions for working with mutexes & conditionals */ /* helper functions for working with mutexes & conditionals */
void void
lock_data(const char *func, uint line); lock_data(const char *func, uint line);
@ -104,7 +101,6 @@ private:
pthread_cond_t COND_state; pthread_cond_t COND_state;
Event_queue *queue; Event_queue *queue;
Event_db_repository *db_repository;
uint mutex_last_locked_at_line; uint mutex_last_locked_at_line;
uint mutex_last_unlocked_at_line; uint mutex_last_unlocked_at_line;

View File

@ -17,10 +17,10 @@
#include "mysql_priv.h" #include "mysql_priv.h"
#include "events.h" #include "events.h"
#include "event_data_objects.h" #include "event_data_objects.h"
#include "event_scheduler.h"
#include "event_db_repository.h" #include "event_db_repository.h"
#include "sp_head.h" #include "event_queue.h"
#include "event_scheduler_ng.h" #include "event_scheduler_ng.h"
#include "sp_head.h"
/* /*
TODO list : TODO list :
@ -48,6 +48,21 @@ Warning:
*/ */
/*
If the user (un)intentionally removes an event directly from mysql.event
the following sequence has to be used to be able to remove the in-memory
counterpart.
1. CREATE EVENT the_name ON SCHEDULE EVERY 1 SECOND DISABLE DO SELECT 1;
2. DROP EVENT the_name
In other words, the first one will create a row in mysql.event . In the
second step because there will be a line, disk based drop will pass and
the scheduler will remove the memory counterpart. The reason is that
in-memory queue does not check whether the event we try to drop from memory
is disabled. Disabled events are not kept in-memory because they are not
eligible for execution.
*/
const char *event_scheduler_state_names[]= const char *event_scheduler_state_names[]=
{ "OFF", "0", "ON", "1", "SUSPEND", "2", NullS }; { "OFF", "0", "ON", "1", "SUSPEND", "2", NullS };
@ -284,17 +299,15 @@ Events::open_event_table(THD *thd, enum thr_lock_type lock_type,
*/ */
int int
Events::create_event(THD *thd, Event_parse_data *parse_data, uint create_options, Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists,
uint *rows_affected) uint *rows_affected)
{ {
int ret; int ret;
DBUG_ENTER("Events::create_event"); DBUG_ENTER("Events::create_event");
if (!(ret= db_repository-> if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists,
create_event(thd, parse_data,
create_options & HA_LEX_CREATE_IF_NOT_EXISTS,
rows_affected))) rows_affected)))
{ {
if ((ret= event_queue->create_event(thd, parse_data, true))) if ((ret= event_queue->create_event(thd, parse_data)))
my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret); my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret);
} }
/* 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 */
@ -350,9 +363,10 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *new_name,
SYNOPSIS SYNOPSIS
Events::drop_event() Events::drop_event()
thd THD thd THD
name event's name name Event's name
drop_if_exists if set and the event not existing => warning onto the stack if_exists When set and the event does not exist => warning onto
rows_affected affected number of rows is returned heres the stack
rows_affected Affected number of rows is returned heres
RETURN VALUE RETURN VALUE
0 OK 0 OK
@ -360,15 +374,13 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *new_name,
*/ */
int int
Events::drop_event(THD *thd, sp_name *name, bool drop_if_exists, Events::drop_event(THD *thd, sp_name *name, bool if_exists, uint *rows_affected)
uint *rows_affected)
{ {
int ret; int ret;
DBUG_ENTER("Events::drop_event"); DBUG_ENTER("Events::drop_event");
if (!(ret= db_repository->drop_event(thd, name->m_db, name->m_name, if (!(ret= db_repository->drop_event(thd, name->m_db, name->m_name, if_exists,
drop_if_exists, rows_affected))) rows_affected)))
{ {
if ((ret= event_queue->drop_event(thd, name))) if ((ret= event_queue->drop_event(thd, name)))
my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret); my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret);
@ -401,7 +413,7 @@ Events::show_create_event(THD *thd, sp_name *spn)
DBUG_PRINT("enter", ("name: %*s", spn->m_name.length, spn->m_name.str)); DBUG_PRINT("enter", ("name: %*s", spn->m_name.length, spn->m_name.str));
thd->reset_n_backup_open_tables_state(&backup); thd->reset_n_backup_open_tables_state(&backup);
ret= db_repository->find_event(thd, spn->m_db, spn->m_name, &et, NULL, thd->mem_root); ret= db_repository->find_event(thd, spn->m_db, spn->m_name, &et, NULL);
thd->restore_backup_open_tables_state(&backup); thd->restore_backup_open_tables_state(&backup);
if (!ret) if (!ret)
@ -472,7 +484,7 @@ Events::drop_schema_events(THD *thd, char *db)
DBUG_ENTER("evex_drop_db_events"); DBUG_ENTER("evex_drop_db_events");
DBUG_PRINT("enter", ("dropping events from %s", db)); DBUG_PRINT("enter", ("dropping events from %s", db));
ret= event_queue->drop_schema_events(thd, db_lex); event_queue->drop_schema_events(thd, db_lex);
ret= db_repository->drop_schema_events(thd, db_lex); ret= db_repository->drop_schema_events(thd, db_lex);
DBUG_RETURN(ret); DBUG_RETURN(ret);
@ -500,9 +512,8 @@ Events::init()
Event_db_repository *db_repo; Event_db_repository *db_repo;
DBUG_ENTER("Events::init"); DBUG_ENTER("Events::init");
db_repository->init_repository(); db_repository->init_repository();
event_queue->init(db_repository); event_queue->init_queue(db_repository, scheduler_ng);
event_queue->scheduler= scheduler_ng; scheduler_ng->init_scheduler(event_queue);
scheduler_ng->init(event_queue);
/* it should be an assignment! */ /* it should be an assignment! */
if (opt_event_scheduler) if (opt_event_scheduler)
@ -532,8 +543,9 @@ Events::deinit()
DBUG_ENTER("Events::deinit"); DBUG_ENTER("Events::deinit");
scheduler_ng->stop(); scheduler_ng->stop();
scheduler_ng->deinit(); scheduler_ng->deinit_scheduler();
event_queue->deinit();
event_queue->deinit_queue();
db_repository->deinit_repository(); db_repository->deinit_repository();
DBUG_VOID_RETURN; DBUG_VOID_RETURN;

View File

@ -75,7 +75,7 @@ public:
get_instance(); get_instance();
int int
create_event(THD *thd, Event_parse_data *parse_data, uint create_options, create_event(THD *thd, Event_parse_data *parse_data, bool if_exists,
uint *rows_affected); uint *rows_affected);
int int
@ -83,7 +83,7 @@ public:
uint *rows_affected); uint *rows_affected);
int int
drop_event(THD *thd, sp_name *name, bool drop_if_exists, uint *rows_affected); drop_event(THD *thd, sp_name *name, bool if_exists, uint *rows_affected);
int int
drop_schema_events(THD *thd, char *db); drop_schema_events(THD *thd, char *db);
@ -105,9 +105,9 @@ public:
int int
dump_internal_status(THD *thd); dump_internal_status(THD *thd);
Event_db_repository *db_repository;
Event_queue *event_queue; Event_queue *event_queue;
Event_scheduler_ng *scheduler_ng; Event_scheduler_ng *scheduler_ng;
Event_db_repository *db_repository;
private: private:
/* Singleton DP is used */ /* Singleton DP is used */

View File

@ -57,7 +57,6 @@
#include <myisam.h> #include <myisam.h>
#include <my_dir.h> #include <my_dir.h>
#include "event_scheduler.h"
#include "events.h" #include "events.h"
/* WITH_BERKELEY_STORAGE_ENGINE */ /* WITH_BERKELEY_STORAGE_ENGINE */
@ -3894,7 +3893,6 @@ bool
sys_var_event_scheduler::update(THD *thd, set_var *var) sys_var_event_scheduler::update(THD *thd, set_var *var)
{ {
int res; int res;
Event_scheduler *scheduler= Event_scheduler::get_instance();
/* here start the thread if not running. */ /* here start the thread if not running. */
DBUG_ENTER("sys_var_event_scheduler::update"); DBUG_ENTER("sys_var_event_scheduler::update");
if (Events::opt_event_scheduler == 0) if (Events::opt_event_scheduler == 0)
@ -3927,8 +3925,6 @@ sys_var_event_scheduler::update(THD *thd, set_var *var)
byte *sys_var_event_scheduler::value_ptr(THD *thd, enum_var_type type, byte *sys_var_event_scheduler::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base) LEX_STRING *base)
{ {
Event_scheduler *scheduler= Event_scheduler::get_instance();
if (Events::opt_event_scheduler == 0) if (Events::opt_event_scheduler == 0)
thd->sys_var_tmp.long_value= 0; thd->sys_var_tmp.long_value= 0;
else if (Events::get_instance()->is_started()) else if (Events::get_instance()->is_started())

View File

@ -3832,25 +3832,26 @@ end_with_restore_list:
case SQLCOM_CREATE_EVENT: case SQLCOM_CREATE_EVENT:
case SQLCOM_ALTER_EVENT: case SQLCOM_ALTER_EVENT:
{ {
uint rows_affected= 1; uint affected= 1;
DBUG_ASSERT(lex->event_parse_data); DBUG_ASSERT(lex->event_parse_data);
switch (lex->sql_command) { switch (lex->sql_command) {
case SQLCOM_CREATE_EVENT: case SQLCOM_CREATE_EVENT:
res= Events::get_instance()->create_event(thd, lex->event_parse_data, res= Events::get_instance()->
(uint) lex->create_info.options, create_event(thd, lex->event_parse_data,
&rows_affected); lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS,
&affected);
break; break;
case SQLCOM_ALTER_EVENT: case SQLCOM_ALTER_EVENT:
res= Events::get_instance()->update_event(thd, lex->event_parse_data, res= Events::get_instance()->
lex->spname, &rows_affected); update_event(thd, lex->event_parse_data, lex->spname, &affected);
break; break;
default:; default:;
} }
DBUG_PRINT("info", ("CREATE/ALTER/DROP returned error code=%d af_rows=%d", DBUG_PRINT("info",("DDL error code=%d affected=%d", res, affected));
res, rows_affected));
if (!res) if (!res)
send_ok(thd, rows_affected); send_ok(thd, affected);
/* Don't do it, if we are inside a SP */
if (!thd->spcont) if (!thd->spcont)
{ {
delete lex->sphead; delete lex->sphead;
@ -3867,8 +3868,7 @@ end_with_restore_list:
if (! lex->spname->m_db.str) if (! lex->spname->m_db.str)
{ {
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
res= true; goto error;
break;
} }
if (check_access(thd, EVENT_ACL, lex->spname->m_db.str, 0, 0, 0, if (check_access(thd, EVENT_ACL, lex->spname->m_db.str, 0, 0, 0,
is_schema_db(lex->spname->m_db.str))) is_schema_db(lex->spname->m_db.str)))
@ -3885,11 +3885,10 @@ end_with_restore_list:
res= Events::get_instance()->show_create_event(thd, lex->spname); res= Events::get_instance()->show_create_event(thd, lex->spname);
else else
{ {
uint rows_affected= 1; uint affected= 1;
if (!(res= Events::get_instance()->drop_event(thd, lex->spname, if (!(res= Events::get_instance()->
lex->drop_if_exists, drop_event(thd, lex->spname, lex->drop_if_exists, &affected)))
&rows_affected))) send_ok(thd, affected);
send_ok(thd, rows_affected);
} }
break; break;
} }