summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <dlenev@mockturtle.local>2006-09-14 23:53:35 +0400
committerunknown <dlenev@mockturtle.local>2006-09-14 23:53:35 +0400
commitf1e6d23944e48d8293d38acaccb91a423b1e6ac8 (patch)
treea407ae8ad0f336c756ce35ff39da6ccfac735913 /sql
parent6a490444a615b9fd8ee057f71c215175cd6eb4cc (diff)
parent2d8bd8767527864e4b9b2328e3d9b8d4bf676e4a (diff)
downloadmariadb-git-f1e6d23944e48d8293d38acaccb91a423b1e6ac8.tar.gz
Merge bk-internal.mysql.com:/home/bk/mysql-5.1
into mockturtle.local:/home/dlenev/src/mysql-5.1-rt-merge BitKeeper/etc/ignore: auto-union client/mysqltest.c: Auto merged libmysqld/Makefile.am: Auto merged mysql-test/mysql-test-run.pl: Auto merged sql/CMakeLists.txt: Auto merged sql/Makefile.am: Auto merged sql/mysqld.cc: Auto merged sql/set_var.cc: Auto merged sql/sql_class.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_lex.cc: Auto merged sql/sql_lex.h: Auto merged sql/share/errmsg.txt: Auto merged sql/sql_parse.cc: Auto merged sql/sql_show.cc: Auto merged sql/sql_yacc.yy: Auto merged
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt3
-rw-r--r--sql/Makefile.am8
-rw-r--r--sql/event_data_objects.cc (renamed from sql/event_timed.cc)1356
-rw-r--r--sql/event_data_objects.h285
-rw-r--r--sql/event_db_repository.cc979
-rw-r--r--sql/event_db_repository.h102
-rw-r--r--sql/event_queue.cc956
-rw-r--r--sql/event_queue.h121
-rw-r--r--sql/event_scheduler.cc2449
-rw-r--r--sql/event_scheduler.h212
-rw-r--r--sql/event_timed.h217
-rw-r--r--sql/events.cc1368
-rw-r--r--sql/events.h147
-rw-r--r--sql/events_priv.h79
-rw-r--r--sql/lex.h1
-rw-r--r--sql/mysqld.cc43
-rw-r--r--sql/set_var.cc58
-rw-r--r--sql/set_var.h6
-rw-r--r--sql/share/errmsg.txt4
-rw-r--r--sql/sp_head.cc3
-rw-r--r--sql/sql_class.cc1
-rw-r--r--sql/sql_db.cc2
-rw-r--r--sql/sql_lex.cc6
-rw-r--r--sql/sql_lex.h14
-rw-r--r--sql/sql_parse.cc118
-rw-r--r--sql/sql_show.cc185
-rw-r--r--sql/sql_show.h2
-rw-r--r--sql/sql_test.cc4
-rw-r--r--sql/sql_yacc.yy420
-rw-r--r--sql/table.cc44
-rw-r--r--sql/table.h6
31 files changed, 4453 insertions, 4746 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index a089278448c..8d97edd4652 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -50,7 +50,8 @@ ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.cc
sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc
sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc
time.cc tztime.cc uniques.cc unireg.cc item_xmlfunc.cc
- rpl_tblmap.cc sql_binlog.cc event_scheduler.cc event_timed.cc
+ rpl_tblmap.cc sql_binlog.cc event_scheduler.cc event_data_objects.cc
+ event_queue.cc event_db_repository.cc
sql_tablespace.cc events.cc ../sql-common/my_user.c
partition_info.cc rpl_injector.cc sql_locale.cc
${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
diff --git a/sql/Makefile.am b/sql/Makefile.am
index d1ebea45d0c..e472e6ae8f0 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -63,8 +63,9 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
tztime.h my_decimal.h\
sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \
parse_file.h sql_view.h sql_trigger.h \
- sql_array.h sql_cursor.h events.h events_priv.h \
- sql_plugin.h authors.h sql_partition.h event_timed.h \
+ sql_array.h sql_cursor.h events.h \
+ event_db_repository.h event_queue.h \
+ sql_plugin.h authors.h sql_partition.h event_data_objects.h \
partition_info.h partition_element.h event_scheduler.h \
contributors.h
mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
@@ -101,7 +102,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
tztime.cc my_time.c my_user.c my_decimal.cc\
sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \
sp_cache.cc parse_file.cc sql_trigger.cc \
- event_scheduler.cc events.cc event_timed.cc \
+ event_scheduler.cc event_data_objects.cc \
+ event_queue.cc event_db_repository.cc events.cc \
sql_plugin.cc sql_binlog.cc \
sql_builtin.cc sql_tablespace.cc partition_info.cc
diff --git a/sql/event_timed.cc b/sql/event_data_objects.cc
index 98369e0e055..6f865a2bcac 100644
--- a/sql/event_timed.cc
+++ b/sql/event_data_objects.cc
@@ -16,111 +16,63 @@
#define MYSQL_LEX 1
#include "mysql_priv.h"
-#include "events_priv.h"
#include "events.h"
-#include "event_timed.h"
+#include "event_data_objects.h"
+#include "event_db_repository.h"
#include "sp_head.h"
-/*
- Constructor
-
- SYNOPSIS
- Event_timed::Event_timed()
-*/
+#define EVEX_MAX_INTERVAL_VALUE 1000000000L
-Event_timed::Event_timed():in_spawned_thread(0),locked_by_thread_id(0),
- running(0), thread_id(0), status_changed(false),
- last_executed_changed(false), expression(0),
- created(0), modified(0),
- on_completion(Event_timed::ON_COMPLETION_DROP),
- status(Event_timed::ENABLED), sphead(0),
- sql_mode(0), body_begin(0), dropped(false),
- 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();
-}
+static bool
+event_change_security_context(THD *thd, LEX_STRING user, LEX_STRING host,
+ LEX_STRING db, Security_context *backup);
+static void
+event_restore_security_context(THD *thd, Security_context *backup);
/*
- Destructor
+ Returns a new instance
SYNOPSIS
- Event_timed::~Event_timed()
-*/
-
-Event_timed::~Event_timed()
-{
- deinit_mutexes();
-
- if (free_sphead_on_delete)
- 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
+ Event_parse_data::new_instance()
- SYNOPSIS
- Event_timed::is_running()
+ RETURN VALUE
+ Address or NULL in case of error
+
+ NOTE
+ Created on THD's mem_root
*/
-bool
-Event_timed::is_running()
+Event_parse_data *
+Event_parse_data::new_instance(THD *thd)
{
- bool ret;
-
- VOID(pthread_mutex_lock(&this->LOCK_running));
- ret= running;
- VOID(pthread_mutex_unlock(&this->LOCK_running));
-
- return ret;
+ return new (thd->mem_root) Event_parse_data;
}
/*
- Init all member variables
+ Constructor
SYNOPSIS
- Event_timed::init()
+ Event_parse_data::Event_parse_data()
*/
-void
-Event_timed::init()
+Event_parse_data::Event_parse_data()
+ :on_completion(ON_COMPLETION_DROP), status(ENABLED),
+ item_starts(NULL), item_ends(NULL), item_execute_at(NULL),
+ starts_null(TRUE), ends_null(TRUE), execute_at_null(TRUE),
+ item_expression(NULL), expression(0)
{
- DBUG_ENTER("Event_timed::init");
-
- dbname.str= name.str= body.str= comment.str= 0;
- dbname.length= name.length= body.length= comment.length= 0;
+ DBUG_ENTER("Event_parse_data::Event_parse_data");
+ /* Actually in the parser STARTS is always set */
set_zero_time(&starts, MYSQL_TIMESTAMP_DATETIME);
set_zero_time(&ends, MYSQL_TIMESTAMP_DATETIME);
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
- set_zero_time(&last_executed, MYSQL_TIMESTAMP_DATETIME);
- starts_null= ends_null= execute_at_null= TRUE;
-
- definer_user.str= definer_host.str= 0;
- definer_user.length= definer_host.length= 0;
- sql_mode= 0;
+ body.str= comment.str= NULL;
+ body.length= comment.length= 0;
DBUG_VOID_RETURN;
}
@@ -130,30 +82,25 @@ Event_timed::init()
Set a name of the event
SYNOPSIS
- Event_timed::init_name()
+ Event_parse_data::init_name()
thd THD
spn the name extracted in the parser
*/
void
-Event_timed::init_name(THD *thd, sp_name *spn)
+Event_parse_data::init_name(THD *thd, sp_name *spn)
{
- DBUG_ENTER("Event_timed::init_name");
- /* During parsing, we must use thd->mem_root */
- MEM_ROOT *root= thd->mem_root;
+ DBUG_ENTER("Event_parse_data::init_name");
/* We have to copy strings to get them into the right memroot */
dbname.length= spn->m_db.length;
- dbname.str= strmake_root(root, spn->m_db.str, spn->m_db.length);
+ dbname.str= thd->strmake(spn->m_db.str, spn->m_db.length);
name.length= spn->m_name.length;
- name.str= strmake_root(root, spn->m_name.str, spn->m_name.length);
+ name.str= thd->strmake(spn->m_name.str, spn->m_name.length);
if (spn->m_qname.length == 0)
spn->init_qname(thd);
- DBUG_PRINT("dbname", ("len=%d db=%s",dbname.length, dbname.str));
- DBUG_PRINT("name", ("len=%d name=%s",name.length, name.str));
-
DBUG_VOID_RETURN;
}
@@ -162,22 +109,22 @@ Event_timed::init_name(THD *thd, sp_name *spn)
Set body of the event - what should be executed.
SYNOPSIS
- Event_timed::init_body()
+ Event_parse_data::init_body()
thd THD
NOTE
The body is extracted by copying all data between the
start of the body set by another method and the current pointer in Lex.
-
+
Some questionable removal of characters is done in here, and that part
should be refactored when the parser is smarter.
*/
void
-Event_timed::init_body(THD *thd)
+Event_parse_data::init_body(THD *thd)
{
- DBUG_ENTER("Event_timed::init_body");
- DBUG_PRINT("info", ("body=[%s] body_begin=0x%ld end=0x%ld", body_begin,
+ DBUG_ENTER("Event_parse_data::init_body");
+ DBUG_PRINT("info", ("body=[%s] body_begin=0x%lx end=0x%lx", body_begin,
body_begin, thd->lex->ptr));
body.length= thd->lex->ptr - body_begin;
@@ -195,7 +142,7 @@ Event_timed::init_body(THD *thd)
continue;
}
- /*
+ /*
consume closing comments
This is arguably wrong, but it's the best we have until the parser is
@@ -212,7 +159,7 @@ Event_timed::init_body(THD *thd)
*/
if ((*(body_end - 1) == '*') && (*body_end == '/'))
{
- DBUG_PRINT("info", ("consumend one '*" "/' comment in the query '%s'",
+ DBUG_PRINT("info", ("consumend one '*" "/' comment in the query '%s'",
body_begin));
body.length-= 2;
body_end-= 2;
@@ -228,54 +175,58 @@ Event_timed::init_body(THD *thd)
++body_begin;
--body.length;
}
- body.str= strmake_root(thd->mem_root, (char *)body_begin, body.length);
+ body.str= thd->strmake((char *)body_begin, body.length);
DBUG_VOID_RETURN;
}
/*
- Set time for execution for one time events.
+ Sets time for execution for one-time event.
SYNOPSIS
- Event_timed::init_execute_at()
- expr when (datetime)
+ Event_parse_data::init_execute_at()
+ thd Thread
RETURN VALUE
- 0 OK
- EVEX_PARSE_ERROR fix_fields failed
- EVEX_BAD_PARAMS datetime is in the past
- ER_WRONG_VALUE wrong value for execute at
+ 0 OK
+ ER_WRONG_VALUE Wrong value for execute at (reported)
*/
int
-Event_timed::init_execute_at(THD *thd, Item *expr)
+Event_parse_data::init_execute_at(THD *thd)
{
my_bool not_used;
TIME ltime;
my_time_t t;
-
TIME time_tmp;
- DBUG_ENTER("Event_timed::init_execute_at");
- if (expr->fix_fields(thd, &expr))
- DBUG_RETURN(EVEX_PARSE_ERROR);
+ DBUG_ENTER("Event_parse_data::init_execute_at");
+
+ if (!item_execute_at)
+ DBUG_RETURN(0);
+
+ if (item_execute_at->fix_fields(thd, &item_execute_at))
+ goto wrong_value;
/* no starts and/or ends in case of execute_at */
DBUG_PRINT("info", ("starts_null && ends_null should be 1 is %d",
(starts_null && ends_null)));
DBUG_ASSERT(starts_null && ends_null);
-
+
/* let's check whether time is in the past */
- thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,
+ thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,
(my_time_t) thd->query_start());
- if ((not_used= expr->get_date(&ltime, TIME_NO_ZERO_DATE)))
- DBUG_RETURN(ER_WRONG_VALUE);
+ if ((not_used= item_execute_at->get_date(&ltime, TIME_NO_ZERO_DATE)))
+ goto wrong_value;
if (TIME_to_ulonglong_datetime(&ltime) <
TIME_to_ulonglong_datetime(&time_tmp))
- DBUG_RETURN(EVEX_BAD_PARAMS);
+ {
+ my_error(ER_EVENT_EXEC_TIME_IN_THE_PAST, MYF(0));
+ DBUG_RETURN(ER_WRONG_VALUE);
+ }
/*
This may result in a 1970-01-01 date if ltime is > 2037-xx-xx.
@@ -287,48 +238,64 @@ Event_timed::init_execute_at(THD *thd, Item *expr)
if (!t)
{
DBUG_PRINT("error", ("Execute AT after year 2037"));
- DBUG_RETURN(ER_WRONG_VALUE);
+ goto wrong_value;
}
execute_at_null= FALSE;
execute_at= ltime;
DBUG_RETURN(0);
+
+wrong_value:
+ report_bad_value("AT", item_execute_at);
+ DBUG_RETURN(ER_WRONG_VALUE);
}
/*
- Set time for execution for transient events.
+ Sets time for execution of multi-time event.s
SYNOPSIS
- Event_timed::init_interval()
- expr how much?
- new_interval what is the interval
+ Event_parse_data::init_interval()
+ thd Thread
RETURN VALUE
- 0 OK
- EVEX_PARSE_ERROR fix_fields failed
- EVEX_BAD_PARAMS Interval is not positive
- EVEX_MICROSECOND_UNSUP Microseconds are not supported.
+ 0 OK
+ EVEX_BAD_PARAMS Interval is not positive or MICROSECOND (reported)
+ ER_WRONG_VALUE Wrong value for interval (reported)
*/
int
-Event_timed::init_interval(THD *thd, Item *expr, interval_type new_interval)
+Event_parse_data::init_interval(THD *thd)
{
String value;
INTERVAL interval_tmp;
- DBUG_ENTER("Event_timed::init_interval");
+ DBUG_ENTER("Event_parse_data::init_interval");
+ if (!item_expression)
+ DBUG_RETURN(0);
- if (expr->fix_fields(thd, &expr))
- DBUG_RETURN(EVEX_PARSE_ERROR);
+ switch (interval) {
+ case INTERVAL_MINUTE_MICROSECOND:
+ case INTERVAL_HOUR_MICROSECOND:
+ case INTERVAL_DAY_MICROSECOND:
+ case INTERVAL_SECOND_MICROSECOND:
+ case INTERVAL_MICROSECOND:
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND");
+ DBUG_RETURN(EVEX_BAD_PARAMS);
+ default:
+ break;
+ }
+
+ if (item_expression->fix_fields(thd, &item_expression))
+ goto wrong_value;
value.alloc(MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN);
- if (get_interval_value(expr, new_interval, &value, &interval_tmp))
- DBUG_RETURN(EVEX_PARSE_ERROR);
+ if (get_interval_value(item_expression, interval, &value, &interval_tmp))
+ goto wrong_value;
expression= 0;
- switch (new_interval) {
+ switch (interval) {
case INTERVAL_YEAR:
expression= interval_tmp.year;
break;
@@ -366,44 +333,37 @@ Event_timed::init_interval(THD *thd, Item *expr, interval_type new_interval)
interval_tmp.minute)*60
+ interval_tmp.second;
break;
- case INTERVAL_MINUTE_MICROSECOND: /* day and hour are 0 */
- case INTERVAL_HOUR_MICROSECOND: /* day is anyway 0 */
- case INTERVAL_DAY_MICROSECOND:
- DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
- expression= ((((interval_tmp.day*24) + interval_tmp.hour)*60+
- interval_tmp.minute)*60 +
- interval_tmp.second) * 1000000L + interval_tmp.second_part;
- break;
case INTERVAL_HOUR_MINUTE:
expression= interval_tmp.hour * 60 + interval_tmp.minute;
break;
case INTERVAL_MINUTE_SECOND:
expression= interval_tmp.minute * 60 + interval_tmp.second;
break;
- case INTERVAL_SECOND_MICROSECOND:
- DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
- expression= interval_tmp.second * 1000000L + interval_tmp.second_part;
- break;
- case INTERVAL_MICROSECOND:
- DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
case INTERVAL_LAST:
DBUG_ASSERT(0);
+ default:
+ ;/* these are the microsec stuff */
}
if (interval_tmp.neg || expression > EVEX_MAX_INTERVAL_VALUE)
+ {
+ my_error(ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG, MYF(0));
DBUG_RETURN(EVEX_BAD_PARAMS);
+ }
- interval= new_interval;
DBUG_RETURN(0);
+
+wrong_value:
+ report_bad_value("INTERVAL", item_execute_at);
+ DBUG_RETURN(ER_WRONG_VALUE);
}
/*
- Set activation time.
+ Sets STARTS.
SYNOPSIS
- Event_timed::init_starts()
- expr how much?
- interval what is the interval
+ Event_parse_data::init_starts()
+ expr how much?
NOTES
Note that activation time is not execution time.
@@ -414,25 +374,26 @@ Event_timed::init_interval(THD *thd, Item *expr, interval_type new_interval)
same time.
RETURN VALUE
- 0 OK
- EVEX_PARSE_ERROR fix_fields failed
- EVEX_BAD_PARAMS starts before now
+ 0 OK
+ ER_WRONG_VALUE Starts before now
*/
int
-Event_timed::init_starts(THD *thd, Item *new_starts)
+Event_parse_data::init_starts(THD *thd)
{
my_bool not_used;
TIME ltime, time_tmp;
my_time_t t;
- DBUG_ENTER("Event_timed::init_starts");
+ DBUG_ENTER("Event_parse_data::init_starts");
+ if (!item_starts)
+ DBUG_RETURN(0);
- if (new_starts->fix_fields(thd, &new_starts))
- DBUG_RETURN(EVEX_PARSE_ERROR);
+ if (item_starts->fix_fields(thd, &item_starts))
+ goto wrong_value;
- if ((not_used= new_starts->get_date(&ltime, TIME_NO_ZERO_DATE)))
- DBUG_RETURN(EVEX_BAD_PARAMS);
+ if ((not_used= item_starts->get_date(&ltime, TIME_NO_ZERO_DATE)))
+ goto wrong_value;
/* Let's check whether time is in the past */
thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,
@@ -442,7 +403,7 @@ Event_timed::init_starts(THD *thd, Item *new_starts)
DBUG_PRINT("info",("starts=%lld", TIME_to_ulonglong_datetime(&ltime)));
if (TIME_to_ulonglong_datetime(&ltime) <
TIME_to_ulonglong_datetime(&time_tmp))
- DBUG_RETURN(EVEX_BAD_PARAMS);
+ goto wrong_value;
/*
This may result in a 1970-01-01 date if ltime is > 2037-xx-xx.
@@ -452,24 +413,24 @@ Event_timed::init_starts(THD *thd, Item *new_starts)
*/
my_tz_UTC->gmt_sec_to_TIME(&ltime,t=TIME_to_timestamp(thd, &ltime, &not_used));
if (!t)
- {
- DBUG_PRINT("error", ("STARTS after year 2037"));
- DBUG_RETURN(EVEX_BAD_PARAMS);
- }
+ goto wrong_value;
starts= ltime;
starts_null= FALSE;
DBUG_RETURN(0);
+
+wrong_value:
+ report_bad_value("STARTS", item_starts);
+ DBUG_RETURN(ER_WRONG_VALUE);
}
/*
- Set deactivation time.
+ Sets ENDS (deactivation time).
SYNOPSIS
- Event_timed::init_ends()
+ Event_parse_data::init_ends()
thd THD
- new_ends when?
NOTES
Note that activation time is not execution time.
@@ -481,26 +442,26 @@ Event_timed::init_starts(THD *thd, Item *new_starts)
RETURN VALUE
0 OK
- EVEX_PARSE_ERROR fix_fields failed
- ER_WRONG_VALUE starts distant date (after year 2037)
- EVEX_BAD_PARAMS ENDS before STARTS
+ EVEX_BAD_PARAMS Error (reported)
*/
int
-Event_timed::init_ends(THD *thd, Item *new_ends)
+Event_parse_data::init_ends(THD *thd)
{
TIME ltime, ltime_now;
my_bool not_used;
my_time_t t;
- DBUG_ENTER("Event_timed::init_ends");
+ DBUG_ENTER("Event_parse_data::init_ends");
+ if (!item_ends)
+ DBUG_RETURN(0);
- if (new_ends->fix_fields(thd, &new_ends))
- DBUG_RETURN(EVEX_PARSE_ERROR);
+ if (item_ends->fix_fields(thd, &item_ends))
+ goto error_bad_params;
DBUG_PRINT("info", ("convert to TIME"));
- if ((not_used= new_ends->get_date(&ltime, TIME_NO_ZERO_DATE)))
- DBUG_RETURN(EVEX_BAD_PARAMS);
+ if ((not_used= item_ends->get_date(&ltime, TIME_NO_ZERO_DATE)))
+ goto error_bad_params;
/*
This may result in a 1970-01-01 date if ltime is > 2037-xx-xx.
@@ -511,15 +472,12 @@ Event_timed::init_ends(THD *thd, Item *new_ends)
DBUG_PRINT("info", ("get the UTC time"));
my_tz_UTC->gmt_sec_to_TIME(&ltime,t=TIME_to_timestamp(thd, &ltime, &not_used));
if (!t)
- {
- DBUG_PRINT("error", ("ENDS after year 2037"));
- DBUG_RETURN(EVEX_BAD_PARAMS);
- }
+ goto error_bad_params;
/* Check whether ends is after starts */
DBUG_PRINT("info", ("ENDS after STARTS?"));
if (!starts_null && my_time_compare(&starts, &ltime) != -1)
- DBUG_RETURN(EVEX_BAD_PARAMS);
+ goto error_bad_params;
/*
The parser forces starts to be provided but one day STARTS could be
@@ -529,32 +487,65 @@ Event_timed::init_ends(THD *thd, Item *new_ends)
DBUG_PRINT("info", ("ENDS after NOW?"));
my_tz_UTC->gmt_sec_to_TIME(&ltime_now, thd->query_start());
if (my_time_compare(&ltime_now, &ltime) == 1)
- DBUG_RETURN(EVEX_BAD_PARAMS);
+ goto error_bad_params;
ends= ltime;
ends_null= FALSE;
DBUG_RETURN(0);
+
+error_bad_params:
+ my_error(ER_EVENT_ENDS_BEFORE_STARTS, MYF(0));
+ DBUG_RETURN(EVEX_BAD_PARAMS);
}
/*
- Sets comment.
+ Prints an error message about invalid value. Internally used
+ during input data verification
SYNOPSIS
- Event_timed::init_comment()
- thd THD - used for memory allocation
- comment the string.
+ Event_parse_data::report_bad_value()
+ item_name The name of the parameter
+ bad_item The parameter
*/
void
-Event_timed::init_comment(THD *thd, LEX_STRING *set_comment)
+Event_parse_data::report_bad_value(const char *item_name, Item *bad_item)
{
- DBUG_ENTER("Event_timed::init_comment");
+ char buff[120];
+ String str(buff,(uint32) sizeof(buff), system_charset_info);
+ String *str2= bad_item->fixed? bad_item->val_str(&str):NULL;
+ my_error(ER_WRONG_VALUE, MYF(0), item_name, str2? str2->c_ptr_safe():"NULL");
+}
- comment.str= strmake_root(thd->mem_root, set_comment->str,
- comment.length= set_comment->length);
- DBUG_VOID_RETURN;
+/*
+ Checks for validity the data gathered during the parsing phase.
+
+ SYNOPSIS
+ Event_parse_data::check_parse_data()
+ thd Thread
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error (reported)
+*/
+
+bool
+Event_parse_data::check_parse_data(THD *thd)
+{
+ bool ret;
+ DBUG_ENTER("Event_parse_data::check_parse_data");
+ DBUG_PRINT("info", ("execute_at=0x%lx expr=0x%lx starts=0x%lx ends=0x%lx",
+ item_execute_at, item_expression, item_starts, item_ends));
+
+ init_name(thd, identifier);
+
+ init_definer(thd);
+
+ ret= init_execute_at(thd) || init_interval(thd) || init_starts(thd) ||
+ init_ends(thd);
+ DBUG_RETURN(ret);
}
@@ -562,51 +553,235 @@ Event_timed::init_comment(THD *thd, LEX_STRING *set_comment)
Inits definer (definer_user and definer_host) during parsing.
SYNOPSIS
- Event_timed::init_definer()
-
- RETURN VALUE
- 0 OK
+ Event_parse_data::init_definer()
+ thd Thread
*/
-int
-Event_timed::init_definer(THD *thd)
+void
+Event_parse_data::init_definer(THD *thd)
{
- DBUG_ENTER("Event_timed::init_definer");
+ int definer_user_len;
+ int definer_host_len;
+ DBUG_ENTER("Event_parse_data::init_definer");
DBUG_PRINT("info",("init definer_user thd->mem_root=0x%lx "
"thd->sec_ctx->priv_user=0x%lx", thd->mem_root,
thd->security_ctx->priv_user));
- definer_user.str= strdup_root(thd->mem_root, thd->security_ctx->priv_user);
- definer_user.length= strlen(thd->security_ctx->priv_user);
- DBUG_PRINT("info",("init definer_host thd->s_c->priv_host=0x%lx",
- thd->security_ctx->priv_host));
- definer_host.str= strdup_root(thd->mem_root, thd->security_ctx->priv_host);
- definer_host.length= strlen(thd->security_ctx->priv_host);
+ definer_user_len= strlen(thd->security_ctx->priv_user);
+ definer_host_len= strlen(thd->security_ctx->priv_host);
+ /* + 1 for @ */
DBUG_PRINT("info",("init definer as whole"));
- definer.length= definer_user.length + definer_host.length + 1;
- definer.str= alloc_root(thd->mem_root, definer.length + 1);
+ definer.length= definer_user_len + definer_host_len + 1;
+ definer.str= thd->alloc(definer.length + 1);
DBUG_PRINT("info",("copy the user"));
- memcpy(definer.str, definer_user.str, definer_user.length);
- definer.str[definer_user.length]= '@';
+ memcpy(definer.str, thd->security_ctx->priv_user, definer_user_len);
+ definer.str[definer_user_len]= '@';
DBUG_PRINT("info",("copy the host"));
- memcpy(definer.str + definer_user.length + 1, definer_host.str,
- definer_host.length);
+ memcpy(definer.str + definer_user_len + 1, thd->security_ctx->priv_host,
+ definer_host_len);
definer.str[definer.length]= '\0';
- DBUG_PRINT("info",("definer initted"));
+ DBUG_PRINT("info",("definer [%s] initted", definer.str));
- DBUG_RETURN(0);
+ DBUG_VOID_RETURN;
}
/*
- Loads an event from a row from mysql.event
+ Constructor
SYNOPSIS
- Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
+ Event_basic::Event_basic()
+*/
+
+Event_basic::Event_basic()
+{
+ DBUG_ENTER("Event_basic::Event_basic");
+ /* init memory root */
+ init_alloc_root(&mem_root, 256, 512);
+ dbname.str= name.str= NULL;
+ dbname.length= name.length= 0;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Destructor
+
+ SYNOPSIS
+ Event_basic::Event_basic()
+*/
+
+Event_basic::~Event_basic()
+{
+ DBUG_ENTER("Event_basic::~Event_basic");
+ free_root(&mem_root, MYF(0));
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Short function to load a char column into a LEX_STRING
+
+ SYNOPSIS
+ Event_basic::load_string_field()
+ field_name The field( enum_events_table_field is not actually used
+ because it's unknown in event_data_objects.h)
+ fields The Field array
+ field_value The value
+*/
+
+bool
+Event_basic::load_string_fields(Field **fields, ...)
+{
+ bool ret= FALSE;
+ va_list args;
+ enum enum_events_table_field field_name;
+ LEX_STRING *field_value;
+
+ DBUG_ENTER("Event_basic::load_string_fields");
+
+ va_start(args, fields);
+ field_name= (enum enum_events_table_field) va_arg(args, int);
+ 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)
+ {
+ ret= TRUE;
+ break;
+ }
+ field_value->length= strlen(field_value->str);
+
+ field_name= (enum enum_events_table_field) va_arg(args, int);
+ }
+ va_end(args);
+
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ Constructor
+
+ SYNOPSIS
+ Event_queue_element::Event_queue_element()
+*/
+
+Event_queue_element::Event_queue_element():
+ status_changed(FALSE), last_executed_changed(FALSE),
+ on_completion(ON_COMPLETION_DROP), status(ENABLED),
+ expression(0), dropped(FALSE), execution_count(0)
+{
+ DBUG_ENTER("Event_queue_element::Event_queue_element");
+
+ set_zero_time(&starts, MYSQL_TIMESTAMP_DATETIME);
+ set_zero_time(&ends, MYSQL_TIMESTAMP_DATETIME);
+ set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
+ set_zero_time(&last_executed, MYSQL_TIMESTAMP_DATETIME);
+ starts_null= ends_null= execute_at_null= TRUE;
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Destructor
+
+ SYNOPSIS
+ Event_queue_element::Event_queue_element()
+*/
+Event_queue_element::~Event_queue_element()
+{
+}
+
+
+/*
+ Constructor
+
+ SYNOPSIS
+ Event_timed::Event_timed()
+*/
+
+Event_timed::Event_timed():
+ created(0), modified(0), sql_mode(0)
+{
+ DBUG_ENTER("Event_timed::Event_timed");
+ init();
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Destructor
+
+ SYNOPSIS
+ Event_timed::~Event_timed()
+*/
+
+Event_timed::~Event_timed()
+{
+}
+
+
+/*
+ Constructor
+
+ SYNOPSIS
+ Event_job_data::Event_job_data()
+*/
+
+Event_job_data::Event_job_data()
+ :thd(NULL), sphead(NULL), sql_mode(0)
+{
+}
+
+
+/*
+ Destructor
+
+ SYNOPSIS
+ Event_timed::~Event_timed()
+*/
+
+Event_job_data::~Event_job_data()
+{
+ DBUG_ENTER("Event_job_data::~Event_job_data");
+ delete sphead;
+ sphead= NULL;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Init all member variables
+
+ SYNOPSIS
+ Event_timed::init()
+*/
+
+void
+Event_timed::init()
+{
+ DBUG_ENTER("Event_timed::init");
+
+ definer_user.str= definer_host.str= body.str= comment.str= NULL;
+ definer_user.length= definer_host.length= body.length= comment.length= 0;
+
+ sql_mode= 0;
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Loads an event's body from a row from mysql.event
+
+ SYNOPSIS
+ Event_job_data::load_from_row(MEM_ROOT *mem_root, TABLE *table)
RETURN VALUE
0 OK
@@ -619,124 +794,185 @@ Event_timed::init_definer(THD *thd)
*/
int
-Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
+Event_job_data::load_from_row(TABLE *table)
{
char *ptr;
- Event_timed *et;
uint len;
- bool res1, res2;
-
- DBUG_ENTER("Event_timed::load_from_row");
+ DBUG_ENTER("Event_job_data::load_from_row");
if (!table)
goto error;
- et= this;
-
- if (table->s->fields != Events::FIELD_COUNT)
+ if (table->s->fields != ET_FIELD_COUNT)
goto error;
- if ((et->dbname.str= get_field(mem_root,
- table->field[Events::FIELD_DB])) == NULL)
- goto error;
+ load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name,
+ ET_FIELD_BODY, &body, ET_FIELD_DEFINER, &definer,
+ ET_FIELD_COUNT);
- et->dbname.length= strlen(et->dbname.str);
+ ptr= strchr(definer.str, '@');
- if ((et->name.str= get_field(mem_root,
- table->field[Events::FIELD_NAME])) == NULL)
- goto error;
+ if (! ptr)
+ ptr= definer.str;
- et->name.length= strlen(et->name.str);
+ len= ptr - definer.str;
+ definer_user.str= strmake_root(&mem_root, definer.str, len);
+ definer_user.length= len;
+ len= definer.length - len - 1;
+ /* 1:because of @ */
+ definer_host.str= strmake_root(&mem_root, ptr + 1, len);
+ definer_host.length= len;
- if ((et->body.str= get_field(mem_root,
- table->field[Events::FIELD_BODY])) == NULL)
- goto error;
+ sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
+
+ DBUG_RETURN(0);
+error:
+ DBUG_RETURN(EVEX_GET_FIELD_FAILED);
+}
+
+
+/*
+ Loads an event from a row from mysql.event
- et->body.length= strlen(et->body.str);
+ SYNOPSIS
+ Event_queue_element::load_from_row(MEM_ROOT *mem_root, TABLE *table)
+
+ RETURN VALUE
+ 0 OK
+ EVEX_GET_FIELD_FAILED Error
- if ((et->definer.str= get_field(mem_root,
- table->field[Events::FIELD_DEFINER])) == NullS)
+ NOTES
+ 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
+Event_queue_element::load_from_row(TABLE *table)
+{
+ char *ptr;
+ bool res1, res2;
+
+ DBUG_ENTER("Event_queue_element::load_from_row");
+
+ if (!table)
goto error;
- et->definer.length= strlen(et->definer.str);
- ptr= strchr(et->definer.str, '@');
+ if (table->s->fields != ET_FIELD_COUNT)
+ goto error;
- if (! ptr)
- ptr= et->definer.str;
+ load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name,
+ ET_FIELD_DEFINER, &definer, ET_FIELD_COUNT);
- len= ptr - et->definer.str;
+ starts_null= table->field[ET_FIELD_STARTS]->is_null();
+ res1= table->field[ET_FIELD_STARTS]->get_date(&starts, TIME_NO_ZERO_DATE);
- et->definer_user.str= strmake_root(mem_root, et->definer.str, len);
- et->definer_user.length= len;
- len= et->definer.length - len - 1; //1 is because of @
- et->definer_host.str= strmake_root(mem_root, ptr + 1, len);/* 1:because of @*/
- et->definer_host.length= len;
-
- et->starts_null= table->field[Events::FIELD_STARTS]->is_null();
- res1= table->field[Events::FIELD_STARTS]->
- get_date(&et->starts,TIME_NO_ZERO_DATE);
+ ends_null= table->field[ET_FIELD_ENDS]->is_null();
+ res2= table->field[ET_FIELD_ENDS]->get_date(&ends, TIME_NO_ZERO_DATE);
- et->ends_null= table->field[Events::FIELD_ENDS]->is_null();
- res2= table->field[Events::FIELD_ENDS]->get_date(&et->ends, TIME_NO_ZERO_DATE);
-
- if (!table->field[Events::FIELD_INTERVAL_EXPR]->is_null())
- et->expression= table->field[Events::FIELD_INTERVAL_EXPR]->val_int();
+ if (!table->field[ET_FIELD_INTERVAL_EXPR]->is_null())
+ expression= table->field[ET_FIELD_INTERVAL_EXPR]->val_int();
else
- et->expression= 0;
+ expression= 0;
/*
- If res1 and res2 are true then both fields are empty.
- Hence if Events::FIELD_EXECUTE_AT is empty there is an error.
+ If res1 and res2 are TRUE then both fields are empty.
+ Hence, if ET_FIELD_EXECUTE_AT is empty there is an error.
*/
- et->execute_at_null=
- table->field[Events::FIELD_EXECUTE_AT]->is_null();
- DBUG_ASSERT(!(et->starts_null && et->ends_null && !et->expression &&
- et->execute_at_null));
- if (!et->expression &&
- table->field[Events::FIELD_EXECUTE_AT]-> get_date(&et->execute_at,
- TIME_NO_ZERO_DATE))
+ execute_at_null= table->field[ET_FIELD_EXECUTE_AT]->is_null();
+ DBUG_ASSERT(!(starts_null && ends_null && !expression && execute_at_null));
+ if (!expression &&
+ table->field[ET_FIELD_EXECUTE_AT]->get_date(&execute_at,
+ TIME_NO_ZERO_DATE))
goto error;
/*
In DB the values start from 1 but enum interval_type starts
from 0
*/
- if (!table->field[Events::FIELD_TRANSIENT_INTERVAL]->is_null())
- et->interval= (interval_type) ((ulonglong)
- table->field[Events::FIELD_TRANSIENT_INTERVAL]->val_int() - 1);
+ if (!table->field[ET_FIELD_TRANSIENT_INTERVAL]->is_null())
+ interval= (interval_type) ((ulonglong)
+ table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_int() - 1);
else
- et->interval= (interval_type) 0;
+ interval= (interval_type) 0;
+
+ table->field[ET_FIELD_LAST_EXECUTED]->get_date(&last_executed,
+ TIME_NO_ZERO_DATE);
+ last_executed_changed= FALSE;
- et->created= table->field[Events::FIELD_CREATED]->val_int();
- et->modified= table->field[Events::FIELD_MODIFIED]->val_int();
- table->field[Events::FIELD_LAST_EXECUTED]->
- get_date(&et->last_executed, TIME_NO_ZERO_DATE);
+ if ((ptr= get_field(&mem_root, table->field[ET_FIELD_STATUS])) == NullS)
+ goto error;
- last_executed_changed= false;
+ DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", name.str, ptr));
+ status= (ptr[0]=='E'? Event_queue_element::ENABLED:
+ Event_queue_element::DISABLED);
/* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
- if ((ptr= get_field(mem_root, table->field[Events::FIELD_STATUS])) == NullS)
+ if ((ptr= get_field(&mem_root,
+ table->field[ET_FIELD_ON_COMPLETION])) == NullS)
goto error;
- DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->name.str, ptr));
- et->status= (ptr[0]=='E'? Event_timed::ENABLED:Event_timed::DISABLED);
+ on_completion= (ptr[0]=='D'? Event_queue_element::ON_COMPLETION_DROP:
+ Event_queue_element::ON_COMPLETION_PRESERVE);
- /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
- if ((ptr= get_field(mem_root,
- table->field[Events::FIELD_ON_COMPLETION])) == NullS)
+ DBUG_RETURN(0);
+error:
+ DBUG_RETURN(EVEX_GET_FIELD_FAILED);
+}
+
+
+/*
+ Loads an event from a row from mysql.event
+
+ SYNOPSIS
+ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
+
+ RETURN VALUE
+ 0 OK
+ EVEX_GET_FIELD_FAILED Error
+
+ NOTES
+ 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
+Event_timed::load_from_row(TABLE *table)
+{
+ char *ptr;
+ uint len;
+
+ DBUG_ENTER("Event_timed::load_from_row");
+
+ if (Event_queue_element::load_from_row(table))
goto error;
- et->on_completion= (ptr[0]=='D'? Event_timed::ON_COMPLETION_DROP:
- Event_timed::ON_COMPLETION_PRESERVE);
+ load_string_fields(table->field, ET_FIELD_BODY, &body, ET_FIELD_COUNT);
- et->comment.str= get_field(mem_root, table->field[Events::FIELD_COMMENT]);
- if (et->comment.str != NullS)
- et->comment.length= strlen(et->comment.str);
+ ptr= strchr(definer.str, '@');
+
+ if (! ptr)
+ ptr= definer.str;
+
+ len= ptr - definer.str;
+ definer_user.str= strmake_root(&mem_root, definer.str, len);
+ definer_user.length= len;
+ len= definer.length - len - 1;
+ /* 1:because of @ */
+ definer_host.str= strmake_root(&mem_root, ptr + 1, len);
+ definer_host.length= len;
+
+ created= table->field[ET_FIELD_CREATED]->val_int();
+ modified= table->field[ET_FIELD_MODIFIED]->val_int();
+
+ comment.str= get_field(&mem_root, table->field[ET_FIELD_COMMENT]);
+ if (comment.str != NullS)
+ comment.length= strlen(comment.str);
else
- et->comment.length= 0;
-
+ comment.length= 0;
- et->sql_mode= (ulong) table->field[Events::FIELD_SQL_MODE]->val_int();
+ sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
DBUG_RETURN(0);
error:
@@ -755,7 +991,7 @@ error:
time_now current time
i_value quantity of time type interval to add
i_type type of interval to add (SECOND, MINUTE, HOUR, WEEK ...)
-
+
RETURN VALUE
0 OK
1 Error
@@ -835,7 +1071,7 @@ bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec,
{
longlong seconds_diff;
long microsec_diff;
-
+
if (calc_time_diff(time_now, start, 1, &seconds_diff, &microsec_diff))
{
DBUG_PRINT("error", ("negative difference"));
@@ -877,20 +1113,22 @@ bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec,
interval.month= (diff_months / months)*months;
/*
Check if the same month as last_exec (always set - prerequisite)
- An event happens at most once per month so there is no way to schedule
- it two times for the current month. This saves us from two calls to
- date_add_interval() if the event was just executed. But if the scheduler
- is started and there was at least 1 scheduled date skipped this one does
- not help and two calls to date_add_interval() will be done, which is a
- bit more expensive but compared to the rareness of the case is neglectable.
+ An event happens at most once per month so there is no way to
+ schedule it two times for the current month. This saves us from two
+ calls to date_add_interval() if the event was just executed. But if
+ the scheduler is started and there was at least 1 scheduled date
+ skipped this one does not help and two calls to date_add_interval()
+ will be done, which is a bit more expensive but compared to the
+ rareness of the case is neglectable.
*/
- if (time_now->year==last_exec->year && time_now->month==last_exec->month)
+ if (time_now->year == last_exec->year &&
+ time_now->month == last_exec->month)
interval.month+= months;
tmp= *start;
if ((ret= date_add_interval(&tmp, INTERVAL_MONTH, interval)))
goto done;
-
+
/* If `tmp` is still before time_now just add one more time the interval */
if (my_time_compare(&tmp, time_now) == -1)
{
@@ -914,7 +1152,7 @@ done:
Computes next execution time.
SYNOPSIS
- Event_timed::compute_next_execution_time()
+ Event_queue_element::compute_next_execution_time()
RETURN VALUE
FALSE OK
@@ -926,18 +1164,18 @@ done:
*/
bool
-Event_timed::compute_next_execution_time()
+Event_queue_element::compute_next_execution_time()
{
TIME time_now;
int tmp;
- DBUG_ENTER("Event_timed::compute_next_execution_time");
- DBUG_PRINT("enter", ("starts=%llu ends=%llu last_executed=%llu",
+ DBUG_ENTER("Event_queue_element::compute_next_execution_time");
+ DBUG_PRINT("enter", ("starts=%llu ends=%llu last_executed=%llu this=0x%lx",
TIME_to_ulonglong_datetime(&starts),
TIME_to_ulonglong_datetime(&ends),
- TIME_to_ulonglong_datetime(&last_executed)));
+ TIME_to_ulonglong_datetime(&last_executed), this));
- if (status == Event_timed::DISABLED)
+ if (status == Event_queue_element::DISABLED)
{
DBUG_PRINT("compute_next_execution_time",
("Event %s is DISABLED", name.str));
@@ -951,11 +1189,11 @@ Event_timed::compute_next_execution_time()
{
DBUG_PRINT("info",("One-time event %s.%s of was already executed",
dbname.str, name.str, definer.str));
- dropped= (on_completion == Event_timed::ON_COMPLETION_DROP);
+ dropped= (on_completion == Event_queue_element::ON_COMPLETION_DROP);
DBUG_PRINT("info",("One-time event will be dropped=%d.", dropped));
- status= Event_timed::DISABLED;
- status_changed= true;
+ status= Event_queue_element::DISABLED;
+ status_changed= TRUE;
}
goto ret;
}
@@ -971,11 +1209,11 @@ Event_timed::compute_next_execution_time()
/* time_now is after ends. don't execute anymore */
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
execute_at_null= TRUE;
- if (on_completion == Event_timed::ON_COMPLETION_DROP)
- dropped= true;
+ if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
+ dropped= TRUE;
DBUG_PRINT("info", ("Dropped=%d", dropped));
- status= Event_timed::DISABLED;
- status_changed= true;
+ status= Event_queue_element::DISABLED;
+ status_changed= TRUE;
goto ret;
}
@@ -1037,10 +1275,10 @@ Event_timed::compute_next_execution_time()
/* Next execution after ends. No more executions */
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
execute_at_null= TRUE;
- if (on_completion == Event_timed::ON_COMPLETION_DROP)
- dropped= true;
- status= Event_timed::DISABLED;
- status_changed= true;
+ if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
+ dropped= TRUE;
+ status= Event_queue_element::DISABLED;
+ status_changed= TRUE;
}
else
{
@@ -1051,7 +1289,7 @@ Event_timed::compute_next_execution_time()
}
goto ret;
}
- else if (starts_null && ends_null)
+ else if (starts_null && ends_null)
{
/* starts is always set, so this is a dead branch !! */
DBUG_PRINT("info", ("Neither STARTS nor ENDS are set"));
@@ -1095,7 +1333,7 @@ Event_timed::compute_next_execution_time()
{
TIME next_exec;
- if (get_next_time(&next_exec, &starts, &time_now,
+ if (get_next_time(&next_exec, &starts, &time_now,
last_executed.year? &last_executed:&starts,
expression, interval))
goto err;
@@ -1130,10 +1368,10 @@ Event_timed::compute_next_execution_time()
DBUG_PRINT("info", ("Next execution after ENDS. Stop executing."));
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
execute_at_null= TRUE;
- status= Event_timed::DISABLED;
- status_changed= true;
- if (on_completion == Event_timed::ON_COMPLETION_DROP)
- dropped= true;
+ status= Event_queue_element::DISABLED;
+ status_changed= TRUE;
+ if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
+ dropped= TRUE;
}
else
{
@@ -1147,11 +1385,12 @@ Event_timed::compute_next_execution_time()
goto ret;
}
ret:
- DBUG_PRINT("info", ("ret=0"));
- DBUG_RETURN(false);
+ DBUG_PRINT("info", ("ret=0 execute_at=%llu",
+ TIME_to_ulonglong_datetime(&execute_at)));
+ DBUG_RETURN(FALSE);
err:
DBUG_PRINT("info", ("ret=1"));
- DBUG_RETURN(true);
+ DBUG_RETURN(TRUE);
}
@@ -1160,12 +1399,12 @@ err:
time according to thd->query_start(), so the THD's clock.
SYNOPSIS
- Event_timed::drop()
+ Event_queue_element::mark_last_executed()
thd thread context
*/
void
-Event_timed::mark_last_executed(THD *thd)
+Event_queue_element::mark_last_executed(THD *thd)
{
TIME time_now;
@@ -1173,7 +1412,9 @@ Event_timed::mark_last_executed(THD *thd)
my_tz_UTC->gmt_sec_to_TIME(&time_now, (my_time_t) thd->query_start());
last_executed= time_now; /* was execute_at */
- last_executed_changed= true;
+ last_executed_changed= TRUE;
+
+ execution_count++;
}
@@ -1181,7 +1422,7 @@ Event_timed::mark_last_executed(THD *thd)
Drops the event
SYNOPSIS
- Event_timed::drop()
+ Event_queue_element::drop()
thd thread context
RETURN VALUE
@@ -1194,12 +1435,12 @@ Event_timed::mark_last_executed(THD *thd)
*/
int
-Event_timed::drop(THD *thd)
+Event_queue_element::drop(THD *thd)
{
- uint tmp= 0;
- DBUG_ENTER("Event_timed::drop");
+ DBUG_ENTER("Event_queue_element::drop");
- DBUG_RETURN(db_drop_event(thd, this, false, &tmp));
+ DBUG_RETURN(Events::get_instance()->
+ drop_event(thd, dbname, name, FALSE, TRUE));
}
@@ -1207,26 +1448,24 @@ Event_timed::drop(THD *thd)
Saves status and last_executed_at to the disk if changed.
SYNOPSIS
- Event_timed::update_fields()
+ Event_queue_element::update_timing_fields()
thd - thread context
RETURN VALUE
- 0 OK
- EVEX_OPEN_TABLE_FAILED Error while opening mysql.event for writing
- EVEX_WRITE_ROW_FAILED On error to write to disk
-
- others return code from SE in case deletion of the event
- row failed.
+ FALSE OK
+ TRUE Error while opening mysql.event for writing or during
+ write on disk
*/
bool
-Event_timed::update_fields(THD *thd)
+Event_queue_element::update_timing_fields(THD *thd)
{
TABLE *table;
+ Field **fields;
Open_tables_state backup;
- int ret;
+ int ret= FALSE;
- DBUG_ENTER("Event_timed::update_time_fields");
+ DBUG_ENTER("Event_queue_element::update_timing_fields");
DBUG_PRINT("enter", ("name: %*s", name.length, name.str));
@@ -1236,14 +1475,14 @@ Event_timed::update_fields(THD *thd)
thd->reset_n_backup_open_tables_state(&backup);
- if (Events::open_event_table(thd, TL_WRITE, &table))
+ if (Events::get_instance()->open_event_table(thd, TL_WRITE, &table))
{
- ret= EVEX_OPEN_TABLE_FAILED;
+ ret= TRUE;
goto done;
}
-
-
- if ((ret= evex_db_find_event_by_name(thd, dbname, name, table)))
+ fields= table->field;
+ if ((ret= Events::get_instance()->db_repository->
+ find_named_event(thd, dbname, name, table)))
goto done;
store_record(table,record[1]);
@@ -1252,20 +1491,20 @@ Event_timed::update_fields(THD *thd)
if (last_executed_changed)
{
- table->field[Events::FIELD_LAST_EXECUTED]->set_notnull();
- table->field[Events::FIELD_LAST_EXECUTED]->store_time(&last_executed,
+ fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
+ fields[ET_FIELD_LAST_EXECUTED]->store_time(&last_executed,
MYSQL_TIMESTAMP_DATETIME);
- last_executed_changed= false;
+ last_executed_changed= FALSE;
}
if (status_changed)
{
- table->field[Events::FIELD_STATUS]->set_notnull();
- table->field[Events::FIELD_STATUS]->store((longlong)status, true);
- status_changed= false;
+ 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= EVEX_WRITE_ROW_FAILED;
+ if ((table->file->ha_update_row(table->record[1], table->record[0])))
+ ret= TRUE;
done:
close_thread_tables(thd);
@@ -1294,8 +1533,8 @@ int
Event_timed::get_create_event(THD *thd, String *buf)
{
int multipl= 0;
- char tmp_buff[128];
- String expr_buf(tmp_buff, sizeof(tmp_buff), system_charset_info);
+ char tmp_buf[2 * STRING_BUFFER_USUAL_SIZE];
+ String expr_buf(tmp_buf, sizeof(tmp_buf), system_charset_info);
expr_buf.length(0);
DBUG_ENTER("get_create_event");
@@ -1308,10 +1547,9 @@ Event_timed::get_create_event(THD *thd, String *buf)
buf->append(STRING_WITH_LEN("CREATE EVENT "));
append_identifier(thd, buf, name.str, name.length);
- buf->append(STRING_WITH_LEN(" ON SCHEDULE "));
if (expression)
{
- buf->append(STRING_WITH_LEN("EVERY "));
+ buf->append(STRING_WITH_LEN(" ON SCHEDULE EVERY "));
buf->append(expr_buf);
buf->append(' ');
LEX_STRING *ival= &interval_type_to_name[interval];
@@ -1320,7 +1558,7 @@ Event_timed::get_create_event(THD *thd, String *buf)
else
{
char dtime_buff[20*2+32];/* +32 to make my_snprintf_{8bit|ucs2} happy */
- buf->append(STRING_WITH_LEN("AT '"));
+ buf->append(STRING_WITH_LEN(" ON SCHEDULE AT '"));
/*
Pass the buffer and the second param tells fills the buffer and
returns the number of chars to copy.
@@ -1352,46 +1590,64 @@ Event_timed::get_create_event(THD *thd, String *buf)
/*
+ Get SHOW CREATE EVENT as string
+
+ SYNOPSIS
+ Event_job_data::get_create_event(THD *thd, String *buf)
+ thd Thread
+ buf String*, should be already allocated. CREATE EVENT goes inside.
+
+ RETURN VALUE
+ 0 OK
+ EVEX_MICROSECOND_UNSUP Error (for now if mysql.event has been
+ tampered and MICROSECONDS interval or
+ derivative has been put there.
+*/
+
+int
+Event_job_data::get_fake_create_event(THD *thd, String *buf)
+{
+ DBUG_ENTER("Event_job_data::get_create_event");
+ buf->append(STRING_WITH_LEN("CREATE EVENT anonymous ON SCHEDULE "
+ "EVERY 3337 HOUR DO "));
+ buf->append(body.str, body.length);
+
+ DBUG_RETURN(0);
+}
+
+
+/*
Executes the event (the underlying sp_head object);
SYNOPSIS
- evex_fill_row()
+ Event_job_data::execute()
thd THD
- mem_root If != NULL use it to compile the event on it
RETURN VALUE
0 success
-99 No rights on this.dbname.str
- -100 event in execution (parallel execution is impossible)
others retcodes of sp_head::execute_procedure()
*/
int
-Event_timed::execute(THD *thd, MEM_ROOT *mem_root)
+Event_job_data::execute(THD *thd)
{
+ Security_context save_ctx;
/* this one is local and not needed after exec */
- Security_context security_ctx;
int ret= 0;
- DBUG_ENTER("Event_timed::execute");
- DBUG_PRINT("info", (" EVEX EXECUTING event %s.%s [EXPR:%d]",
- dbname.str, name.str, (int) expression));
-
- VOID(pthread_mutex_lock(&this->LOCK_running));
- if (running)
- {
- VOID(pthread_mutex_unlock(&this->LOCK_running));
- DBUG_RETURN(-100);
- }
- running= true;
- VOID(pthread_mutex_unlock(&this->LOCK_running));
+ DBUG_ENTER("Event_job_data::execute");
+ DBUG_PRINT("info", ("EXECUTING %s.%s", dbname.str, name.str));
- if (!sphead && (ret= compile(thd, mem_root)))
+ if ((ret= compile(thd, NULL)))
goto done;
+
+ event_change_security_context(thd, definer_user, definer_host, dbname,
+ &save_ctx);
/*
- THD::~THD will clean this or if there is DROP DATABASE in the SP then
- it will be free there. It should not point to our buffer which is allocated
- on a mem_root.
+ THD::~THD will clean this or if there is DROP DATABASE in the
+ SP then it will be free there. It should not point to our buffer
+ which is allocated on a mem_root.
*/
thd->db= my_strdup(dbname.str, MYF(0));
thd->db_length= dbname.length;
@@ -1412,41 +1668,14 @@ Event_timed::execute(THD *thd, MEM_ROOT *mem_root)
ret= -99;
}
- VOID(pthread_mutex_lock(&this->LOCK_running));
- running= false;
- /* Will compile every time a new sp_head on different root */
- free_sp();
- VOID(pthread_mutex_unlock(&this->LOCK_running));
-
+ event_restore_security_context(thd, &save_ctx);
done:
- /*
- 1. Don't cache sphead if allocated on another mem_root
- 2. Don't call security_ctx.destroy() because this will free our dbname.str
- name.str and definer.str
- */
- if (mem_root && sphead)
- {
- delete sphead;
- sphead= 0;
- }
- DBUG_PRINT("info", (" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d",
- dbname.str, name.str, (int) expression, ret));
+ thd->end_statement();
+ thd->cleanup_after_query();
- DBUG_RETURN(ret);
-}
+ DBUG_PRINT("info", ("EXECUTED %s.%s ret=%d", dbname.str, name.str, ret));
-
-/*
- Frees the memory of the sp_head object we hold
- SYNOPSIS
- Event_timed::free_sp()
-*/
-
-void
-Event_timed::free_sp()
-{
- delete sphead;
- sphead= 0;
+ DBUG_RETURN(ret);
}
@@ -1455,7 +1684,7 @@ Event_timed::free_sp()
sp_head object held by the event
SYNOPSIS
- Event_timed::compile()
+ Event_job_data::compile()
thd thread context, used for memory allocation mostly
mem_root if != NULL then this memory root is used for allocs
instead of thd->mem_root
@@ -1467,7 +1696,7 @@ Event_timed::free_sp()
*/
int
-Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
+Event_job_data::compile(THD *thd, MEM_ROOT *mem_root)
{
int ret= 0;
MEM_ROOT *tmp_mem_root= 0;
@@ -1477,22 +1706,19 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
char *old_query;
uint old_query_len;
ulong old_sql_mode= thd->variables.sql_mode;
- char create_buf[2048];
+ char create_buf[15 * STRING_BUFFER_USUAL_SIZE];
String show_create(create_buf, sizeof(create_buf), system_charset_info);
CHARSET_INFO *old_character_set_client,
*old_collation_connection,
*old_character_set_results;
- Security_context *save_ctx;
- /* this one is local and not needed after exec */
- Security_context security_ctx;
+ Security_context save_ctx;
- DBUG_ENTER("Event_timed::compile");
+ DBUG_ENTER("Event_job_data::compile");
show_create.length(0);
- switch (get_create_event(thd, &show_create)) {
+ switch (get_fake_create_event(thd, &show_create)) {
case EVEX_MICROSECOND_UNSUP:
- sql_print_error("Scheduler");
DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
case 0:
break;
@@ -1526,15 +1752,14 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
thd->db= dbname.str;
thd->db_length= dbname.length;
- thd->query= show_create.c_ptr();
+ thd->query= show_create.c_ptr_safe();
thd->query_length= show_create.length();
DBUG_PRINT("info", ("query:%s",thd->query));
- change_security_context(thd, definer_user, definer_host, dbname,
- &security_ctx, &save_ctx);
+ event_change_security_context(thd, definer_user, definer_host, dbname,
+ &save_ctx);
thd->lex= &lex;
- lex_start(thd, (uchar*)thd->query, thd->query_length);
- lex.et_compile_phase= TRUE;
+ mysql_init_query(thd, (uchar*) thd->query, thd->query_length);
if (MYSQLparse((void *)thd) || thd->is_fatal_error)
{
DBUG_PRINT("error", ("error during compile or thd->is_fatal_error=%d",
@@ -1543,33 +1768,28 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
Free lex associated resources
QQ: Do we really need all this stuff here?
*/
- sql_print_error("error during compile of %s.%s or thd->is_fatal_error=%d",
+ sql_print_error("SCHEDULER: Error during compilation of %s.%s or "
+ "thd->is_fatal_error=%d",
dbname.str, name.str, thd->is_fatal_error);
- if (lex.sphead)
- {
- if (&lex != thd->lex)
- thd->lex->sphead->restore_lex(thd);
- delete lex.sphead;
- lex.sphead= 0;
- }
+
+ lex.unit.cleanup();
+ delete lex.sphead;
+ sphead= lex.sphead= NULL;
ret= EVEX_COMPILE_ERROR;
goto done;
}
DBUG_PRINT("note", ("success compiling %s.%s", dbname.str, name.str));
- sphead= lex.et->sphead;
- sphead->m_db= dbname;
+ sphead= lex.sphead;
sphead->set_definer(definer.str, definer.length);
sphead->set_info(0, 0, &lex.sp_chistics, sql_mode);
sphead->optimize();
ret= 0;
done:
- lex.et->free_sphead_on_delete= false;
- lex.et->deinit_mutexes();
lex_end(&lex);
- restore_security_context(thd, save_ctx);
+ event_restore_security_context(thd, &save_ctx);
DBUG_PRINT("note", ("return old data on its place. set back NAMES"));
thd->lex= old_lex;
@@ -1591,276 +1811,104 @@ 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
-
- SYNOPSIS
- event_timed_name_equal()
-
- RETURN VALUE
- TRUE names are equal
- FALSE names are not equal
-*/
-
-bool
-event_timed_name_equal(Event_timed *et, LEX_STRING *name)
-{
- return !sortcmp_lex_string(et->name, *name, system_charset_info);
-}
-
-
/*
Checks whether two events are in the same schema
SYNOPSIS
- event_timed_db_equal()
+ event_basic_db_equal()
+ db Schema
+ et Compare et->dbname to `db`
RETURN VALUE
- TRUE schemas are equal
- FALSE schemas are not equal
-*/
-
-bool
-event_timed_db_equal(Event_timed *et, LEX_STRING *db)
-{
- return !sortcmp_lex_string(et->dbname, *db, system_charset_info);
-}
-
-
-/*
- Checks whether two events have the same definer
-
- SYNOPSIS
- event_timed_definer_equal()
-
- Returns
- TRUE definers are equal
- FALSE definers are not equal
+ TRUE Equal
+ FALSE Not equal
*/
bool
-event_timed_definer_equal(Event_timed *et, LEX_STRING *definer)
+event_basic_db_equal(LEX_STRING db, Event_basic *et)
{
- return !sortcmp_lex_string(et->definer, *definer, system_charset_info);
+ return !sortcmp_lex_string(et->dbname, db, system_charset_info);
}
/*
- Checks whether two events are equal by identifiers
+ Checks whether an event has equal `db` and `name`
SYNOPSIS
- event_timed_identifier_equal()
+ event_basic_identifier_equal()
+ db Schema
+ name Name
+ et The event object
RETURN VALUE
- TRUE equal
- FALSE not equal
+ TRUE Equal
+ FALSE Not equal
*/
bool
-event_timed_identifier_equal(Event_timed *a, Event_timed *b)
+event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b)
{
- return event_timed_name_equal(a, &b->name) &&
- event_timed_db_equal(a, &b->dbname) &&
- event_timed_definer_equal(a, &b->definer);
+ return !sortcmp_lex_string(name, b->name, system_charset_info) &&
+ !sortcmp_lex_string(db, b->dbname, system_charset_info);
}
/*
- Switches the security context
+ Switches the security context.
+
SYNOPSIS
- change_security_context()
+ event_change_security_context()
thd Thread
user The user
host The host of the user
db The schema for which the security_ctx will be loaded
- s_ctx Security context to load state into
backup Where to store the old context
-
+
RETURN VALUE
- 0 - OK
- 1 - Error (generates error too)
+ FALSE OK
+ TRUE Error (generates error too)
*/
-bool
-change_security_context(THD *thd, LEX_STRING user, LEX_STRING host,
- LEX_STRING db, Security_context *s_ctx,
- Security_context **backup)
+static bool
+event_change_security_context(THD *thd, LEX_STRING user, LEX_STRING host,
+ LEX_STRING db, Security_context *backup)
{
- DBUG_ENTER("change_security_context");
+ DBUG_ENTER("event_change_security_context");
DBUG_PRINT("info",("%s@%s@%s", user.str, host.str, db.str));
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- s_ctx->init();
- *backup= 0;
- if (acl_getroot_no_password(s_ctx, user.str, host.str, host.str, db.str))
+
+ *backup= thd->main_security_ctx;
+ if (acl_getroot_no_password(&thd->main_security_ctx, user.str, host.str,
+ host.str, db.str))
{
my_error(ER_NO_SUCH_USER, MYF(0), user.str, host.str);
DBUG_RETURN(TRUE);
}
- *backup= thd->security_ctx;
- thd->security_ctx= s_ctx;
+ thd->security_ctx= &thd->main_security_ctx;
#endif
DBUG_RETURN(FALSE);
-}
+}
/*
- Restores the security context
+ Restores the security context.
+
SYNOPSIS
- restore_security_context()
- thd - thread
- backup - switch to this context
+ event_restore_security_context()
+ thd Thread
+ backup Context to switch to
*/
-void
-restore_security_context(THD *thd, Security_context *backup)
+static void
+event_restore_security_context(THD *thd, Security_context *backup)
{
- DBUG_ENTER("restore_security_context");
+ DBUG_ENTER("event_restore_security_context");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (backup)
- thd->security_ctx= backup;
+ {
+ thd->main_security_ctx= *backup;
+ thd->security_ctx= &thd->main_security_ctx;
+ }
#endif
DBUG_VOID_RETURN;
}
diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h
new file mode 100644
index 00000000000..e7e96d299fb
--- /dev/null
+++ b/sql/event_data_objects.h
@@ -0,0 +1,285 @@
+#ifndef _EVENT_DATA_OBJECTS_H_
+#define _EVENT_DATA_OBJECTS_H_
+/* Copyright (C) 2004-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#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
+
+
+class sp_head;
+class Sql_alloc;
+
+
+class Event_basic
+{
+protected:
+ MEM_ROOT mem_root;
+
+public:
+ LEX_STRING dbname;
+ LEX_STRING name;
+ LEX_STRING definer;// combination of user and host
+
+ Event_basic();
+ virtual ~Event_basic();
+
+ virtual int
+ load_from_row(TABLE *table) = 0;
+
+protected:
+ bool
+ load_string_fields(Field **fields, ...);
+};
+
+
+
+class Event_queue_element : public Event_basic
+{
+protected:
+ bool status_changed;
+ bool last_executed_changed;
+
+public:
+ enum enum_status
+ {
+ ENABLED = 1,
+ DISABLED
+ };
+
+ enum enum_on_completion
+ {
+ ON_COMPLETION_DROP = 1,
+ ON_COMPLETION_PRESERVE
+ };
+
+ enum enum_on_completion on_completion;
+ enum enum_status status;
+ TIME last_executed;
+
+ TIME execute_at;
+ TIME starts;
+ TIME ends;
+ my_bool starts_null;
+ my_bool ends_null;
+ my_bool execute_at_null;
+
+ longlong expression;
+ interval_type interval;
+
+ bool dropped;
+
+ uint execution_count;
+
+ Event_queue_element();
+ virtual ~Event_queue_element();
+
+ virtual int
+ load_from_row(TABLE *table);
+
+ bool
+ compute_next_execution_time();
+
+ int
+ drop(THD *thd);
+
+ void
+ mark_last_executed(THD *thd);
+
+ bool
+ update_timing_fields(THD *thd);
+
+ static void *operator new(size_t size)
+ {
+ void *p;
+ DBUG_ENTER("Event_queue_element::new(size)");
+ p= my_malloc(size, MYF(0));
+ DBUG_PRINT("info", ("alloc_ptr=0x%lx", p));
+ DBUG_RETURN(p);
+ }
+
+ static void operator delete(void *ptr, size_t size)
+ {
+ DBUG_ENTER("Event_queue_element::delete(ptr,size)");
+ DBUG_PRINT("enter", ("free_ptr=0x%lx", ptr));
+ TRASH(ptr, size);
+ my_free((gptr) ptr, MYF(0));
+ DBUG_VOID_RETURN;
+ }
+};
+
+
+class Event_timed : public Event_queue_element
+{
+ Event_timed(const Event_timed &); /* Prevent use of these */
+ void operator=(Event_timed &);
+
+public:
+ LEX_STRING body;
+
+ LEX_STRING definer_user;
+ LEX_STRING definer_host;
+
+ LEX_STRING comment;
+
+ ulonglong created;
+ ulonglong modified;
+
+ ulong sql_mode;
+
+ Event_timed();
+ virtual ~Event_timed();
+
+ void
+ init();
+
+ virtual int
+ load_from_row(TABLE *table);
+
+ int
+ get_create_event(THD *thd, String *buf);
+};
+
+
+class Event_job_data : public Event_basic
+{
+public:
+ THD *thd;
+ sp_head *sphead;
+
+ LEX_STRING body;
+ LEX_STRING definer_user;
+ LEX_STRING definer_host;
+
+ ulong sql_mode;
+
+ uint execution_count;
+
+ Event_job_data();
+ virtual ~Event_job_data();
+
+ virtual int
+ load_from_row(TABLE *table);
+
+ int
+ execute(THD *thd);
+
+ int
+ compile(THD *thd, MEM_ROOT *mem_root);
+private:
+ int
+ get_fake_create_event(THD *thd, String *buf);
+
+ Event_job_data(const Event_job_data &); /* Prevent use of these */
+ void operator=(Event_job_data &);
+};
+
+
+class Event_parse_data : public Sql_alloc
+{
+public:
+ enum enum_status
+ {
+ ENABLED = 1,
+ DISABLED
+ };
+
+ enum enum_on_completion
+ {
+ ON_COMPLETION_DROP = 1,
+ ON_COMPLETION_PRESERVE
+ };
+ enum enum_on_completion on_completion;
+ enum enum_status status;
+
+ const uchar *body_begin;
+
+ LEX_STRING dbname;
+ LEX_STRING name;
+ LEX_STRING definer;// combination of user and host
+ LEX_STRING body;
+ LEX_STRING comment;
+
+ Item* item_starts;
+ Item* item_ends;
+ Item* item_execute_at;
+
+ TIME starts;
+ TIME ends;
+ TIME execute_at;
+ my_bool starts_null;
+ my_bool ends_null;
+ my_bool execute_at_null;
+
+ sp_name *identifier;
+ Item* item_expression;
+ longlong expression;
+ interval_type interval;
+
+ static Event_parse_data *
+ new_instance(THD *thd);
+
+ bool
+ check_parse_data(THD *thd);
+
+ void
+ init_body(THD *thd);
+
+private:
+
+ void
+ init_definer(THD *thd);
+
+ void
+ init_name(THD *thd, sp_name *spn);
+
+ int
+ init_execute_at(THD *thd);
+
+ int
+ init_interval(THD *thd);
+
+ int
+ init_starts(THD *thd);
+
+ int
+ init_ends(THD *thd);
+
+ Event_parse_data();
+ ~Event_parse_data();
+
+ void
+ report_bad_value(const char *item_name, Item *bad_item);
+
+ Event_parse_data(const Event_parse_data &); /* Prevent use of these */
+ void operator=(Event_parse_data &);
+};
+
+
+/* Compares only the schema part of the identifier */
+bool
+event_basic_db_equal(LEX_STRING db, Event_basic *et);
+
+/* Compares the whole identifier*/
+bool
+event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b);
+
+
+#endif /* _EVENT_DATA_OBJECTS_H_ */
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
new file mode 100644
index 00000000000..464c26044c7
--- /dev/null
+++ b/sql/event_db_repository.cc
@@ -0,0 +1,979 @@
+/* Copyright (C) 2004-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysql_priv.h"
+#include "event_db_repository.h"
+#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] =
+{
+ {
+ { C_STRING_WITH_LEN("db") },
+ { C_STRING_WITH_LEN("char(64)") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("name") },
+ { C_STRING_WITH_LEN("char(64)") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("body") },
+ { C_STRING_WITH_LEN("longblob") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("definer") },
+ { C_STRING_WITH_LEN("char(77)") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("execute_at") },
+ { C_STRING_WITH_LEN("datetime") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("interval_value") },
+ { C_STRING_WITH_LEN("int(11)") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("interval_field") },
+ { C_STRING_WITH_LEN("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')") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("created") },
+ { C_STRING_WITH_LEN("timestamp") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("modified") },
+ { C_STRING_WITH_LEN("timestamp") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("last_executed") },
+ { C_STRING_WITH_LEN("datetime") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("starts") },
+ { C_STRING_WITH_LEN("datetime") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("ends") },
+ { C_STRING_WITH_LEN("datetime") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("status") },
+ { C_STRING_WITH_LEN("enum('ENABLED','DISABLED')") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("on_completion") },
+ { C_STRING_WITH_LEN("enum('DROP','PRESERVE')") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("sql_mode") },
+ { C_STRING_WITH_LEN("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')") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("comment") },
+ { C_STRING_WITH_LEN("char(64)") },
+ { C_STRING_WITH_LEN("utf8") }
+ }
+};
+
+
+/*
+ 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
+
+ RETURN VALUE
+ 0 OK
+ EVEX_GENERAL_ERROR Bad data
+ EVEX_GET_FIELD_FAILED Field count does not match. table corrupted?
+
+ DESCRIPTION
+ Used both when an event is created and when it is altered.
+*/
+
+static int
+mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et,
+ my_bool is_update)
+{
+ CHARSET_INFO *scs= system_charset_info;
+ enum enum_events_table_field f_num;
+ Field **fields= table->field;
+
+ DBUG_ENTER("mysql_event_fill_row");
+
+ DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str));
+ DBUG_PRINT("info", ("name =[%s]", et->name.str));
+ DBUG_PRINT("info", ("body =[%s]", et->body.str));
+
+ if (fields[f_num= ET_FIELD_DEFINER]->
+ store(et->definer.str, et->definer.length, scs))
+ goto err_truncate;
+
+ if (fields[f_num= ET_FIELD_DB]->store(et->dbname.str, et->dbname.length, scs))
+ goto err_truncate;
+
+ if (fields[f_num= ET_FIELD_NAME]->store(et->name.str, et->name.length, scs))
+ goto err_truncate;
+
+ /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/
+ fields[ET_FIELD_ON_COMPLETION]->store((longlong)et->on_completion, TRUE);
+
+ fields[ET_FIELD_STATUS]->store((longlong)et->status, TRUE);
+
+ /*
+ Change the SQL_MODE only if body was present in an ALTER EVENT and of course
+ always during CREATE EVENT.
+ */
+ if (et->body.str)
+ {
+ fields[ET_FIELD_SQL_MODE]->store((longlong)thd->variables.sql_mode, TRUE);
+ if (fields[f_num= ET_FIELD_BODY]->store(et->body.str, et->body.length, scs))
+ goto err_truncate;
+ }
+
+ if (et->expression)
+ {
+ fields[ET_FIELD_INTERVAL_EXPR]->set_notnull();
+ fields[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, TRUE);
+
+ fields[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull();
+ /*
+ In the enum (C) intervals start from 0 but in mysql enum valid values
+ start from 1. Thus +1 offset is needed!
+ */
+ fields[ET_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->interval+1, TRUE);
+
+ fields[ET_FIELD_EXECUTE_AT]->set_null();
+
+ if (!et->starts_null)
+ {
+ fields[ET_FIELD_STARTS]->set_notnull();
+ fields[ET_FIELD_STARTS]->store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME);
+ }
+
+ if (!et->ends_null)
+ {
+ fields[ET_FIELD_ENDS]->set_notnull();
+ fields[ET_FIELD_ENDS]->store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME);
+ }
+ }
+ else if (et->execute_at.year)
+ {
+ fields[ET_FIELD_INTERVAL_EXPR]->set_null();
+ fields[ET_FIELD_TRANSIENT_INTERVAL]->set_null();
+ fields[ET_FIELD_STARTS]->set_null();
+ fields[ET_FIELD_ENDS]->set_null();
+
+ fields[ET_FIELD_EXECUTE_AT]->set_notnull();
+ fields[ET_FIELD_EXECUTE_AT]->
+ store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME);
+ }
+ 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
+ */
+ }
+
+ ((Field_timestamp *)fields[ET_FIELD_MODIFIED])->set_time();
+
+ if (et->comment.str)
+ {
+ if (fields[f_num= ET_FIELD_COMMENT]->
+ store(et->comment.str, et->comment.length, scs))
+ goto err_truncate;
+ }
+
+ DBUG_RETURN(0);
+
+err_truncate:
+ my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), fields[f_num]->field_name);
+ DBUG_RETURN(EVEX_GENERAL_ERROR);
+}
+
+
+/*
+ Performs an index scan of event_table (mysql.event) and fills schema_table.
+
+ SYNOPSIS
+ Event_db_repository::index_read_for_db_for_i_s()
+ thd Thread
+ schema_table The I_S.EVENTS table
+ event_table The event table to use for loading (mysql.event)
+ db For which schema to do an index scan.
+
+ RETURN VALUE
+ 0 OK
+ 1 Error
+*/
+
+bool
+Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
+ TABLE *event_table,
+ const char *db)
+{
+ int ret=0;
+ CHARSET_INFO *scs= system_charset_info;
+ KEY *key_info;
+ uint key_len;
+ byte *key_buf= NULL;
+ LINT_INIT(key_buf);
+
+ DBUG_ENTER("Event_db_repository::index_read_for_db_for_i_s");
+
+ DBUG_PRINT("info", ("Using prefix scanning on PK"));
+ event_table->file->ha_index_init(0, 1);
+ event_table->field[ET_FIELD_DB]->store(db, strlen(db), scs);
+ key_info= event_table->key_info;
+ key_len= key_info->key_part[0].store_length;
+
+ if (!(key_buf= (byte *)alloc_root(thd->mem_root, key_len)))
+ {
+ ret= 1;
+ /* Don't send error, it would be done by sql_alloc_error_handler() */
+ }
+ else
+ {
+ key_copy(key_buf, event_table->record[0], key_info, key_len);
+ if (!(ret= event_table->file->index_read(event_table->record[0], key_buf,
+ key_len, HA_READ_PREFIX)))
+ {
+ DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret));
+ do
+ {
+ ret= copy_event_to_schema_table(thd, schema_table, event_table);
+ if (ret == 0)
+ ret= event_table->file->index_next_same(event_table->record[0],
+ key_buf, key_len);
+ } while (ret == 0);
+ }
+ DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
+ }
+ event_table->file->ha_index_end();
+ /* ret is guaranteed to be != 0 */
+ if (ret == HA_ERR_END_OF_FILE || ret == HA_ERR_KEY_NOT_FOUND)
+ DBUG_RETURN(FALSE);
+
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Performs a table scan of event_table (mysql.event) and fills schema_table.
+
+ SYNOPSIS
+ Events_db_repository::table_scan_all_for_i_s()
+ thd Thread
+ schema_table The I_S.EVENTS in memory table
+ event_table The event table to use for loading.
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error
+*/
+
+bool
+Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table,
+ TABLE *event_table)
+{
+ int ret;
+ READ_RECORD read_record_info;
+ DBUG_ENTER("Event_db_repository::table_scan_all_for_i_s");
+
+ init_read_record(&read_record_info, thd, event_table, NULL, 1, 0);
+
+ /*
+ rr_sequential, in read_record(), returns 137==HA_ERR_END_OF_FILE,
+ but rr_handle_error returns -1 for that reason. Thus, read_record()
+ returns -1 eventually.
+ */
+ do
+ {
+ ret= read_record_info.read_record(&read_record_info);
+ if (ret == 0)
+ ret= copy_event_to_schema_table(thd, schema_table, event_table);
+ } while (ret == 0);
+
+ DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
+ end_read_record(&read_record_info);
+
+ /* ret is guaranteed to be != 0 */
+ DBUG_RETURN(ret == -1? FALSE:TRUE);
+}
+
+
+/*
+ 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
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error
+*/
+
+int
+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);
+ }
+
+ /*
+ 1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order
+ thus we won't order it. OTOH, SHOW EVENTS will be
+ ordered.
+ 2. SHOW EVENTS => PRIMARY KEY with prefix scanning on (db)
+ Reasoning: Events are per schema, therefore a scan over an index
+ will save use from doing a table scan and comparing
+ every single row's `db` with the schema which we show.
+ */
+ if (db)
+ ret= index_read_for_db_for_i_s(thd, schema_table, event_table, db);
+ else
+ 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
+
+ RETURN VALUE
+ 1 Cannot lock table
+ 2 The table is corrupted - different number of fields
+ 0 OK
+*/
+
+int
+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;
+
+ if (simple_open_n_lock_tables(thd, &tables))
+ DBUG_RETURN(1);
+
+ 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);
+}
+
+
+/*
+ Creates an event in mysql.event
+
+ 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
+
+ RETURN VALUE
+ 0 OK
+ EVEX_GENERAL_ERROR Failure
+
+ DESCRIPTION
+ Creates an event. Relies on mysql_event_fill_row which is shared with
+ ::update_event. The name of the event is inside "et".
+*/
+
+bool
+Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
+ my_bool create_if_not)
+{
+ int ret= 0;
+ CHARSET_INFO *scs= system_charset_info;
+ 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;
+
+ 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;
+ }
+
+
+ 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 (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;
+ }
+ my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str);
+ goto err;
+ }
+
+ 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;
+ }
+
+ restore_record(table, s->default_values); // Get default values for fields
+
+ if (system_charset_info->cset->
+ numchars(system_charset_info, parse_data->dbname.str,
+ parse_data->dbname.str + parse_data->dbname.length) >
+ table->field[ET_FIELD_DB]->char_length())
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->dbname.str);
+ goto err;
+ }
+
+ if (system_charset_info->cset->
+ numchars(system_charset_info, parse_data->name.str,
+ parse_data->name.str + parse_data->name.length) >
+ table->field[ET_FIELD_NAME]->char_length())
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->name.str);
+ goto err;
+ }
+
+ 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.year))
+ {
+ DBUG_PRINT("error", ("neither expression nor execute_at are set!"));
+ my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0));
+ goto err;
+ }
+
+ ((Field_timestamp *)table->field[ET_FIELD_CREATED])->set_time();
+
+ /*
+ 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;
+
+ /* 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]))
+ {
+ my_error(ER_EVENT_STORE_FAILED, MYF(0), parse_data->name.str, ret);
+ goto err;
+ }
+
+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);
+ if (table)
+ close_thread_tables(thd);
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ 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)
+
+ NOTES
+ sp_name is passed since this is the name of the event to
+ alter in case of RENAME TO.
+*/
+
+bool
+Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
+ LEX_STRING *new_dbname, LEX_STRING *new_name)
+{
+ CHARSET_INFO *scs= system_charset_info;
+ TABLE *table= NULL;
+ 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;
+ }
+
+ if (check_parse_params(thd, parse_data))
+ goto err;
+
+ 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))
+ {
+ my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str);
+ goto err;
+ }
+ }
+ /*
+ ...and then if there is such an event. Don't exchange the blocks
+ because you will get error 120 from table handler because new_name will
+ overwrite the key and SE will tell us that it cannot find the already found
+ row (copied into record[1] later
+ */
+ if (find_named_event(thd, parse_data->dbname, parse_data->name, table))
+ {
+ my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), parse_data->name.str);
+ goto err;
+ }
+
+ store_record(table,record[1]);
+
+ /* Don't update create on row update. */
+ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+
+ /*
+ mysql_event_fill_row() calls my_error() in case of error so no need to
+ handle it here
+ */
+ if (mysql_event_fill_row(thd, table, parse_data, TRUE))
+ goto err;
+
+ if (new_dbname)
+ {
+ table->field[ET_FIELD_DB]->store(new_dbname->str, new_dbname->length, scs);
+ table->field[ET_FIELD_NAME]->store(new_name->str, new_name->length, scs);
+ }
+
+ /* 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])))
+ {
+ my_error(ER_EVENT_STORE_FAILED, MYF(0), parse_data->name.str, res);
+ goto err;
+ }
+
+ /* close mysql.event or we crash later when loading the event from disk */
+ close_thread_tables(thd);
+ DBUG_RETURN(FALSE);
+
+err:
+ if (table)
+ close_thread_tables(thd);
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Drops an event
+
+ 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
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error (reported)
+*/
+
+bool
+Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
+ bool drop_if_exists)
+{
+ TABLE *table= NULL;
+ Open_tables_state backup;
+ int ret;
+
+ 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 (!(ret= find_named_event(thd, 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));
+ }
+ else
+ {
+ 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);
+ }
+
+done:
+ if (table)
+ close_thread_tables(thd);
+ thd->restore_backup_open_tables_state(&backup);
+
+ DBUG_RETURN(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
+
+ RETURN VALUE
+ FALSE OK
+ TRUE No such event
+*/
+
+bool
+Event_db_repository::find_named_event(THD *thd, LEX_STRING db, LEX_STRING name,
+ TABLE *table)
+{
+ byte key[MAX_KEY_LENGTH];
+ DBUG_ENTER("Event_db_repository::find_named_event");
+ DBUG_PRINT("enter", ("name: %.*s", name.length, name.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 (db.length > table->field[ET_FIELD_DB]->field_length ||
+ name.length > table->field[ET_FIELD_NAME]->field_length)
+ DBUG_RETURN(TRUE);
+
+ 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);
+
+ 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_PRINT("info", ("Row not found"));
+ DBUG_RETURN(TRUE);
+ }
+
+ DBUG_PRINT("info", ("Row found!"));
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Drops all events in the selected database, from mysql.event.
+
+ SYNOPSIS
+ Event_db_repository::drop_schema_events()
+ thd Thread
+ schema The database to clean from events
+*/
+
+void
+Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema)
+{
+ DBUG_ENTER("Event_db_repository::drop_schema_events");
+ drop_events_by_field(thd, ET_FIELD_DB, schema);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Drops all events by field which has specific value of the 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
+*/
+
+void
+Event_db_repository::drop_events_by_field(THD *thd,
+ enum enum_events_table_field field,
+ LEX_STRING field_value)
+{
+ int ret= 0;
+ TABLE *table= NULL;
+ READ_RECORD read_record_info;
+ DBUG_ENTER("Event_db_repository::drop_events_by_field");
+ 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);
+ while (!ret && !(read_record_info.read_record(&read_record_info)) )
+ {
+ 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))
+ {
+ DBUG_PRINT("info", ("Dropping"));
+ ret= table->file->ha_delete_row(table->record[0]);
+ }
+ }
+ end_read_record(&read_record_info);
+ close_thread_tables(thd);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ 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()
+ thd [in] Thread context
+ dbname [in] Event's db name
+ name [in] Event's name
+ etn [out] The loaded event
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error (reported)
+*/
+
+bool
+Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname,
+ LEX_STRING name, Event_basic *etn)
+{
+ TABLE *table= NULL;
+ int ret= 0;
+ Open_tables_state backup;
+
+ DBUG_ENTER("Event_db_repository::load_named_event");
+ DBUG_PRINT("enter",("thd=0x%lx name:%*s",thd, name.length, name.str));
+
+ thd->reset_n_backup_open_tables_state(&backup);
+
+ 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(table)))
+ my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event");
+
+ 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(ret);
+}
diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h
new file mode 100644
index 00000000000..ed74edd7e19
--- /dev/null
+++ b/sql/event_db_repository.h
@@ -0,0 +1,102 @@
+#ifndef _EVENT_DB_REPOSITORY_H_
+#define _EVENT_DB_REPOSITORY_H_
+/* Copyright (C) 2004-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#define EVEX_OPEN_TABLE_FAILED -1
+
+enum enum_events_table_field
+{
+ ET_FIELD_DB = 0,
+ ET_FIELD_NAME,
+ ET_FIELD_BODY,
+ ET_FIELD_DEFINER,
+ ET_FIELD_EXECUTE_AT,
+ ET_FIELD_INTERVAL_EXPR,
+ ET_FIELD_TRANSIENT_INTERVAL,
+ ET_FIELD_CREATED,
+ ET_FIELD_MODIFIED,
+ ET_FIELD_LAST_EXECUTED,
+ ET_FIELD_STARTS,
+ ET_FIELD_ENDS,
+ ET_FIELD_STATUS,
+ ET_FIELD_ON_COMPLETION,
+ ET_FIELD_SQL_MODE,
+ ET_FIELD_COMMENT,
+ ET_FIELD_COUNT /* a cool trick to count the number of fields :) */
+};
+
+
+int
+events_table_index_read_for_db(THD *thd, TABLE *schema_table,
+ TABLE *event_table);
+
+int
+events_table_scan_all(THD *thd, TABLE *schema_table, TABLE *event_table);
+
+class Event_basic;
+class Event_parse_data;
+
+class Event_db_repository
+{
+public:
+ Event_db_repository(){}
+
+ bool
+ create_event(THD *thd, Event_parse_data *parse_data, my_bool create_if_not);
+
+ bool
+ update_event(THD *thd, Event_parse_data *parse_data, LEX_STRING *new_dbname,
+ LEX_STRING *new_name);
+
+ bool
+ drop_event(THD *thd, LEX_STRING db, LEX_STRING name, bool drop_if_exists);
+
+ void
+ drop_schema_events(THD *thd, LEX_STRING schema);
+
+ bool
+ find_named_event(THD *thd, LEX_STRING db, LEX_STRING name, TABLE *table);
+
+ bool
+ load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_basic *et);
+
+ int
+ open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
+
+ int
+ fill_schema_events(THD *thd, TABLE_LIST *tables, const char *db);
+
+private:
+ void
+ drop_events_by_field(THD *thd, enum enum_events_table_field field,
+ LEX_STRING field_value);
+ bool
+ index_read_for_db_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table,
+ const char *db);
+
+ bool
+ table_scan_all_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table);
+
+ static bool
+ check_system_tables(THD *thd);
+
+ /* Prevent use of these */
+ Event_db_repository(const Event_db_repository &);
+ void operator=(Event_db_repository &);
+};
+
+#endif /* _EVENT_DB_REPOSITORY_H_ */
diff --git a/sql/event_queue.cc b/sql/event_queue.cc
new file mode 100644
index 00000000000..539d0ab2734
--- /dev/null
+++ b/sql/event_queue.cc
@@ -0,0 +1,956 @@
+/* Copyright (C) 2004-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysql_priv.h"
+#include "event_queue.h"
+#include "event_data_objects.h"
+#include "event_db_repository.h"
+
+
+#define EVENT_QUEUE_INITIAL_SIZE 30
+#define EVENT_QUEUE_EXTENT 30
+
+#ifdef __GNUC__
+#if __GNUC__ >= 2
+#define SCHED_FUNC __FUNCTION__
+#endif
+#else
+#define SCHED_FUNC "<unknown>"
+#endif
+
+#define LOCK_QUEUE_DATA() lock_data(SCHED_FUNC, __LINE__)
+#define UNLOCK_QUEUE_DATA() unlock_data(SCHED_FUNC, __LINE__)
+
+struct event_queue_param
+{
+ THD *thd;
+ Event_queue *queue;
+ pthread_mutex_t LOCK_loaded;
+ pthread_cond_t COND_loaded;
+ bool loading_finished;
+};
+
+
+/*
+ Compares the execute_at members of two Event_queue_element instances.
+ Used as callback for the prioritized queue when shifting
+ elements inside.
+
+ SYNOPSIS
+ event_queue_element_data_compare_q()
+ vptr Not used (set it to NULL)
+ a First Event_queue_element object
+ b Second Event_queue_element object
+
+ RETURN VALUE
+ -1 a->execute_at < b->execute_at
+ 0 a->execute_at == b->execute_at
+ 1 a->execute_at > b->execute_at
+
+ NOTES
+ execute_at.second_part is not considered during comparison
+*/
+
+static int
+event_queue_element_compare_q(void *vptr, byte* a, byte *b)
+{
+ return my_time_compare(&((Event_queue_element *)a)->execute_at,
+ &((Event_queue_element *)b)->execute_at);
+}
+
+
+/*
+ Constructor of class Event_queue.
+
+ SYNOPSIS
+ Event_queue::Event_queue()
+*/
+
+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_last_unlocked_in_func= mutex_last_locked_in_func=
+ mutex_last_attempted_lock_in_func= "";
+ set_zero_time(&next_activation_at, MYSQL_TIMESTAMP_DATETIME);
+}
+
+
+/*
+ Inits mutexes.
+
+ SYNOPSIS
+ Event_queue::init_mutexes()
+*/
+
+void
+Event_queue::init_mutexes()
+{
+ pthread_mutex_init(&LOCK_event_queue, MY_MUTEX_INIT_FAST);
+}
+
+
+/*
+ Destroys mutexes.
+
+ SYNOPSIS
+ Event_queue::deinit_mutexes()
+*/
+
+void
+Event_queue::deinit_mutexes()
+{
+ pthread_mutex_destroy(&LOCK_event_queue);
+}
+
+
+/*
+ This is a queue's constructor. Until this method is called, the
+ queue is unusable. We don't use a C++ constructor instead in
+ order to be able to check the return value. The queue is
+ initialized once at server startup. Initialization can fail in
+ case of a failure reading events from the database or out of
+ memory.
+
+ SYNOPSIS
+ Event_queue::init()
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error
+*/
+
+bool
+Event_queue::init_queue(THD *thd, Event_db_repository *db_repo)
+{
+ bool res;
+ struct event_queue_param *event_queue_param_value= NULL;
+
+ DBUG_ENTER("Event_queue::init_queue");
+ DBUG_PRINT("enter", ("this=0x%lx", this));
+
+ LOCK_QUEUE_DATA();
+ db_repository= db_repo;
+
+ if (init_queue_ex(&queue, EVENT_QUEUE_INITIAL_SIZE , 0 /*offset*/,
+ 0 /*max_on_top*/, event_queue_element_compare_q,
+ NULL, EVENT_QUEUE_EXTENT))
+ {
+ sql_print_error("SCHEDULER: Can't initialize the execution queue");
+ goto err;
+ }
+
+ if (sizeof(my_time_t) != sizeof(time_t))
+ {
+ sql_print_error("SCHEDULER: sizeof(my_time_t) != sizeof(time_t) ."
+ "The scheduler may not work correctly. Stopping");
+ DBUG_ASSERT(0);
+ goto err;
+ }
+
+ res= load_events_from_db(thd);
+ UNLOCK_QUEUE_DATA();
+ if (res)
+ deinit_queue();
+
+ DBUG_RETURN(res);
+
+err:
+ UNLOCK_QUEUE_DATA();
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Deinits the queue. Remove all elements from it and destroys them
+ too.
+
+ SYNOPSIS
+ Event_queue::deinit_queue()
+*/
+
+void
+Event_queue::deinit_queue()
+{
+ DBUG_ENTER("Event_queue::deinit_queue");
+
+ LOCK_QUEUE_DATA();
+ empty_queue();
+ delete_queue(&queue);
+ UNLOCK_QUEUE_DATA();
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ 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
+
+ RETURN VALUE
+ OP_OK OK or scheduler not working
+ OP_LOAD_ERROR Error during loading from disk
+*/
+
+int
+Event_queue::create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
+{
+ int res;
+ Event_queue_element *new_element;
+ DBUG_ENTER("Event_queue::create_event");
+ DBUG_PRINT("enter", ("thd=0x%lx et=%s.%s",thd, dbname.str, name.str));
+
+ new_element= new Event_queue_element();
+ res= db_repository->load_named_event(thd, dbname, name, new_element);
+ if (res || new_element->status == Event_queue_element::DISABLED)
+ delete new_element;
+ else
+ {
+ new_element->compute_next_execution_time();
+
+ LOCK_QUEUE_DATA();
+ DBUG_PRINT("info", ("new event in the queue 0x%lx", new_element));
+ queue_insert_safe(&queue, (byte *) new_element);
+ dbug_dump_queue(thd->query_start());
+ pthread_cond_broadcast(&COND_queue_state);
+ UNLOCK_QUEUE_DATA();
+ }
+
+ DBUG_RETURN(res);
+}
+
+
+/*
+ Updates an event from the scheduler queue
+
+ SYNOPSIS
+ Event_queue::update_event()
+ thd Thread
+ dbname Schema of the event
+ name Name of the event
+ new_schema New schema, in case of RENAME TO, otherwise NULL
+ new_name New name, in case of RENAME TO, otherwise NULL
+
+ RETURN VALUE
+ OP_OK OK or scheduler not working
+ OP_LOAD_ERROR Error during loading from disk
+*/
+
+int
+Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
+ LEX_STRING *new_schema, LEX_STRING *new_name)
+{
+ int res;
+ Event_queue_element *new_element;
+
+ DBUG_ENTER("Event_queue::update_event");
+ DBUG_PRINT("enter", ("thd=0x%lx et=[%s.%s]", thd, dbname.str, name.str));
+
+ new_element= new Event_queue_element();
+
+ res= db_repository->load_named_event(thd, new_schema ? *new_schema:dbname,
+ new_name ? *new_name:name, new_element);
+ if (res)
+ {
+ delete new_element;
+ goto end;
+ }
+ else if (new_element->status == Event_queue_element::DISABLED)
+ {
+ DBUG_PRINT("info", ("The event is disabled."));
+ /*
+ Destroy the object but don't skip to end: because we may have to remove
+ object from the cache.
+ */
+ delete new_element;
+ new_element= NULL;
+ }
+ else
+ new_element->compute_next_execution_time();
+
+ LOCK_QUEUE_DATA();
+ find_n_remove_event(dbname, name);
+
+ /* If not disabled event */
+ if (new_element)
+ {
+ DBUG_PRINT("info", ("new event in the Q 0x%lx", new_element));
+ queue_insert_safe(&queue, (byte *) new_element);
+ pthread_cond_broadcast(&COND_queue_state);
+ }
+
+ dbug_dump_queue(thd->query_start());
+ UNLOCK_QUEUE_DATA();
+
+end:
+ DBUG_PRINT("info", ("res=%d", res));
+ DBUG_RETURN(res);
+}
+
+
+/*
+ Drops an event from the queue
+
+ SYNOPSIS
+ Event_queue::drop_event()
+ thd Thread
+ dbname Schema of the event to drop
+ name Name of the event to drop
+*/
+
+void
+Event_queue::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
+{
+ DBUG_ENTER("Event_queue::drop_event");
+ DBUG_PRINT("enter", ("thd=0x%lx db=%s name=%s", thd, dbname.str, name.str));
+
+ LOCK_QUEUE_DATA();
+ find_n_remove_event(dbname, name);
+ dbug_dump_queue(thd->query_start());
+ UNLOCK_QUEUE_DATA();
+
+ /*
+ We don't signal here because the scheduler will catch the change
+ next time it wakes up.
+ */
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Drops all events from the in-memory queue and disk that match
+ certain pattern evaluated by a comparator function
+
+ SYNOPSIS
+ Event_queue::drop_matching_events()
+ thd THD
+ pattern A pattern string
+ comparator The function to use for comparing
+
+ RETURN VALUE
+ >=0 Number of dropped events
+
+ NOTE
+ Expected is the caller to acquire lock on LOCK_event_queue
+*/
+
+void
+Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern,
+ bool (*comparator)(LEX_STRING, Event_basic *))
+{
+ uint i= 0;
+ DBUG_ENTER("Event_queue::drop_matching_events");
+ DBUG_PRINT("enter", ("pattern=%s", pattern.str));
+
+ while (i < queue.elements)
+ {
+ Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i);
+ DBUG_PRINT("info", ("[%s.%s]?", et->dbname.str, et->name.str));
+ if (comparator(pattern, et))
+ {
+ /*
+ The queue is ordered. If we remove an element, then all elements
+ after it will shift one position to the left, if we imagine it as
+ an array from left to the right. In this case we should not
+ increment the counter and the (i < queue.elements) condition is ok.
+ */
+ queue_remove(&queue, i);
+ delete et;
+ }
+ else
+ i++;
+ }
+ /*
+ We don't call pthread_cond_broadcast(&COND_queue_state);
+ If we remove the top event:
+ 1. The queue is empty. The scheduler will wake up at some time and
+ realize 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;
+}
+
+
+/*
+ Drops all events from the in-memory queue and disk that are from
+ certain schema.
+
+ SYNOPSIS
+ Event_queue::drop_schema_events()
+ thd HD
+ schema The schema name
+*/
+
+void
+Event_queue::drop_schema_events(THD *thd, LEX_STRING schema)
+{
+ DBUG_ENTER("Event_queue::drop_schema_events");
+ LOCK_QUEUE_DATA();
+ drop_matching_events(thd, schema, event_basic_db_equal);
+ UNLOCK_QUEUE_DATA();
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Searches for an event in the queue
+
+ SYNOPSIS
+ Event_queue::find_n_remove_event()
+ db The schema of the event to find
+ name The event to find
+
+ NOTE
+ The caller should do the locking also the caller is responsible for
+ actual signalling in case an event is removed from the queue.
+*/
+
+void
+Event_queue::find_n_remove_event(LEX_STRING db, LEX_STRING name)
+{
+ uint i;
+ DBUG_ENTER("Event_queue::find_n_remove_event");
+
+ for (i= 0; i < queue.elements; ++i)
+ {
+ Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i);
+ DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?", db.str, name.str,
+ et->dbname.str, et->name.str));
+ if (event_basic_identifier_equal(db, name, et))
+ {
+ queue_remove(&queue, i);
+ delete et;
+ break;
+ }
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ 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.
+
+ SYNOPSIS
+ Event_queue::load_events_from_db()
+ thd - Thread context. Used for memory allocation in some cases.
+
+ RETURN VALUE
+ 0 OK
+ !0 Error (EVEX_OPEN_TABLE_FAILED, EVEX_MICROSECOND_UNSUP,
+ EVEX_COMPILE_ERROR) - in all these cases mysql.event was
+ tampered.
+
+ NOTES
+ Reports the error to the console
+*/
+
+int
+Event_queue::load_events_from_db(THD *thd)
+{
+ TABLE *table;
+ READ_RECORD read_record_info;
+ int ret= -1;
+ uint count= 0;
+ bool clean_the_queue= TRUE;
+
+ DBUG_ENTER("Event_queue::load_events_from_db");
+ DBUG_PRINT("enter", ("thd=0x%lx", thd));
+
+ if ((ret= db_repository->open_event_table(thd, TL_READ, &table)))
+ {
+ sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open");
+ DBUG_RETURN(EVEX_OPEN_TABLE_FAILED);
+ }
+
+ init_read_record(&read_record_info, thd, table ,NULL,1,0);
+ while (!(read_record_info.read_record(&read_record_info)))
+ {
+ Event_queue_element *et;
+ if (!(et= new Event_queue_element))
+ {
+ DBUG_PRINT("info", ("Out of memory"));
+ break;
+ }
+ DBUG_PRINT("info", ("Loading event from row."));
+
+ if ((ret= et->load_from_row(table)))
+ {
+ sql_print_error("SCHEDULER: Error while loading from mysql.event. "
+ "Table probably corrupted");
+ break;
+ }
+ if (et->status != Event_queue_element::ENABLED)
+ {
+ DBUG_PRINT("info",("%s is disabled",et->name.str));
+ delete et;
+ continue;
+ }
+
+ /* let's find when to be executed */
+ if (et->compute_next_execution_time())
+ {
+ sql_print_error("SCHEDULER: Error while computing execution time of %s.%s."
+ " Skipping", et->dbname.str, et->name.str);
+ continue;
+ }
+
+ {
+ Event_job_data temp_job_data;
+ DBUG_PRINT("info", ("Event %s loaded from row. ", et->name.str));
+
+ temp_job_data.load_from_row(table);
+
+ /*
+ We load only on scheduler root just to check whether the body
+ compiles.
+ */
+ switch (ret= temp_job_data.compile(thd, thd->mem_root)) {
+ case EVEX_MICROSECOND_UNSUP:
+ sql_print_error("SCHEDULER: mysql.event is tampered. MICROSECOND is not "
+ "supported but found in mysql.event");
+ break;
+ case EVEX_COMPILE_ERROR:
+ sql_print_error("SCHEDULER: Error while compiling %s.%s. Aborting load",
+ et->dbname.str, et->name.str);
+ break;
+ default:
+ break;
+ }
+ 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."));
+ queue_insert_safe(&queue, (byte *) et);
+ count++;
+ }
+ clean_the_queue= FALSE;
+end:
+ end_read_record(&read_record_info);
+
+ if (clean_the_queue)
+ {
+ empty_queue();
+ ret= -1;
+ }
+ else
+ {
+ ret= 0;
+ sql_print_information("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);
+}
+
+
+/*
+ Recalculates activation times in the queue. There is one reason for
+ that. Because the values (execute_at) by which the queue is ordered are
+ changed by calls to compute_next_execution_time() on a request from the
+ scheduler thread, if it is not running then the values won't be updated.
+ Once the scheduler is started again the values has to be recalculated
+ so they are right for the current time.
+
+ SYNOPSIS
+ Event_queue::recalculate_activation_times()
+ thd Thread
+*/
+
+void
+Event_queue::recalculate_activation_times(THD *thd)
+{
+ uint i;
+ DBUG_ENTER("Event_queue::recalculate_activation_times");
+
+ LOCK_QUEUE_DATA();
+ DBUG_PRINT("info", ("%u loaded events to be recalculated", queue.elements));
+ for (i= 0; i < queue.elements; i++)
+ {
+ ((Event_queue_element*)queue_element(&queue, i))->compute_next_execution_time();
+ ((Event_queue_element*)queue_element(&queue, i))->update_timing_fields(thd);
+ }
+ queue_fix(&queue);
+ UNLOCK_QUEUE_DATA();
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Empties the queue and destroys the Event_queue_element objects in the
+ queue.
+
+ SYNOPSIS
+ Event_queue::empty_queue()
+
+ NOTE
+ Should be called with LOCK_event_queue locked
+*/
+
+void
+Event_queue::empty_queue()
+{
+ uint i;
+ DBUG_ENTER("Event_queue::empty_queue");
+ DBUG_PRINT("enter", ("Purging the queue. %d element(s)", queue.elements));
+ sql_print_information("SCHEDULER: Purging queue. %u events", queue.elements);
+ /* empty the queue */
+ for (i= 0; i < queue.elements; ++i)
+ {
+ Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i);
+ delete et;
+ }
+ resize_queue(&queue, 0);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Dumps the queue to the trace log.
+
+ SYNOPSIS
+ Event_queue::dbug_dump_queue()
+ now Current timestamp
+*/
+
+void
+Event_queue::dbug_dump_queue(time_t now)
+{
+#ifndef DBUG_OFF
+ Event_queue_element *et;
+ uint i;
+ DBUG_ENTER("Event_queue::dbug_dump_queue");
+ DBUG_PRINT("info", ("Dumping queue . Elements=%u", queue.elements));
+ for (i = 0; i < queue.elements; i++)
+ {
+ et= ((Event_queue_element*)queue_element(&queue, i));
+ DBUG_PRINT("info",("et=0x%lx db=%s name=%s",et, et->dbname.str, et->name.str));
+ DBUG_PRINT("info", ("exec_at=%llu starts=%llu ends=%llu execs_so_far=%u"
+ " expr=%lld et.exec_at=%d now=%d (et.exec_at - now)=%d if=%d",
+ TIME_to_ulonglong_datetime(&et->execute_at),
+ TIME_to_ulonglong_datetime(&et->starts),
+ TIME_to_ulonglong_datetime(&et->ends),
+ et->execution_count,
+ et->expression, 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));
+ }
+ DBUG_VOID_RETURN;
+#endif
+}
+
+static const char *queue_empty_msg= "Waiting on empty queue";
+static const char *queue_wait_msg= "Waiting for next activation";
+
+/*
+ Checks whether the top of the queue is elligible for execution and
+ returns an Event_job_data instance in case it should be executed.
+ `now` is compared against `execute_at` of the top element in the queue.
+
+ SYNOPSIS
+ Event_queue::get_top_for_execution_if_time()
+ thd [in] Thread
+ now [in] Current timestamp
+ job_data [out] The object to execute
+ abstime [out] Time to sleep
+
+ RETURN VALUE
+ FALSE No error. If *job_data==NULL then top not elligible for execution.
+ Could be that there is no top. If abstime->tv_sec is set to value
+ greater than zero then use abstime with pthread_cond_timedwait().
+ If abstime->tv_sec is zero then sleep with pthread_cond_wait().
+ abstime->tv_nsec is always zero.
+ TRUE Error
+
+*/
+
+bool
+Event_queue::get_top_for_execution_if_time(THD *thd, Event_job_data **job_data)
+{
+ bool ret= FALSE;
+ struct timespec top_time;
+ struct timespec *abstime;
+ Event_queue_element *top= NULL;
+ bool to_free= FALSE;
+ bool to_drop= FALSE;
+ *job_data= NULL;
+ DBUG_ENTER("Event_queue::get_top_for_execution_if_time");
+
+ top_time.tv_nsec= 0;
+ LOCK_QUEUE_DATA();
+ for (;;)
+ {
+ int res;
+
+ thd->end_time();
+ time_t now= thd->query_start();
+ abstime= NULL;
+
+ if (queue.elements)
+ {
+ top= ((Event_queue_element*) queue_element(&queue, 0));
+ top_time.tv_sec= sec_since_epoch_TIME(&top->execute_at);
+
+ abstime= &top_time;
+ }
+
+ if (!abstime || abstime->tv_sec > now)
+ {
+ const char *msg;
+ if (abstime)
+ {
+ next_activation_at= top->execute_at;
+ msg= queue_wait_msg;
+ }
+ else
+ {
+ set_zero_time(&next_activation_at, MYSQL_TIMESTAMP_DATETIME);
+ msg= queue_wait_msg;
+ }
+
+ cond_wait(thd, abstime, msg, SCHED_FUNC, __LINE__);
+ if (thd->killed)
+ {
+ DBUG_PRINT("info", ("thd->killed=%d", thd->killed));
+ goto end;
+ }
+ /*
+ The queue could have been emptied. Therefore it's safe to start from
+ the beginning. Moreover, this way we will get also the new top, if
+ the element at the top has been changed.
+ */
+ continue;
+ }
+
+ DBUG_PRINT("info", ("Ready for execution"));
+ if (!(*job_data= new Event_job_data()))
+ {
+ ret= TRUE;
+ break;
+ }
+ if ((res= db_repository->load_named_event(thd, top->dbname, top->name,
+ *job_data)))
+ {
+ DBUG_PRINT("error", ("Got %d from load_named_event", res));
+ delete *job_data;
+ *job_data= NULL;
+ ret= TRUE;
+ break;
+ }
+
+ top->mark_last_executed(thd);
+ if (top->compute_next_execution_time())
+ top->status= Event_queue_element::DISABLED;
+ DBUG_PRINT("info", ("event %s status is %d", top->name.str, top->status));
+
+ (*job_data)->execution_count= top->execution_count;
+
+ top->update_timing_fields(thd);
+ if (((top->execute_at.year && !top->expression) || top->execute_at_null) ||
+ (top->status == Event_queue_element::DISABLED))
+ {
+ DBUG_PRINT("info", ("removing from the queue"));
+ sql_print_information("SCHEDULER: Last execution of %s.%s. %s",
+ top->dbname.str, top->name.str,
+ top->dropped? "Dropping.":"");
+ to_free= TRUE;
+ to_drop= top->dropped;
+ queue_remove(&queue, 0);
+ }
+ else
+ queue_replaced(&queue);
+
+ dbug_dump_queue(now);
+ break;
+ }
+end:
+ UNLOCK_QUEUE_DATA();
+ if (to_drop)
+ {
+ DBUG_PRINT("info", ("Dropping from disk"));
+ top->drop(thd);
+ }
+ if (to_free)
+ delete top;
+
+ DBUG_PRINT("info", ("returning %d. et_new=0x%lx abstime.tv_sec=%d ",
+ ret, *job_data, abstime? abstime->tv_sec:0));
+
+ if (*job_data)
+ DBUG_PRINT("info", ("db=%s name=%s definer=%s", (*job_data)->dbname.str,
+ (*job_data)->name.str, (*job_data)->definer.str));
+
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ Auxiliary function for locking LOCK_event_queue. Used by the
+ LOCK_QUEUE_DATA macro
+
+ SYNOPSIS
+ Event_queue::lock_data()
+ func Which function is requesting mutex lock
+ line On which line mutex lock is requested
+*/
+
+void
+Event_queue::lock_data(const char *func, uint line)
+{
+ DBUG_ENTER("Event_queue::lock_data");
+ DBUG_PRINT("enter", ("func=%s line=%u", func, line));
+ mutex_last_attempted_lock_in_func= func;
+ mutex_last_attempted_lock_at_line= line;
+ mutex_queue_data_attempting_lock= TRUE;
+ pthread_mutex_lock(&LOCK_event_queue);
+ mutex_last_attempted_lock_in_func= "";
+ mutex_last_attempted_lock_at_line= 0;
+ mutex_queue_data_attempting_lock= FALSE;
+
+ mutex_last_locked_in_func= func;
+ mutex_last_locked_at_line= line;
+ mutex_queue_data_locked= TRUE;
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Auxiliary function for unlocking LOCK_event_queue. Used by the
+ UNLOCK_QUEUE_DATA macro
+
+ SYNOPSIS
+ Event_queue::unlock_data()
+ func Which function is requesting mutex unlock
+ line On which line mutex unlock is requested
+*/
+
+void
+Event_queue::unlock_data(const char *func, uint line)
+{
+ DBUG_ENTER("Event_queue::unlock_data");
+ DBUG_PRINT("enter", ("func=%s line=%u", func, line));
+ mutex_last_unlocked_at_line= line;
+ mutex_queue_data_locked= FALSE;
+ mutex_last_unlocked_in_func= func;
+ pthread_mutex_unlock(&LOCK_event_queue);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Wrapper for pthread_cond_wait/timedwait
+
+ SYNOPSIS
+ Event_queue::cond_wait()
+ thd Thread (Could be NULL during shutdown procedure)
+ msg Message for thd->proc_info
+ abstime If not null then call pthread_cond_timedwait()
+ func Which function is requesting cond_wait
+ line On which line cond_wait is requested
+*/
+
+void
+Event_queue::cond_wait(THD *thd, struct timespec *abstime, const char* msg,
+ const char *func, uint line)
+{
+ DBUG_ENTER("Event_queue::cond_wait");
+ waiting_on_cond= TRUE;
+ mutex_last_unlocked_at_line= line;
+ mutex_queue_data_locked= FALSE;
+ mutex_last_unlocked_in_func= func;
+
+ thd->enter_cond(&COND_queue_state, &LOCK_event_queue, msg);
+
+ DBUG_PRINT("info", ("pthread_cond_%swait", abstime? "timed":""));
+ if (!abstime)
+ pthread_cond_wait(&COND_queue_state, &LOCK_event_queue);
+ else
+ pthread_cond_timedwait(&COND_queue_state, &LOCK_event_queue, abstime);
+
+ mutex_last_locked_in_func= func;
+ mutex_last_locked_at_line= line;
+ mutex_queue_data_locked= TRUE;
+ waiting_on_cond= FALSE;
+
+ /*
+ This will free the lock so we need to relock. Not the best thing to
+ do but we need to obey cond_wait()
+ */
+ thd->exit_cond("");
+ lock_data(func, line);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Dumps the internal status of the queue
+
+ SYNOPSIS
+ Event_queue::dump_internal_status()
+*/
+
+void
+Event_queue::dump_internal_status()
+{
+ DBUG_ENTER("Event_queue::dump_internal_status");
+
+ /* element count */
+ puts("");
+ puts("Event queue status:");
+ printf("Element count : %u\n", queue.elements);
+ printf("Data locked : %s\n", mutex_queue_data_locked? "YES":"NO");
+ printf("Attempting lock : %s\n", mutex_queue_data_attempting_lock? "YES":"NO");
+ printf("LLA : %s:%u\n", mutex_last_locked_in_func,
+ mutex_last_locked_at_line);
+ printf("LUA : %s:%u\n", mutex_last_unlocked_in_func,
+ mutex_last_unlocked_at_line);
+ if (mutex_last_attempted_lock_at_line)
+ printf("Last lock attempt at: %s:%u\n", mutex_last_attempted_lock_in_func,
+ mutex_last_attempted_lock_at_line);
+ printf("WOC : %s\n", waiting_on_cond? "YES":"NO");
+ printf("Next activation : %04d-%02d-%02d %02d:%02d:%02d\n",
+ next_activation_at.year, next_activation_at.month,
+ next_activation_at.day, next_activation_at.hour,
+ next_activation_at.minute, next_activation_at.second);
+
+ DBUG_VOID_RETURN;
+}
diff --git a/sql/event_queue.h b/sql/event_queue.h
new file mode 100644
index 00000000000..5b70506d388
--- /dev/null
+++ b/sql/event_queue.h
@@ -0,0 +1,121 @@
+#ifndef _EVENT_QUEUE_H_
+#define _EVENT_QUEUE_H_
+/* Copyright (C) 2004-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+class Event_basic;
+class Event_db_repository;
+class Event_job_data;
+class Event_queue_element;
+
+class THD;
+class Event_scheduler;
+
+class Event_queue
+{
+public:
+ Event_queue();
+
+ void
+ init_mutexes();
+
+ void
+ deinit_mutexes();
+
+ bool
+ init_queue(THD *thd, Event_db_repository *db_repo);
+
+ void
+ deinit_queue();
+
+ /* Methods for queue management follow */
+
+ int
+ create_event(THD *thd, LEX_STRING dbname, LEX_STRING name);
+
+ int
+ update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
+ LEX_STRING *new_schema, LEX_STRING *new_name);
+
+ void
+ drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name);
+
+ void
+ drop_schema_events(THD *thd, LEX_STRING schema);
+
+ void
+ recalculate_activation_times(THD *thd);
+
+ bool
+ get_top_for_execution_if_time(THD *thd, Event_job_data **job_data);
+
+ void
+ dump_internal_status();
+
+ int
+ load_events_from_db(THD *thd);
+
+protected:
+ void
+ find_n_remove_event(LEX_STRING db, LEX_STRING name);
+
+
+ void
+ drop_matching_events(THD *thd, LEX_STRING pattern,
+ bool (*)(LEX_STRING, Event_basic *));
+
+ void
+ empty_queue();
+
+ void
+ dbug_dump_queue(time_t now);
+
+ /* LOCK_event_queue is the mutex which protects the access to the queue. */
+ pthread_mutex_t LOCK_event_queue;
+ pthread_cond_t COND_queue_state;
+
+ Event_db_repository *db_repository;
+
+ Event_scheduler *scheduler;
+
+ /* The sorted queue with the Event_job_data objects */
+ QUEUE queue;
+
+ TIME next_activation_at;
+
+ uint mutex_last_locked_at_line;
+ uint mutex_last_unlocked_at_line;
+ uint mutex_last_attempted_lock_at_line;
+ const char* mutex_last_locked_in_func;
+ const char* mutex_last_unlocked_in_func;
+ const char* mutex_last_attempted_lock_in_func;
+ bool mutex_queue_data_locked;
+ 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_ */
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index 1b4a0d290e6..6f9f6887c12 100644
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -15,228 +15,10 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "mysql_priv.h"
-#include "events_priv.h"
#include "events.h"
-#include "event_timed.h"
+#include "event_data_objects.h"
#include "event_scheduler.h"
-#include "sp_head.h"
-
-/*
- ToDo:
- 1. Talk to Alik to get a check for configure.in for my_time_t and time_t
- 2. Look at guardian.h|cc to see its life cycle, has similarities.
-*/
-
-
-/*
- The scheduler is implemented as class Event_scheduler. Only one instance is
- kept during the runtime of the server, by implementing the Singleton DP.
- Object instance is always there because the memory is allocated statically
- and initialized when the OS loader loads mysqld. This initialization is
- bare. Extended initialization is done during the call to
- Event_scheduler::init() in Events::init(). The reason for that late initialization
- is that some subsystems needed to boot the Scheduler are not available at
- earlier stages of the mysqld boot procedure. Events::init() is called in
- mysqld.cc . If the mysqld is started with --event-scheduler=0 then
- no initialization takes place and the scheduler is unavailable during this
- server run. The server should be started with --event-scheduler=1 to have
- the scheduler initialized and able to execute jobs. This starting alwa
- s implies that the jobs execution will start immediately. If the server
- is started with --event-scheduler=2 then the scheduler is started in suspended
- state. Default state, if --event-scheduler is not specified is 2.
-
- The scheduler only manages execution of the events. Their creation,
- alteration and deletion is delegated to other routines found in event.cc .
- These routines interact with the scheduler :
- - CREATE EVENT -> Event_scheduler::create_event()
- - ALTER EVENT -> Event_scheduler::update_event()
- - DROP EVENT -> Event_scheduler::drop_event()
-
- There is one mutex in the single Event_scheduler object which controls
- the simultaneous access to the objects invariants. Using one lock makes
- it easy to follow the workflow. This mutex is LOCK_scheduler_data. It is
- initialized in Event_scheduler::init(). Which in turn is called by the
- Facade class Events in event.cc, coming from init_thread_environment() from
- mysqld.cc -> no concurrency at this point. It's destroyed in
- Events::destroy_mutexes() called from clean_up_mutexes() in mysqld.cc .
-
- The full initialization is done in Event_scheduler::init() called from
- Events::init(). It's done before any requests coming in, so this is a
- guarantee for not having concurrency.
-
- The scheduler is started with Event_scheduler::start() and stopped with
- Event_scheduler::stop(). When the scheduler starts it loads all events
- from mysql.event table. Unfortunately, there is a race condition between
- the event disk management functions and the scheduler ones
- (add/replace/drop_event & load_events_from_db()), because the operations
- do not happen under one global lock but the disk operations are guarded
- by the MYISAM lock on mysql.event. In the same time, the queue operations
- are guarded by LOCK_scheduler_data. If the scheduler is start()-ed during
- server startup and stopped()-ed during server shutdown (in Events::shutdown()
- called by kill_server() in mysqld.cc) these races does not exist.
-
- Since the user may want to temporarily inhibit execution of events the
- scheduler can be suspended and then it can be forced to resume its
- operations. The API call to perform these is
- Event_scheduler::suspend_or_resume(enum enum_suspend_or_resume) .
- When the scheduler is suspended the main scheduler thread, which ATM
- happens to have thread_id 1, locks on a condition COND_suspend_or_resume.
- When this is signal is sent for the reverse operation the main scheduler
- loops continues to roll and execute events.
-
- When the scheduler is suspended all add/replace/drop_event() operations
- work as expected and the modify the queue but no events execution takes
- place.
-
- In contrast to the previous scheduler implementation, found in
- event_executor.cc, the start, shutdown, suspend and resume are synchronous
- operations. As a whole all operations are synchronized and no busy waits
- are used except in stop_all_running_events(), which waits until all
- running event worker threads have finished. It would have been nice to
- use a conditional on which this method will wait and the last thread to
- finish would signal it but this implies subclassing THD.
-
- The scheduler does not keep a counter of how many event worker threads are
- running, at any specific moment, because this will copy functionality
- already existing in the server. Namely, all THDs are registered in the
- global `threads` array. THD has member variable system_thread which
- identifies the type of thread. Connection threads being NON_SYSTEM_THREAD,
- all other have their enum value. Important for the scheduler are
- SYSTEM_THREAD_EVENT_SCHEDULER and SYSTEM_THREAD_EVENT_WORKER.
-
- Class THD subclasses class ilink, which is the linked list of all threads.
- When a THD instance is destroyed it's being removed from threads, thus
- no manual intervention is needed. On the contrary registering is manual
- with threads.append() . Traversing the threads array every time a subclass
- of THD, for instance if we would have had THD_scheduler_worker to see
- how many events we have and whether the scheduler is shutting down will
- take much time and lead to a deadlock. stop_all_running_events() is called
- under LOCK_scheduler_data. If the THD_scheduler_worker was aware of
- the single Event_scheduler instance it will try to check
- Event_scheduler::state but for this it would need to acquire
- LOCK_scheduler_data => deadlock. Thus stop_all_running_events() uses a
- busy wait.
-
- DROP DATABASE DDL should drop all events defined in a specific schema.
- DROP USER also should drop all events who has as definer the user being
- dropped (this one is not addressed at the moment but a hook exists). For
- this specific needs Event_scheduler::drop_matching_events() is
- implemented. Which expects a callback to be applied on every object in
- the queue. Thus events that match specific schema or user, will be
- removed from the queue. The exposed interface is :
- - Event_scheduler::drop_schema_events()
- - Event_scheduler::drop_user_events()
-
- This bulk dropping happens under LOCK_scheduler_data, thus no two or
- more threads can execute it in parallel. However, DROP DATABASE is also
- synchronized, currently, in the server thus this does not impact the
- overall performance. In addition, DROP DATABASE is not that often
- executed DDL.
-
- Though the interface to the scheduler is only through the public methods
- of class Event_scheduler, there are currently few functions which are
- used during its operations. Namely :
- - static evex_print_warnings()
- After every event execution all errors/warnings are dumped, so the user
- can see in case of a problem what the problem was.
-
- - static init_event_thread()
- This function is both used by event_scheduler_thread() and
- event_worker_thread(). It initializes the THD structure. The
- initialization looks pretty similar to the one in slave.cc done for the
- replication threads. However, though the similarities it cannot be
- factored out to have one routine.
-
- - static event_scheduler_thread()
- Because our way to register functions to be used by the threading library
- does not allow usage of static methods this function is used to start the
- scheduler in it. It does THD initialization and then calls
- Event_scheduler::run().
-
- - static event_worker_thread()
- With already stated the reason for not being able to use methods, this
- function executes the worker threads.
-
- The execution of events is, to some extent, synchronized to inhibit race
- conditions when Event_timed::thread_id is being updated with the thread_id of
- the THD in which the event is being executed. The thread_id is in the
- Event_timed object because we need to be able to kill quickly a specific
- event during ALTER/DROP EVENT without traversing the global `threads` array.
- However, this makes the scheduler's code more complicated. The event worker
- thread is started by Event_timed::spawn_now(), which in turn calls
- pthread_create(). The thread_id which will be associated in init_event_thread
- is not known in advance thus the registering takes place in
- event_worker_thread(). This registering has to be synchronized under
- LOCK_scheduler_data, so no kill_event() on a object in
- replace_event/drop_event/drop_matching_events() could take place.
-
- This synchronization is done through class Worker_thread_param that is
- local to this file. Event_scheduler::execute_top() is called under
- LOCK_scheduler_data. This method :
- 1. Creates an instance of Worker_thread_param on the stack
- 2. Locks Worker_thread_param::LOCK_started
- 3. Calls Event_timed::spawn_now() which in turn creates a new thread.
- 4. Locks on Worker_thread_param::COND_started_or_stopped and waits till the
- worker thread send signal. The code is spurious wake-up safe because
- Worker_thread_param::started is checked.
- 5. The worker thread initializes its THD, then sets Event_timed::thread_id,
- sets Worker_thread_param::started to TRUE and sends back
- Worker_thread_param::COND_started. From this moment on, the event
- is being executed and could be killed by using Event_timed::thread_id.
- When Event_timed::spawn_thread_finish() is called in the worker thread,
- it sets thread_id to 0. From this moment on, the worker thread should not
- touch the Event_timed instance.
-
-
- The life-cycle of the server is a FSA.
- enum enum_state Event_scheduler::state keeps the state of the scheduler.
-
- The states are:
-
- |---UNINITIALIZED
- |
- | |------------------> IN_SHUTDOWN
- --> INITIALIZED -> COMMENCING ---> RUNNING ----------|
- ^ ^ | | ^ |
- | |- CANTSTART <--| | |- SUSPENDED <-|
- |______________________________|
-
- - UNINITIALIZED :The object is created and only the mutex is initialized
- - INITIALIZED :All member variables are initialized
- - COMMENCING :The scheduler is starting, no other attempt to start
- should succeed before the state is back to INITIALIZED.
- - CANTSTART :Set by the ::run() method in case it can't start for some
- reason. In this case the connection thread that tries to
- start the scheduler sees that some error has occurred and
- returns an error to the user. Finally, the connection
- thread sets the state to INITIALIZED, so further attempts
- to start the scheduler could be made.
- - RUNNING :The scheduler is running. New events could be added,
- dropped, altered. The scheduler could be stopped.
- - SUSPENDED :Like RUNNING but execution of events does not take place.
- Operations on the memory queue are possible.
- - IN_SHUTDOWN :The scheduler is shutting down, due to request by setting
- the global event_scheduler to 0/FALSE, or because of a
- KILL command sent by a user to the master thread.
-
- In every method the macros LOCK_SCHEDULER_DATA() and UNLOCK_SCHEDULER_DATA()
- are used for (un)locking purposes. They are used to save the programmer
- from typing everytime
- lock_data(__FUNCTION__, __LINE__);
- All locking goes through Event_scheduler::lock_data() and ::unlock_data().
- These two functions then record in variables where for last time
- LOCK_scheduler_data was locked and unlocked (two different variables). In
- multithreaded environment, in some cases they make no sense but are useful for
- inspecting deadlocks without having the server debug log turned on and the
- server is still running.
-
- The same strategy is used for conditional variables.
- Event_scheduler::cond_wait() is invoked from all places with parameter
- an enum enum_cond_vars. In this manner, it's possible to inspect the last
- on which condition the last call to cond_wait() was waiting. If the server
- was started with debug trace switched on, the trace file also holds information
- about conditional variables used.
-*/
+#include "event_queue.h"
#ifdef __GNUC__
#if __GNUC__ >= 2
@@ -246,102 +28,41 @@
#define SCHED_FUNC "<unknown>"
#endif
-#define LOCK_SCHEDULER_DATA() lock_data(SCHED_FUNC, __LINE__)
-#define UNLOCK_SCHEDULER_DATA() unlock_data(SCHED_FUNC, __LINE__)
+#define LOCK_DATA() lock_data(SCHED_FUNC, __LINE__)
+#define UNLOCK_DATA() unlock_data(SCHED_FUNC, __LINE__)
+#define COND_STATE_WAIT(mythd, abstime, msg) \
+ cond_wait(mythd, abstime, msg, SCHED_FUNC, __LINE__)
+extern pthread_attr_t connection_attrib;
-#ifndef DBUG_OFF
static
-LEX_STRING states_names[] =
-{
- {(char*) STRING_WITH_LEN("UNINITIALIZED")},
- {(char*) STRING_WITH_LEN("INITIALIZED")},
- {(char*) STRING_WITH_LEN("COMMENCING")},
- {(char*) STRING_WITH_LEN("CANTSTART")},
- {(char*) STRING_WITH_LEN("RUNNING")},
- {(char*) STRING_WITH_LEN("SUSPENDED")},
- {(char*) STRING_WITH_LEN("IN_SHUTDOWN")}
-};
-#endif
-
-
-Event_scheduler
-Event_scheduler::singleton;
-
-
-const char * const
-Event_scheduler::cond_vars_names[Event_scheduler::COND_LAST] =
+const LEX_STRING scheduler_states_names[] =
{
- "new work",
- "started or stopped",
- "suspend or resume"
+ { C_STRING_WITH_LEN("UNINITIALIZED") },
+ { C_STRING_WITH_LEN("INITIALIZED") },
+ { C_STRING_WITH_LEN("RUNNING") },
+ { C_STRING_WITH_LEN("STOPPING") }
};
-
-class Worker_thread_param
-{
-public:
- Event_timed *et;
- pthread_mutex_t LOCK_started;
- pthread_cond_t COND_started;
- bool started;
-
- Worker_thread_param(Event_timed *etn):et(etn), started(FALSE)
- {
- pthread_mutex_init(&LOCK_started, MY_MUTEX_INIT_FAST);
- pthread_cond_init(&COND_started, NULL);
- }
-
- ~Worker_thread_param()
- {
- pthread_mutex_destroy(&LOCK_started);
- pthread_cond_destroy(&COND_started);
- }
+struct scheduler_param {
+ THD *thd;
+ Event_scheduler *scheduler;
};
/*
- Compares the execute_at members of 2 Event_timed instances.
- Used as callback for the prioritized queue when shifting
- elements inside.
-
- SYNOPSIS
- event_timed_compare_q()
-
- vptr - not used (set it to NULL)
- a - first Event_timed object
- b - second Event_timed object
-
- RETURN VALUE
- -1 - a->execute_at < b->execute_at
- 0 - a->execute_at == b->execute_at
- 1 - a->execute_at > b->execute_at
-
- NOTES
- execute_at.second_part is not considered during comparison
-*/
-
-static int
-event_timed_compare_q(void *vptr, byte* a, byte *b)
-{
- return my_time_compare(&((Event_timed *)a)->execute_at,
- &((Event_timed *)b)->execute_at);
-}
-
-
-/*
Prints the stack of infos, warnings, errors from thd to
the console so it can be fetched by the logs-into-tables and
checked later.
SYNOPSIS
evex_print_warnings
- thd - thread used during the execution of the event
- et - the event itself
+ thd Thread used during the execution of the event
+ et The event itself
*/
static void
-evex_print_warnings(THD *thd, Event_timed *et)
+evex_print_warnings(THD *thd, Event_job_data *et)
{
MYSQL_ERROR *err;
DBUG_ENTER("evex_print_warnings");
@@ -354,9 +75,7 @@ evex_print_warnings(THD *thd, Event_timed *et)
prefix.length(0);
prefix.append("SCHEDULER: [");
- append_identifier(thd, &prefix, et->definer_user.str, et->definer_user.length);
- prefix.append('@');
- append_identifier(thd, &prefix, et->definer_host.str, et->definer_host.length);
+ append_identifier(thd, &prefix, et->definer.str, et->definer.length);
prefix.append("][", 2);
append_identifier(thd,&prefix, et->dbname.str, et->dbname.length);
prefix.append('.');
@@ -381,86 +100,116 @@ evex_print_warnings(THD *thd, Event_timed *et)
/*
- Inits an scheduler thread handler, both the main and a worker
+ Performs post initialization of structures in a new thread.
+
+ SYNOPSIS
+ post_init_event_thread()
+ thd Thread
+*/
+
+bool
+post_init_event_thread(THD *thd)
+{
+ my_thread_init();
+ pthread_detach_this_thread();
+ thd->real_id= pthread_self();
+ if (init_thr_lock() || thd->store_globals())
+ {
+ thd->cleanup();
+ return TRUE;
+ }
+
+#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
+ sigset_t set;
+ VOID(sigemptyset(&set)); // Get mask in use
+ VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
+#endif
+ pthread_mutex_lock(&LOCK_thread_count);
+ threads.append(thd);
+ thread_count++;
+ thread_running++;
+ pthread_mutex_unlock(&LOCK_thread_count);
+
+ return FALSE;
+}
+
+
+/*
+ Cleans up the THD and the threaded environment of the thread.
+
+ SYNOPSIS
+ deinit_event_thread()
+ thd Thread
+*/
+
+void
+deinit_event_thread(THD *thd)
+{
+ thd->proc_info= "Clearing";
+ DBUG_ASSERT(thd->net.buff != 0);
+ net_end(&thd->net);
+ DBUG_PRINT("exit", ("Event thread finishing"));
+ pthread_mutex_lock(&LOCK_thread_count);
+ thread_count--;
+ thread_running--;
+ delete thd;
+ pthread_mutex_unlock(&LOCK_thread_count);
+
+ my_thread_end();
+}
+
+
+/*
+ Performs pre- pthread_create() initialisation of THD. Do this
+ in the thread that will pass THD to the child thread. In the
+ child thread call post_init_event_thread().
SYNOPSIS
- init_event_thread()
- thd - the THD of the thread. Has to be allocated by the caller.
+ pre_init_event_thread()
+ thd The THD of the thread. Has to be allocated by the caller.
NOTES
1. The host of the thead is my_localhost
2. thd->net is initted with NULL - no communication.
-
- RETURN VALUE
- 0 OK
- -1 Error
*/
-static int
-init_event_thread(THD** t, enum enum_thread_type thread_type)
+void
+pre_init_event_thread(THD* thd)
{
- THD *thd= *t;
- thd->thread_stack= (char*)t; // remember where our stack is
- DBUG_ENTER("init_event_thread");
+ DBUG_ENTER("pre_init_event_thread");
thd->client_capabilities= 0;
thd->security_ctx->master_access= 0;
thd->security_ctx->db_access= 0;
thd->security_ctx->host_or_ip= (char*)my_localhost;
- my_net_init(&thd->net, 0);
+ my_net_init(&thd->net, NULL);
+ thd->security_ctx->set_user((char*)"event_scheduler");
thd->net.read_timeout= slave_net_timeout;
thd->slave_thread= 0;
thd->options|= OPTION_AUTO_IS_NULL;
thd->client_capabilities|= CLIENT_MULTI_RESULTS;
- thd->real_id=pthread_self();
- VOID(pthread_mutex_lock(&LOCK_thread_count));
+ pthread_mutex_lock(&LOCK_thread_count);
thd->thread_id= thread_id++;
- threads.append(thd);
- thread_count++;
- thread_running++;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
-
- if (init_thr_lock() || thd->store_globals())
- {
- thd->cleanup();
- DBUG_RETURN(-1);
- }
-
-#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
- sigset_t set;
- VOID(sigemptyset(&set)); // Get mask in use
- VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
-#endif
+ pthread_mutex_unlock(&LOCK_thread_count);
/*
Guarantees that we will see the thread in SHOW PROCESSLIST though its
vio is NULL.
*/
- thd->system_thread= thread_type;
thd->proc_info= "Initialized";
thd->version= refresh_version;
thd->set_time();
- DBUG_RETURN(0);
+ DBUG_VOID_RETURN;
}
/*
- Inits the main scheduler thread and then calls Event_scheduler::run()
- of arg.
+ Function that executes the scheduler,
SYNOPSIS
event_scheduler_thread()
- arg void* ptr to Event_scheduler
-
- NOTES
- 1. The host of the thead is my_localhost
- 2. thd->net is initted with NULL - no communication.
- 3. The reason to have a proxy function is that it's not possible to
- use a method as function to be executed in a spawned thread:
- - our pthread_hander_t macro uses extern "C"
- - separating thread setup from the real execution loop is also to be
- considered good.
+ arg Pointer to `struct scheduler_param`
RETURN VALUE
0 OK
@@ -470,58 +219,21 @@ pthread_handler_t
event_scheduler_thread(void *arg)
{
/* needs to be first for thread_stack */
- THD *thd= NULL;
- Event_scheduler *scheduler= (Event_scheduler *) arg;
+ THD *thd= (THD *)((struct scheduler_param *) arg)->thd;
+ Event_scheduler *scheduler= ((struct scheduler_param *) arg)->scheduler;
- DBUG_ENTER("event_scheduler_thread");
+ my_free((char*)arg, MYF(0));
- my_thread_init();
- pthread_detach_this_thread();
+ thd->thread_stack= (char *)&thd; // remember where our stack is
- /* note that constructor of THD uses DBUG_ ! */
- if (!(thd= new THD) || init_event_thread(&thd, SYSTEM_THREAD_EVENT_SCHEDULER))
- {
- sql_print_error("SCHEDULER: Cannot init manager event thread.");
- scheduler->report_error_during_start();
- }
- else
- {
- thd->security_ctx->set_user((char*)"event_scheduler");
+ DBUG_ENTER("event_scheduler_thread");
- sql_print_information("SCHEDULER: Manager thread booting");
- if (Event_scheduler::check_system_tables(thd))
- scheduler->report_error_during_start();
- else
- scheduler->run(thd);
+ if (!post_init_event_thread(thd))
+ scheduler->run(thd);
- /*
- NOTE: Don't touch `scheduler` after this point because we have notified
- the
- thread which shuts us down that we have finished cleaning. In this
- very moment a new scheduler thread could be started and a crash is
- not welcome.
- */
- }
+ deinit_event_thread(thd);
- /*
- If we cannot create THD then don't decrease because we haven't touched
- thread_count and thread_running in init_event_thread() which was never
- called. In init_event_thread() thread_count and thread_running are
- always increased even in the case the method returns an error.
- */
- if (thd)
- {
- thd->proc_info= "Clearing";
- DBUG_ASSERT(thd->net.buff != 0);
- net_end(&thd->net);
- pthread_mutex_lock(&LOCK_thread_count);
- thread_count--;
- thread_running--;
- delete thd;
- pthread_mutex_unlock(&LOCK_thread_count);
- }
- my_thread_end();
- DBUG_RETURN(0); // Can't return anything here
+ DBUG_RETURN(0); // Against gcc warnings
}
@@ -531,7 +243,7 @@ event_scheduler_thread(void *arg)
SYNOPSIS
event_worker_thread()
- arg The Event_timed object to be processed
+ arg The Event_job_data object to be processed
RETURN VALUE
0 OK
@@ -540,1241 +252,392 @@ event_scheduler_thread(void *arg)
pthread_handler_t
event_worker_thread(void *arg)
{
- THD *thd; /* needs to be first for thread_stack */
- Worker_thread_param *param= (Worker_thread_param *) arg;
- Event_timed *event= param->et;
+ /* needs to be first for thread_stack */
+ THD *thd;
+ Event_job_data *event= (Event_job_data *)arg;
int ret;
- bool startup_error= FALSE;
- Security_context *save_ctx;
- /* this one is local and not needed after exec */
- Security_context security_ctx;
- DBUG_ENTER("event_worker_thread");
- DBUG_PRINT("enter", ("event=[%s.%s]", event->dbname.str, event->name.str));
+ thd= event->thd;
- my_thread_init();
- pthread_detach_this_thread();
+ thd->thread_stack= (char *) &thd; // remember where our stack is
+ DBUG_ENTER("event_worker_thread");
- if (!(thd= new THD) || init_event_thread(&thd, SYSTEM_THREAD_EVENT_WORKER))
+ if (!post_init_event_thread(thd))
{
- sql_print_error("SCHEDULER: Startup failure.");
- startup_error= TRUE;
- event->spawn_thread_finish(thd);
- }
- else
- event->set_thread_id(thd->thread_id);
+ DBUG_PRINT("info", ("Baikonur, time is %d, BURAN reporting and operational."
+ "THD=0x%lx", time(NULL), thd));
+
+ sql_print_information("SCHEDULER: [%s.%s of %s] executing in thread %lu. "
+ "Execution %u",
+ event->dbname.str, event->name.str,
+ event->definer.str, thd->thread_id,
+ event->execution_count);
- DBUG_PRINT("info", ("master_access=%d db_access=%d",
- thd->security_ctx->master_access, thd->security_ctx->db_access));
- /*
- If we don't change it before we send the signal back, then an intermittent
- DROP EVENT will take LOCK_scheduler_data and try to kill this thread, because
- event->thread_id is already real. However, because thd->security_ctx->user
- is not initialized then a crash occurs in kill_one_thread(). Thus, we have
- to change the context before sending the signal. We are under
- LOCK_scheduler_data being held by Event_scheduler::run() -> ::execute_top().
- */
- change_security_context(thd, event->definer_user, event->definer_host,
- event->dbname, &security_ctx, &save_ctx);
- DBUG_PRINT("info", ("master_access=%d db_access=%d",
- thd->security_ctx->master_access, thd->security_ctx->db_access));
-
- /* Signal the scheduler thread that we have started successfully */
- pthread_mutex_lock(&param->LOCK_started);
- param->started= TRUE;
- pthread_cond_signal(&param->COND_started);
- pthread_mutex_unlock(&param->LOCK_started);
-
- if (!startup_error)
- {
- thd->init_for_queries();
thd->enable_slow_log= TRUE;
- event->set_thread_id(thd->thread_id);
- sql_print_information("SCHEDULER: [%s.%s of %s] executing in thread %lu",
- event->dbname.str, event->name.str,
- event->definer.str, thd->thread_id);
+ ret= event->execute(thd);
- ret= event->execute(thd, thd->mem_root);
evex_print_warnings(thd, event);
- sql_print_information("SCHEDULER: [%s.%s of %s] executed. RetCode=%d",
- event->dbname.str, event->name.str,
- event->definer.str, ret);
+
+ sql_print_information("SCHEDULER: [%s.%s of %s] executed in thread %lu. "
+ "RetCode=%d", event->dbname.str, event->name.str,
+ event->definer.str, thd->thread_id, ret);
if (ret == EVEX_COMPILE_ERROR)
sql_print_information("SCHEDULER: COMPILE ERROR for event %s.%s of %s",
event->dbname.str, event->name.str,
event->definer.str);
else if (ret == EVEX_MICROSECOND_UNSUP)
sql_print_information("SCHEDULER: MICROSECOND is not supported");
-
- DBUG_PRINT("info", ("master_access=%d db_access=%d",
- thd->security_ctx->master_access, thd->security_ctx->db_access));
-
- /* If true is returned, we are expected to free it */
- if (event->spawn_thread_finish(thd))
- {
- DBUG_PRINT("info", ("Freeing object pointer"));
- delete event;
- }
}
+ DBUG_PRINT("info", ("BURAN %s.%s is landing!", event->dbname.str,
+ event->name.str));
+ delete event;
- if (thd)
- {
- thd->proc_info= "Clearing";
- DBUG_ASSERT(thd->net.buff != 0);
- /*
- Free it here because net.vio is NULL for us => THD::~THD will check it
- and won't call net_end(&net); See also replication code.
- */
- net_end(&thd->net);
- DBUG_PRINT("info", ("Worker thread %lu exiting", thd->thread_id));
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- thread_count--;
- thread_running--;
- delete thd;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
- }
+ deinit_event_thread(thd);
- my_thread_end();
DBUG_RETURN(0); // Can't return anything here
}
/*
- Constructor of class Event_scheduler.
-
- SYNOPSIS
- Event_scheduler::Event_scheduler()
-*/
-
-Event_scheduler::Event_scheduler()
- :state(UNINITIALIZED), start_scheduler_suspended(FALSE),
- thread_id(0), mutex_last_locked_at_line(0),
- mutex_last_unlocked_at_line(0), mutex_last_locked_in_func(""),
- mutex_last_unlocked_in_func(""), cond_waiting_on(COND_NONE),
- mutex_scheduler_data_locked(FALSE)
-{
-}
-
-
-/*
- Returns the singleton instance of the class.
+ Performs initialization of the scheduler data, outside of the
+ threading primitives.
SYNOPSIS
- Event_scheduler::get_instance()
-
- RETURN VALUE
- address
+ Event_scheduler::init_scheduler()
*/
-Event_scheduler*
-Event_scheduler::get_instance()
-{
- DBUG_ENTER("Event_scheduler::get_instance");
- DBUG_RETURN(&singleton);
-}
-
-
-/*
- The implementation of full-fledged initialization.
-
- SYNOPSIS
- Event_scheduler::init()
-
- RETURN VALUE
- FALSE OK
- TRUE Error
-*/
-
-bool
-Event_scheduler::init()
+void
+Event_scheduler::init_scheduler(Event_queue *q)
{
- int i= 0;
- bool ret= FALSE;
- DBUG_ENTER("Event_scheduler::init");
- DBUG_PRINT("enter", ("this=%p", this));
-
- LOCK_SCHEDULER_DATA();
- for (;i < COND_LAST; i++)
- if (pthread_cond_init(&cond_vars[i], NULL))
- {
- sql_print_error("SCHEDULER: Unable to initalize conditions");
- ret= TRUE;
- goto end;
- }
-
- /* init memory root */
- init_alloc_root(&scheduler_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
-
- if (init_queue_ex(&queue, 30 /*num_el*/, 0 /*offset*/, 0 /*smallest_on_top*/,
- event_timed_compare_q, NULL, 30 /*auto_extent*/))
- {
- sql_print_error("SCHEDULER: Can't initialize the execution queue");
- ret= TRUE;
- goto end;
- }
-
- if (sizeof(my_time_t) != sizeof(time_t))
- {
- sql_print_error("SCHEDULER: sizeof(my_time_t) != sizeof(time_t) ."
- "The scheduler may not work correctly. Stopping.");
- DBUG_ASSERT(0);
- ret= TRUE;
- goto end;
- }
-
+ LOCK_DATA();
+ queue= q;
+ started_events= 0;
+ scheduler_thd= NULL;
state= INITIALIZED;
-end:
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(ret);
+ UNLOCK_DATA();
}
-/*
- Frees all memory allocated by the scheduler object.
-
- SYNOPSIS
- Event_scheduler::destroy()
-
- RETURN VALUE
- FALSE OK
- TRUE Error
-*/
-
void
-Event_scheduler::destroy()
-{
- DBUG_ENTER("Event_scheduler");
-
- LOCK_SCHEDULER_DATA();
- switch (state) {
- case UNINITIALIZED:
- break;
- case INITIALIZED:
- delete_queue(&queue);
- free_root(&scheduler_root, MYF(0));
- int i;
- for (i= 0; i < COND_LAST; i++)
- pthread_cond_destroy(&cond_vars[i]);
- state= UNINITIALIZED;
- break;
- default:
- sql_print_error("SCHEDULER: Destroying while state is %d", state);
- /* I trust my code but ::safe() > ::sorry() */
- DBUG_ASSERT(0);
- break;
- }
- UNLOCK_SCHEDULER_DATA();
-
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Creates an event in the scheduler queue
-
- SYNOPSIS
- Event_scheduler::create_event()
- et The event to add
- check_existence Whether to check if already loaded.
-
- RETURN VALUE
- OP_OK OK or scheduler not working
- OP_LOAD_ERROR Error during loading from disk
-*/
-
-enum Event_scheduler::enum_error_code
-Event_scheduler::create_event(THD *thd, Event_timed *et, bool check_existence)
-{
- enum enum_error_code res;
- Event_timed *et_new;
- DBUG_ENTER("Event_scheduler::create_event");
- DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et,&LOCK_scheduler_data));
-
- LOCK_SCHEDULER_DATA();
- if (!is_running_or_suspended())
- {
- DBUG_PRINT("info", ("scheduler not running but %d. doing nothing", state));
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(OP_OK);
- }
- if (check_existence && find_event(et, FALSE))
- {
- res= OP_ALREADY_EXISTS;
- goto end;
- }
-
- /* We need to load the event on scheduler_root */
- if (!(res= load_named_event(thd, et, &et_new)))
- {
- queue_insert_safe(&queue, (byte *) et_new);
- DBUG_PRINT("info", ("Sending COND_new_work"));
- pthread_cond_signal(&cond_vars[COND_new_work]);
- }
- else if (res == OP_DISABLED_EVENT)
- res= OP_OK;
-end:
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(res);
-}
-
-
-/*
- Drops an event from the scheduler queue
-
- SYNOPSIS
- Event_scheduler::drop_event()
- etn The event to drop
- state Wait the event or kill&drop
-
- RETURN VALUE
- FALSE OK (replaced or scheduler not working)
- TRUE Failure
-*/
-
-bool
-Event_scheduler::drop_event(THD *thd, Event_timed *et)
-{
- int res;
- Event_timed *et_old;
- DBUG_ENTER("Event_scheduler::drop_event");
- DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et,&LOCK_scheduler_data));
-
- LOCK_SCHEDULER_DATA();
- if (!is_running_or_suspended())
- {
- DBUG_PRINT("info", ("scheduler not running but %d. doing nothing", state));
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(OP_OK);
- }
-
- if (!(et_old= find_event(et, TRUE)))
- DBUG_PRINT("info", ("No such event found, probably DISABLED"));
-
- UNLOCK_SCHEDULER_DATA();
-
- /* See comments in ::replace_event() why this is split in two parts. */
- 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;
- /*
- We don't signal COND_new_work here because:
- 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:
- sql_print_error("SCHEDULER: Got unexpected error %d", res);
- DBUG_ASSERT(0);
- }
- }
-
- DBUG_RETURN(FALSE);
-}
-
-
-/*
- Updates an event from the scheduler queue
-
- SYNOPSIS
- Event_scheduler::replace_event()
- et The event to replace(add) into the queue
- state Async or sync stopping
-
- RETURN VALUE
- OP_OK OK or scheduler not working
- OP_LOAD_ERROR Error during loading from disk
- OP_ALREADY_EXISTS Event already in the queue
-*/
-
-enum Event_scheduler::enum_error_code
-Event_scheduler::update_event(THD *thd, Event_timed *et,
- LEX_STRING *new_schema,
- LEX_STRING *new_name)
-{
- enum enum_error_code res;
- Event_timed *et_old, *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_scheduler::update_event");
- DBUG_PRINT("enter", ("thd=%p et=%p et=[%s.%s] lock=%p",
- thd, et, et->dbname.str, et->name.str, &LOCK_scheduler_data));
-
- LOCK_SCHEDULER_DATA();
- if (!is_running_or_suspended())
- {
- DBUG_PRINT("info", ("scheduler not running but %d. doing nothing", state));
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(OP_OK);
- }
-
- if (!(et_old= find_event(et, TRUE)))
- DBUG_PRINT("info", ("%s.%s not found cached, probably was DISABLED",
- 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;
- }
- /*
- We need to load the event (it's strings but on the object itself)
- on scheduler_root. et_new could be NULL :
- 1. Error occured
- 2. If the replace is DISABLED, we don't load it into the queue.
- */
- if (!(res= load_named_event(thd, et, &et_new)))
- {
- queue_insert_safe(&queue, (byte *) et_new);
- DBUG_PRINT("info", ("Sending COND_new_work"));
- pthread_cond_signal(&cond_vars[COND_new_work]);
- }
- else if (res == OP_DISABLED_EVENT)
- res= OP_OK;
-
- if (new_schema && new_name)
- {
- et->dbname= old_schema;
- et->name= old_name;
- }
-
- UNLOCK_SCHEDULER_DATA();
- /*
- Andrey: Is this comment still truthful ???
-
- We don't move this code above because a potential kill_thread will call
- THD::awake(). Which in turn will try to acqure mysys_var->current_mutex,
- which is LOCK_scheduler_data 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_scheduler_data however is not needed because
- we don't touch any invariant of the scheduler anymore. ::drop_event() does
- the same.
- */
- 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;
- /*
- We don't signal COND_new_work here because:
- 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);
-}
+Event_scheduler::deinit_scheduler() {}
/*
- Searches for an event in the scheduler queue
+ Inits scheduler's threading primitives.
SYNOPSIS
- Event_scheduler::find_event()
- etn The event to find
- comparator The function to use for comparing
- remove_from_q If found whether to remove from the Q
-
- RETURN VALUE
- NULL Not found
- otherwise Address
-
- NOTE
- The caller should do the locking also the caller is responsible for
- actual signalling in case an event is removed from the queue
- (signalling COND_new_work for instance).
-*/
-
-Event_timed *
-Event_scheduler::find_event(Event_timed *etn, bool remove_from_q)
-{
- uint i;
- DBUG_ENTER("Event_scheduler::find_event");
-
- for (i= 0; i < queue.elements; ++i)
- {
- Event_timed *et= (Event_timed *) queue_element(&queue, i);
- DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?", etn->dbname.str, etn->name.str,
- et->dbname.str, et->name.str));
- if (event_timed_identifier_equal(etn, et))
- {
- if (remove_from_q)
- queue_remove(&queue, i);
- DBUG_RETURN(et);
- }
- }
-
- DBUG_RETURN(NULL);
-}
-
-
-/*
- Drops all events from the in-memory queue and disk that match
- certain pattern evaluated by a comparator function
-
- SYNOPSIS
- Event_scheduler::drop_matching_events()
- thd THD
- pattern A pattern string
- comparator The function to use for comparing
-
- RETURN VALUE
- -1 Scheduler not working
- >=0 Number of dropped events
-
- NOTE
- Expected is the caller to acquire lock on LOCK_scheduler_data
+ Event_scheduler::init_mutexes()
*/
void
-Event_scheduler::drop_matching_events(THD *thd, LEX_STRING *pattern,
- bool (*comparator)(Event_timed *,LEX_STRING *))
+Event_scheduler::init_mutexes()
{
- DBUG_ENTER("Event_scheduler::drop_matching_events");
- DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern->length, pattern->str,
- state));
- if (is_running_or_suspended())
- {
- uint i= 0, dropped= 0;
- while (i < queue.elements)
- {
- Event_timed *et= (Event_timed *) queue_element(&queue, i);
- DBUG_PRINT("info", ("[%s.%s]?", et->dbname.str, et->name.str));
- if (comparator(et, pattern))
- {
- /*
- The queue is ordered. If we remove an element, then all elements after
- it will shift one position to the left, if we imagine it as an array
- from left to the right. In this case we should not increment the
- counter and the (i < queue.elements) condition is ok.
- */
- 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;
- ++dropped;
- break;
- default:
- DBUG_ASSERT(0);
- }
- }
- else
- i++;
- }
- DBUG_PRINT("info", ("Dropped %lu", dropped));
- }
- /*
- Don't send COND_new_work because no need to wake up the scheduler thread.
- When it wakes next time up it will recalculate how much more it should
- sleep if the top of the queue has been changed by this method.
- */
-
- DBUG_VOID_RETURN;
+ pthread_mutex_init(&LOCK_scheduler_state, MY_MUTEX_INIT_FAST);
+ pthread_cond_init(&COND_state, NULL);
}
/*
- Drops all events from the in-memory queue and disk that are from
- certain schema.
+ Deinits scheduler's threading primitives.
SYNOPSIS
- Event_scheduler::drop_schema_events()
- thd THD
- db The schema name
-
- RETURN VALUE
- -1 Scheduler not working
- >=0 Number of dropped events
+ Event_scheduler::deinit_mutexes()
*/
-int
-Event_scheduler::drop_schema_events(THD *thd, LEX_STRING *schema)
+void
+Event_scheduler::deinit_mutexes()
{
- int ret;
- DBUG_ENTER("Event_scheduler::drop_schema_events");
- LOCK_SCHEDULER_DATA();
- if (is_running_or_suspended())
- drop_matching_events(thd, schema, event_timed_db_equal);
-
- ret= db_drop_events_from_table(thd, schema);
- UNLOCK_SCHEDULER_DATA();
-
- DBUG_RETURN(ret);
+ pthread_mutex_destroy(&LOCK_scheduler_state);
+ pthread_cond_destroy(&COND_state);
}
-extern pthread_attr_t connection_attrib;
-
-
/*
- Starts the event scheduler
+ Starts the scheduler (again). Creates a new THD and passes it to
+ a forked thread. Does not wait for acknowledgement from the new
+ thread that it has started. Asynchronous starting. Most of the
+ needed initializations are done in the current thread to minimize
+ the chance of failure in the spawned thread.
SYNOPSIS
Event_scheduler::start()
RETURN VALUE
FALSE OK
- TRUE Error
+ TRUE Error (not reported)
*/
bool
Event_scheduler::start()
{
+ THD *new_thd= NULL;
bool ret= FALSE;
pthread_t th;
+ struct scheduler_param *scheduler_param_value;
DBUG_ENTER("Event_scheduler::start");
- LOCK_SCHEDULER_DATA();
- /* If already working or starting don't make another attempt */
- DBUG_ASSERT(state == INITIALIZED);
+ LOCK_DATA();
+ DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state]));
if (state > INITIALIZED)
- {
- DBUG_PRINT("info", ("scheduler is already running or starting"));
- ret= TRUE;
goto end;
- }
- /*
- Now if another thread calls start it will bail-out because the branch
- above will be executed. Thus no two or more child threads will be forked.
- If the child thread cannot start for some reason then `state` is set
- to CANTSTART and COND_started is also signaled. In this case we
- set `state` back to INITIALIZED so another attempt to start the scheduler
- can be made.
- */
- state= COMMENCING;
- /* Fork */
- if (pthread_create(&th, &connection_attrib, event_scheduler_thread,
- (void*)this))
+ if (!(new_thd= new THD))
{
- DBUG_PRINT("error", ("cannot create a new thread"));
- state= INITIALIZED;
+ sql_print_error("SCHEDULER: Cannot init manager event thread");
ret= TRUE;
goto end;
}
+ pre_init_event_thread(new_thd);
+ new_thd->system_thread= SYSTEM_THREAD_EVENT_SCHEDULER;
+ new_thd->command= COM_DAEMON;
- /* Wait till the child thread has booted (w/ or wo success) */
- while (!is_running_or_suspended() && state != CANTSTART)
- cond_wait(COND_started_or_stopped, &LOCK_scheduler_data);
+ scheduler_param_value=
+ (struct scheduler_param *)my_malloc(sizeof(struct scheduler_param), MYF(0));
+ scheduler_param_value->thd= new_thd;
+ scheduler_param_value->scheduler= this;
- /*
- If we cannot start for some reason then don't prohibit further attempts.
- Set back to INITIALIZED.
- */
- if (state == CANTSTART)
+ scheduler_thd= new_thd;
+ DBUG_PRINT("info", ("Setting state go RUNNING"));
+ state= RUNNING;
+ DBUG_PRINT("info", ("Forking new thread for scheduduler. THD=0x%lx", new_thd));
+ if (pthread_create(&th, &connection_attrib, event_scheduler_thread,
+ (void*)scheduler_param_value))
{
+ DBUG_PRINT("error", ("cannot create a new thread"));
state= INITIALIZED;
+ scheduler_thd= NULL;
ret= TRUE;
- goto end;
- }
+ new_thd->proc_info= "Clearing";
+ DBUG_ASSERT(new_thd->net.buff != 0);
+ net_end(&new_thd->net);
+ pthread_mutex_lock(&LOCK_thread_count);
+ thread_count--;
+ thread_running--;
+ delete new_thd;
+ pthread_mutex_unlock(&LOCK_thread_count);
+ }
end:
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(ret);
-}
-
-
-/*
- Starts the event scheduler in suspended mode.
-
- SYNOPSIS
- Event_scheduler::start_suspended()
+ UNLOCK_DATA();
- RETURN VALUE
- TRUE OK
- FALSE Error
-*/
-
-bool
-Event_scheduler::start_suspended()
-{
- DBUG_ENTER("Event_scheduler::start_suspended");
- start_scheduler_suspended= TRUE;
- DBUG_RETURN(start());
-}
-
-
-
-/*
- Report back that we cannot start. Used for ocasions where
- we can't go into ::run() and have to report externally.
-
- SYNOPSIS
- Event_scheduler::report_error_during_start()
-*/
-
-inline void
-Event_scheduler::report_error_during_start()
-{
- DBUG_ENTER("Event_scheduler::report_error_during_start");
-
- LOCK_SCHEDULER_DATA();
- state= CANTSTART;
- DBUG_PRINT("info", ("Sending back COND_started_or_stopped"));
- pthread_cond_signal(&cond_vars[COND_started_or_stopped]);
- UNLOCK_SCHEDULER_DATA();
-
- DBUG_VOID_RETURN;
+ DBUG_RETURN(ret);
}
/*
- The internal loop of the event scheduler
+ The main loop of the scheduler.
SYNOPSIS
Event_scheduler::run()
thd Thread
RETURN VALUE
- FALSE OK
- TRUE Failure
+ FALSE OK
+ TRUE Error (Serious error)
*/
bool
Event_scheduler::run(THD *thd)
{
- int ret;
+ int res= FALSE;
struct timespec abstime;
+ Event_job_data *job_data;
DBUG_ENTER("Event_scheduler::run");
- DBUG_PRINT("enter", ("thd=%p", thd));
-
- LOCK_SCHEDULER_DATA();
- ret= load_events_from_db(thd);
-
- if (!ret)
- {
- thread_id= thd->thread_id;
- state= start_scheduler_suspended? SUSPENDED:RUNNING;
- start_scheduler_suspended= FALSE;
- }
- else
- state= CANTSTART;
-
- DBUG_PRINT("info", ("Sending back COND_started_or_stopped"));
- pthread_cond_signal(&cond_vars[COND_started_or_stopped]);
- if (ret)
- {
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(TRUE);
- }
- if (!check_n_suspend_if_needed(thd))
- UNLOCK_SCHEDULER_DATA();
sql_print_information("SCHEDULER: Manager thread started with id %lu",
thd->thread_id);
- abstime.tv_nsec= 0;
- while (is_running_or_suspended())
- {
- Event_timed *et;
-
- LOCK_SCHEDULER_DATA();
- if (check_n_wait_for_non_empty_queue(thd))
- continue;
-
- /* On TRUE data is unlocked, go back to the beginning */
- if (check_n_suspend_if_needed(thd))
- continue;
+ /*
+ Recalculate the values in the queue because there could have been stops
+ in executions of the scheduler and some times could have passed by.
+ */
+ queue->recalculate_activation_times(thd);
- /* Guaranteed locked here */
- if (state == IN_SHUTDOWN || shutdown_in_progress)
+ while (is_running())
+ {
+ /* Gets a minimized version */
+ if (queue->get_top_for_execution_if_time(thd, &job_data))
{
- UNLOCK_SCHEDULER_DATA();
+ sql_print_information("SCHEDULER: Serious error during getting next "
+ "event to execute. Stopping");
break;
}
- DBUG_ASSERT(state == RUNNING);
-
- et= (Event_timed *)queue_top(&queue);
-
- /* Skip disabled events */
- if (et->status != Event_timed::ENABLED)
- {
- /*
- It could be a one-timer scheduled for a time, already in the past when the
- scheduler was suspended.
- */
- sql_print_information("SCHEDULER: Found a disabled event %*s.%*s in the queue",
- et->dbname.length, et->dbname.str, et->name.length,
- et->name.str);
- queue_remove(&queue, 0);
- /* ToDo: check this again */
- if (et->dropped)
- et->drop(thd);
- delete et;
- UNLOCK_SCHEDULER_DATA();
- continue;
- }
- thd->proc_info= (char *)"Computing";
- DBUG_PRINT("evex manager",("computing time to sleep till next exec"));
- /* Timestamp is in UTC */
- abstime.tv_sec= sec_since_epoch_TIME(&et->execute_at);
- thd->end_time();
- if (abstime.tv_sec > thd->query_start())
+ DBUG_PRINT("info", ("get_top returned job_data=0x%lx", job_data));
+ if (job_data)
{
- /* Event trigger time is in the future */
- thd->proc_info= (char *)"Sleep";
- DBUG_PRINT("info", ("Going to sleep. Should wakeup after approx %d secs",
- abstime.tv_sec - thd->query_start()));
- DBUG_PRINT("info", ("Entering condition because waiting for activation"));
- /*
- Use THD::enter_cond()/exit_cond() or we won't be able to kill a
- sleeping thread. Though ::stop() can do it by sending COND_new_work
- an user can't by just issuing 'KILL x'; . In the latter case
- pthread_cond_timedwait() will wait till `abstime`.
- "Sleeping until next time"
- */
- thd->enter_cond(&cond_vars[COND_new_work],&LOCK_scheduler_data,"Sleeping");
-
- pthread_cond_timedwait(&cond_vars[COND_new_work], &LOCK_scheduler_data,
- &abstime);
-
- DBUG_PRINT("info", ("Manager woke up. state is %d", state));
- /*
- If we get signal we should recalculate the whether it's the right time
- because there could be :
- 1. Spurious wake-up
- 2. The top of the queue was changed (new one becase of add/drop/replace)
- */
- /* This will do implicit UNLOCK_SCHEDULER_DATA() */
- thd->exit_cond("");
+ if ((res= execute_top(thd, job_data)))
+ break;
}
else
{
- thd->proc_info= (char *)"Executing";
- /*
- Execute the event. An error may occur if a thread cannot be forked.
- In this case stop the manager.
- We should enter ::execute_top() with locked LOCK_scheduler_data.
- */
- int ret= execute_top(thd);
- UNLOCK_SCHEDULER_DATA();
- if (ret)
- break;
+ DBUG_ASSERT(thd->killed);
+ DBUG_PRINT("info", ("job_data is NULL, the thread was killed"));
}
+ DBUG_PRINT("info", ("state=%s", scheduler_states_names[state].str));
}
-
- thd->proc_info= (char *)"Cleaning";
-
- LOCK_SCHEDULER_DATA();
- /*
- It's possible that a user has used (SQL)COM_KILL. Hence set the appropriate
- state because it is only set by ::stop().
- */
- if (state != IN_SHUTDOWN)
- {
- DBUG_PRINT("info", ("We got KILL but the but not from ::stop()"));
- state= IN_SHUTDOWN;
- }
- UNLOCK_SCHEDULER_DATA();
-
- sql_print_information("SCHEDULER: Shutting down");
-
- thd->proc_info= (char *)"Cleaning queue";
- clean_queue(thd);
- THD_CHECK_SENTRY(thd);
-
- /* free mamager_root memory but don't destroy the root */
- thd->proc_info= (char *)"Cleaning memory root";
- free_root(&scheduler_root, MYF(0));
- THD_CHECK_SENTRY(thd);
-
- /*
- We notify the waiting thread which shutdowns us that we have cleaned.
- There are few more instructions to be executed in this pthread but
- they don't affect manager structures thus it's safe to signal already
- at this point.
- */
- LOCK_SCHEDULER_DATA();
- thd->proc_info= (char *)"Sending shutdown signal";
- DBUG_PRINT("info", ("Sending COND_started_or_stopped"));
- if (state == IN_SHUTDOWN)
- pthread_cond_signal(&cond_vars[COND_started_or_stopped]);
-
+ LOCK_DATA();
+ DBUG_PRINT("info", ("Signalling back to the stopper COND_state"));
state= INITIALIZED;
- /*
- We set it here because ::run() can stop not only because of ::stop()
- call but also because of `KILL x`
- */
- thread_id= 0;
+ pthread_cond_signal(&COND_state);
+ UNLOCK_DATA();
sql_print_information("SCHEDULER: Stopped");
- UNLOCK_SCHEDULER_DATA();
-
- /* We have modified, we set back */
- thd->query= NULL;
- thd->query_length= 0;
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(res);
}
/*
- Executes the top element of the queue. Auxiliary method for ::run().
+ Creates a new THD instance and then forks a new thread, while passing
+ the THD pointer and job_data to it.
SYNOPSIS
Event_scheduler::execute_top()
RETURN VALUE
- FALSE OK
- TRUE Failure
-
- NOTE
- NO locking is done. EXPECTED is that the caller should have locked
- the queue (w/ LOCK_scheduler_data).
+ FALSE OK
+ TRUE Error (Serious error)
*/
bool
-Event_scheduler::execute_top(THD *thd)
+Event_scheduler::execute_top(THD *thd, Event_job_data *job_data)
{
- int spawn_ret_code;
- bool ret= FALSE;
+ THD *new_thd;
+ pthread_t th;
+ int res= 0;
DBUG_ENTER("Event_scheduler::execute_top");
- DBUG_PRINT("enter", ("thd=%p", thd));
-
- Event_timed *et= (Event_timed *)queue_top(&queue);
-
- /* Is it good idea to pass a stack address ?*/
- Worker_thread_param param(et);
-
- pthread_mutex_lock(&param.LOCK_started);
- /*
- We don't lock LOCK_scheduler_data fpr workers_increment() because it's a
- pre-requisite for calling the current_method.
- */
- switch ((spawn_ret_code= et->spawn_now(event_worker_thread, &param))) {
- case EVENT_EXEC_CANT_FORK:
- /*
- We don't lock LOCK_scheduler_data here because it's a pre-requisite
- for calling the current_method.
- */
- sql_print_error("SCHEDULER: Problem while trying to create a thread");
- ret= TRUE;
- break;
- case EVENT_EXEC_ALREADY_EXEC:
- /*
- We don't lock LOCK_scheduler_data here because it's a pre-requisite
- for calling the current_method.
- */
- sql_print_information("SCHEDULER: %s.%s in execution. Skip this time.",
- et->dbname.str, et->name.str);
- if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == Event_timed::DISABLED)
- queue_remove(&queue, 0);// 0 is top, internally 1
- else
- queue_replaced(&queue);
- break;
- default:
- DBUG_ASSERT(!spawn_ret_code);
- if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == Event_timed::DISABLED)
- queue_remove(&queue, 0);// 0 is top, internally 1
- else
- queue_replaced(&queue);
- /*
- We don't lock LOCK_scheduler_data here because it's a pre-requisite
- for calling the current_method.
- */
- if (likely(!spawn_ret_code))
- {
- /* Wait the forked thread to start */
- do {
- pthread_cond_wait(&param.COND_started, &param.LOCK_started);
- } while (!param.started);
- }
- /*
- param was allocated on the stack so no explicit delete as well as
- in this moment it's no more used in the spawned thread so it's safe
- to be deleted.
- */
- break;
- }
- pthread_mutex_unlock(&param.LOCK_started);
- /* `param` is on the stack and will be destructed by the compiler */
-
- DBUG_RETURN(ret);
-}
-
+ if (!(new_thd= new THD()))
+ goto error;
-/*
- Cleans the scheduler's queue. Auxiliary method for ::run().
-
- SYNOPSIS
- Event_scheduler::clean_queue()
- thd Thread
-*/
+ pre_init_event_thread(new_thd);
+ new_thd->system_thread= SYSTEM_THREAD_EVENT_WORKER;
+ job_data->thd= new_thd;
+ DBUG_PRINT("info", ("BURAN %s@%s ready for start t-3..2..1..0..ignition",
+ job_data->dbname.str, job_data->name.str));
-void
-Event_scheduler::clean_queue(THD *thd)
-{
- CHARSET_INFO *scs= system_charset_info;
- uint i;
- DBUG_ENTER("Event_scheduler::clean_queue");
- DBUG_PRINT("enter", ("thd=%p", thd));
+ /* Major failure */
+ if ((res= pthread_create(&th, &connection_attrib, event_worker_thread,
+ job_data)))
+ goto error;
- LOCK_SCHEDULER_DATA();
- stop_all_running_events(thd);
- UNLOCK_SCHEDULER_DATA();
+ ++started_events;
- sql_print_information("SCHEDULER: Emptying the queue");
+ DBUG_PRINT("info", ("Launch succeeded. BURAN is in THD=0x%lx", new_thd));
+ DBUG_RETURN(FALSE);
- /* empty the queue */
- for (i= 0; i < queue.elements; ++i)
+error:
+ DBUG_PRINT("error", ("Baikonur, we have a problem! res=%d", res));
+ if (new_thd)
{
- Event_timed *et= (Event_timed *) queue_element(&queue, i);
- et->free_sp();
- delete et;
+ new_thd->proc_info= "Clearing";
+ DBUG_ASSERT(new_thd->net.buff != 0);
+ net_end(&new_thd->net);
+ pthread_mutex_lock(&LOCK_thread_count);
+ thread_count--;
+ thread_running--;
+ delete new_thd;
+ pthread_mutex_unlock(&LOCK_thread_count);
}
- resize_queue(&queue, 0);
-
- DBUG_VOID_RETURN;
+ delete job_data;
+ DBUG_RETURN(TRUE);
}
/*
- Stops all running events
+ Checks whether the state of the scheduler is RUNNING
SYNOPSIS
- Event_scheduler::stop_all_running_events()
- thd Thread
-
- NOTE
- LOCK_scheduler data must be acquired prior to call to this method
+ Event_scheduler::is_running()
+
+ RETURN VALUE
+ TRUE RUNNING
+ FALSE Not RUNNING
*/
-void
-Event_scheduler::stop_all_running_events(THD *thd)
+bool
+Event_scheduler::is_running()
{
- 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;
+ LOCK_DATA();
+ bool ret= (state == RUNNING);
+ UNLOCK_DATA();
+ return ret;
}
/*
- Stops the event scheduler
+ Stops the scheduler (again). Waits for acknowledgement from the
+ scheduler that it has stopped - synchronous stopping.
SYNOPSIS
Event_scheduler::stop()
RETURN VALUE
- OP_OK OK
- OP_CANT_KILL Error during stopping of manager thread
- OP_NOT_RUNNING Manager not working
-
- NOTE
- The caller must have acquited LOCK_scheduler_data.
+ FALSE OK
+ TRUE Error (not reported)
*/
-enum Event_scheduler::enum_error_code
+bool
Event_scheduler::stop()
{
THD *thd= current_thd;
DBUG_ENTER("Event_scheduler::stop");
- DBUG_PRINT("enter", ("thd=%p", current_thd));
-
- LOCK_SCHEDULER_DATA();
- if (!is_running_or_suspended())
- {
- /*
- One situation to be here is if there was a start that forked a new
- thread but the new thread did not acquire yet LOCK_scheduler_data.
- Hence, in this case return an error.
- */
- DBUG_PRINT("info", ("manager not running but %d. doing nothing", state));
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(OP_NOT_RUNNING);
- }
- state= IN_SHUTDOWN;
+ DBUG_PRINT("enter", ("thd=0x%lx", current_thd));
- DBUG_PRINT("info", ("Manager thread has id %d", thread_id));
- sql_print_information("SCHEDULER: Killing manager thread %lu", thread_id);
-
- /*
- Sending the COND_new_work to ::run() is a way to get this working without
- race conditions. If we use kill_one_thread() it will call THD::awake() and
- because in ::run() both THD::enter_cond()/::exit_cond() are used,
- THD::awake() will try to lock LOCK_scheduler_data. If we UNLOCK it before,
- then the pthread_cond_signal(COND_started_or_stopped) could be signaled in
- ::run() and we can miss the signal before we relock. A way is to use
- another mutex for this shutdown procedure but better not.
- */
- pthread_cond_signal(&cond_vars[COND_new_work]);
- /* Or we are suspended - then we should wake up */
- pthread_cond_signal(&cond_vars[COND_suspend_or_resume]);
+ LOCK_DATA();
+ DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state]));
+ if (state != RUNNING)
+ goto end;
/* Guarantee we don't catch spurious signals */
- sql_print_information("SCHEDULER: Waiting the manager thread to reply");
- while (state != INITIALIZED)
- {
+ do {
DBUG_PRINT("info", ("Waiting for COND_started_or_stopped from the manager "
- "thread. Current value of state is %d . "
- "workers count=%d", state, workers_count()));
- cond_wait(COND_started_or_stopped, &LOCK_scheduler_data);
- }
- DBUG_PRINT("info", ("Manager thread has cleaned up. Set state to INIT"));
- UNLOCK_SCHEDULER_DATA();
-
- DBUG_RETURN(OP_OK);
-}
-
-
-/*
- Suspends or resumes the scheduler.
- SUSPEND - it won't execute any event till resumed.
- RESUME - it will resume if suspended.
-
- SYNOPSIS
- Event_scheduler::suspend_or_resume()
-
- RETURN VALUE
- OP_OK OK
-*/
-
-enum Event_scheduler::enum_error_code
-Event_scheduler::suspend_or_resume(
- enum Event_scheduler::enum_suspend_or_resume action)
-{
- DBUG_ENTER("Event_scheduler::suspend_or_resume");
- DBUG_PRINT("enter", ("action=%d", action));
-
- LOCK_SCHEDULER_DATA();
+ "thread. Current value of state is %s . "
+ "workers count=%d", scheduler_states_names[state].str,
+ workers_count()));
+ /*
+ NOTE: We don't use kill_one_thread() because it can't kill COM_DEAMON
+ threads. In addition, kill_one_thread() requires THD but during shutdown
+ current_thd is NULL. Hence, if kill_one_thread should be used it has to
+ be modified to kill also daemons, by adding a flag, and also we have to
+ create artificial THD here. To save all this work, we just do what
+ kill_one_thread() does to kill a thread. See also sql_repl.cc for similar
+ usage.
+ */
- if ((action == SUSPEND && state == SUSPENDED) ||
- (action == RESUME && state == RUNNING))
- {
- DBUG_PRINT("info", ("Either trying to suspend suspended or resume "
- "running scheduler. Doing nothing."));
- }
- else
- {
- /* Wake the main thread up if he is asleep */
- DBUG_PRINT("info", ("Sending signal"));
- if (action==SUSPEND)
- {
- state= SUSPENDED;
- pthread_cond_signal(&cond_vars[COND_new_work]);
- }
- else
- {
- state= RUNNING;
- pthread_cond_signal(&cond_vars[COND_suspend_or_resume]);
- }
- DBUG_PRINT("info", ("Waiting on COND_suspend_or_resume"));
- cond_wait(COND_suspend_or_resume, &LOCK_scheduler_data);
- DBUG_PRINT("info", ("Got response"));
- }
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(OP_OK);
+ state= STOPPING;
+ DBUG_PRINT("info", ("Manager thread has id %d", scheduler_thd->thread_id));
+ /* Lock from delete */
+ pthread_mutex_lock(&scheduler_thd->LOCK_delete);
+ /* This will wake up the thread if it waits on Queue's conditional */
+ sql_print_information("SCHEDULER: Killing manager thread %lu",
+ scheduler_thd->thread_id);
+ scheduler_thd->awake(THD::KILL_CONNECTION);
+ pthread_mutex_unlock(&scheduler_thd->LOCK_delete);
+
+ /* thd could be 0x0, when shutting down */
+ sql_print_information("SCHEDULER: Waiting the manager thread to reply");
+ COND_STATE_WAIT(thd, NULL, "Waiting scheduler to stop");
+ } while (state == STOPPING);
+ DBUG_PRINT("info", ("Manager thread has cleaned up. Set state to INIT"));
+ /*
+ The rationale behind setting it to NULL here but not destructing it
+ beforehand is because the THD will be deinited in event_scheduler_thread().
+ It's more clear when the post_init and the deinit is done in one function.
+ Here we just mark that the scheduler doesn't have a THD anymore. Though for
+ milliseconds the old thread could exist we can't use it anymore. When we
+ unlock the mutex in this function a little later the state will be
+ INITIALIZED. Therefore, a connection thread could enter the critical section
+ and will create a new THD object.
+ */
+ scheduler_thd= NULL;
+end:
+ UNLOCK_DATA();
+ DBUG_RETURN(FALSE);
}
/*
- Returns the number of executing events.
+ Returns the number of living event worker threads.
SYNOPSIS
Event_scheduler::workers_count()
@@ -1787,157 +650,33 @@ Event_scheduler::workers_count()
uint count= 0;
DBUG_ENTER("Event_scheduler::workers_count");
- VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
+ 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)
++count;
- }
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ pthread_mutex_unlock(&LOCK_thread_count);
DBUG_PRINT("exit", ("%d", count));
- DBUG_RETURN(count);
+ DBUG_RETURN(count);
}
/*
- Checks and suspends if needed
-
- SYNOPSIS
- Event_scheduler::check_n_suspend_if_needed()
- thd Thread
-
- RETURN VALUE
- FALSE Not suspended, we haven't slept
- TRUE We were suspended. LOCK_scheduler_data is unlocked.
-
- NOTE
- The caller should have locked LOCK_scheduler_data!
- The mutex will be unlocked in case this function returns TRUE
-*/
-
-bool
-Event_scheduler::check_n_suspend_if_needed(THD *thd)
-{
- bool was_suspended= FALSE;
- DBUG_ENTER("Event_scheduler::check_n_suspend_if_needed");
- if (thd->killed && !shutdown_in_progress)
- {
- state= SUSPENDED;
- thd->killed= THD::NOT_KILLED;
- }
- if (state == SUSPENDED)
- {
- thd->enter_cond(&cond_vars[COND_suspend_or_resume], &LOCK_scheduler_data,
- "Suspended");
- /* Send back signal to the thread that asked us to suspend operations */
- pthread_cond_signal(&cond_vars[COND_suspend_or_resume]);
- sql_print_information("SCHEDULER: Suspending operations");
- was_suspended= TRUE;
- }
- while (state == SUSPENDED)
- {
- cond_wait(COND_suspend_or_resume, &LOCK_scheduler_data);
- DBUG_PRINT("info", ("Woke up after waiting on COND_suspend_or_resume"));
- if (state != SUSPENDED)
- {
- pthread_cond_signal(&cond_vars[COND_suspend_or_resume]);
- sql_print_information("SCHEDULER: Resuming operations");
- }
- }
- if (was_suspended)
- {
- if (queue.elements)
- {
- uint i;
- DBUG_PRINT("info", ("We have to recompute the execution times"));
-
- for (i= 0; i < queue.elements; i++)
- {
- ((Event_timed*)queue_element(&queue, i))->compute_next_execution_time();
- ((Event_timed*)queue_element(&queue, i))->update_fields(thd);
- }
- queue_fix(&queue);
- }
- /* This will implicitly unlock LOCK_scheduler_data */
- thd->exit_cond("");
- }
- DBUG_RETURN(was_suspended);
-}
-
-
-/*
- Checks for empty queue and waits till new element gets in
-
- SYNOPSIS
- Event_scheduler::check_n_wait_for_non_empty_queue()
- thd Thread
-
- RETURN VALUE
- FALSE Did not wait - LOCK_scheduler_data still locked.
- TRUE Waited - LOCK_scheduler_data unlocked.
-
- NOTE
- The caller should have locked LOCK_scheduler_data!
-*/
-
-bool
-Event_scheduler::check_n_wait_for_non_empty_queue(THD *thd)
-{
- bool slept= FALSE;
- DBUG_ENTER("Event_scheduler::check_n_wait_for_non_empty_queue");
- DBUG_PRINT("enter", ("q.elements=%lu state=%s",
- queue.elements, states_names[state]));
-
- if (!queue.elements)
- thd->enter_cond(&cond_vars[COND_new_work], &LOCK_scheduler_data,
- "Empty queue, sleeping");
-
- /* Wait in a loop protecting against catching spurious signals */
- while (!queue.elements && state == RUNNING)
- {
- slept= TRUE;
- DBUG_PRINT("info", ("Entering condition because of empty queue"));
- cond_wait(COND_new_work, &LOCK_scheduler_data);
- DBUG_PRINT("info", ("Manager woke up. Hope we have events now. state=%d",
- state));
- /*
- exit_cond does implicit mutex_UNLOCK, we needed it locked if
- 1. we loop again
- 2. end the current loop and start doing calculations
- */
- }
- if (slept)
- thd->exit_cond("");
-
- DBUG_PRINT("exit", ("q.elements=%lu state=%s thd->killed=%d",
- queue.elements, states_names[state], thd->killed));
-
- DBUG_RETURN(slept);
-}
-
-
-/*
- Wrapper for pthread_mutex_lock
+ Auxiliary function for locking LOCK_scheduler_state. Used
+ by the LOCK_DATA macro.
SYNOPSIS
Event_scheduler::lock_data()
- mutex Mutex to lock
- line The line number on which the lock is done
-
- RETURN VALUE
- Error code of pthread_mutex_lock()
+ func Which function is requesting mutex lock
+ line On which line mutex lock is requested
*/
-inline void
+void
Event_scheduler::lock_data(const char *func, uint line)
{
- DBUG_ENTER("Event_scheduler::lock_mutex");
- DBUG_PRINT("enter", ("mutex_lock=%p func=%s line=%u",
- &LOCK_scheduler_data, func, line));
- pthread_mutex_lock(&LOCK_scheduler_data);
+ DBUG_ENTER("Event_scheduler::lock_data");
+ DBUG_PRINT("enter", ("func=%s line=%u", func, line));
+ pthread_mutex_lock(&LOCK_scheduler_state);
mutex_last_locked_in_func= func;
mutex_last_locked_at_line= line;
mutex_scheduler_data_locked= TRUE;
@@ -1946,518 +685,98 @@ Event_scheduler::lock_data(const char *func, uint line)
/*
- Wrapper for pthread_mutex_unlock
+ Auxiliary function for unlocking LOCK_scheduler_state. Used
+ by the UNLOCK_DATA macro.
SYNOPSIS
Event_scheduler::unlock_data()
- mutex Mutex to unlock
- line The line number on which the unlock is done
+ func Which function is requesting mutex unlock
+ line On which line mutex unlock is requested
*/
-inline void
+void
Event_scheduler::unlock_data(const char *func, uint line)
{
- DBUG_ENTER("Event_scheduler::UNLOCK_mutex");
- DBUG_PRINT("enter", ("mutex_unlock=%p func=%s line=%u",
- &LOCK_scheduler_data, func, line));
+ DBUG_ENTER("Event_scheduler::unlock_data");
+ DBUG_PRINT("enter", ("func=%s line=%u", func, line));
mutex_last_unlocked_at_line= line;
mutex_scheduler_data_locked= FALSE;
mutex_last_unlocked_in_func= func;
- pthread_mutex_unlock(&LOCK_scheduler_data);
+ pthread_mutex_unlock(&LOCK_scheduler_state);
DBUG_VOID_RETURN;
}
/*
- Wrapper for pthread_cond_wait
+ Wrapper for pthread_cond_wait/timedwait
SYNOPSIS
Event_scheduler::cond_wait()
- cond Conditional to wait for
- mutex Mutex of the conditional
-
- RETURN VALUE
- Error code of pthread_cond_wait()
+ thd Thread (Could be NULL during shutdown procedure)
+ abstime If not null then call pthread_cond_timedwait()
+ msg Message for thd->proc_info
+ func Which function is requesting cond_wait
+ line On which line cond_wait is requested
*/
-inline int
-Event_scheduler::cond_wait(enum Event_scheduler::enum_cond_vars cond,
- pthread_mutex_t *mutex)
+void
+Event_scheduler::cond_wait(THD *thd, struct timespec *abstime, const char* msg,
+ const char *func, uint line)
{
- int ret;
DBUG_ENTER("Event_scheduler::cond_wait");
- DBUG_PRINT("enter", ("cond=%s mutex=%p", cond_vars_names[cond], mutex));
- ret= pthread_cond_wait(&cond_vars[cond_waiting_on=cond], mutex);
- cond_waiting_on= COND_NONE;
- DBUG_RETURN(ret);
-}
-
-
-/*
- Checks whether the scheduler is in a running or suspended state.
-
- SYNOPSIS
- Event_scheduler::is_running_or_suspended()
-
- RETURN VALUE
- TRUE Either running or suspended
- FALSE IN_SHUTDOWN, not started, etc.
-*/
-
-inline bool
-Event_scheduler::is_running_or_suspended()
-{
- return (state == SUSPENDED || state == RUNNING);
-}
-
-
-/*
- Returns the current state of the scheduler
-
- SYNOPSIS
- Event_scheduler::get_state()
-*/
-
-enum Event_scheduler::enum_state
-Event_scheduler::get_state()
-{
- enum Event_scheduler::enum_state ret;
- DBUG_ENTER("Event_scheduler::get_state");
- /* lock_data & unlock_data are not static */
- pthread_mutex_lock(&singleton.LOCK_scheduler_data);
- ret= singleton.state;
- pthread_mutex_unlock(&singleton.LOCK_scheduler_data);
- DBUG_RETURN(ret);
-}
-
-
-/*
- Returns whether the scheduler was initialized.
-
- SYNOPSIS
- Event_scheduler::initialized()
-
- RETURN VALUE
- FALSE Was not initialized so far
- TRUE Was initialized
-*/
-
-bool
-Event_scheduler::initialized()
-{
- DBUG_ENTER("Event_scheduler::initialized");
- DBUG_RETURN(Event_scheduler::get_state() != UNINITIALIZED);
-}
-
-
-/*
- Returns the number of elements in the queue
-
- SYNOPSIS
- Event_scheduler::events_count()
-
- RETURN VALUE
- 0 Number of Event_timed objects in the queue
-*/
-
-uint
-Event_scheduler::events_count()
-{
- uint n;
- DBUG_ENTER("Event_scheduler::events_count");
- LOCK_SCHEDULER_DATA();
- n= queue.elements;
- UNLOCK_SCHEDULER_DATA();
-
- DBUG_RETURN(n);
-}
-
-
-/*
- Looks for a named event in mysql.event and then loads it from
- the table, compiles and inserts it into the cache.
-
- SYNOPSIS
- Event_scheduler::load_named_event()
- 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
-*/
-
-enum Event_scheduler::enum_error_code
-Event_scheduler::load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new)
-{
- int ret= 0;
- MEM_ROOT *tmp_mem_root;
- Event_timed *et_loaded= NULL;
- Open_tables_state backup;
-
- DBUG_ENTER("Event_scheduler::load_and_compile_event");
- DBUG_PRINT("enter",("thd=%p name:%*s",thd, etn->name.length, etn->name.str));
-
- thd->reset_n_backup_open_tables_state(&backup);
- /* No need to use my_error() here because db_find_event() has done it */
- {
- sp_name spn(etn->dbname, etn->name);
- ret= db_find_event(thd, &spn, &et_loaded, NULL, &scheduler_root);
- }
- 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);
+ waiting_on_cond= TRUE;
+ mutex_last_unlocked_at_line= line;
+ mutex_scheduler_data_locked= FALSE;
+ mutex_last_unlocked_in_func= func;
+ if (thd)
+ thd->enter_cond(&COND_state, &LOCK_scheduler_state, msg);
- if (et_loaded->status != Event_timed::ENABLED)
+ DBUG_PRINT("info", ("pthread_cond_%swait", abstime? "timed":""));
+ if (!abstime)
+ pthread_cond_wait(&COND_state, &LOCK_scheduler_state);
+ else
+ pthread_cond_timedwait(&COND_state, &LOCK_scheduler_state, abstime);
+ if (thd)
{
/*
- 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.
+ This will free the lock so we need to relock. Not the best thing to
+ do but we need to obey cond_wait()
*/
- delete et_loaded;
- DBUG_RETURN(OP_DISABLED_EVENT);
- }
-
- et_loaded->compute_next_execution_time();
- *etn_new= et_loaded;
-
- DBUG_RETURN(OP_OK);
-}
-
-
-/*
- Loads all ENABLED events from mysql.event into the prioritized
- queue. Called during scheduler main thread initialization. Compiles
- the events. Creates Event_timed instances for every ENABLED event
- from mysql.event.
-
- SYNOPSIS
- Event_scheduler::load_events_from_db()
- thd - Thread context. Used for memory allocation in some cases.
-
- RETURN VALUE
- 0 OK
- !0 Error (EVEX_OPEN_TABLE_FAILED, EVEX_MICROSECOND_UNSUP,
- EVEX_COMPILE_ERROR) - in all these cases mysql.event was
- tampered.
-
- NOTES
- Reports the error to the console
-*/
-
-int
-Event_scheduler::load_events_from_db(THD *thd)
-{
- TABLE *table;
- READ_RECORD read_record_info;
- int ret= -1;
- uint count= 0;
- bool clean_the_queue= FALSE;
- /* Compile the events on this root but only for syntax check, then discard */
- MEM_ROOT boot_root;
-
- DBUG_ENTER("Event_scheduler::load_events_from_db");
- DBUG_PRINT("enter", ("thd=%p", thd));
-
- if (state > COMMENCING)
- {
- DBUG_ASSERT(0);
- sql_print_error("SCHEDULER: Trying to load events while already running.");
- DBUG_RETURN(EVEX_GENERAL_ERROR);
- }
-
- if ((ret= Events::open_event_table(thd, TL_READ, &table)))
- {
- sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open.");
- DBUG_RETURN(EVEX_OPEN_TABLE_FAILED);
- }
-
- init_alloc_root(&boot_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
- init_read_record(&read_record_info, thd, table ,NULL,1,0);
- while (!(read_record_info.read_record(&read_record_info)))
- {
- Event_timed *et;
- if (!(et= new Event_timed))
- {
- DBUG_PRINT("info", ("Out of memory"));
- clean_the_queue= TRUE;
- break;
- }
- DBUG_PRINT("info", ("Loading event from row."));
-
- if ((ret= et->load_from_row(&scheduler_root, table)))
- {
- clean_the_queue= TRUE;
- sql_print_error("SCHEDULER: Error while loading from mysql.event. "
- "Table probably corrupted");
- break;
- }
- if (et->status != Event_timed::ENABLED)
- {
- DBUG_PRINT("info",("%s is disabled",et->name.str));
- delete et;
- continue;
- }
-
- DBUG_PRINT("info", ("Event %s loaded from row. ", et->name.str));
-
- /* We load only on scheduler root just to check whether the body compiles */
- switch (ret= et->compile(thd, &boot_root)) {
- case EVEX_MICROSECOND_UNSUP:
- et->free_sp();
- sql_print_error("SCHEDULER: mysql.event is tampered. MICROSECOND is not "
- "supported but found in mysql.event");
- goto end;
- case EVEX_COMPILE_ERROR:
- sql_print_error("SCHEDULER: Error while compiling %s.%s. Aborting load.",
- et->dbname.str, et->name.str);
- goto end;
- default:
- /* Free it, it will be compiled again on the worker thread */
- et->free_sp();
- break;
- }
-
- /* let's find when to be executed */
- if (et->compute_next_execution_time())
- {
- sql_print_error("SCHEDULER: Error while computing execution time of %s.%s."
- " Skipping", et->dbname.str, et->name.str);
- continue;
- }
-
- DBUG_PRINT("load_events_from_db", ("Adding %p to the exec list."));
- queue_insert_safe(&queue, (byte *) et);
- count++;
- }
-end:
- end_read_record(&read_record_info);
- free_root(&boot_root, MYF(0));
-
- if (clean_the_queue)
- {
- for (count= 0; count < queue.elements; ++count)
- queue_remove(&queue, 0);
- ret= -1;
- }
- else
- {
- ret= 0;
- sql_print_information("SCHEDULER: Loaded %d event%s", count, (count == 1)?"":"s");
- }
-
- /* Force close to free memory */
- thd->version--;
-
- close_thread_tables(thd);
-
- DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count));
- DBUG_RETURN(ret);
-}
-
-
-/*
- 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
- Event_scheduler::check_system_tables()
-*/
-
-bool
-Event_scheduler::check_system_tables(THD *thd)
-{
- TABLE_LIST tables;
- bool not_used;
- Open_tables_state backup;
- bool ret;
-
- DBUG_ENTER("Event_scheduler::check_system_tables");
- DBUG_PRINT("enter", ("thd=%p", 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;
-
- if ((ret= simple_open_n_lock_tables(thd, &tables)))
- sql_print_error("Cannot open mysql.db");
- else
- {
- 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);
- }
- if (ret)
- DBUG_RETURN(TRUE);
-
- bzero((char*) &tables, sizeof(tables));
- tables.db= (char*) "mysql";
- tables.table_name= tables.alias= (char*) "user";
- tables.lock_type= TL_READ;
-
- if ((ret= simple_open_n_lock_tables(thd, &tables)))
- sql_print_error("Cannot open mysql.db");
- else
- {
- 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 29");
- ret= TRUE;
- }
- close_thread_tables(thd);
+ thd->exit_cond("");
+ LOCK_DATA();
}
-
- thd->restore_backup_open_tables_state(&backup);
-
- DBUG_RETURN(ret);
-}
-
-
-/*
- Inits mutexes.
-
- SYNOPSIS
- Event_scheduler::init_mutexes()
-*/
-
-void
-Event_scheduler::init_mutexes()
-{
- pthread_mutex_init(&singleton.LOCK_scheduler_data, MY_MUTEX_INIT_FAST);
+ mutex_last_locked_in_func= func;
+ mutex_last_locked_at_line= line;
+ mutex_scheduler_data_locked= TRUE;
+ waiting_on_cond= FALSE;
+ DBUG_VOID_RETURN;
}
/*
- Destroys mutexes.
+ Dumps the internal status of the scheduler
SYNOPSIS
- Event_scheduler::destroy_mutexes()
+ Event_scheduler::dump_internal_status()
*/
void
-Event_scheduler::destroy_mutexes()
-{
- pthread_mutex_destroy(&singleton.LOCK_scheduler_data);
-}
-
-
-/*
- Dumps some data about the internal status of the scheduler.
-
- SYNOPSIS
- Event_scheduler::dump_internal_status()
- thd THD
+Event_scheduler::dump_internal_status()
+{
+ DBUG_ENTER("Event_scheduler::dump_internal_status");
+
+ puts("");
+ puts("Event scheduler status:");
+ printf("State : %s\n", scheduler_states_names[state].str);
+ printf("Thread id : %lu\n", scheduler_thd? scheduler_thd->thread_id : 0);
+ printf("LLA : %s:%u\n", mutex_last_locked_in_func,
+ mutex_last_locked_at_line);
+ printf("LUA : %s:%u\n", mutex_last_unlocked_in_func,
+ mutex_last_unlocked_at_line);
+ printf("WOC : %s\n", waiting_on_cond? "YES":"NO");
+ printf("Workers : %u\n", workers_count());
+ printf("Executed : %llu\n", started_events);
+ printf("Data locked: %s\n", mutex_scheduler_data_locked ? "YES":"NO");
- RETURN VALUE
- 0 OK
- 1 Error
-*/
-
-int
-Event_scheduler::dump_internal_status(THD *thd)
-{
- DBUG_ENTER("dump_internal_status");
-#ifndef DBUG_OFF
- CHARSET_INFO *scs= system_charset_info;
- Protocol *protocol= thd->protocol;
- List<Item> field_list;
- int ret;
- char tmp_buff[5*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);
- int_string.length(0);
-
- field_list.push_back(new Item_empty_string("Name", 20));
- field_list.push_back(new Item_empty_string("Value",20));
- if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
- Protocol::SEND_EOF))
- DBUG_RETURN(1);
-
- protocol->prepare_for_resend();
- protocol->store(STRING_WITH_LEN("state"), scs);
- protocol->store(states_names[singleton.state].str,
- states_names[singleton.state].length,
- scs);
-
- ret= protocol->write();
- /*
- If not initialized - don't show anything else. get_instance()
- will otherwise implicitly initialize it. We don't want that.
- */
- if (singleton.state >= INITIALIZED)
- {
- /* last locked at*/
- /*
- The first thing to do, or get_instance() will overwrite the values.
- mutex_last_locked_at_line / mutex_last_unlocked_at_line
- */
- protocol->prepare_for_resend();
- protocol->store(STRING_WITH_LEN("last locked at"), scs);
- tmp_string.length(scs->cset->snprintf(scs, (char*) tmp_string.ptr(),
- tmp_string.alloced_length(), "%s::%d",
- singleton.mutex_last_locked_in_func,
- singleton.mutex_last_locked_at_line));
- protocol->store(&tmp_string);
- ret= protocol->write();
-
- /* last unlocked at*/
- protocol->prepare_for_resend();
- protocol->store(STRING_WITH_LEN("last unlocked at"), scs);
- tmp_string.length(scs->cset->snprintf(scs, (char*) tmp_string.ptr(),
- tmp_string.alloced_length(), "%s::%d",
- singleton.mutex_last_unlocked_in_func,
- singleton.mutex_last_unlocked_at_line));
- protocol->store(&tmp_string);
- ret= protocol->write();
-
- /* waiting on */
- protocol->prepare_for_resend();
- protocol->store(STRING_WITH_LEN("waiting on condition"), scs);
- tmp_string.length(scs->cset->
- snprintf(scs, (char*) tmp_string.ptr(),
- tmp_string.alloced_length(), "%s",
- (singleton.cond_waiting_on != COND_NONE) ?
- cond_vars_names[singleton.cond_waiting_on]:
- "NONE"));
- protocol->store(&tmp_string);
- ret= protocol->write();
-
- Event_scheduler *scheduler= get_instance();
-
- /* workers_count */
- protocol->prepare_for_resend();
- protocol->store(STRING_WITH_LEN("workers_count"), scs);
- int_string.set((longlong) scheduler->workers_count(), scs);
- protocol->store(&int_string);
- ret= protocol->write();
-
- /* queue.elements */
- protocol->prepare_for_resend();
- protocol->store(STRING_WITH_LEN("queue.elements"), scs);
- int_string.set((longlong) scheduler->queue.elements, scs);
- protocol->store(&int_string);
- ret= protocol->write();
-
- /* scheduler_data_locked */
- protocol->prepare_for_resend();
- protocol->store(STRING_WITH_LEN("scheduler data locked"), scs);
- int_string.set((longlong) scheduler->mutex_scheduler_data_locked, scs);
- protocol->store(&int_string);
- ret= protocol->write();
- }
- send_eof(thd);
-#endif
- DBUG_RETURN(0);
+ DBUG_VOID_RETURN;
}
diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h
index 5ae310bab2a..dffaf8c056c 100644
--- a/sql/event_scheduler.h
+++ b/sql/event_scheduler.h
@@ -16,235 +16,105 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-class Event_timed;
+class Event_queue;
+class Event_job_data;
-class THD;
-typedef bool * (*event_timed_identifier_comparator)(Event_timed*, Event_timed*);
+void
+pre_init_event_thread(THD* thd);
-int
-events_init();
+bool
+post_init_event_thread(THD* thd);
void
-events_shutdown();
+deinit_event_thread(THD *thd);
class Event_scheduler
{
public:
- /* Return codes */
- enum enum_error_code
- {
- OP_OK= 0,
- OP_NOT_RUNNING,
- OP_CANT_KILL,
- OP_CANT_INIT,
- OP_DISABLED_EVENT,
- OP_LOAD_ERROR,
- OP_ALREADY_EXISTS
- };
-
- enum enum_state
- {
- UNINITIALIZED= 0,
- INITIALIZED,
- COMMENCING,
- CANTSTART,
- RUNNING,
- SUSPENDED,
- IN_SHUTDOWN
- };
-
- enum enum_suspend_or_resume
- {
- SUSPEND= 1,
- RESUME= 2
- };
-
- /* Singleton access */
- static Event_scheduler*
- get_instance();
-
- /* Methods for queue management follow */
-
- enum enum_error_code
- create_event(THD *thd, Event_timed *et, bool check_existence);
-
- enum enum_error_code
- update_event(THD *thd, Event_timed *et, LEX_STRING *new_schema,
- LEX_STRING *new_name);
-
- bool
- drop_event(THD *thd, Event_timed *et);
-
-
- int
- drop_schema_events(THD *thd, LEX_STRING *schema);
-
- int
- drop_user_events(THD *thd, LEX_STRING *definer, uint *dropped_num)
- { DBUG_ASSERT(0); return 0;}
-
- uint
- events_count();
+ Event_scheduler():state(UNINITIALIZED){}
+ ~Event_scheduler(){}
/* State changing methods follow */
bool
start();
- enum enum_error_code
- stop();
-
bool
- start_suspended();
+ stop();
+ /*
+ Need to be public because has to be called from the function
+ passed to pthread_create.
+ */
bool
run(THD *thd);
- enum enum_error_code
- suspend_or_resume(enum enum_suspend_or_resume action);
-
- bool
- init();
+ void
+ init_scheduler(Event_queue *queue);
void
- destroy();
+ deinit_scheduler();
- static void
+ void
init_mutexes();
-
- static void
- destroy_mutexes();
void
- report_error_during_start();
+ deinit_mutexes();
/* Information retrieving methods follow */
-
- enum enum_state
- get_state();
-
bool
- initialized();
-
- static int
- dump_internal_status(THD *thd);
+ is_running();
- static bool
- check_system_tables(THD *thd);
+ void
+ dump_internal_status();
private:
- Event_timed *
- find_event(Event_timed *etn, bool remove_from_q);
-
uint
workers_count();
- bool
- is_running_or_suspended();
/* helper functions */
bool
- execute_top(THD *thd);
+ execute_top(THD *thd, Event_job_data *job_data);
+ /* helper functions for working with mutexes & conditionals */
void
- clean_queue(THD *thd);
-
- void
- stop_all_running_events(THD *thd);
-
- enum enum_error_code
- load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new);
-
- int
- load_events_from_db(THD *thd);
+ lock_data(const char *func, uint line);
void
- drop_matching_events(THD *thd, LEX_STRING *pattern,
- bool (*)(Event_timed *,LEX_STRING *));
+ unlock_data(const char *func, uint line);
- bool
- check_n_suspend_if_needed(THD *thd);
+ void
+ cond_wait(THD *thd, struct timespec *abstime, const char* msg,
+ const char *func, uint line);
- bool
- check_n_wait_for_non_empty_queue(THD *thd);
+ pthread_mutex_t LOCK_scheduler_state;
- /* Singleton DP is used */
- Event_scheduler();
-
- enum enum_cond_vars
+ enum enum_state
{
- COND_NONE= -1,
- /*
- COND_new_work is a conditional used to signal that there is a change
- of the queue that should inform the executor thread that new event should
- be executed sooner than previously expected, because of add/replace event.
- */
- COND_new_work= 0,
- /*
- COND_started is a conditional used to synchronize the thread in which
- ::start() was called and the spawned thread. ::start() spawns a new thread
- and then waits on COND_started but also checks when awaken that `state` is
- either RUNNING or CANTSTART. Then it returns back.
- */
- COND_started_or_stopped,
- /*
- Conditional used for signalling from the scheduler thread back to the
- thread that calls ::suspend() or ::resume. Synchronizing the calls.
- */
- COND_suspend_or_resume,
- /* Must be always last */
- COND_LAST
+ UNINITIALIZED = 0,
+ INITIALIZED,
+ RUNNING,
+ STOPPING
};
- /* Singleton instance */
- static Event_scheduler singleton;
-
- /* This is the current status of the life-cycle of the manager. */
+ /* This is the current status of the life-cycle of the scheduler. */
enum enum_state state;
- /* Set to start the scheduler in suspended state */
- bool start_scheduler_suspended;
-
- /*
- LOCK_scheduler_data is the mutex which protects the access to the
- manager's queue as well as used when signalling COND_new_work,
- COND_started and COND_shutdown.
- */
- pthread_mutex_t LOCK_scheduler_data;
-
- /*
- 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;
+ THD *scheduler_thd;
- pthread_cond_t cond_vars[COND_LAST];
- static const char * const cond_vars_names[COND_LAST];
+ pthread_cond_t COND_state;
- /* The MEM_ROOT of the object */
- MEM_ROOT scheduler_root;
+ Event_queue *queue;
- /* The sorted queue with the Event_timed objects */
- QUEUE queue;
-
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;
- enum enum_cond_vars cond_waiting_on;
bool mutex_scheduler_data_locked;
+ 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);
-
- int
- cond_wait(enum enum_cond_vars, pthread_mutex_t *mutex);
+ ulonglong started_events;
private:
/* Prevent use of these */
diff --git a/sql/event_timed.h b/sql/event_timed.h
deleted file mode 100644
index 0652cece361..00000000000
--- a/sql/event_timed.h
+++ /dev/null
@@ -1,217 +0,0 @@
-#ifndef _EVENT_TIMED_H_
-#define _EVENT_TIMED_H_
-/* Copyright (C) 2004-2006 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-
-#define EVEX_OK 0
-#define EVEX_KEY_NOT_FOUND -1
-#define EVEX_OPEN_TABLE_FAILED -2
-#define EVEX_WRITE_ROW_FAILED -3
-#define EVEX_DELETE_ROW_FAILED -4
-#define EVEX_GET_FIELD_FAILED -5
-#define EVEX_PARSE_ERROR -6
-#define EVEX_INTERNAL_ERROR -7
-#define EVEX_NO_DB_ERROR -8
-#define EVEX_COMPILE_ERROR -19
-#define EVEX_GENERAL_ERROR -20
-#define EVEX_BAD_IDENTIFIER -21
-#define EVEX_BODY_TOO_LONG -22
-#define EVEX_BAD_PARAMS -23
-#define EVEX_NOT_RUNNING -24
-#define EVEX_MICROSECOND_UNSUP -25
-#define EVEX_CANT_KILL -26
-
-#define EVENT_EXEC_NO_MORE (1L << 0)
-#define EVENT_NOT_USED (1L << 1)
-#define EVENT_FREE_WHEN_FINISHED (1L << 2)
-
-
-class sp_head;
-
-class Event_timed
-{
- Event_timed(const Event_timed &); /* Prevent use of these */
- 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 last_executed_changed;
-
-public:
- enum enum_status
- {
- ENABLED = 1,
- DISABLED
- };
-
- enum enum_on_completion
- {
- ON_COMPLETION_DROP = 1,
- ON_COMPLETION_PRESERVE
- };
-
- TIME last_executed;
-
- LEX_STRING dbname;
- LEX_STRING name;
- LEX_STRING body;
-
- LEX_STRING definer_user;
- LEX_STRING definer_host;
- LEX_STRING definer;// combination of user and host
-
- LEX_STRING comment;
- TIME starts;
- TIME ends;
- TIME execute_at;
- my_bool starts_null;
- my_bool ends_null;
- my_bool execute_at_null;
-
- longlong expression;
- interval_type interval;
-
- ulonglong created;
- ulonglong modified;
- enum enum_on_completion on_completion;
- enum enum_status status;
- sp_head *sphead;
- ulong sql_mode;
- const uchar *body_begin;
-
- bool dropped;
- bool free_sphead_on_delete;
- uint flags;//all kind of purposes
-
- static void *operator new(size_t size)
- {
- void *p;
- DBUG_ENTER("Event_timed::new(size)");
- p= my_malloc(size, MYF(0));
- DBUG_PRINT("info", ("alloc_ptr=0x%lx", p));
- DBUG_RETURN(p);
- }
-
- static void *operator new(size_t size, MEM_ROOT *mem_root)
- { return (void*) alloc_root(mem_root, (uint) size); }
-
- static void operator delete(void *ptr, size_t size)
- {
- DBUG_ENTER("Event_timed::delete(ptr,size)");
- DBUG_PRINT("enter", ("free_ptr=0x%lx", ptr));
- TRASH(ptr, size);
- my_free((gptr) ptr, MYF(0));
- DBUG_VOID_RETURN;
- }
-
- static void operator delete(void *ptr, MEM_ROOT *mem_root)
- {
- /*
- Don't free the memory it will be done by the mem_root but
- we need to call the destructor because we free other resources
- which are not allocated on the root but on the heap, or we
- deinit mutexes.
- */
- DBUG_ASSERT(0);
- }
-
- Event_timed();
-
- ~Event_timed();
-
- void
- init();
-
- void
- deinit_mutexes();
-
- int
- init_definer(THD *thd);
-
- int
- init_execute_at(THD *thd, Item *expr);
-
- int
- init_interval(THD *thd, Item *expr, interval_type new_interval);
-
- void
- init_name(THD *thd, sp_name *spn);
-
- int
- init_starts(THD *thd, Item *starts);
-
- int
- init_ends(THD *thd, Item *ends);
-
- void
- init_body(THD *thd);
-
- void
- init_comment(THD *thd, LEX_STRING *set_comment);
-
- int
- load_from_row(MEM_ROOT *mem_root, TABLE *table);
-
- bool
- compute_next_execution_time();
-
- int
- drop(THD *thd);
-
- void
- mark_last_executed(THD *thd);
-
- bool
- update_fields(THD *thd);
-
- int
- get_create_event(THD *thd, String *buf);
-
- int
- execute(THD *thd, MEM_ROOT *mem_root);
-
- int
- 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
- free_sp();
-
- bool
- has_equal_db(Event_timed *etn);
-
- int
- kill_thread(THD *thd);
-
- void
- set_thread_id(ulong tid) { thread_id= tid; }
-};
-
-#endif /* _EVENT_H_ */
diff --git a/sql/events.cc b/sql/events.cc
index 210cc2c4735..10a8be948ef 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -15,11 +15,11 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "mysql_priv.h"
-#include "events_priv.h"
#include "events.h"
-#include "event_timed.h"
+#include "event_data_objects.h"
+#include "event_db_repository.h"
+#include "event_queue.h"
#include "event_scheduler.h"
-#include "sp.h"
#include "sp_head.h"
/*
@@ -41,144 +41,85 @@
- Add logging to file
-Warning:
- - For now parallel execution is not possible because the same sp_head cannot
- be executed few times!!! There is still no lock attached to particular
- event.
*/
-MEM_ROOT evex_mem_root;
-time_t mysql_event_last_create_time= 0L;
-
+/*
+ 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[]=
- { "OFF", "0", "ON", "1", "SUSPEND", "2", NullS };
+/*
+ Keep the order of the first to as in var_typelib
+ sys_var_event_scheduler::value_ptr() references this array. Keep in
+ mind!
+*/
+static const char *opt_event_scheduler_state_names[]=
+ { "OFF", "ON", "0", "1", "DISABLED", NullS };
TYPELIB Events::opt_typelib=
{
- array_elements(event_scheduler_state_names)-1,
+ array_elements(opt_event_scheduler_state_names)-1,
"",
- event_scheduler_state_names,
+ opt_event_scheduler_state_names,
NULL
};
-ulong Events::opt_event_scheduler= 2;
+/*
+ The order should not be changed. We consider OFF to be equivalent of INT 0
+ And ON of 1. If OFF & ON are interchanged the logic in
+ sys_var_event_scheduler::update() will be broken!
+*/
+static const char *var_event_scheduler_state_names[]= { "OFF", "ON", NullS };
-static
-TABLE_FIELD_W_TYPE event_table_fields[Events::FIELD_COUNT] = {
- {
- {(char *) STRING_WITH_LEN("db")},
- {(char *) STRING_WITH_LEN("char(64)")},
- {(char *) STRING_WITH_LEN("utf8")}
- },
- {
- {(char *) STRING_WITH_LEN("name")},
- {(char *) STRING_WITH_LEN("char(64)")},
- {(char *) STRING_WITH_LEN("utf8")}
- },
- {
- {(char *) STRING_WITH_LEN("body")},
- {(char *) STRING_WITH_LEN("longblob")},
- {NULL, 0}
- },
- {
- {(char *) STRING_WITH_LEN("definer")},
- {(char *) STRING_WITH_LEN("char(77)")},
- {(char *) STRING_WITH_LEN("utf8")}
- },
- {
- {(char *) STRING_WITH_LEN("execute_at")},
- {(char *) STRING_WITH_LEN("datetime")},
- {NULL, 0}
- },
- {
- {(char *) STRING_WITH_LEN("interval_value")},
- {(char *) STRING_WITH_LEN("int(11)")},
- {NULL, 0}
- },
- {
- {(char *) STRING_WITH_LEN("interval_field")},
- {(char *) STRING_WITH_LEN("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')")},
- {NULL, 0}
- },
- {
- {(char *) STRING_WITH_LEN("created")},
- {(char *) STRING_WITH_LEN("timestamp")},
- {NULL, 0}
- },
- {
- {(char *) STRING_WITH_LEN("modified")},
- {(char *) STRING_WITH_LEN("timestamp")},
- {NULL, 0}
- },
- {
- {(char *) STRING_WITH_LEN("last_executed")},
- {(char *) STRING_WITH_LEN("datetime")},
- {NULL, 0}
- },
- {
- {(char *) STRING_WITH_LEN("starts")},
- {(char *) STRING_WITH_LEN("datetime")},
- {NULL, 0}
- },
- {
- {(char *) STRING_WITH_LEN("ends")},
- {(char *) STRING_WITH_LEN("datetime")},
- {NULL, 0}
- },
- {
- {(char *) STRING_WITH_LEN("status")},
- {(char *) STRING_WITH_LEN("enum('ENABLED','DISABLED')")},
- {NULL, 0}
- },
- {
- {(char *) STRING_WITH_LEN("on_completion")},
- {(char *) STRING_WITH_LEN("enum('DROP','PRESERVE')")},
- {NULL, 0}
- },
- {
- {(char *) STRING_WITH_LEN("sql_mode")},
- {(char *) STRING_WITH_LEN("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')")},
- {NULL, 0}
- },
- {
- {(char *) STRING_WITH_LEN("comment")},
- {(char *) STRING_WITH_LEN("char(64)")},
- {(char *) STRING_WITH_LEN("utf8")}
- }
+TYPELIB Events::var_typelib=
+{
+ array_elements(var_event_scheduler_state_names)-1,
+ "",
+ var_event_scheduler_state_names,
+ NULL
};
+static
+Event_queue events_event_queue;
+
+static
+Event_scheduler events_event_scheduler;
+
+static
+Event_db_repository events_event_db_repository;
+
+Events Events::singleton;
+
+enum Events::enum_opt_event_scheduler Events::opt_event_scheduler=
+ Events::EVENTS_OFF;
+
+
/*
Compares 2 LEX strings regarding case.
SYNOPSIS
sortcmp_lex_string()
-
- s - first LEX_STRING
- t - second LEX_STRING
- cs - charset
+ s First LEX_STRING
+ t Second LEX_STRING
+ cs Charset
RETURN VALUE
- -1 - s < t
- 0 - s == t
- 1 - s > t
-
- Notes
- TIME.second_part is not considered during comparison
+ -1 s < t
+ 0 s == t
+ 1 s > t
*/
int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
@@ -189,6 +130,24 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
/*
+ Accessor for the singleton instance.
+
+ SYNOPSIS
+ Events::get_instance()
+
+ RETURN VALUE
+ address
+*/
+
+Events *
+Events::get_instance()
+{
+ DBUG_ENTER("Events::get_instance");
+ DBUG_RETURN(&singleton);
+}
+
+
+/*
Reconstructs interval expression from interval type and expression
value that is in form of a value of the smalles entity:
For
@@ -197,19 +156,18 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
SYNOPSIS
Events::reconstruct_interval_expression()
- buf - preallocated String buffer to add the value to
- interval - the interval type (for instance YEAR_MONTH)
- expression - the value in the lowest entity
+ buf Preallocated String buffer to add the value to
+ interval The interval type (for instance YEAR_MONTH)
+ expression The value in the lowest entity
- RETURNS
- 0 - OK
- 1 - Error
+ RETURN VALUE
+ 0 OK
+ 1 Error
*/
int
-Events::reconstruct_interval_expression(String *buf,
- interval_type interval,
- longlong expression)
+Events::reconstruct_interval_expression(String *buf, interval_type interval,
+ longlong expression)
{
ulonglong expr= expression;
char tmp_buff[128], *end;
@@ -269,7 +227,7 @@ common_1_lev_code:
expr= tmp_expr - (tmp_expr/60)*60;
/* the code after the switch will finish */
}
- break;
+ break;
case INTERVAL_DAY_SECOND:
{
ulonglong tmp_expr= expr;
@@ -292,7 +250,7 @@ common_1_lev_code:
expr= tmp_expr - (tmp_expr/60)*60;
/* the code after the switch will finish */
}
- break;
+ break;
case INTERVAL_DAY_MICROSECOND:
case INTERVAL_HOUR_MICROSECOND:
case INTERVAL_MINUTE_MICROSECOND:
@@ -321,9 +279,25 @@ common_1_lev_code:
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()
+*/
+
+Events::Events()
+{
+ scheduler= &events_event_scheduler;
+ event_queue= &events_event_queue;
+ db_repository= &events_event_db_repository;
+}
+
/*
- Open mysql.event table for read
+ Opens mysql.event table with specified lock
SYNOPSIS
Events::open_event_table()
@@ -339,1004 +313,594 @@ common_1_lev_code:
int
Events::open_event_table(THD *thd, enum thr_lock_type lock_type,
- TABLE **table)
+ TABLE **table)
{
- TABLE_LIST tables;
- DBUG_ENTER("open_events_table");
-
- 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))
- DBUG_RETURN(1);
-
- if (table_check_intact(tables.table, Events::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);
+ return db_repository->open_event_table(thd, lock_type, table);
}
/*
- Find row in open mysql.event table representing event
+ The function exported to the world for creating of events.
SYNOPSIS
- evex_db_find_event_aux()
- thd Thread context
- et event_timed object containing dbname & name
- table TABLE object for open mysql.event table.
+ 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
RETURN VALUE
- 0 - Routine found
- EVEX_KEY_NOT_FOUND - No routine with given name
-*/
-
-inline int
-evex_db_find_event_aux(THD *thd, Event_timed *et, TABLE *table)
-{
- return evex_db_find_event_by_name(thd, et->dbname, et->name, table);
-}
+ FALSE OK
+ TRUE Error (Reported)
-
-/*
- Find row in open mysql.event table representing event
-
- SYNOPSIS
- evex_db_find_event_by_name()
- 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
- 0 - Routine found
- EVEX_KEY_NOT_FOUND - No routine with given name
+ 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.
*/
-int
-evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname,
- const LEX_STRING ev_name,
- TABLE *table)
+bool
+Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists)
{
- byte key[MAX_KEY_LENGTH];
- DBUG_ENTER("evex_db_find_event_by_name");
- DBUG_PRINT("enter", ("name: %.*s", ev_name.length, ev_name.str));
+ int ret;
+ DBUG_ENTER("Events::create_event");
+ if (unlikely(check_system_tables_error))
+ {
+ my_error(ER_EVENTS_DB_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
- /*
- 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 (dbname.length > table->field[Events::FIELD_DB]->field_length ||
- ev_name.length > table->field[Events::FIELD_NAME]->field_length)
-
- DBUG_RETURN(EVEX_KEY_NOT_FOUND);
-
- table->field[Events::FIELD_DB]->store(dbname.str, dbname.length,
- &my_charset_bin);
- table->field[Events::FIELD_NAME]->store(ev_name.str, ev_name.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))
+ 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)))
{
- DBUG_PRINT("info", ("Row not found"));
- DBUG_RETURN(EVEX_KEY_NOT_FOUND);
+ if ((ret= event_queue->create_event(thd, parse_data->dbname,
+ parse_data->name)))
+ {
+ DBUG_ASSERT(ret == OP_LOAD_ERROR);
+ my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0));
+ }
}
+ pthread_mutex_unlock(&LOCK_event_metadata);
- DBUG_PRINT("info", ("Row found!"));
- DBUG_RETURN(0);
+ DBUG_RETURN(ret);
}
/*
- Puts some data common to CREATE and ALTER EVENT into a row.
+ The function exported to the world for alteration of events.
SYNOPSIS
- evex_fill_row()
- thd THD
- table the row to fill out
- et Event's data
+ Events::update_event()
+ thd [in] THD
+ parse_data [in] Event's data from parsing stage
+ rename_to [in] Set in case of RENAME TO.
RETURN VALUE
- 0 - OK
- EVEX_GENERAL_ERROR - bad data
- EVEX_GET_FIELD_FAILED - field count does not match. table corrupted?
+ FALSE OK
+ TRUE Error
- DESCRIPTION
- Used both when an event is created and when it is altered.
+ 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
*/
-static int
-evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update)
+bool
+Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to)
{
- CHARSET_INFO *scs= system_charset_info;
- enum Events::enum_table_field field_num;
-
- DBUG_ENTER("evex_fill_row");
-
- DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str));
- DBUG_PRINT("info", ("name =[%s]", et->name.str));
- DBUG_PRINT("info", ("body =[%s]", et->body.str));
-
- if (table->field[field_num= Events::FIELD_DEFINER]->
- store(et->definer.str, et->definer.length, scs))
- goto err_truncate;
-
- if (table->field[field_num= Events::FIELD_DB]->
- store(et->dbname.str, et->dbname.length, scs))
- goto err_truncate;
-
- if (table->field[field_num= Events::FIELD_NAME]->
- store(et->name.str, et->name.length, scs))
- goto err_truncate;
-
- /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/
- table->field[Events::FIELD_ON_COMPLETION]->
- store((longlong)et->on_completion, true);
-
- table->field[Events::FIELD_STATUS]->store((longlong)et->status, true);
-
- /*
- Change the SQL_MODE only if body was present in an ALTER EVENT and of course
- always during CREATE EVENT.
- */
- if (et->body.str)
+ int ret;
+ 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))
{
- table->field[Events::FIELD_SQL_MODE]->
- store((longlong)thd->variables.sql_mode, true);
-
- if (table->field[field_num= Events::FIELD_BODY]->
- store(et->body.str, et->body.length, scs))
- goto err_truncate;
+ my_error(ER_EVENTS_DB_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
}
- if (et->expression)
+ 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)))
{
- table->field[Events::FIELD_INTERVAL_EXPR]->set_notnull();
- table->field[Events::FIELD_INTERVAL_EXPR]->
- store((longlong)et->expression, true);
-
- table->field[Events::FIELD_TRANSIENT_INTERVAL]->set_notnull();
- /*
- In the enum (C) intervals start from 0 but in mysql enum valid values start
- from 1. Thus +1 offset is needed!
- */
- table->field[Events::FIELD_TRANSIENT_INTERVAL]->
- store((longlong)et->interval+1, true);
-
- table->field[Events::FIELD_EXECUTE_AT]->set_null();
-
- if (!et->starts_null)
- {
- table->field[Events::FIELD_STARTS]->set_notnull();
- table->field[Events::FIELD_STARTS]->
- store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME);
- }
-
- if (!et->ends_null)
+ if ((ret= event_queue->update_event(thd, parse_data->dbname,
+ parse_data->name, new_dbname, new_name)))
{
- table->field[Events::FIELD_ENDS]->set_notnull();
- table->field[Events::FIELD_ENDS]->
- store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME);
+ DBUG_ASSERT(ret == OP_LOAD_ERROR);
+ my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0));
}
}
- else if (et->execute_at.year)
- {
- table->field[Events::FIELD_INTERVAL_EXPR]->set_null();
- table->field[Events::FIELD_TRANSIENT_INTERVAL]->set_null();
- table->field[Events::FIELD_STARTS]->set_null();
- table->field[Events::FIELD_ENDS]->set_null();
-
- table->field[Events::FIELD_EXECUTE_AT]->set_notnull();
- table->field[Events::FIELD_EXECUTE_AT]->
- store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME);
- }
- 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
- */
- }
-
- ((Field_timestamp *)table->field[Events::FIELD_MODIFIED])->set_time();
+ pthread_mutex_unlock(&LOCK_event_metadata);
- if (et->comment.str)
- {
- if (table->field[field_num= Events::FIELD_COMMENT]->
- store(et->comment.str, et->comment.length, scs))
- goto err_truncate;
- }
-
- DBUG_RETURN(0);
-err_truncate:
- my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), table->field[field_num]->field_name);
- DBUG_RETURN(EVEX_GENERAL_ERROR);
+ DBUG_RETURN(ret);
}
/*
- Creates an event in mysql.event
+ Drops an event
SYNOPSIS
- db_create_event()
- thd THD
- et Event_timed object containing information for the event
- create_if_not If an warning should be generated in case event exists
- rows_affected How many rows were affected
+ 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
+ only_from_disk [in] Whether to remove the event from the queue too.
+ In case of Event_job_data::drop() it's needed to
+ do only disk drop because Event_queue will handle
+ removal from memory queue.
RETURN VALUE
- 0 - OK
- EVEX_GENERAL_ERROR - Failure
-
- DESCRIPTION
- Creates an event. Relies on evex_fill_row which is shared with
- db_update_event. The name of the event is inside "et".
+ FALSE OK
+ TRUE Error (reported)
*/
-int
-db_create_event(THD *thd, Event_timed *et, my_bool create_if_not,
- uint *rows_affected)
+bool
+Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists,
+ bool only_from_disk)
{
- int ret= 0;
- CHARSET_INFO *scs= system_charset_info;
- TABLE *table;
- char old_db_buf[NAME_LEN+1];
- LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
- bool dbchanged= FALSE;
- DBUG_ENTER("db_create_event");
- DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str));
-
- *rows_affected= 0;
- DBUG_PRINT("info", ("open mysql.event for update"));
- if (Events::open_event_table(thd, TL_WRITE, &table))
- {
- my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
- goto err;
- }
-
- DBUG_PRINT("info", ("check existance of an event with the same name"));
- if (!evex_db_find_event_aux(thd, et, table))
- {
- if (create_if_not)
- {
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
- et->name.str);
- goto ok;
- }
- my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), et->name.str);
- goto err;
- }
-
- DBUG_PRINT("info", ("non-existant, go forward"));
- if ((ret= sp_use_new_db(thd, et->dbname, &old_db, 0, &dbchanged)))
- {
- my_error(ER_BAD_DB_ERROR, MYF(0));
- goto err;
- }
-
- restore_record(table, s->default_values); // Get default values for fields
-
- if (system_charset_info->cset->numchars(system_charset_info, et->dbname.str,
- et->dbname.str + et->dbname.length)
- > EVEX_DB_FIELD_LEN)
- {
- my_error(ER_TOO_LONG_IDENT, MYF(0), et->dbname.str);
- goto err;
- }
- if (system_charset_info->cset->numchars(system_charset_info, et->name.str,
- et->name.str + et->name.length)
- > EVEX_DB_FIELD_LEN)
- {
- my_error(ER_TOO_LONG_IDENT, MYF(0), et->name.str);
- goto err;
- }
-
- if (et->body.length > table->field[Events::FIELD_BODY]->field_length)
- {
- my_error(ER_TOO_LONG_BODY, MYF(0), et->name.str);
- goto err;
- }
-
- if (!(et->expression) && !(et->execute_at.year))
- {
- DBUG_PRINT("error", ("neither expression nor execute_at are set!"));
- my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0));
- goto err;
- }
-
- ((Field_timestamp *)table->field[Events::FIELD_CREATED])->set_time();
-
- /*
- 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, false)))
- goto err;
-
- if (table->file->ha_write_row(table->record[0]))
+ int ret;
+ DBUG_ENTER("Events::drop_event");
+ if (unlikely(check_system_tables_error))
{
- my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
- goto err;
+ my_error(ER_EVENTS_DB_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
}
-#ifdef USE_THIS_CODE_AS_TEMPLATE_WHEN_EVENT_REPLICATION_IS_AGREED
- if (mysql_bin_log.is_open())
+ 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)))
{
- thd->clear_error();
- /* Such a statement can always go directly to binlog, no trans cache */
- thd->binlog_query(THD::MYSQL_QUERY_TYPE, thd->query, thd->query_length,
- FALSE, FALSE);
+ if (!only_from_disk)
+ event_queue->drop_event(thd, dbname, name);
}
-#endif
-
- *rows_affected= 1;
-ok:
- if (dbchanged)
- (void) mysql_change_db(thd, old_db.str, 1);
- if (table)
- close_thread_tables(thd);
- DBUG_RETURN(EVEX_OK);
-
-err:
- if (dbchanged)
- (void) mysql_change_db(thd, old_db.str, 1);
- if (table)
- close_thread_tables(thd);
- DBUG_RETURN(EVEX_GENERAL_ERROR);
+ pthread_mutex_unlock(&LOCK_event_metadata);
+ DBUG_RETURN(ret);
}
/*
- Used to execute ALTER EVENT. Pendant to Events::update_event().
+ Drops all events from a schema
SYNOPSIS
- db_update_event()
- thd THD
- sp_name the name of the event to alter
- et event's data
-
- RETURN VALUE
- 0 OK
- EVEX_GENERAL_ERROR Error occured (my_error() called)
-
- NOTES
- sp_name is passed since this is the name of the event to
- alter in case of RENAME TO.
+ Events::drop_schema_events()
+ thd Thread
+ db ASCIIZ schema name
*/
-static int
-db_update_event(THD *thd, Event_timed *et, sp_name *new_name)
+void
+Events::drop_schema_events(THD *thd, char *db)
{
- CHARSET_INFO *scs= system_charset_info;
- TABLE *table;
- int ret= EVEX_OPEN_TABLE_FAILED;
- DBUG_ENTER("db_update_event");
- DBUG_PRINT("enter", ("dbname: %.*s", et->dbname.length, et->dbname.str));
- DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str));
- DBUG_PRINT("enter", ("user: %.*s", et->definer.length, et->definer.str));
- if (new_name)
- DBUG_PRINT("enter", ("rename to: %.*s", new_name->m_name.length,
- new_name->m_name.str));
-
- if (Events::open_event_table(thd, TL_WRITE, &table))
- {
- my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
- goto err;
- }
+ LEX_STRING const db_lex= { db, strlen(db) };
- /* first look whether we overwrite */
- if (new_name)
- {
- if (!sortcmp_lex_string(et->name, new_name->m_name, scs) &&
- !sortcmp_lex_string(et->dbname, new_name->m_db, scs))
- {
- my_error(ER_EVENT_SAME_NAME, MYF(0), et->name.str);
- goto err;
- }
-
- if (!evex_db_find_event_by_name(thd,new_name->m_db,new_name->m_name,table))
- {
- my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str);
- goto err;
- }
- }
- /*
- ...and then if there is such an event. Don't exchange the blocks
- because you will get error 120 from table handler because new_name will
- overwrite the key and SE will tell us that it cannot find the already found
- row (copied into record[1] later
- */
- if (EVEX_KEY_NOT_FOUND == evex_db_find_event_aux(thd, et, table))
- {
- my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str);
- goto err;
- }
-
- store_record(table,record[1]);
-
- /* 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 err;
-
- if (new_name)
- {
- table->field[Events::FIELD_DB]->
- store(new_name->m_db.str, new_name->m_db.length, scs);
- table->field[Events::FIELD_NAME]->
- store(new_name->m_name.str, new_name->m_name.length, scs);
- }
-
- if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
+ DBUG_ENTER("Events::drop_schema_events");
+ DBUG_PRINT("enter", ("dropping events from %s", db));
+ if (unlikely(check_system_tables_error))
{
- my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
- goto err;
+ my_error(ER_EVENTS_DB_ERROR, MYF(0));
+ DBUG_VOID_RETURN;
}
- /* close mysql.event or we crash later when loading the event from disk */
- close_thread_tables(thd);
- DBUG_RETURN(0);
+ pthread_mutex_lock(&LOCK_event_metadata);
+ event_queue->drop_schema_events(thd, db_lex);
+ db_repository->drop_schema_events(thd, db_lex);
+ pthread_mutex_unlock(&LOCK_event_metadata);
-err:
- if (table)
- close_thread_tables(thd);
- DBUG_RETURN(EVEX_GENERAL_ERROR);
+ DBUG_VOID_RETURN;
}
/*
- Looks for a named event in mysql.event and in case of success returns
- an object will data loaded from the table.
+ SHOW CREATE EVENT
SYNOPSIS
- db_find_event()
- thd THD
- name the name of the event to find
- ett event's data if event is found
- tbl TABLE object to use when not NULL
-
- NOTES
- 1) Use sp_name for look up, return in **ett if found
- 2) tbl is not closed at exit
+ Events::show_create_event()
+ thd Thread context
+ spn The name of the event (db, name)
RETURN VALUE
- 0 ok In this case *ett is set to the event
- # error *ett == 0
+ FALSE OK
+ TRUE Error during writing to the wire
*/
-int
-db_find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl,
- MEM_ROOT *root)
+bool
+Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
{
- TABLE *table;
+ CHARSET_INFO *scs= system_charset_info;
int ret;
- Event_timed *et= NULL;
- DBUG_ENTER("db_find_event");
- DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
+ Event_timed *et= new Event_timed();
- if (!root)
- root= &evex_mem_root;
-
- if (tbl)
- table= tbl;
- else if (Events::open_event_table(thd, TL_READ, &table))
+ 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_EVENT_OPEN_TABLE_FAILED, MYF(0));
- ret= EVEX_GENERAL_ERROR;
- goto done;
+ my_error(ER_EVENTS_DB_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
}
- if ((ret= evex_db_find_event_by_name(thd, name->m_db, name->m_name, table)))
- {
- my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str);
- goto done;
- }
- et= new Event_timed;
-
- /*
- 1)The table should not be closed beforehand. ::load_from_row() only loads
- and does not compile
+ ret= db_repository->load_named_event(thd, dbname, name, et);
- 2)::load_from_row() is silent on error therefore we emit error msg here
- */
- if ((ret= et->load_from_row(root, table)))
+ if (!ret)
{
- my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0));
- goto done;
- }
+ 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;
-done:
- if (ret)
- {
- delete et;
- et= 0;
- }
- /* don't close the table if we haven't opened it ourselves */
- if (!tbl && table)
- close_thread_tables(thd);
- *ett= et;
- DBUG_RETURN(ret);
-}
+ show_str.length(0);
+ show_str.set_charset(system_charset_info);
+ if (et->get_create_event(thd, &show_str))
+ goto err;
-/*
- The function exported to the world for creating of events.
+ field_list.push_back(new Item_empty_string("Event", NAME_LEN));
- SYNOPSIS
- Events::create_event()
- thd THD
- et event's data
- create_options Options specified when in the query. We are
- interested whether there is IF NOT EXISTS
- rows_affected How many rows were affected
+ sql_mode_str=
+ sys_var_thd_sql_mode::symbolic_mode_representation(thd, et->sql_mode,
+ &sql_mode_len);
- RETURN VALUE
- 0 OK
- !0 Error
+ field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));
- 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.
-*/
+ field_list.push_back(new Item_empty_string("Create Event",
+ show_str.length()));
-int
-Events::create_event(THD *thd, Event_timed *et, uint create_options,
- uint *rows_affected)
-{
- int ret;
+ if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF))
+ goto err;
- DBUG_ENTER("Events::create_event");
- DBUG_PRINT("enter", ("name: %*s options:%d", et->name.length,
- et->name.str, create_options));
+ protocol->prepare_for_resend();
+ protocol->store(et->name.str, et->name.length, scs);
- if (!(ret = db_create_event(thd, et,
- create_options & HA_LEX_CREATE_IF_NOT_EXISTS,
- rows_affected)))
- {
- Event_scheduler *scheduler= Event_scheduler::get_instance();
- if (scheduler->initialized() &&
- (ret= scheduler->create_event(thd, et, true)))
- 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 */
+ protocol->store((char*) sql_mode_str, sql_mode_len, scs);
+ protocol->store(show_str.c_ptr(), show_str.length(), scs);
+ ret= protocol->write();
+ send_eof(thd);
+ }
+ delete et;
DBUG_RETURN(ret);
+err:
+ delete et;
+ DBUG_RETURN(TRUE);
}
/*
- The function exported to the world for alteration of events.
+ Proxy for Event_db_repository::fill_schema_events.
+ Callback for I_S from sql_show.cc
SYNOPSIS
- Events::update_event()
- thd THD
- et event's data
- new_name set in case of RENAME TO.
+ Events::fill_schema_events()
+ thd Thread context
+ tables The schema table
+ cond Unused
RETURN VALUE
- 0 OK
- !0 Error
-
- 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)
+ 0 OK
+ !0 Error
*/
int
-Events::update_event(THD *thd, Event_timed *et, sp_name *new_name,
- uint *rows_affected)
+Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
{
- int ret;
+ char *db= NULL;
+ 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);
+ }
- DBUG_ENTER("Events::update_event");
- DBUG_PRINT("enter", ("name: %*s", et->name.length, et->name.str));
/*
- db_update_event() opens & closes the table to prevent
- crash later in the code when loading and compiling the new definition.
- Also on error conditions my_error() is called so no need to handle here
+ If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to
+ be NULL. Let's do an assert anyway.
*/
- if (!(ret= db_update_event(thd, et, new_name)))
+ if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS)
{
- Event_scheduler *scheduler= Event_scheduler::get_instance();
- if (scheduler->initialized() &&
- (ret= scheduler->update_event(thd, et,
- new_name? &new_name->m_db: NULL,
- new_name? &new_name->m_name: NULL)))
- my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret);
+ DBUG_ASSERT(thd->lex->select_lex.db);
+ if (check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0,
+ is_schema_db(thd->lex->select_lex.db)))
+ DBUG_RETURN(1);
+ db= thd->lex->select_lex.db;
}
- DBUG_RETURN(ret);
+ DBUG_RETURN(myself->db_repository->fill_schema_events(thd, tables, db));
}
/*
- Drops an event
+ Inits the scheduler's structures.
SYNOPSIS
- db_drop_event()
- thd THD
- et event's name
- drop_if_exists if set and the event not existing => warning onto the stack
- rows_affected affected number of rows is returned heres
+ Events::init()
+
+ NOTES
+ This function is not synchronized.
RETURN VALUE
- 0 OK
- !0 Error (my_error() called)
+ FALSE OK
+ TRUE Error in case the scheduler can't start
*/
-int db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists,
- uint *rows_affected)
+bool
+Events::init()
{
- TABLE *table;
- Open_tables_state backup;
- int ret;
+ THD *thd;
+ bool res= FALSE;
+ DBUG_ENTER("Events::init");
- DBUG_ENTER("db_drop_event");
- ret= EVEX_OPEN_TABLE_FAILED;
+ if (opt_event_scheduler == Events::EVENTS_DISABLED)
+ DBUG_RETURN(FALSE);
- thd->reset_n_backup_open_tables_state(&backup);
- if (Events::open_event_table(thd, TL_WRITE, &table))
+ /* We need a temporary THD during boot */
+ if (!(thd= new THD()))
{
- my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
- goto done;
+ res= TRUE;
+ goto end;
}
+ /*
+ The thread stack does not start from this function but we cannot
+ guess the real value. So better some value that doesn't assert than
+ no value.
+ */
+ thd->thread_stack= (char*) &thd;
+ thd->store_globals();
- if (!(ret= evex_db_find_event_aux(thd, et, table)))
+ if (check_system_tables(thd))
{
- if ((ret= table->file->ha_delete_row(table->record[0])))
- {
- my_error(ER_EVENT_CANNOT_DELETE, MYF(0));
- goto done;
- }
+ check_system_tables_error= TRUE;
+ sql_print_error("SCHEDULER: The system tables are damaged. "
+ "The scheduler subsystem will be unusable during this run.");
+ goto end;
}
- else if (ret == EVEX_KEY_NOT_FOUND)
- {
- 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", et->name.str);
- ret= 0;
- } else
- my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str);
- goto done;
+ check_system_tables_error= FALSE;
+
+ if (event_queue->init_queue(thd, db_repository))
+ {
+ sql_print_error("SCHEDULER: Error while loading from disk.");
+ goto end;
}
+ scheduler->init_scheduler(event_queue);
+ DBUG_ASSERT(opt_event_scheduler == Events::EVENTS_ON ||
+ opt_event_scheduler == Events::EVENTS_OFF);
+ if (opt_event_scheduler == Events::EVENTS_ON)
+ res= scheduler->start();
-done:
- /*
- evex_drop_event() is used by Event_timed::drop therefore
- we have to close our thread tables.
- */
- close_thread_tables(thd);
- thd->restore_backup_open_tables_state(&backup);
- DBUG_RETURN(ret);
+end:
+ delete thd;
+ /* Remember that we don't have a THD */
+ my_pthread_setspecific_ptr(THR_THD, NULL);
+
+ DBUG_RETURN(res);
}
/*
- Drops an event
+ Cleans up scheduler's resources. Called at server shutdown.
SYNOPSIS
- Events::drop_event()
- thd THD
- et event's name
- drop_if_exists if set and the event not existing => warning onto the stack
- rows_affected affected number of rows is returned heres
+ Events::deinit()
- RETURN VALUE
- 0 OK
- !0 Error (reported)
+ NOTES
+ This function is not synchronized.
*/
-int
-Events::drop_event(THD *thd, Event_timed *et, bool drop_if_exists,
- uint *rows_affected)
+void
+Events::deinit()
{
- int ret;
-
- DBUG_ENTER("Events::drop_event");
- if (!(ret= db_drop_event(thd, et, drop_if_exists, rows_affected)))
+ DBUG_ENTER("Events::deinit");
+ if (likely(!check_system_tables_error))
{
- Event_scheduler *scheduler= Event_scheduler::get_instance();
- if (scheduler->initialized() && (ret= scheduler->drop_event(thd, et)))
- my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret);
+ scheduler->stop();
+ scheduler->deinit_scheduler();
+
+ event_queue->deinit_queue();
}
- DBUG_RETURN(ret);
+ DBUG_VOID_RETURN;
}
/*
- SHOW CREATE EVENT
+ Inits Events mutexes
SYNOPSIS
- Events::show_create_event()
- thd THD
- spn the name of the event (db, name)
-
- RETURN VALUE
- 0 OK
- 1 Error during writing to the wire
+ Events::init_mutexes()
+ thd Thread
*/
-int
-Events::show_create_event(THD *thd, sp_name *spn)
+void
+Events::init_mutexes()
{
- int ret;
- Event_timed *et= NULL;
- Open_tables_state backup;
-
- DBUG_ENTER("evex_update_event");
- DBUG_PRINT("enter", ("name: %*s", spn->m_name.length, spn->m_name.str));
-
- thd->reset_n_backup_open_tables_state(&backup);
- ret= db_find_event(thd, spn, &et, NULL, thd->mem_root);
- thd->restore_backup_open_tables_state(&backup);
-
- if (!ret)
- {
- Protocol *protocol= thd->protocol;
- char show_str_buf[768];
- String show_str(show_str_buf, sizeof(show_str_buf), system_charset_info);
- List<Item> field_list;
- byte *sql_mode_str;
- ulong sql_mode_len=0;
-
- show_str.length(0);
- show_str.set_charset(system_charset_info);
-
- if (et->get_create_event(thd, &show_str))
- goto err;
-
- field_list.push_back(new Item_empty_string("Event", NAME_LEN));
-
- 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("sql_mode", sql_mode_len));
+ pthread_mutex_init(&LOCK_event_metadata, MY_MUTEX_INIT_FAST);
+ event_queue->init_mutexes();
+ scheduler->init_mutexes();
+}
- field_list.push_back(new Item_empty_string("Create Event",
- show_str.length()));
- if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
- Protocol::SEND_EOF))
- goto err;
- protocol->prepare_for_resend();
- protocol->store(et->name.str, et->name.length, system_charset_info);
+/*
+ Destroys Events mutexes
- protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info);
+ SYNOPSIS
+ Events::destroy_mutexes()
+*/
- protocol->store(show_str.c_ptr(), show_str.length(), system_charset_info);
- ret= protocol->write();
- send_eof(thd);
- }
- delete et;
- DBUG_RETURN(ret);
-err:
- delete et;
- DBUG_RETURN(1);
+void
+Events::destroy_mutexes()
+{
+ event_queue->deinit_mutexes();
+ scheduler->deinit_mutexes();
+ pthread_mutex_destroy(&LOCK_event_metadata);
}
/*
- Drops all events from a schema
+ Dumps the internal status of the scheduler and the memory cache
+ into a table with two columns - Name & Value. Different properties
+ which could be useful for debugging for instance deadlocks are
+ returned.
SYNOPSIS
- Events::drop_schema_events()
- thd Thread
- db ASCIIZ schema name
-
- RETURN VALUE
- 0 OK
- !0 Error
+ Events::dump_internal_status()
*/
-int
-Events::drop_schema_events(THD *thd, char *db)
+void
+Events::dump_internal_status()
{
- int ret= 0;
- LEX_STRING db_lex= {db, strlen(db)};
-
- DBUG_ENTER("evex_drop_db_events");
- DBUG_PRINT("enter", ("dropping events from %s", db));
+ DBUG_ENTER("Events::dump_internal_status");
+ puts("\n\n\nEvents status:");
+ puts("LLA = Last Locked At LUA = Last Unlocked At");
+ puts("WOC = Waiting On Condition DL = Data Locked");
- Event_scheduler *scheduler= Event_scheduler::get_instance();
- if (scheduler->initialized())
- ret= scheduler->drop_schema_events(thd, &db_lex);
- else
- ret= db_drop_events_from_table(thd, &db_lex);
+ scheduler->dump_internal_status();
+ event_queue->dump_internal_status();
- DBUG_RETURN(ret);
+ DBUG_VOID_RETURN;
}
/*
- Drops all events in the selected database, from mysql.event.
+ Starts execution of events by the scheduler
SYNOPSIS
- evex_drop_db_events_from_table()
- thd Thread
- db Schema name
+ Events::start_execution_of_events()
RETURN VALUE
- 0 OK
- !0 Error from ha_delete_row
+ FALSE OK
+ TRUE Error
*/
-int
-db_drop_events_from_table(THD *thd, LEX_STRING *db)
+bool
+Events::start_execution_of_events()
{
- int ret;
- TABLE *table;
- READ_RECORD read_record_info;
- DBUG_ENTER("db_drop_events_from_table");
- DBUG_PRINT("info", ("dropping events from %s", db->str));
-
- if ((ret= Events::open_event_table(thd, TL_WRITE, &table)))
+ DBUG_ENTER("Events::start_execution_of_events");
+ if (unlikely(check_system_tables_error))
{
- if (my_errno != ENOENT)
- sql_print_error("Table mysql.event is damaged. Got error %d on open",
- my_errno);
- DBUG_RETURN(ret);
+ my_error(ER_EVENTS_DB_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
}
- /* 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);
- while (!(read_record_info.read_record(&read_record_info)) && !ret)
- {
- char *et_db= get_field(thd->mem_root,
- table->field[Events::FIELD_DB]);
-
- LEX_STRING et_db_lex= {et_db, strlen(et_db)};
- DBUG_PRINT("info", ("Current event %s.%s", et_db,
- get_field(thd->mem_root,
- table->field[Events::FIELD_NAME])));
-
- if (!sortcmp_lex_string(et_db_lex, *db, system_charset_info))
- {
- DBUG_PRINT("info", ("Dropping"));
- if ((ret= table->file->ha_delete_row(table->record[0])))
- my_error(ER_EVENT_DROP_FAILED, MYF(0),
- get_field(thd->mem_root,
- table->field[Events::FIELD_NAME]));
- }
- }
- end_read_record(&read_record_info);
- thd->version--; /* Force close to free memory */
-
- close_thread_tables(thd);
-
- DBUG_RETURN(ret);
+ DBUG_RETURN(scheduler->start());
}
-
/*
- Inits the scheduler's structures.
+ 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::init()
-
- NOTES
- This function is not synchronized.
+ Events::stop_execution_of_events()
RETURN VALUE
- 0 OK
- 1 Error
+ FALSE OK
+ TRUE Error
*/
-int
-Events::init()
+bool
+Events::stop_execution_of_events()
{
- int ret= 0;
- DBUG_ENTER("Events::init");
-
- /* it should be an assignment! */
- if (opt_event_scheduler)
+ DBUG_ENTER("Events::stop_execution_of_events");
+ if (unlikely(check_system_tables_error))
{
- Event_scheduler *scheduler= Event_scheduler::get_instance();
- DBUG_ASSERT(opt_event_scheduler == 1 || opt_event_scheduler == 2);
- DBUG_RETURN(scheduler->init() ||
- (opt_event_scheduler == 1? scheduler->start():
- scheduler->start_suspended()));
+ my_error(ER_EVENTS_DB_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
}
- DBUG_RETURN(0);
+ DBUG_RETURN(scheduler->stop());
}
/*
- Cleans up scheduler's resources. Called at server shutdown.
+ Checks whether the scheduler is running or not.
SYNOPSIS
- Events::shutdown()
+ Events::is_started()
- NOTES
- This function is not synchronized.
+ RETURN VALUE
+ TRUE Yes
+ FALSE No
*/
-void
-Events::shutdown()
+bool
+Events::is_execution_of_events_started()
{
- DBUG_ENTER("Events::shutdown");
- Event_scheduler *scheduler= Event_scheduler::get_instance();
- if (scheduler->initialized())
+ DBUG_ENTER("Events::is_execution_of_events_started");
+ if (unlikely(check_system_tables_error))
{
- scheduler->stop();
- scheduler->destroy();
+ my_error(ER_EVENTS_DB_ERROR, MYF(0));
+ DBUG_RETURN(FALSE);
}
-
- DBUG_VOID_RETURN;
+ DBUG_RETURN(scheduler->is_running());
}
+
/*
- Proxy for Event_scheduler::dump_internal_status
+ 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::dump_internal_status()
+ Events::check_system_tables()
thd Thread
-
+
RETURN VALUE
- 0 OK
- !0 Error
+ FALSE OK
+ TRUE Error
*/
-int
-Events::dump_internal_status(THD *thd)
+bool
+Events::check_system_tables(THD *thd)
{
- return Event_scheduler::dump_internal_status(thd);
-}
+ TABLE_LIST tables;
+ Open_tables_state backup;
+ bool ret= FALSE;
+ DBUG_ENTER("Events::check_system_tables");
+ DBUG_PRINT("enter", ("thd=0x%lx", thd));
-/*
- Inits Events mutexes
+ thd->reset_n_backup_open_tables_state(&backup);
- SYNOPSIS
- Events::init_mutexes()
- thd Thread
-*/
+ bzero((char*) &tables, sizeof(tables));
+ tables.db= (char*) "mysql";
+ tables.table_name= tables.alias= (char*) "db";
+ tables.lock_type= TL_READ;
-void
-Events::init_mutexes()
-{
- Event_scheduler::init_mutexes();
-}
+ if ((ret= simple_open_n_lock_tables(thd, &tables)))
+ {
+ sql_print_error("SCHEDULER: Cannot open mysql.db");
+ ret= TRUE;
+ }
+ 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;
-/*
- Destroys Events mutexes
+ if (simple_open_n_lock_tables(thd, &tables))
+ {
+ sql_print_error("SCHEDULER: Cannot open mysql.user");
+ ret= TRUE;
+ }
+ else
+ {
+ 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);
+ }
- SYNOPSIS
- Events::destroy_mutexes()
-*/
+ thd->restore_backup_open_tables_state(&backup);
-void
-Events::destroy_mutexes()
-{
- Event_scheduler::destroy_mutexes();
+ DBUG_RETURN(ret);
}
diff --git a/sql/events.h b/sql/events.h
index 66cce6e7777..b5d2866aa38 100644
--- a/sql/events.h
+++ b/sql/events.h
@@ -16,78 +16,125 @@
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;
+class Event_parse_data;
+class Event_db_repository;
+class Event_queue;
+class Event_queue_element;
+class Event_scheduler;
+
+/* Return codes */
+enum enum_events_error_code
+{
+ OP_OK= 0,
+ OP_NOT_RUNNING,
+ OP_CANT_KILL,
+ OP_CANT_INIT,
+ OP_DISABLED_EVENT,
+ OP_LOAD_ERROR,
+ OP_ALREADY_EXISTS
+};
+
+
+int
+sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs);
-class Event_timed;
class Events
{
public:
- static ulong opt_event_scheduler;
- static TYPELIB opt_typelib;
-
- enum enum_table_field
+ /*
+ Quite NOT the best practice and will be removed once
+ Event_timed::drop() and Event_timed is fixed not do drop directly
+ or other scheme will be found.
+ */
+ friend class Event_queue_element;
+
+ /* The order should match the order in opt_typelib */
+ enum enum_opt_event_scheduler
{
- FIELD_DB = 0,
- FIELD_NAME,
- FIELD_BODY,
- FIELD_DEFINER,
- FIELD_EXECUTE_AT,
- FIELD_INTERVAL_EXPR,
- FIELD_TRANSIENT_INTERVAL,
- FIELD_CREATED,
- FIELD_MODIFIED,
- FIELD_LAST_EXECUTED,
- FIELD_STARTS,
- FIELD_ENDS,
- FIELD_STATUS,
- FIELD_ON_COMPLETION,
- FIELD_SQL_MODE,
- FIELD_COMMENT,
- FIELD_COUNT /* a cool trick to count the number of fields :) */
+ EVENTS_OFF= 0,
+ EVENTS_ON= 1,
+ EVENTS_DISABLED= 5
};
- static int
- create_event(THD *thd, Event_timed *et, uint create_options,
- uint *rows_affected);
+ static enum_opt_event_scheduler opt_event_scheduler;
+ static TYPELIB opt_typelib;
+ static TYPELIB var_typelib;
- static int
- update_event(THD *thd, Event_timed *et, sp_name *new_name,
- uint *rows_affected);
+ bool
+ init();
- static int
- drop_event(THD *thd, Event_timed *et, bool drop_if_exists,
- uint *rows_affected);
+ void
+ deinit();
- static int
+ void
+ init_mutexes();
+
+ void
+ destroy_mutexes();
+
+ bool
+ start_execution_of_events();
+
+ bool
+ stop_execution_of_events();
+
+ bool
+ is_execution_of_events_started();
+
+ static Events *
+ get_instance();
+
+ 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);
+
+ bool
+ drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists,
+ bool only_from_disk);
+
+ void
+ drop_schema_events(THD *thd, char *db);
+
+ int
open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
- static int
- show_create_event(THD *thd, sp_name *spn);
+ bool
+ show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name);
+ /* Needed for both SHOW CREATE EVENT and INFORMATION_SCHEMA */
static int
reconstruct_interval_expression(String *buf, interval_type interval,
longlong expression);
static int
- drop_schema_events(THD *thd, char *db);
-
- static int
- dump_internal_status(THD *thd);
-
- static int
- init();
-
- static void
- shutdown();
-
- static void
- init_mutexes();
-
- static void
- destroy_mutexes();
+ fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */);
+ void
+ dump_internal_status();
private:
+ bool
+ check_system_tables(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;
+
/* Prevent use of these */
Events(const Events &);
void operator=(Events &);
diff --git a/sql/events_priv.h b/sql/events_priv.h
deleted file mode 100644
index ed02cb7055b..00000000000
--- a/sql/events_priv.h
+++ /dev/null
@@ -1,79 +0,0 @@
-#ifndef _EVENT_PRIV_H_
-#define _EVENT_PRIV_H_
-/* Copyright (C) 2004-2006 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-#define EVENT_EXEC_STARTED 0
-#define EVENT_EXEC_ALREADY_EXEC 1
-#define EVENT_EXEC_CANT_FORK 2
-
-#define EVEX_DB_FIELD_LEN 64
-#define EVEX_NAME_FIELD_LEN 64
-#define EVEX_MAX_INTERVAL_VALUE 2147483647L
-
-class Event_timed;
-
-int
-evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname,
- const LEX_STRING ev_name,
- TABLE *table);
-
-int
-db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists,
- uint *rows_affected);
-int
-db_find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl,
- MEM_ROOT *root);
-
-int
-db_create_event(THD *thd, Event_timed *et, my_bool create_if_not,
- uint *rows_affected);
-
-int
-db_drop_events_from_table(THD *thd, LEX_STRING *db);
-
-int
-sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs);
-
-/* Compares only the name part of the identifier */
-bool
-event_timed_name_equal(Event_timed *et, LEX_STRING *name);
-
-/* Compares only the schema part of the identifier */
-bool
-event_timed_db_equal(Event_timed *et, LEX_STRING *db);
-
-/*
- Compares only the definer part of the identifier. Use during DROP USER
- to drop user's events. (Still not implemented)
-*/
-bool
-event_timed_definer_equal(Event_timed *et, LEX_STRING *definer);
-
-/* Compares the whole identifier*/
-bool
-event_timed_identifier_equal(Event_timed *a, Event_timed *b);
-
-
-bool
-change_security_context(THD *thd, LEX_STRING user, LEX_STRING host,
- LEX_STRING db, Security_context *s_ctx,
- Security_context **backup);
-
-void
-restore_security_context(THD *thd, Security_context *backup);
-
-#endif /* _EVENT_PRIV_H_ */
diff --git a/sql/lex.h b/sql/lex.h
index b8d6a662754..f19c9413e0e 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -452,7 +452,6 @@ static SYMBOL symbols[] = {
{ "RTREE", SYM(RTREE_SYM)},
{ "SAVEPOINT", SYM(SAVEPOINT_SYM)},
{ "SCHEDULE", SYM(SCHEDULE_SYM)},
- { "SCHEDULER", SYM(SCHEDULER_SYM)},
{ "SCHEMA", SYM(DATABASE)},
{ "SCHEMAS", SYM(DATABASES)},
{ "SECOND", SYM(SECOND_SYM)},
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 445027fa368..6ccd4707248 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -837,7 +837,7 @@ static void close_connections(void)
DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
tmp->thread_id));
/* We skip slave threads & scheduler on this first loop through. */
- if (tmp->slave_thread || tmp->system_thread == SYSTEM_THREAD_EVENT_SCHEDULER)
+ if (tmp->slave_thread)
continue;
tmp->killed= THD::KILL_CONNECTION;
@@ -856,7 +856,7 @@ static void close_connections(void)
}
(void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list
- Events::shutdown();
+ Events::get_instance()->deinit();
end_slave();
if (thread_count)
@@ -1294,7 +1294,7 @@ static void clean_up_mutexes()
(void) pthread_mutex_destroy(&LOCK_bytes_sent);
(void) pthread_mutex_destroy(&LOCK_bytes_received);
(void) pthread_mutex_destroy(&LOCK_user_conn);
- Events::destroy_mutexes();
+ Events::get_instance()->destroy_mutexes();
#ifdef HAVE_OPENSSL
(void) pthread_mutex_destroy(&LOCK_des_key_file);
#ifndef HAVE_YASSL
@@ -2872,7 +2872,7 @@ static int init_thread_environment()
(void) pthread_mutex_init(&LOCK_server_started, MY_MUTEX_INIT_FAST);
(void) pthread_cond_init(&COND_server_started,NULL);
sp_cache_init();
- Events::init_mutexes();
+ Events::get_instance()->init_mutexes();
/* Parameter for threads created for connections */
(void) pthread_attr_init(&connection_attrib);
(void) pthread_attr_setdetachstate(&connection_attrib,
@@ -3658,7 +3658,8 @@ we force server id to 2, but this MySQL server will not act as a slave.");
if (!opt_noacl)
{
- Events::init();
+ if (Events::get_instance()->init())
+ unireg_abort(1);
}
#if defined(__NT__) || defined(HAVE_SMEM)
handle_connections_methods();
@@ -5001,9 +5002,9 @@ struct my_option my_long_options[] =
(gptr*) &global_system_variables.engine_condition_pushdown,
(gptr*) &global_system_variables.engine_condition_pushdown,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ /* See how it's handled in get_one_option() */
{"event-scheduler", OPT_EVENT_SCHEDULER, "Enable/disable the event scheduler.",
- (gptr*) &Events::opt_event_scheduler, (gptr*) &Events::opt_event_scheduler, 0, GET_ULONG,
- REQUIRED_ARG, 2/*default*/, 0/*min-value*/, 2/*max-value*/, 0, 0, 0},
+ NULL, NULL, 0, GET_STR, OPT_ARG, 0, 0, 0, 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 (disabled by default). With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running. Disable with --skip-external-locking.",
@@ -7305,20 +7306,30 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
#endif
case OPT_EVENT_SCHEDULER:
if (!argument)
- Events::opt_event_scheduler= 2;
+ Events::opt_event_scheduler= Events::EVENTS_DISABLED;
else
{
int type;
- if ((type=find_type(argument, &Events::opt_typelib, 1)) <= 0)
- {
- fprintf(stderr,"Unknown option to event-scheduler: %s\n",argument);
- exit(1);
- }
/*
- type= 1 2 3 4 5 6
- (OFF | 0) - (ON | 1) - (2 | SUSPEND)
+ type= 5 1 2 3 4
+ (DISABLE ) - (OFF | ON) - (0 | 1)
*/
- Events::opt_event_scheduler= (type-1) / 2;
+ 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;
+ }
}
break;
case (int) OPT_SKIP_NEW:
diff --git a/sql/set_var.cc b/sql/set_var.cc
index bc2f0e0d823..e09c75573eb 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -57,7 +57,7 @@
#include <myisam.h>
#include <my_dir.h>
-#include "event_scheduler.h"
+#include "events.h"
/* WITH_INNOBASE_STORAGE_ENGINE */
extern uint innobase_flush_log_at_trx_commit;
@@ -3899,6 +3899,7 @@ bool sys_var_thd_dbug::update(THD *thd, set_var *var)
return 0;
}
+
byte *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b)
{
char buf[256];
@@ -3910,6 +3911,12 @@ byte *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b)
}
+bool sys_var_event_scheduler::check(THD *thd, set_var *var)
+{
+ return check_enum(thd, var, &Events::var_typelib);
+}
+
+
/*
The update method of the global variable event_scheduler.
If event_scheduler is switched from 0 to 1 then the scheduler main
@@ -3928,30 +3935,30 @@ byte *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b)
bool
sys_var_event_scheduler::update(THD *thd, set_var *var)
{
- enum Event_scheduler::enum_error_code res;
- Event_scheduler *scheduler= Event_scheduler::get_instance();
+ 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");
+ DBUG_RETURN(TRUE);
+ }
DBUG_PRINT("new_value", ("%lu", (bool)var->save_result.ulong_value));
- if (!scheduler->initialized())
+
+ Item_result var_type= var->value->result_type();
+
+ 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
{
- my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--event-scheduler=0");
- DBUG_RETURN(true);
+ DBUG_ASSERT(0);
}
+ if (res)
+ my_error(ER_EVENT_SET_VAR_ERROR, MYF(0));
- if (var->save_result.ulonglong_value < 1 ||
- var->save_result.ulonglong_value > 2)
- {
- char buf[64];
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "event_scheduler",
- llstr(var->save_result.ulonglong_value, buf));
- DBUG_RETURN(true);
- }
- if ((res= scheduler->suspend_or_resume(var->save_result.ulonglong_value == 1?
- Event_scheduler::RESUME :
- Event_scheduler::SUSPEND)))
- my_error(ER_EVENT_SET_VAR_ERROR, MYF(0), (uint) res);
DBUG_RETURN((bool) res);
}
@@ -3959,16 +3966,15 @@ sys_var_event_scheduler::update(THD *thd, set_var *var)
byte *sys_var_event_scheduler::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
- Event_scheduler *scheduler= Event_scheduler::get_instance();
-
- if (!scheduler->initialized())
- thd->sys_var_tmp.long_value= 0;
- else if (scheduler->get_state() == Event_scheduler::RUNNING)
- thd->sys_var_tmp.long_value= 1;
+ 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
- thd->sys_var_tmp.long_value= 2;
+ state= Events::EVENTS_OFF; // This should be OFF
- return (byte*) &thd->sys_var_tmp;
+ return (byte*) Events::opt_typelib.type_names[state];
}
diff --git a/sql/set_var.h b/sql/set_var.h
index a63bcc4a55d..01669b378e1 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -932,6 +932,12 @@ public:
sys_var_long_ptr(name_arg, NULL, NULL) {};
bool update(THD *thd, set_var *var);
byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ SHOW_TYPE type() { return SHOW_CHAR; }
+ bool check(THD *thd, set_var *var);
+ bool check_update_type(Item_result type)
+ {
+ return type != STRING_RESULT && type != INT_RESULT;
+ }
};
#ifdef HAVE_ROW_BASED_REPLICATION
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index 28d81447c66..37f54899615 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -5978,6 +5978,10 @@ ER_BAD_LOG_ENGINE
eng "One can use only CSV and MyISAM engines for the log tables"
ER_CANT_DROP_LOG_TABLE
eng "Cannot drop log table if log is enabled"
+ER_EVENT_RECURSIVITY_FORBIDDEN
+ eng "Recursivity of EVENT DDL statements is forbidden when body is present"
+ER_EVENTS_DB_ERROR
+ eng "Cannot proceed because the tables used by events were found damaged at server start"
ER_ONLY_INTEGERS_ALLOWED
eng "Only normal integers allowed as number here"
ER_USERNAME
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index fc4aa5e26d6..1ff33368a37 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -196,7 +196,6 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_PRIVILEGES:
case SQLCOM_SHOW_PROCESSLIST:
case SQLCOM_SHOW_PROC_CODE:
- case SQLCOM_SHOW_SCHEDULER_STATUS:
case SQLCOM_SHOW_SLAVE_HOSTS:
case SQLCOM_SHOW_SLAVE_STAT:
case SQLCOM_SHOW_STATUS:
@@ -657,10 +656,12 @@ sp_head::create(THD *thd)
sp_head::~sp_head()
{
+ DBUG_ENTER("sp_head::~sp_head");
destroy();
delete m_next_cached_sp;
if (m_thd)
restore_thd_mem_root(m_thd);
+ DBUG_VOID_RETURN;
}
void
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 3fc182e20f3..e3181fb67cf 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -2084,6 +2084,7 @@ bool Security_context::set_user(char *user_arg)
return user == 0;
}
+
/****************************************************************************
Handling of open and locked tables states.
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 372a350566f..3960236e828 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -949,7 +949,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
exit:
(void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */
- error= Events::drop_schema_events(thd, db);
+ Events::get_instance()->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
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index b9e52b62244..2b00a69aa60 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -169,14 +169,16 @@ void lex_start(THD *thd, const uchar *buf, uint length)
lex->sql_command= SQLCOM_END;
lex->duplicates= DUP_ERROR;
lex->ignore= 0;
+ lex->spname= NULL;
lex->sphead= NULL;
lex->spcont= NULL;
lex->proc_list.first= 0;
- lex->escape_used= lex->et_compile_phase= FALSE;
+ lex->escape_used= FALSE;
lex->reset_query_tables_list(FALSE);
+ lex->expr_allows_subselect= TRUE;
lex->name= 0;
- lex->et= NULL;
+ lex->event_parse_data= NULL;
lex->nest_level=0 ;
lex->allow_sum_func= 0;
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index ce0af39a041..b7781c9aae8 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -27,7 +27,7 @@ class sp_instr;
class sp_pcontext;
class st_alter_tablespace;
class partition_info;
-class Event_timed;
+class Event_parse_data;
#ifdef MYSQL_SERVER
/*
@@ -113,7 +113,6 @@ enum enum_sql_command {
SQLCOM_SHOW_CONTRIBUTORS,
SQLCOM_CREATE_EVENT, SQLCOM_ALTER_EVENT, SQLCOM_DROP_EVENT,
SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS,
- SQLCOM_SHOW_SCHEDULER_STATUS,
/* This should be the last !!! */
@@ -958,6 +957,14 @@ typedef struct st_lex : public Query_tables_list
*/
nesting_map allow_sum_func;
enum_sql_command sql_command;
+ /*
+ Usually `expr` rule of yacc is quite reused but some commands better
+ not support subqueries which comes standard with this rule, like
+ KILL, HA_READ, CREATE/ALTER EVENT etc. Set this to `false` to get
+ syntax error back.
+ */
+ bool expr_allows_subselect;
+
thr_lock_type lock_option;
enum SSL_type ssl_type; /* defined in violite.h */
enum my_lex_states next_state;
@@ -1035,8 +1042,7 @@ typedef struct st_lex : public Query_tables_list
st_sp_chistics sp_chistics;
- Event_timed *et;
- bool et_compile_phase;
+ Event_parse_data *event_parse_data;
bool only_view; /* used for SHOW CREATE TABLE/VIEW */
/*
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 7c600f7463e..55c090e1982 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -27,7 +27,7 @@
#include "sp.h"
#include "sp_cache.h"
#include "events.h"
-#include "event_timed.h"
+#include "event_data_objects.h"
#ifdef HAVE_OPENSSL
/*
@@ -3888,73 +3888,43 @@ end_with_restore_list:
}
case SQLCOM_CREATE_EVENT:
case SQLCOM_ALTER_EVENT:
- case SQLCOM_DROP_EVENT:
{
- uint rows_affected= 1;
- DBUG_ASSERT(lex->et);
- do {
- if (! lex->et->dbname.str ||
- (lex->sql_command == SQLCOM_ALTER_EVENT && lex->spname &&
- !lex->spname->m_db.str))
- {
- my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
- res= true;
- break;
- }
-
- if (check_access(thd, EVENT_ACL, lex->et->dbname.str, 0, 0, 0,
- is_schema_db(lex->et->dbname.str)) ||
- (lex->sql_command == SQLCOM_ALTER_EVENT && lex->spname &&
- (check_access(thd, EVENT_ACL, lex->spname->m_db.str, 0, 0, 0,
- is_schema_db(lex->spname->m_db.str)))))
- break;
-
- if (end_active_trans(thd))
- {
- res= -1;
- break;
- }
-
- switch (lex->sql_command) {
- case SQLCOM_CREATE_EVENT:
- res= Events::create_event(thd, lex->et,
- (uint) lex->create_info.options,
- &rows_affected);
- break;
- case SQLCOM_ALTER_EVENT:
- res= Events::update_event(thd, lex->et, lex->spname,
- &rows_affected);
- break;
- case SQLCOM_DROP_EVENT:
- res= Events::drop_event(thd, lex->et, lex->drop_if_exists,
- &rows_affected);
- default:;
- }
- DBUG_PRINT("info", ("CREATE/ALTER/DROP returned error code=%d af_rows=%d",
- res, rows_affected));
- if (!res)
- send_ok(thd, rows_affected);
+ DBUG_ASSERT(lex->event_parse_data);
+ 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);
+ break;
+ case SQLCOM_ALTER_EVENT:
+ res= Events::get_instance()->update_event(thd, lex->event_parse_data,
+ lex->spname);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+ DBUG_PRINT("info",("DDL error code=%d", res));
+ if (!res)
+ send_ok(thd);
- /* lex->unit.cleanup() is called outside, no need to call it here */
- } while (0);
+ /* Don't do it, if we are inside a SP */
if (!thd->spcont)
{
- lex->et->free_sphead_on_delete= true;
- lex->et->free_sp();
- lex->et->deinit_mutexes();
+ delete lex->sphead;
+ lex->sphead= NULL;
}
-
+
+ /* 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);
- DBUG_ASSERT(lex->et);
if (! lex->spname->m_db.str)
{
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
- res= true;
- break;
+ goto error;
}
if (check_access(thd, EVENT_ACL, lex->spname->m_db.str, 0, 0, 0,
is_schema_db(lex->spname->m_db.str)))
@@ -3963,18 +3933,25 @@ end_with_restore_list:
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;
}
- res= Events::show_create_event(thd, lex->spname);
- break;
- }
-#ifndef DBUG_OFF
- case SQLCOM_SHOW_SCHEDULER_STATUS:
- {
- res= Events::dump_internal_status(thd);
+
+ 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
+ {
+ uint affected= 1;
+ if (!(res= Events::get_instance()->drop_event(thd,
+ lex->spname->m_db,
+ lex->spname->m_name,
+ lex->drop_if_exists,
+ FALSE)))
+ send_ok(thd);
+ }
break;
}
-#endif
case SQLCOM_CREATE_FUNCTION: // UDF function
{
if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0))
@@ -6065,14 +6042,6 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
{
delete lex->sphead;
lex->sphead= NULL;
- if (lex->et)
- {
- lex->et->free_sphead_on_delete= true;
- /* alloced on thd->mem_root so no real memory free but dtor call */
- lex->et->free_sp();
- lex->et->deinit_mutexes();
- lex->et= NULL;
- }
}
else
{
@@ -6109,13 +6078,6 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
delete lex->sphead;
lex->sphead= NULL;
}
- if (lex->et)
- {
- lex->et->free_sphead_on_delete= true;
- lex->et->free_sp();
- lex->et->deinit_mutexes();
- lex->et= NULL;
- }
}
thd->proc_info="freeing items";
thd->end_statement();
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 9a41c139f4e..eddba067d3a 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -27,7 +27,7 @@
#include "authors.h"
#include "contributors.h"
#include "events.h"
-#include "event_timed.h"
+#include "event_data_objects.h"
#include <my_dir.h>
#ifdef WITH_PARTITION_STORAGE_ENGINE
@@ -4219,7 +4219,7 @@ extern LEX_STRING interval_type_to_name[];
1 Error
*/
-static int
+int
copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
{
const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
@@ -4230,7 +4230,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
restore_record(sch_table, s->default_values);
- if (et.load_from_row(thd->mem_root, event_table))
+ if (et.load_from_row(event_table))
{
my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0));
DBUG_RETURN(1);
@@ -4354,183 +4354,6 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
}
-/*
- Performs an index scan of event_table (mysql.event) and fills schema_table.
-
- Synopsis
- events_table_index_read_for_db()
- thd Thread
- schema_table The I_S.EVENTS table
- event_table The event table to use for loading (mysql.event)
-
- Returns
- 0 OK
- 1 Error
-*/
-
-static
-int events_table_index_read_for_db(THD *thd, TABLE *schema_table,
- TABLE *event_table)
-{
- int ret=0;
- CHARSET_INFO *scs= system_charset_info;
- KEY *key_info;
- uint key_len;
- byte *key_buf= NULL;
- LINT_INIT(key_buf);
-
- DBUG_ENTER("schema_events_do_index_scan");
-
- DBUG_PRINT("info", ("Using prefix scanning on PK"));
- event_table->file->ha_index_init(0, 1);
- event_table->field[Events::FIELD_DB]->
- store(thd->lex->select_lex.db, strlen(thd->lex->select_lex.db), scs);
- key_info= event_table->key_info;
- key_len= key_info->key_part[0].store_length;
-
- if (!(key_buf= (byte *)alloc_root(thd->mem_root, key_len)))
- {
- ret= 1;
- /* don't send error, it would be done by sql_alloc_error_handler() */
- }
- else
- {
- key_copy(key_buf, event_table->record[0], key_info, key_len);
- if (!(ret= event_table->file->index_read(event_table->record[0], key_buf,
- key_len, HA_READ_PREFIX)))
- {
- DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret));
- do
- {
- ret= copy_event_to_schema_table(thd, schema_table, event_table);
- if (ret == 0)
- ret= event_table->file->index_next_same(event_table->record[0],
- key_buf, key_len);
- } while (ret == 0);
- }
- DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
- }
- event_table->file->ha_index_end();
- /* ret is guaranteed to be != 0 */
- if (ret == HA_ERR_END_OF_FILE || ret == HA_ERR_KEY_NOT_FOUND)
- DBUG_RETURN(0);
- DBUG_RETURN(1);
-}
-
-
-/*
- Performs a table scan of event_table (mysql.event) and fills schema_table.
-
- Synopsis
- events_table_scan_all()
- thd Thread
- schema_table The I_S.EVENTS in memory table
- event_table The event table to use for loading.
-
- Returns
- 0 OK
- 1 Error
-*/
-
-static
-int events_table_scan_all(THD *thd, TABLE *schema_table,
- TABLE *event_table)
-{
- int ret;
- READ_RECORD read_record_info;
-
- DBUG_ENTER("schema_events_do_table_scan");
- init_read_record(&read_record_info, thd, event_table, NULL, 1, 0);
-
- /*
- rr_sequential, in read_record(), returns 137==HA_ERR_END_OF_FILE,
- but rr_handle_error returns -1 for that reason. Thus, read_record()
- returns -1 eventually.
- */
- do
- {
- ret= read_record_info.read_record(&read_record_info);
- if (ret == 0)
- ret= copy_event_to_schema_table(thd, schema_table, event_table);
- }
- while (ret == 0);
-
- DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
- end_read_record(&read_record_info);
-
- /* ret is guaranteed to be != 0 */
- DBUG_RETURN(ret == -1? 0:1);
-}
-
-
-/*
- Fills I_S.EVENTS with data loaded from mysql.event. Also used by
- SHOW EVENTS
-
- Synopsis
- fill_schema_events()
- thd Thread
- tables The schema table
- cond Unused
-
- Returns
- 0 OK
- 1 Error
-*/
-
-int fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
-{
- TABLE *schema_table= tables->table;
- TABLE *event_table= NULL;
- Open_tables_state backup;
- int ret= 0;
-
- DBUG_ENTER("fill_schema_events");
- /*
- If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to
- be NULL. Let's do an assert anyway.
- */
- if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS)
- {
- DBUG_ASSERT(thd->lex->select_lex.db);
- if (check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0,
- is_schema_db(thd->lex->select_lex.db)))
- DBUG_RETURN(1);
- }
-
- DBUG_PRINT("info",("db=%s", thd->lex->select_lex.db?
- thd->lex->select_lex.db:"(null)"));
-
- thd->reset_n_backup_open_tables_state(&backup);
- if (Events::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);
- }
-
- /*
- 1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order
- thus we won't order it. OTOH, SHOW EVENTS will be
- ordered.
- 2. SHOW EVENTS => PRIMARY KEY with prefix scanning on (db)
- Reasoning: Events are per schema, therefore a scan over an index
- will save use from doing a table scan and comparing
- every single row's `db` with the schema which we show.
- */
- if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS)
- ret= events_table_index_read_for_db(thd, schema_table, event_table);
- else
- ret= events_table_scan_all(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);
-}
-
-
int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond)
{
DBUG_ENTER("fill_open_tables");
@@ -5633,7 +5456,7 @@ ST_SCHEMA_TABLE schema_tables[]=
{"ENGINES", engines_fields_info, create_schema_table,
fill_schema_engines, make_old_format, 0, -1, -1, 0},
{"EVENTS", events_fields_info, create_schema_table,
- fill_schema_events, make_old_format, 0, -1, -1, 0},
+ Events::fill_schema_events, make_old_format, 0, -1, -1, 0},
{"FILES", files_fields_info, create_schema_table,
fill_schema_files, 0, 0, -1, -1, 0},
{"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table,
diff --git a/sql/sql_show.h b/sql/sql_show.h
index 6fce5e94ca3..681d1232b39 100644
--- a/sql/sql_show.h
+++ b/sql/sql_show.h
@@ -14,4 +14,6 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
HA_CREATE_INFO *create_info_arg);
int view_store_create_info(THD *thd, TABLE_LIST *table, String *buff);
+int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table);
+
#endif /* SQL_SHOW_H */
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index b28aa4a1ce5..c4c40ea63c8 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -28,6 +28,8 @@
#include <sys/malloc.h>
#endif
+#include "events.h"
+
static const char *lock_descriptions[] =
{
"No lock",
@@ -532,5 +534,7 @@ Estimated memory (with thread stack): %ld\n",
(int) info.keepcost,
(long) (thread_count * thread_stack + info.hblkhd + info.arena));
#endif
+
+ Events::get_instance()->dump_internal_status();
puts("");
}
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index b1a1e9cb9af..a9539a3df98 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -38,7 +38,7 @@
#include "sp_pcontext.h"
#include "sp_rcontext.h"
#include "sp.h"
-#include "event_timed.h"
+#include "event_data_objects.h"
#include <myisam.h>
#include <myisammrg.h>
@@ -590,7 +590,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token RTREE_SYM
%token SAVEPOINT_SYM
%token SCHEDULE_SYM
-%token SCHEDULER_SYM
%token SECOND_MICROSECOND_SYM
%token SECOND_SYM
%token SECURITY_SYM
@@ -907,7 +906,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa
load_data opt_field_or_var_spec fields_or_vars opt_load_data_set_spec
definer view_replace_or_algorithm view_replace view_algorithm_opt
- view_algorithm view_or_trigger_or_sp view_or_trigger_or_sp_tail
+ view_algorithm view_or_trigger_or_sp_or_event
+ view_or_trigger_or_sp_or_event_tail
view_suid view_tail view_list_opt view_list view_select
view_check_option trigger_tail sp_tail
install uninstall partition_entry binlog_base64_event
@@ -1284,29 +1284,41 @@ create:
lex->name=$4.str;
lex->create_info.options=$3;
}
- | CREATE EVENT_SYM opt_if_not_exists sp_name
+ | CREATE
+ {
+ Lex->create_view_mode= VIEW_CREATE_NEW;
+ Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED;
+ Lex->create_view_suid= TRUE;
+ }
+ view_or_trigger_or_sp_or_event
+ {}
+ | CREATE USER clear_privileges grant_list
+ {
+ Lex->sql_command = SQLCOM_CREATE_USER;
+ }
+ | CREATE LOGFILE_SYM GROUP logfile_group_info
+ {
+ Lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP;
+ }
+ | CREATE TABLESPACE tablespace_info
+ {
+ Lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE;
+ }
+ ;
+
+
+event_tail:
+ EVENT_SYM opt_if_not_exists sp_name
/*
BE CAREFUL when you add a new rule to update the block where
YYTHD->client_capabilities is set back to original value
*/
{
- LEX *lex=Lex;
-
- if (lex->et)
- {
- /*
- Recursive events are not possible because recursive SPs
- are not also possible. lex->sp_head is not stacked.
- */
- // ToDo Andrey : Change the error message
- my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT");
- YYABORT;
- }
-
- lex->create_info.options= $3;
+ Lex->create_info.options= $2;
- if (!(lex->et= new(YYTHD->mem_root) Event_timed())) // implicitly calls Event_timed::init()
+ if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD)))
YYABORT;
+ Lex->event_parse_data->identifier= $3;
/*
We have to turn of CLIENT_MULTI_QUERIES while parsing a
@@ -1316,11 +1328,9 @@ create:
$<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES);
- if (!lex->et_compile_phase)
- {
- lex->et->init_name(YYTHD, $4);
- lex->et->init_definer(YYTHD);
- }
+ Lex->sql_command= SQLCOM_CREATE_EVENT;
+ /* We need that for disallowing subqueries */
+ Lex->expr_allows_subselect= FALSE;
}
ON SCHEDULE_SYM ev_schedule_time
opt_ev_on_completion
@@ -1330,160 +1340,62 @@ create:
{
/*
Restore flag if it was cleared above
- $1 - CREATE
- $2 - EVENT_SYM
- $3 - opt_if_not_exists
- $4 - sp_name
- $5 - the block above
+ $1 - EVENT_SYM
+ $2 - opt_if_not_exists
+ $3 - sp_name
+ $4 - the block above
*/
- YYTHD->client_capabilities |= $<ulong_num>5;
+ YYTHD->client_capabilities |= $<ulong_num>4;
/*
sql_command is set here because some rules in ev_sql_stmt
can overwrite it
*/
Lex->sql_command= SQLCOM_CREATE_EVENT;
+ Lex->expr_allows_subselect= TRUE;
}
- | CREATE
- {
- Lex->create_view_mode= VIEW_CREATE_NEW;
- Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED;
- Lex->create_view_suid= TRUE;
- }
- view_or_trigger_or_sp
- {}
- | CREATE USER clear_privileges grant_list
- {
- Lex->sql_command = SQLCOM_CREATE_USER;
- }
- | CREATE LOGFILE_SYM GROUP logfile_group_info
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP;
- }
- | CREATE TABLESPACE tablespace_info
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE;
- }
- ;
ev_schedule_time: EVERY_SYM expr interval
{
- LEX *lex=Lex;
- if (!lex->et_compile_phase)
- {
- switch (lex->et->init_interval(YYTHD , $2, $3)) {
- case EVEX_PARSE_ERROR:
- yyerror(ER(ER_SYNTAX_ERROR));
- YYABORT;
- break;
- case EVEX_BAD_PARAMS:
- my_error(ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG, MYF(0));
- case EVEX_MICROSECOND_UNSUP:
- my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND");
- YYABORT;
- break;
- }
- }
+ Lex->event_parse_data->item_expression= $2;
+ Lex->event_parse_data->interval= $3;
}
ev_starts
ev_ends
| AT_SYM expr
{
- LEX *lex=Lex;
- if (!lex->et_compile_phase)
- {
- switch (lex->et->init_execute_at(YYTHD, $2)) {
- case EVEX_PARSE_ERROR:
- yyerror(ER(ER_SYNTAX_ERROR));
- YYABORT;
- break;
- case ER_WRONG_VALUE:
- {
- char buff[120];
- String str(buff,(uint32) sizeof(buff), system_charset_info);
- String *str2= $2->val_str(&str);
- my_error(ER_WRONG_VALUE, MYF(0), "AT",
- str2? str2->c_ptr_safe():"NULL");
- YYABORT;
- break;
- }
- case EVEX_BAD_PARAMS:
- my_error(ER_EVENT_EXEC_TIME_IN_THE_PAST, MYF(0));
- YYABORT;
- break;
- }
- }
+ Lex->event_parse_data->item_execute_at= $2;
}
;
opt_ev_status: /* empty */ { $$= 0; }
| ENABLE_SYM
{
- LEX *lex=Lex;
- if (!lex->et_compile_phase)
- lex->et->status= Event_timed::ENABLED;
+ Lex->event_parse_data->status= Event_parse_data::ENABLED;
$$= 1;
}
| DISABLE_SYM
{
- LEX *lex=Lex;
-
- if (!lex->et_compile_phase)
- lex->et->status= Event_timed::DISABLED;
+ Lex->event_parse_data->status= Event_parse_data::DISABLED;
$$= 1;
}
;
ev_starts: /* empty */
{
- Lex->et->init_starts(YYTHD, new Item_func_now_local());
+ Lex->event_parse_data->item_starts= new Item_func_now_local();
}
| STARTS_SYM expr
{
- LEX *lex= Lex;
- if (!lex->et_compile_phase)
- {
-
- switch (lex->et->init_starts(YYTHD, $2)) {
- case EVEX_PARSE_ERROR:
- yyerror(ER(ER_SYNTAX_ERROR));
- YYABORT;
- break;
- case EVEX_BAD_PARAMS:
- {
- char buff[20];
- String str(buff,(uint32) sizeof(buff), system_charset_info);
- String *str2= $2->val_str(&str);
- my_error(ER_WRONG_VALUE, MYF(0), "STARTS",
- str2 ? str2->c_ptr_safe() : NULL);
- YYABORT;
- break;
- }
- }
- }
+ Lex->event_parse_data->item_starts= $2;
}
;
ev_ends: /* empty */
| ENDS_SYM expr
{
- LEX *lex= Lex;
- if (!lex->et_compile_phase)
- {
- switch (lex->et->init_ends(YYTHD, $2)) {
- case EVEX_PARSE_ERROR:
- yyerror(ER(ER_SYNTAX_ERROR));
- YYABORT;
- break;
- case EVEX_BAD_PARAMS:
- my_error(ER_EVENT_ENDS_BEFORE_STARTS, MYF(0));
- YYABORT;
- break;
- }
- }
+ Lex->event_parse_data->item_ends= $2;
}
;
@@ -1494,16 +1406,14 @@ opt_ev_on_completion: /* empty */ { $$= 0; }
ev_on_completion:
ON COMPLETION_SYM PRESERVE_SYM
{
- LEX *lex=Lex;
- if (!lex->et_compile_phase)
- lex->et->on_completion= Event_timed::ON_COMPLETION_PRESERVE;
+ Lex->event_parse_data->on_completion=
+ Event_parse_data::ON_COMPLETION_PRESERVE;
$$= 1;
}
| ON COMPLETION_SYM NOT_SYM PRESERVE_SYM
{
- LEX *lex=Lex;
- if (!lex->et_compile_phase)
- lex->et->on_completion= Event_timed::ON_COMPLETION_DROP;
+ Lex->event_parse_data->on_completion=
+ Event_parse_data::ON_COMPLETION_DROP;
$$= 1;
}
;
@@ -1511,64 +1421,65 @@ ev_on_completion:
opt_ev_comment: /* empty */ { $$= 0; }
| COMMENT_SYM TEXT_STRING_sys
{
- LEX *lex= Lex;
- if (!lex->et_compile_phase)
- {
- lex->comment= $2;
- lex->et->init_comment(YYTHD, &$2);
- }
- $$= 1;
+ Lex->comment= Lex->event_parse_data->comment= $2;
}
;
ev_sql_stmt:
{
LEX *lex= Lex;
- sp_head *sp;
-
- $<sphead>$= lex->sphead;
- if (!lex->sphead)
+ /*
+ This stops the following :
+ - CREATE EVENT ... DO CREATE EVENT ...;
+ - ALTER EVENT ... DO CREATE EVENT ...;
+ - CREATE EVENT ... DO ALTER EVENT DO ....;
+ - CREATE PROCEDURE ... BEGIN CREATE EVENT ... END|
+ This allows:
+ - CREATE EVENT ... DO DROP EVENT yyy;
+ - CREATE EVENT ... DO ALTER EVENT yyy;
+ (the nested ALTER EVENT can have anything but DO clause)
+ - ALTER EVENT ... DO ALTER EVENT yyy;
+ (the nested ALTER EVENT can have anything but DO clause)
+ - ALTER EVENT ... DO DROP EVENT yyy;
+ - CREATE PROCEDURE ... BEGIN ALTER EVENT ... END|
+ (the nested ALTER EVENT can have anything but DO clause)
+ - CREATE PROCEDURE ... BEGIN DROP EVENT ... END|
+ */
+ if (lex->sphead)
{
- if (!(sp= new sp_head()))
- YYABORT;
-
- sp->reset_thd_mem_root(YYTHD);
- sp->init(lex);
+ my_error(ER_EVENT_RECURSIVITY_FORBIDDEN, MYF(0));
+ YYABORT;
+ }
+
+ if (!(lex->sphead= new sp_head()))
+ YYABORT;
- sp->m_type= TYPE_ENUM_PROCEDURE;
+ lex->sphead->reset_thd_mem_root(YYTHD);
+ lex->sphead->init(lex);
+ lex->sphead->init_sp_name(YYTHD, Lex->event_parse_data->identifier);
- lex->sphead= sp;
+ lex->sphead->m_type= TYPE_ENUM_PROCEDURE;
- bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
- lex->sphead->m_chistics= &lex->sp_chistics;
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ lex->sphead->m_chistics= &lex->sp_chistics;
- lex->sphead->m_body_begin= lex->ptr;
- }
+ lex->sphead->m_body_begin= lex->ptr;
+
+ Lex->event_parse_data->body_begin= lex->ptr;
- if (!lex->et_compile_phase)
- lex->et->body_begin= lex->ptr;
}
ev_sql_stmt_inner
{
LEX *lex=Lex;
- if (!$<sphead>1)
- {
- sp_head *sp= lex->sphead;
- // return back to the original memory root ASAP
- sp->init_strings(YYTHD, lex);
- sp->restore_thd_mem_root(YYTHD);
+ /* return back to the original memory root ASAP */
+ lex->sphead->init_strings(YYTHD, lex);
+ lex->sphead->restore_thd_mem_root(YYTHD);
- lex->sp_chistics.suid= SP_IS_SUID;//always the definer!
+ lex->sp_chistics.suid= SP_IS_SUID; //always the definer!
- lex->et->sphead= lex->sphead;
- lex->sphead= NULL;
- }
- if (!lex->et_compile_phase)
- {
- lex->et->init_body(YYTHD);
- }
+ Lex->event_parse_data->init_body(YYTHD);
}
;
@@ -1643,10 +1554,10 @@ create_function_tail:
if (lex->definer != NULL)
{
/*
- DEFINER is a concept meaningful when interpreting SQL code.
- UDF functions are compiled.
- Using DEFINER with UDF has therefore no semantic,
- and is considered a parsing error.
+ DEFINER is a concept meaningful when interpreting SQL code.
+ UDF functions are compiled.
+ Using DEFINER with UDF has therefore no semantic,
+ and is considered a parsing error.
*/
my_error(ER_WRONG_USAGE, MYF(0), "SONAME", "DEFINER");
YYABORT;
@@ -1685,10 +1596,10 @@ create_function_tail:
sp->m_type= TYPE_ENUM_FUNCTION;
lex->sphead= sp;
/*
- * We have to turn of CLIENT_MULTI_QUERIES while parsing a
- * stored procedure, otherwise yylex will chop it into pieces
- * at each ';'.
- */
+ We have to turn off CLIENT_MULTI_QUERIES while parsing a
+ stored procedure, otherwise yylex will chop it into pieces
+ at each ';'.
+ */
$<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
lex->sphead->m_param_begin= lex->tok_start+1;
@@ -4790,41 +4701,33 @@ alter:
{}
| ALTER EVENT_SYM sp_name
/*
- BE CAREFUL when you add a new rule to update the block where
- YYTHD->client_capabilities is set back to original value
+ BE CAREFUL when you add a new rule to update the block where
+ YYTHD->client_capabilities is set back to original value
*/
{
- LEX *lex=Lex;
- Event_timed *et;
-
- if (lex->et)
- {
- /*
- Recursive events are not possible because recursive SPs
- are not also possible. lex->sp_head is not stacked.
- */
- my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT");
- YYABORT;
- }
- lex->spname= 0;//defensive programming
+ /*
+ It is safe to use Lex->spname because
+ ALTER EVENT xxx RENATE TO yyy DO ALTER EVENT RENAME TO
+ is not allowed. Lex->spname is used in the case of RENAME TO
+ If it had to be supported spname had to be added to
+ Event_parse_data.
+ */
- if (!(et= new (YYTHD->mem_root) Event_timed()))// implicitly calls Event_timed::init()
+ if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD)))
YYABORT;
- lex->et = et;
-
- if (!lex->et_compile_phase)
- {
- et->init_definer(YYTHD);
- et->init_name(YYTHD, $3);
- }
+ Lex->event_parse_data->identifier= $3;
/*
- We have to turn of CLIENT_MULTI_QUERIES while parsing a
- stored procedure, otherwise yylex will chop it into pieces
- at each ';'.
+ We have to turn off CLIENT_MULTI_QUERIES while parsing a
+ stored procedure, otherwise yylex will chop it into pieces
+ at each ';'.
*/
$<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
+
+ Lex->sql_command= SQLCOM_ALTER_EVENT;
+ /* we need that for disallowing subqueries */
+ Lex->expr_allows_subselect= FALSE;
}
ev_alter_on_schedule_completion
opt_ev_rename_to
@@ -4840,16 +4743,17 @@ alter:
*/
YYTHD->client_capabilities |= $<ulong_num>4;
- /*
- sql_command is set here because some rules in ev_sql_stmt
- can overwrite it
- */
if (!($5 || $6 || $7 || $8 || $9))
{
yyerror(ER(ER_SYNTAX_ERROR));
YYABORT;
}
+ /*
+ sql_command is set here because some rules in ev_sql_stmt
+ can overwrite it
+ */
Lex->sql_command= SQLCOM_ALTER_EVENT;
+ Lex->expr_allows_subselect= TRUE;
}
| ALTER TABLESPACE alter_tablespace_info
{
@@ -4882,9 +4786,11 @@ ev_alter_on_schedule_completion: /* empty */ { $$= 0;}
opt_ev_rename_to: /* empty */ { $$= 0;}
| RENAME TO_SYM sp_name
{
- LEX *lex=Lex;
- lex->spname= $3; //use lex's spname to hold the new name
- //the original name is in the Event_timed object
+ /*
+ Use lex's spname to hold the new name.
+ The original name is in the Event_parse_data object
+ */
+ Lex->spname= $3;
$$= 1;
}
;
@@ -4908,7 +4814,7 @@ alter_commands:
| remove_partitioning
| partitioning
/*
- This part was added for release 5.1 by Mikael Ronström.
+ This part was added for release 5.1 by Mikael Ronström.
From here we insert a number of commands to manage the partitions of a
partitioned table such as adding partitions, dropping partitions,
reorganising partitions in various manners. In future releases the list
@@ -7176,8 +7082,7 @@ select_derived2:
{
LEX *lex= Lex;
lex->derived_tables|= DERIVED_SUBQUERY;
- if (lex->sql_command == (int)SQLCOM_HA_READ ||
- lex->sql_command == (int)SQLCOM_KILL)
+ if (!lex->expr_allows_subselect)
{
yyerror(ER(ER_SYNTAX_ERROR));
YYABORT;
@@ -7796,29 +7701,9 @@ drop:
}
| DROP EVENT_SYM if_exists sp_name
{
- LEX *lex=Lex;
-
- if (lex->et)
- {
- /*
- Recursive events are not possible because recursive SPs
- are not also possible. lex->sp_head is not stacked.
- */
- my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT");
- YYABORT;
- }
-
- if (!(lex->et= new (YYTHD->mem_root) Event_timed()))
- YYABORT;
-
- if (!lex->et_compile_phase)
- {
- lex->et->init_name(YYTHD, $4);
- lex->et->init_definer(YYTHD);
- }
-
- lex->sql_command = SQLCOM_DROP_EVENT;
- lex->drop_if_exists= $3;
+ Lex->drop_if_exists= $3;
+ Lex->spname= $4;
+ Lex->sql_command = SQLCOM_DROP_EVENT;
}
| DROP TRIGGER_SYM sp_name
{
@@ -8226,15 +8111,6 @@ show_param:
if (prepare_schema_table(YYTHD, lex, 0, SCH_EVENTS))
YYABORT;
}
- | SCHEDULER_SYM STATUS_SYM
- {
-#ifndef DBUG_OFF
- Lex->sql_command= SQLCOM_SHOW_SCHEDULER_STATUS;
-#else
- yyerror(ER(ER_SYNTAX_ERROR));
- YYABORT;
-#endif
- }
| TABLE_SYM STATUS_SYM opt_db wild_and_where
{
LEX *lex= Lex;
@@ -8511,12 +8387,8 @@ show_param:
}
| CREATE EVENT_SYM sp_name
{
- Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT;
Lex->spname= $3;
- Lex->et= new (YYTHD->mem_root) Event_timed();
- if (!Lex->et)
- YYABORT;
- Lex->et->init_definer(YYTHD);
+ Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT;
}
;
@@ -8696,11 +8568,17 @@ purge_option:
/* kill threads */
kill:
- KILL_SYM { Lex->sql_command= SQLCOM_KILL; } kill_option expr
+ KILL_SYM
+ {
+ Lex->sql_command= SQLCOM_KILL;
+ Lex->expr_allows_subselect= FALSE;
+ }
+ kill_option expr
{
LEX *lex=Lex;
lex->value_list.empty();
lex->value_list.push_front($4);
+ Lex->expr_allows_subselect= TRUE;
};
kill_option:
@@ -9615,7 +9493,6 @@ keyword_sp:
| ROW_SYM {}
| RTREE_SYM {}
| SCHEDULE_SYM {}
- | SCHEDULER_SYM {}
| SECOND_SYM {}
| SERIAL_SYM {}
| SERIALIZABLE_SYM {}
@@ -10187,6 +10064,7 @@ handler:
my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER");
YYABORT;
}
+ lex->expr_allows_subselect= FALSE;
lex->sql_command = SQLCOM_HA_READ;
lex->ha_rkey_mode= HA_READ_KEY_EXACT; /* Avoid purify warnings */
lex->current_select->select_limit= new Item_int((int32) 1);
@@ -10194,7 +10072,10 @@ handler:
if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0))
YYABORT;
}
- handler_read_or_scan where_clause opt_limit_clause {}
+ handler_read_or_scan where_clause opt_limit_clause
+ {
+ Lex->expr_allows_subselect= TRUE;
+ }
;
handler_read_or_scan:
@@ -10819,8 +10700,7 @@ subselect_start:
'(' SELECT_SYM
{
LEX *lex=Lex;
- if (lex->sql_command == (int)SQLCOM_HA_READ ||
- lex->sql_command == (int)SQLCOM_KILL)
+ if (!lex->expr_allows_subselect)
{
yyerror(ER(ER_SYNTAX_ERROR));
YYABORT;
@@ -10844,20 +10724,22 @@ subselect_end:
**************************************************************************/
-view_or_trigger_or_sp:
- definer view_or_trigger_or_sp_tail
+view_or_trigger_or_sp_or_event:
+ definer view_or_trigger_or_sp_or_event_tail
{}
| view_replace_or_algorithm definer view_tail
{}
;
-view_or_trigger_or_sp_tail:
+view_or_trigger_or_sp_or_event_tail:
view_tail
{}
| trigger_tail
{}
| sp_tail
{}
+ | event_tail
+ {}
;
/**************************************************************************
diff --git a/sql/table.cc b/sql/table.cc
index fe83398115d..d20818e3644 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -2365,28 +2365,28 @@ bool check_column_name(const char *name)
Checks whether a table is intact. Should be done *just* after the table has
been opened.
- Synopsis
+ 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
+ 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
- 0 - OK
- 1 - There was an error
+ FALSE OK
+ TRUE There was an error
*/
my_bool
-table_check_intact(TABLE *table, uint table_f_count,
- TABLE_FIELD_W_TYPE *table_def, time_t *last_create_time,
- int error_num)
+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)
{
uint i;
my_bool error= FALSE;
@@ -2401,7 +2401,7 @@ table_check_intact(TABLE *table, uint table_f_count,
DBUG_PRINT("info", ("I am suspecting, checking table"));
if (fields_diff_count)
{
- // previous MySQL version
+ /* previous MySQL version */
error= TRUE;
if (MYSQL_VERSION_ID > table->s->mysql_version)
{
@@ -2424,22 +2424,22 @@ table_check_intact(TABLE *table, uint table_f_count,
else
{
/*
- moving from newer mysql to older one -> let's say not an error but
+ 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.
*/
error= FALSE;
}
}
- //definitely something has changed
+ /* 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);
/*
- name changes are not fatal, we use sequence numbers => no prob for us
- but this can show tampered table or broken table.
+ 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)
{
@@ -2453,7 +2453,7 @@ table_check_intact(TABLE *table, uint table_f_count,
}
/*
- IF the type does not match than something is really wrong
+ 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
diff --git a/sql/table.h b/sql/table.h
index 3fb7222cb0d..b90ee280f91 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1029,9 +1029,9 @@ typedef struct st_table_field_w_type
my_bool
-table_check_intact(TABLE *table, uint table_f_count,
- TABLE_FIELD_W_TYPE *table_def, time_t *last_create_time,
- int error_num);
+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);
static inline my_bitmap_map *tmp_use_all_columns(TABLE *table,
MY_BITMAP *bitmap)