summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <andrey@lmy004.>2006-06-28 15:54:09 +0200
committerunknown <andrey@lmy004.>2006-06-28 15:54:09 +0200
commit98211beaee1cffd0ecf28a2157fbb0de3692cc3e (patch)
treeeca43a35436b43e490c1554f13bbcd02616e7fa2 /sql
parent57f3f2568ee27b30f0062b0175faf9f0a1a854e1 (diff)
parent400276c2f577f63ffd3871a91bce207f65d5e682 (diff)
downloadmariadb-git-98211beaee1cffd0ecf28a2157fbb0de3692cc3e.tar.gz
Merge
sql/CMakeLists.txt: Auto merged sql/events.cc: 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.h: Auto merged sql/sql_parse.cc: Auto merged sql/sql_yacc.yy: Auto merged sql/sql_show.cc: SCCS 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)219
-rw-r--r--sql/event_data_objects.h (renamed from sql/event_timed.h)122
-rw-r--r--sql/event_db_repository.cc1038
-rw-r--r--sql/event_db_repository.h122
-rw-r--r--sql/event_queue.cc19
-rw-r--r--sql/event_queue.h20
-rw-r--r--sql/event_scheduler.cc142
-rw-r--r--sql/event_scheduler.h43
-rw-r--r--sql/events.cc924
-rw-r--r--sql/events.h110
-rw-r--r--sql/events_priv.h79
-rw-r--r--sql/mysqld.cc8
-rw-r--r--sql/set_var.cc2
-rw-r--r--sql/share/errmsg.txt3
-rw-r--r--sql/sql_class.cc57
-rw-r--r--sql/sql_class.h8
-rw-r--r--sql/sql_db.cc2
-rw-r--r--sql/sql_lex.h2
-rw-r--r--sql/sql_parse.cc39
-rw-r--r--sql/sql_show.cc183
-rw-r--r--sql/sql_show.h2
-rw-r--r--sql/sql_yacc.yy217
24 files changed, 1980 insertions, 1392 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 9a97b79813b..58f3fb4409e 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -51,7 +51,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
${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
diff --git a/sql/Makefile.am b/sql/Makefile.am
index 387f18c2ae9..49f85b3921d 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -64,8 +64,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 \
+ sql_plugin.h authors.h sql_partition.h event_data_objects.h \
+ event_queue.h event_db_repository.h \
partition_info.h partition_element.h event_scheduler.h \
contributors.h
mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
@@ -104,7 +105,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 events.cc event_data_objects.cc \
+ event_queue.cc event_db_repository.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 4ec875f32a3..228f1a4ff4a 100644
--- a/sql/event_timed.cc
+++ b/sql/event_data_objects.cc
@@ -16,12 +16,104 @@
#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"
+#define EVEX_MAX_INTERVAL_VALUE 2147483647L
+
+
+Event_parse_data *
+Event_parse_data::new_instance(THD *thd)
+{
+ return new (thd->mem_root) Event_parse_data;
+}
+
+
+Event_parse_data::Event_parse_data()
+{
+ item_execute_at= item_expression= item_starts= item_ends= NULL;
+}
+
+
+/*
+ Set body of the event - what should be executed.
+
+ SYNOPSIS
+ Event_timed::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_parse_data::init_body(THD *thd)
+{
+ DBUG_ENTER("Event_parse_data::init_body");
+ DBUG_PRINT("info", ("body=[%s] body_begin=0x%ld end=0x%ld", body_begin,
+ body_begin, thd->lex->ptr));
+
+ body.length= thd->lex->ptr - body_begin;
+ const uchar *body_end= body_begin + body.length - 1;
+
+ /* Trim nuls or close-comments ('*'+'/') or spaces at the end */
+ while (body_begin < body_end)
+ {
+
+ if ((*body_end == '\0') ||
+ (my_isspace(thd->variables.character_set_client, *body_end)))
+ { /* consume NULs and meaningless whitespace */
+ --body.length;
+ --body_end;
+ continue;
+ }
+
+ /*
+ consume closing comments
+
+ This is arguably wrong, but it's the best we have until the parser is
+ changed to be smarter. FIXME PARSER
+
+ See also the sp_head code, where something like this is done also.
+
+ One idea is to keep in the lexer structure the count of the number of
+ open-comments we've entered, and scan left-to-right looking for a
+ closing comment IFF the count is greater than zero.
+
+ Another idea is to remove the closing comment-characters wholly in the
+ parser, since that's where it "removes" the opening characters.
+ */
+ if ((*(body_end - 1) == '*') && (*body_end == '/'))
+ {
+ DBUG_PRINT("info", ("consumend one '*" "/' comment in the query '%s'",
+ body_begin));
+ body.length-= 2;
+ body_end-= 2;
+ continue;
+ }
+
+ break; /* none were found, so we have excised all we can. */
+ }
+
+ /* the first is always whitespace which I cannot skip in the parser */
+ while (my_isspace(thd->variables.character_set_client, *body_begin))
+ {
+ ++body_begin;
+ --body.length;
+ }
+ body.str= thd->strmake((char *)body_begin, body.length);
+
+ DBUG_VOID_RETURN;
+}
+
+
/*
Constructor
@@ -644,29 +736,29 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
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)
+ table->field[ET_FIELD_DB])) == NULL)
goto error;
et->dbname.length= strlen(et->dbname.str);
if ((et->name.str= get_field(mem_root,
- table->field[Events::FIELD_NAME])) == NULL)
+ table->field[ET_FIELD_NAME])) == NULL)
goto error;
et->name.length= strlen(et->name.str);
if ((et->body.str= get_field(mem_root,
- table->field[Events::FIELD_BODY])) == NULL)
+ table->field[ET_FIELD_BODY])) == NULL)
goto error;
et->body.length= strlen(et->body.str);
if ((et->definer.str= get_field(mem_root,
- table->field[Events::FIELD_DEFINER])) == NullS)
+ table->field[ET_FIELD_DEFINER])) == NullS)
goto error;
et->definer.length= strlen(et->definer.str);
@@ -683,27 +775,27 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
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]->
+ et->starts_null= table->field[ET_FIELD_STARTS]->is_null();
+ res1= table->field[ET_FIELD_STARTS]->
get_date(&et->starts,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);
+ et->ends_null= table->field[ET_FIELD_ENDS]->is_null();
+ res2= table->field[ET_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())
+ et->expression= table->field[ET_FIELD_INTERVAL_EXPR]->val_int();
else
et->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.
+ Hence if ET_FIELD_EXECUTE_AT is empty there is an error.
*/
et->execute_at_null=
- table->field[Events::FIELD_EXECUTE_AT]->is_null();
+ table->field[ET_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,
+ table->field[ET_FIELD_EXECUTE_AT]-> get_date(&et->execute_at,
TIME_NO_ZERO_DATE))
goto error;
@@ -711,22 +803,22 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
In DB the values start from 1 but enum interval_type starts
from 0
*/
- if (!table->field[Events::FIELD_TRANSIENT_INTERVAL]->is_null())
+ if (!table->field[ET_FIELD_TRANSIENT_INTERVAL]->is_null())
et->interval= (interval_type) ((ulonglong)
- table->field[Events::FIELD_TRANSIENT_INTERVAL]->val_int() - 1);
+ table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_int() - 1);
else
et->interval= (interval_type) 0;
- et->created= table->field[Events::FIELD_CREATED]->val_int();
- et->modified= table->field[Events::FIELD_MODIFIED]->val_int();
+ et->created= table->field[ET_FIELD_CREATED]->val_int();
+ et->modified= table->field[ET_FIELD_MODIFIED]->val_int();
- table->field[Events::FIELD_LAST_EXECUTED]->
+ table->field[ET_FIELD_LAST_EXECUTED]->
get_date(&et->last_executed, TIME_NO_ZERO_DATE);
last_executed_changed= false;
/* 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_STATUS])) == NullS)
goto error;
DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->name.str, ptr));
@@ -734,20 +826,20 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
/* 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)
+ table->field[ET_FIELD_ON_COMPLETION])) == NullS)
goto error;
et->on_completion= (ptr[0]=='D'? Event_timed::ON_COMPLETION_DROP:
Event_timed::ON_COMPLETION_PRESERVE);
- et->comment.str= get_field(mem_root, table->field[Events::FIELD_COMMENT]);
+ et->comment.str= get_field(mem_root, table->field[ET_FIELD_COMMENT]);
if (et->comment.str != NullS)
et->comment.length= strlen(et->comment.str);
else
et->comment.length= 0;
- et->sql_mode= (ulong) table->field[Events::FIELD_SQL_MODE]->val_int();
+ et->sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
DBUG_RETURN(0);
error:
@@ -1188,6 +1280,7 @@ Event_timed::mark_last_executed(THD *thd)
}
+
/*
Drops the event
@@ -1210,7 +1303,8 @@ Event_timed::drop(THD *thd)
uint tmp= 0;
DBUG_ENTER("Event_timed::drop");
- DBUG_RETURN(db_drop_event(thd, this, false, &tmp));
+ DBUG_RETURN(Events::get_instance()->
+ db_repository->drop_event(thd, dbname, name, false, &tmp));
}
@@ -1247,7 +1341,7 @@ 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;
goto done;
@@ -1263,15 +1357,15 @@ 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,
+ table->field[ET_FIELD_LAST_EXECUTED]->set_notnull();
+ table->field[ET_FIELD_LAST_EXECUTED]->store_time(&last_executed,
MYSQL_TIMESTAMP_DATETIME);
last_executed_changed= false;
}
if (status_changed)
{
- table->field[Events::FIELD_STATUS]->set_notnull();
- table->field[Events::FIELD_STATUS]->store((longlong)status, true);
+ table->field[ET_FIELD_STATUS]->set_notnull();
+ table->field[ET_FIELD_STATUS]->store((longlong)status, true);
status_changed= false;
}
@@ -1541,8 +1635,8 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
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);
+ thd->change_security_context(definer_user, definer_host, dbname,
+ &security_ctx, &save_ctx);
thd->lex= &lex;
lex_start(thd, (uchar*)thd->query, thd->query_length);
lex.et_compile_phase= TRUE;
@@ -1580,7 +1674,7 @@ done:
lex.et->deinit_mutexes();
lex_end(&lex);
- restore_security_context(thd, save_ctx);
+ thd->restore_security_context(save_ctx);
DBUG_PRINT("note", ("return old data on its place. set back NAMES"));
thd->lex= old_lex;
@@ -1781,21 +1875,59 @@ event_timed_db_equal(Event_timed *et, LEX_STRING *db)
}
+
+/*
+ Checks whether two events are equal by identifiers
+
+ SYNOPSIS
+ event_timed_identifier_equal()
+
+ RETURN VALUE
+ TRUE equal
+ FALSE not equal
+*/
+
+bool
+event_timed_identifier_equal(Event_timed *a, Event_timed *b)
+{
+ return event_timed_name_equal(a, &b->name) &&
+ event_timed_db_equal(a, &b->dbname);
+}
+
+
/*
- Checks whether two events have the same definer
+ Checks whether two events have the same name
SYNOPSIS
- event_timed_definer_equal()
+ event_timed_name_equal()
- Returns
- TRUE definers are equal
- FALSE definers are not equal
+ RETURN VALUE
+ TRUE names are equal
+ FALSE names are not equal
*/
bool
-event_timed_definer_equal(Event_timed *et, LEX_STRING *definer)
+event_timed_name_equal(sp_name *name, LEX_STRING *event_name)
{
- return !sortcmp_lex_string(et->definer, *definer, system_charset_info);
+ return !sortcmp_lex_string(name->m_name, *event_name, system_charset_info);
+}
+
+
+/*
+ Checks whether two events are in the same schema
+
+ SYNOPSIS
+ event_timed_db_equal()
+
+ RETURN VALUE
+ TRUE schemas are equal
+ FALSE schemas are not equal
+*/
+
+bool
+event_timed_db_equal(sp_name *name, LEX_STRING *db)
+{
+ return !sortcmp_lex_string(name->m_db, *db, system_charset_info);
}
@@ -1811,11 +1943,10 @@ event_timed_definer_equal(Event_timed *et, LEX_STRING *definer)
*/
bool
-event_timed_identifier_equal(Event_timed *a, Event_timed *b)
+event_timed_identifier_equal(sp_name *a, Event_timed *b)
{
return event_timed_name_equal(a, &b->name) &&
- event_timed_db_equal(a, &b->dbname) &&
- event_timed_definer_equal(a, &b->definer);
+ event_timed_db_equal(a, &b->dbname);
}
diff --git a/sql/event_timed.h b/sql/event_data_objects.h
index 0652cece361..ff547cebd5b 100644
--- a/sql/event_timed.h
+++ b/sql/event_data_objects.h
@@ -1,5 +1,5 @@
-#ifndef _EVENT_TIMED_H_
-#define _EVENT_TIMED_H_
+#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
@@ -40,7 +40,34 @@
#define EVENT_FREE_WHEN_FINISHED (1L << 2)
+#define EVENT_EXEC_STARTED 0
+#define EVENT_EXEC_ALREADY_EXEC 1
+#define EVENT_EXEC_CANT_FORK 2
+
+
class sp_head;
+class Sql_alloc;
+
+class Event_timed;
+
+/* Compares only the schema part of the identifier */
+bool
+event_timed_db_equal(Event_timed *et, LEX_STRING *db);
+
+
+/* Compares the whole identifier*/
+bool
+event_timed_identifier_equal(Event_timed *a, Event_timed *b);
+
+/* Compares only the schema part of the identifier */
+bool
+event_timed_db_equal(sp_name *name, LEX_STRING *db);
+
+
+/* Compares the whole identifier*/
+bool
+event_timed_identifier_equal(sp_name *a, Event_timed *b);
+
class Event_timed
{
@@ -213,5 +240,92 @@ public:
void
set_thread_id(ulong tid) { thread_id= tid; }
};
-
-#endif /* _EVENT_H_ */
+
+
+class Event_parse_data : public Sql_alloc
+{
+ Event_parse_data(const Event_parse_data &); /* Prevent use of these */
+ void operator=(Event_parse_data &);
+
+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 body;
+
+ LEX_STRING definer_user;
+ LEX_STRING definer_host;
+ LEX_STRING definer;// combination of user and host
+
+ 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);
+
+ Event_parse_data();
+ ~Event_parse_data();
+
+ 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);
+
+};
+
+
+class Event_queue_element : public Event_timed
+{
+
+};
+
+#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..fc771b6bac3
--- /dev/null
+++ b/sql/event_db_repository.cc
@@ -0,0 +1,1038 @@
+/* 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 "sp_head.h"
+#include "sp.h"
+#include "events.h"
+#include "sql_show.h"
+
+#define EVEX_DB_FIELD_LEN 64
+#define EVEX_NAME_FIELD_LEN 64
+
+
+time_t mysql_event_last_create_time= 0L;
+
+static
+TABLE_FIELD_W_TYPE event_table_fields[ET_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")}
+ }
+};
+
+
+/*
+ Puts some data common to CREATE and ALTER EVENT into a row.
+
+ SYNOPSIS
+ evex_fill_row()
+ thd THD
+ table the row to fill out
+ et Event's data
+
+ 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
+evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update)
+{
+ CHARSET_INFO *scs= system_charset_info;
+ enum enum_events_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= ET_FIELD_DEFINER]->
+ store(et->definer.str, et->definer.length, scs))
+ goto err_truncate;
+
+ if (table->field[field_num= ET_FIELD_DB]->
+ store(et->dbname.str, et->dbname.length, scs))
+ goto err_truncate;
+
+ if (table->field[field_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()*/
+ table->field[ET_FIELD_ON_COMPLETION]->
+ store((longlong)et->on_completion, true);
+
+ table->field[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)
+ {
+ table->field[ET_FIELD_SQL_MODE]->
+ store((longlong)thd->variables.sql_mode, true);
+
+ if (table->field[field_num= ET_FIELD_BODY]->
+ store(et->body.str, et->body.length, scs))
+ goto err_truncate;
+ }
+
+ if (et->expression)
+ {
+ table->field[ET_FIELD_INTERVAL_EXPR]->set_notnull();
+ table->field[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, true);
+
+ table->field[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!
+ */
+ table->field[ET_FIELD_TRANSIENT_INTERVAL]->
+ store((longlong)et->interval+1, true);
+
+ table->field[ET_FIELD_EXECUTE_AT]->set_null();
+
+ if (!et->starts_null)
+ {
+ table->field[ET_FIELD_STARTS]->set_notnull();
+ table->field[ET_FIELD_STARTS]->
+ store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME);
+ }
+
+ if (!et->ends_null)
+ {
+ table->field[ET_FIELD_ENDS]->set_notnull();
+ table->field[ET_FIELD_ENDS]->
+ store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME);
+ }
+ }
+ else if (et->execute_at.year)
+ {
+ table->field[ET_FIELD_INTERVAL_EXPR]->set_null();
+ table->field[ET_FIELD_TRANSIENT_INTERVAL]->set_null();
+ table->field[ET_FIELD_STARTS]->set_null();
+ table->field[ET_FIELD_ENDS]->set_null();
+
+ table->field[ET_FIELD_EXECUTE_AT]->set_notnull();
+ table->field[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 *)table->field[ET_FIELD_MODIFIED])->set_time();
+
+ if (et->comment.str)
+ {
+ if (table->field[field_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), table->field[field_num]->field_name);
+ DBUG_RETURN(EVEX_GENERAL_ERROR);
+}
+
+
+/*
+ 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
+*/
+
+int
+evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname,
+ const LEX_STRING ev_name,
+ TABLE *table)
+{
+ return Events::get_instance()->db_repository->
+ find_event_by_name(thd, dbname, ev_name, table);
+}
+
+
+/*
+ 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)
+
+ Returns
+ 0 OK
+ 1 Error
+*/
+
+int
+Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
+ TABLE *event_table, 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(0);
+ DBUG_RETURN(1);
+}
+
+
+/*
+ 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.
+
+ Returns
+ 0 OK
+ 1 Error
+*/
+
+int
+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? 0:1);
+}
+
+
+/*
+ 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
+
+ Returns
+ 0 OK
+ 1 Error
+*/
+
+int
+Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables, 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);
+}
+
+
+/*
+ Looks for a named event in mysql.event and in case of success returns
+ an object will data loaded from the table.
+
+ SYNOPSIS
+ Event_db_repository::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
+ root On which root to load the event
+
+ NOTES
+ 1) Use sp_name for look up, return in **ett if found
+ 2) tbl is not closed at exit
+
+ RETURN VALUE
+ 0 ok In this case *ett is set to the event
+ # error *ett == 0
+*/
+
+int
+Event_db_repository::find_event(THD *thd, sp_name *name, Event_timed **ett,
+ TABLE *tbl, MEM_ROOT *root)
+{
+ TABLE *table;
+ int ret;
+ Event_timed *et= NULL;
+ DBUG_ENTER("db_find_event");
+ DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
+
+ if (tbl)
+ table= tbl;
+ else if (open_event_table(thd, TL_READ, &table))
+ {
+ my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
+ ret= EVEX_GENERAL_ERROR;
+ goto done;
+ }
+
+ 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
+
+ 2)::load_from_row() is silent on error therefore we emit error msg here
+ */
+ if ((ret= et->load_from_row(root, table)))
+ {
+ my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0));
+ goto done;
+ }
+
+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);
+}
+
+
+int
+Event_db_repository::init_repository()
+{
+ init_alloc_root(&repo_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
+ return 0;
+}
+
+
+void
+Event_db_repository::deinit_repository()
+{
+ free_root(&repo_root, MYF(0));
+}
+
+
+/*
+ Open mysql.event table for read
+
+ SYNOPSIS
+ Events::open_event_table()
+ thd Thread context
+ lock_type How to lock the table
+ table 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);
+}
+
+
+/*
+ Creates an event in mysql.event
+
+ SYNOPSIS
+ Event_db_repository::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
+
+ 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".
+*/
+
+int
+Event_db_repository::create_event(THD *thd, Event_timed *et,
+ my_bool create_if_not, uint *rows_affected)
+{
+ int ret= 0;
+ CHARSET_INFO *scs= system_charset_info;
+ TABLE *table;
+ char olddb[128];
+ bool dbchanged= false;
+ DBUG_ENTER("Event_db_repository::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 (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_by_name(thd, et->dbname, et->name, 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.str,olddb, sizeof(olddb),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[ET_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[ET_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]))
+ {
+ my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
+ goto err;
+ }
+
+#ifdef USE_THIS_CODE_AS_TEMPLATE_WHEN_EVENT_REPLICATION_IS_AGREED
+ if (mysql_bin_log.is_open())
+ {
+ 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);
+ }
+#endif
+
+ *rows_affected= 1;
+ok:
+ if (dbchanged)
+ (void) mysql_change_db(thd, olddb, 1);
+ if (table)
+ close_thread_tables(thd);
+ DBUG_RETURN(EVEX_OK);
+
+err:
+ if (dbchanged)
+ (void) mysql_change_db(thd, olddb, 1);
+ if (table)
+ close_thread_tables(thd);
+ DBUG_RETURN(EVEX_GENERAL_ERROR);
+}
+
+
+/*
+ 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
+ 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.
+*/
+
+int
+Event_db_repository::update_event(THD *thd, Event_timed *et, sp_name *new_name)
+{
+ CHARSET_INFO *scs= system_charset_info;
+ TABLE *table;
+ int ret= EVEX_OPEN_TABLE_FAILED;
+ DBUG_ENTER("Event_db_repository::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 (open_event_table(thd, TL_WRITE, &table))
+ {
+ my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
+ goto err;
+ }
+
+ /* 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 == find_event_by_name(thd, et->dbname, et->name, 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[ET_FIELD_DB]->
+ store(new_name->m_db.str, new_name->m_db.length, scs);
+ table->field[ET_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])))
+ {
+ my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
+ goto err;
+ }
+
+ /* close mysql.event or we crash later when loading the event from disk */
+ close_thread_tables(thd);
+ DBUG_RETURN(0);
+
+err:
+ if (table)
+ close_thread_tables(thd);
+ DBUG_RETURN(EVEX_GENERAL_ERROR);
+}
+
+
+/*
+ Drops an event
+
+ SYNOPSIS
+ Event_db_repository::drop_event()
+ thd THD
+ db database name
+ name 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
+
+ RETURN VALUE
+ 0 OK
+ !0 Error (my_error() called)
+*/
+
+int
+Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
+ bool drop_if_exists, uint *rows_affected)
+{
+ TABLE *table;
+ Open_tables_state backup;
+ int ret;
+
+ DBUG_ENTER("Event_db_repository::drop_event");
+ DBUG_PRINT("enter", ("db=%s name=%s", db.str, name.str));
+ ret= EVEX_OPEN_TABLE_FAILED;
+
+ thd->reset_n_backup_open_tables_state(&backup);
+ if (open_event_table(thd, TL_WRITE, &table))
+ {
+ my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
+ goto done;
+ }
+
+ if (!(ret= evex_db_find_event_by_name(thd, db, name, table)))
+ {
+ if ((ret= table->file->ha_delete_row(table->record[0])))
+ my_error(ER_EVENT_CANNOT_DELETE, MYF(0));
+ }
+ 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", name.str);
+ ret= 0;
+ } else
+ my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
+ }
+
+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);
+}
+
+
+int
+Event_db_repository::find_event_by_name(THD *thd, LEX_STRING db,
+ LEX_STRING name, TABLE *table)
+{
+ byte key[MAX_KEY_LENGTH];
+ DBUG_ENTER("Event_db_repository::find_event_by_name");
+ 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(EVEX_KEY_NOT_FOUND);
+
+ 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(EVEX_KEY_NOT_FOUND);
+ }
+
+ DBUG_PRINT("info", ("Row found!"));
+ DBUG_RETURN(0);
+}
+
+
+int
+Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema)
+{
+ return drop_events_by_field(thd, ET_FIELD_DB, schema);
+}
+
+
+int
+Event_db_repository::drop_user_events(THD *thd, LEX_STRING definer)
+{
+ return drop_events_by_field(thd, ET_FIELD_DEFINER, definer);
+}
+
+
+/*
+ Drops all events in the selected database, from mysql.event.
+
+ SYNOPSIS
+ drop_events_from_table_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
+
+ RETURN VALUE
+ 0 OK
+ !0 Error from ha_delete_row
+*/
+
+int
+Event_db_repository::drop_events_by_field(THD *thd,
+ enum enum_events_table_field field,
+ LEX_STRING field_value)
+{
+ int ret= 0;
+ TABLE *table;
+ Open_tables_state backup;
+ READ_RECORD read_record_info;
+ DBUG_ENTER("drop_events_from_table_by_field");
+ DBUG_PRINT("enter", ("field=%d field_value=%s", field, field_value.str));
+
+ if (open_event_table(thd, TL_WRITE, &table))
+ {
+ my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
+ DBUG_RETURN(1);
+ }
+
+ /* 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"));
+ 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[ET_FIELD_NAME]));
+ }
+ }
+ end_read_record(&read_record_info);
+ thd->version--; /* Force close to free memory */
+
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ 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
+*/
+
+int
+Event_db_repository::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= find_event(thd, &spn, &et_loaded, NULL, &repo_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);
+
+ if (et_loaded->status != Event_timed::ENABLED)
+ {
+ /*
+ We don't load non-enabled events.
+ In db_find_event() `et_new` was allocated on the heap and not on
+ scheduler_root therefore we delete it here.
+ */
+ delete et_loaded;
+ DBUG_RETURN(OP_DISABLED_EVENT);
+ }
+
+ et_loaded->compute_next_execution_time();
+ *etn_new= et_loaded;
+
+ DBUG_RETURN(OP_OK);
+}
diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h
new file mode 100644
index 00000000000..1cbee96b68f
--- /dev/null
+++ b/sql/event_db_repository.h
@@ -0,0 +1,122 @@
+#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 */
+
+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
+evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname,
+ const LEX_STRING ev_name,
+ TABLE *table);
+
+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);
+
+int
+fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */);
+
+
+class Event_queue_element;
+
+class Event_db_repository
+{
+public:
+ Event_db_repository(){}
+ ~Event_db_repository(){}
+
+ int
+ init_repository();
+
+ void
+ deinit_repository();
+
+ int
+ create_event(THD *thd, Event_timed *et, my_bool create_if_not,
+ uint *rows_affected);
+
+ int
+ update_event(THD *thd, Event_timed *et, sp_name *new_name);
+
+ int
+ drop_event(THD *thd, LEX_STRING db, LEX_STRING name, bool drop_if_exists,
+ uint *rows_affected);
+
+ int
+ drop_schema_events(THD *thd, LEX_STRING schema);
+
+ int
+ drop_user_events(THD *thd, LEX_STRING definer);
+
+ int
+ find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl,
+ MEM_ROOT *root);
+
+ int
+ load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new);
+
+ int
+ find_event_by_name(THD *thd, LEX_STRING db, LEX_STRING name, TABLE *table);
+
+ int
+ open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
+
+ int
+ fill_schema_events(THD *thd, TABLE_LIST *tables, char *db);
+
+private:
+ int
+ drop_events_by_field(THD *thd, enum enum_events_table_field field,
+ LEX_STRING field_value);
+ int
+ index_read_for_db_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table,
+ char *db);
+
+ int
+ table_scan_all_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table);
+
+ MEM_ROOT repo_root;
+
+ /* 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..46f965678c6
--- /dev/null
+++ b/sql/event_queue.cc
@@ -0,0 +1,19 @@
+/* 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"
diff --git a/sql/event_queue.h b/sql/event_queue.h
new file mode 100644
index 00000000000..b3aa6133840
--- /dev/null
+++ b/sql/event_queue.h
@@ -0,0 +1,20 @@
+#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 */
+
+
+#endif /* _EVENT_QUEUE_H_ */
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index 1b4a0d290e6..cb500de53b9 100644
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -15,10 +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 "event_db_repository.h"
#include "sp_head.h"
/*
@@ -574,8 +574,8 @@ event_worker_thread(void *arg)
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);
+ thd->change_security_context(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));
@@ -687,7 +687,7 @@ Event_scheduler::get_instance()
*/
bool
-Event_scheduler::init()
+Event_scheduler::init(Event_db_repository *db_repo)
{
int i= 0;
bool ret= FALSE;
@@ -695,6 +695,7 @@ Event_scheduler::init()
DBUG_PRINT("enter", ("this=%p", this));
LOCK_SCHEDULER_DATA();
+ db_repository= db_repo;
for (;i < COND_LAST; i++)
if (pthread_cond_init(&cond_vars[i], NULL))
{
@@ -783,10 +784,10 @@ Event_scheduler::destroy()
OP_LOAD_ERROR Error during loading from disk
*/
-enum Event_scheduler::enum_error_code
+int
Event_scheduler::create_event(THD *thd, Event_timed *et, bool check_existence)
{
- enum enum_error_code res;
+ int 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));
@@ -805,7 +806,7 @@ Event_scheduler::create_event(THD *thd, Event_timed *et, bool check_existence)
}
/* We need to load the event on scheduler_root */
- if (!(res= load_named_event(thd, et, &et_new)))
+ if (!(res= db_repository->load_named_event(thd, et, &et_new)))
{
queue_insert_safe(&queue, (byte *) et_new);
DBUG_PRINT("info", ("Sending COND_new_work"));
@@ -833,12 +834,13 @@ end:
*/
bool
-Event_scheduler::drop_event(THD *thd, Event_timed *et)
+Event_scheduler::drop_event(THD *thd, sp_name *name)
{
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));
+ DBUG_PRINT("enter", ("thd=%p name=%p lock=%p", thd, name,
+ &LOCK_scheduler_data));
LOCK_SCHEDULER_DATA();
if (!is_running_or_suspended())
@@ -848,7 +850,7 @@ Event_scheduler::drop_event(THD *thd, Event_timed *et)
DBUG_RETURN(OP_OK);
}
- if (!(et_old= find_event(et, TRUE)))
+ if (!(et_old= find_event(name, TRUE)))
DBUG_PRINT("info", ("No such event found, probably DISABLED"));
UNLOCK_SCHEDULER_DATA();
@@ -903,12 +905,12 @@ Event_scheduler::drop_event(THD *thd, Event_timed *et)
OP_ALREADY_EXISTS Event already in the queue
*/
-enum Event_scheduler::enum_error_code
+int
Event_scheduler::update_event(THD *thd, Event_timed *et,
LEX_STRING *new_schema,
LEX_STRING *new_name)
{
- enum enum_error_code res;
+ int res= OP_OK;
Event_timed *et_old, *et_new= NULL;
LEX_STRING old_schema, old_name;
@@ -946,7 +948,7 @@ Event_scheduler::update_event(THD *thd, Event_timed *et,
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)))
+ if (!(res= db_repository->load_named_event(thd, et, &et_new)))
{
queue_insert_safe(&queue, (byte *) et_new);
DBUG_PRINT("info", ("Sending COND_new_work"));
@@ -960,7 +962,7 @@ Event_scheduler::update_event(THD *thd, Event_timed *et,
et->dbname= old_schema;
et->name= old_name;
}
-
+ DBUG_PRINT("info", ("res=%d", res));
UNLOCK_SCHEDULER_DATA();
/*
Andrey: Is this comment still truthful ???
@@ -1050,6 +1052,48 @@ Event_scheduler::find_event(Event_timed *etn, bool remove_from_q)
/*
+ Searches for an event in the scheduler queue
+
+ SYNOPSIS
+ Event_scheduler::find_event()
+ name 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(sp_name *name, 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]?", name->m_db.str, name->m_name.str,
+ et->dbname.str, et->name.str));
+ if (event_timed_identifier_equal(name, 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
@@ -1068,11 +1112,11 @@ Event_scheduler::find_event(Event_timed *etn, bool remove_from_q)
*/
void
-Event_scheduler::drop_matching_events(THD *thd, LEX_STRING *pattern,
+Event_scheduler::drop_matching_events(THD *thd, LEX_STRING pattern,
bool (*comparator)(Event_timed *,LEX_STRING *))
{
DBUG_ENTER("Event_scheduler::drop_matching_events");
- DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern->length, pattern->str,
+ DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern.length, pattern.str,
state));
if (is_running_or_suspended())
{
@@ -1081,7 +1125,7 @@ Event_scheduler::drop_matching_events(THD *thd, LEX_STRING *pattern,
{
Event_timed *et= (Event_timed *) queue_element(&queue, i);
DBUG_PRINT("info", ("[%s.%s]?", et->dbname.str, et->name.str));
- if (comparator(et, pattern))
+ if (comparator(et, &pattern))
{
/*
The queue is ordered. If we remove an element, then all elements after
@@ -1136,7 +1180,7 @@ Event_scheduler::drop_matching_events(THD *thd, LEX_STRING *pattern,
*/
int
-Event_scheduler::drop_schema_events(THD *thd, LEX_STRING *schema)
+Event_scheduler::drop_schema_events(THD *thd, LEX_STRING schema)
{
int ret;
DBUG_ENTER("Event_scheduler::drop_schema_events");
@@ -1144,7 +1188,6 @@ Event_scheduler::drop_schema_events(THD *thd, LEX_STRING *schema)
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);
@@ -1670,7 +1713,7 @@ Event_scheduler::stop_all_running_events(THD *thd)
The caller must have acquited LOCK_scheduler_data.
*/
-enum Event_scheduler::enum_error_code
+int
Event_scheduler::stop()
{
THD *thd= current_thd;
@@ -1735,7 +1778,7 @@ Event_scheduler::stop()
OP_OK OK
*/
-enum Event_scheduler::enum_error_code
+int
Event_scheduler::suspend_or_resume(
enum Event_scheduler::enum_suspend_or_resume action)
{
@@ -2073,59 +2116,6 @@ Event_scheduler::events_count()
}
-/*
- 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);
-
- if (et_loaded->status != Event_timed::ENABLED)
- {
- /*
- We don't load non-enabled events.
- In db_find_event() `et_new` was allocated on the heap and not on
- scheduler_root therefore we delete it here.
- */
- delete et_loaded;
- DBUG_RETURN(OP_DISABLED_EVENT);
- }
-
- et_loaded->compute_next_execution_time();
- *etn_new= et_loaded;
-
- DBUG_RETURN(OP_OK);
-}
/*
@@ -2169,7 +2159,7 @@ Event_scheduler::load_events_from_db(THD *thd)
DBUG_RETURN(EVEX_GENERAL_ERROR);
}
- if ((ret= Events::open_event_table(thd, TL_READ, &table)))
+ 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);
diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h
index 5ae310bab2a..bd099d10839 100644
--- a/sql/event_scheduler.h
+++ b/sql/event_scheduler.h
@@ -16,7 +16,9 @@
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_timed;
+class Event_db_repository;
class THD;
typedef bool * (*event_timed_identifier_comparator)(Event_timed*, Event_timed*);
@@ -30,17 +32,6 @@ events_shutdown();
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
{
@@ -65,22 +56,22 @@ public:
/* Methods for queue management follow */
- enum enum_error_code
+ int
create_event(THD *thd, Event_timed *et, bool check_existence);
- enum enum_error_code
+ int
update_event(THD *thd, Event_timed *et, LEX_STRING *new_schema,
LEX_STRING *new_name);
bool
- drop_event(THD *thd, Event_timed *et);
+ drop_event(THD *thd, sp_name *name);
int
- drop_schema_events(THD *thd, LEX_STRING *schema);
+ drop_schema_events(THD *thd, LEX_STRING schema);
int
- drop_user_events(THD *thd, LEX_STRING *definer, uint *dropped_num)
+ drop_user_events(THD *thd, LEX_STRING *definer)
{ DBUG_ASSERT(0); return 0;}
uint
@@ -91,20 +82,24 @@ public:
bool
start();
- enum enum_error_code
+ int
stop();
bool
start_suspended();
+ /*
+ Need to be public because has to be called from the function
+ passed to pthread_create.
+ */
bool
run(THD *thd);
- enum enum_error_code
+ int
suspend_or_resume(enum enum_suspend_or_resume action);
bool
- init();
+ init(Event_db_repository *db_repo);
void
destroy();
@@ -136,6 +131,9 @@ private:
Event_timed *
find_event(Event_timed *etn, bool remove_from_q);
+ Event_timed *
+ find_event(sp_name *name, bool remove_from_q);
+
uint
workers_count();
@@ -152,14 +150,11 @@ private:
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);
void
- drop_matching_events(THD *thd, LEX_STRING *pattern,
+ drop_matching_events(THD *thd, LEX_STRING pattern,
bool (*)(Event_timed *,LEX_STRING *));
bool
@@ -226,6 +221,8 @@ private:
/* The MEM_ROOT of the object */
MEM_ROOT scheduler_root;
+ Event_db_repository *db_repository;
+
/* The sorted queue with the Event_timed objects */
QUEUE queue;
diff --git a/sql/events.cc b/sql/events.cc
index d67c42326e3..cd4d3dff244 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -15,11 +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.h"
+#include "event_db_repository.h"
#include "sp_head.h"
/*
@@ -48,10 +47,6 @@ Warning:
*/
-MEM_ROOT evex_mem_root;
-time_t mysql_event_last_create_time= 0L;
-
-
const char *event_scheduler_state_names[]=
{ "OFF", "0", "ON", "1", "SUSPEND", "2", NullS };
@@ -63,104 +58,10 @@ TYPELIB Events::opt_typelib=
NULL
};
+Events Events::singleton;
ulong Events::opt_event_scheduler= 2;
-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")}
- }
-};
-
/*
Compares 2 LEX strings regarding case.
@@ -187,6 +88,23 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
(uchar *) t.str,t.length, 0);
}
+/*
+ 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
@@ -201,15 +119,14 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
interval - the interval type (for instance YEAR_MONTH)
expression - the value in the lowest entity
- RETURNS
+ 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;
@@ -341,545 +258,7 @@ int
Events::open_event_table(THD *thd, enum thr_lock_type lock_type,
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);
-}
-
-
-/*
- Find row in open mysql.event table representing event
-
- SYNOPSIS
- evex_db_find_event_aux()
- thd Thread context
- et event_timed object containing dbname & name
- table TABLE object for open mysql.event table.
-
- 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);
-}
-
-
-/*
- 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
-*/
-
-int
-evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname,
- const LEX_STRING ev_name,
- TABLE *table)
-{
- byte key[MAX_KEY_LENGTH];
- DBUG_ENTER("evex_db_find_event_by_name");
- DBUG_PRINT("enter", ("name: %.*s", ev_name.length, ev_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 (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))
- {
- DBUG_PRINT("info", ("Row not found"));
- DBUG_RETURN(EVEX_KEY_NOT_FOUND);
- }
-
- DBUG_PRINT("info", ("Row found!"));
- DBUG_RETURN(0);
-}
-
-
-/*
- Puts some data common to CREATE and ALTER EVENT into a row.
-
- SYNOPSIS
- evex_fill_row()
- thd THD
- table the row to fill out
- et Event's data
-
- 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
-evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update)
-{
- 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)
- {
- 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;
- }
-
- if (et->expression)
- {
- 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)
- {
- table->field[Events::FIELD_ENDS]->set_notnull();
- table->field[Events::FIELD_ENDS]->
- store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME);
- }
- }
- 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();
-
- 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);
-}
-
-
-/*
- Creates an event in mysql.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
-
- 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".
-*/
-
-int
-db_create_event(THD *thd, Event_timed *et, my_bool create_if_not,
- uint *rows_affected)
-{
- int ret= 0;
- CHARSET_INFO *scs= system_charset_info;
- TABLE *table;
- char olddb[128];
- 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.str,olddb, sizeof(olddb),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]))
- {
- my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
- goto err;
- }
-
-#ifdef USE_THIS_CODE_AS_TEMPLATE_WHEN_EVENT_REPLICATION_IS_AGREED
- if (mysql_bin_log.is_open())
- {
- 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);
- }
-#endif
-
- *rows_affected= 1;
-ok:
- if (dbchanged)
- (void) mysql_change_db(thd, olddb, 1);
- if (table)
- close_thread_tables(thd);
- DBUG_RETURN(EVEX_OK);
-
-err:
- if (dbchanged)
- (void) mysql_change_db(thd, olddb, 1);
- if (table)
- close_thread_tables(thd);
- DBUG_RETURN(EVEX_GENERAL_ERROR);
-}
-
-
-/*
- Used to execute ALTER EVENT. Pendant to Events::update_event().
-
- 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.
-*/
-
-static int
-db_update_event(THD *thd, Event_timed *et, sp_name *new_name)
-{
- 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;
- }
-
- /* 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])))
- {
- my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
- goto err;
- }
-
- /* close mysql.event or we crash later when loading the event from disk */
- close_thread_tables(thd);
- DBUG_RETURN(0);
-
-err:
- if (table)
- close_thread_tables(thd);
- DBUG_RETURN(EVEX_GENERAL_ERROR);
-}
-
-
-/*
- Looks for a named event in mysql.event and in case of success returns
- an object will data loaded from the table.
-
- 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
-
- RETURN VALUE
- 0 ok In this case *ett is set to the event
- # error *ett == 0
-*/
-
-int
-db_find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl,
- MEM_ROOT *root)
-{
- TABLE *table;
- int ret;
- Event_timed *et= NULL;
- DBUG_ENTER("db_find_event");
- DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
-
- if (!root)
- root= &evex_mem_root;
-
- if (tbl)
- table= tbl;
- else if (Events::open_event_table(thd, TL_READ, &table))
- {
- my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
- ret= EVEX_GENERAL_ERROR;
- goto done;
- }
-
- 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
-
- 2)::load_from_row() is silent on error therefore we emit error msg here
- */
- if ((ret= et->load_from_row(root, table)))
- {
- my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0));
- goto done;
- }
-
-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);
+ return db_repository->open_event_table(thd, lock_type, table);
}
@@ -904,8 +283,8 @@ done:
*/
int
-Events::create_event(THD *thd, Event_timed *et, uint create_options,
- uint *rows_affected)
+Events::create_event(THD *thd, Event_timed *et, Event_parse_data *parse_data,
+ uint create_options, uint *rows_affected)
{
int ret;
@@ -913,9 +292,10 @@ Events::create_event(THD *thd, Event_timed *et, uint create_options,
DBUG_PRINT("enter", ("name: %*s options:%d", et->name.length,
et->name.str, create_options));
- if (!(ret = db_create_event(thd, et,
- create_options & HA_LEX_CREATE_IF_NOT_EXISTS,
- rows_affected)))
+ if (!(ret= db_repository->
+ create_event(thd, et,
+ create_options & HA_LEX_CREATE_IF_NOT_EXISTS,
+ rows_affected)))
{
Event_scheduler *scheduler= Event_scheduler::get_instance();
if (scheduler->initialized() &&
@@ -948,8 +328,8 @@ Events::create_event(THD *thd, Event_timed *et, uint create_options,
*/
int
-Events::update_event(THD *thd, Event_timed *et, sp_name *new_name,
- uint *rows_affected)
+Events::update_event(THD *thd, Event_timed *et, Event_parse_data *parse_data,
+ sp_name *new_name, uint *rows_affected)
{
int ret;
@@ -960,7 +340,7 @@ Events::update_event(THD *thd, Event_timed *et, sp_name *new_name,
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 (!(ret= db_update_event(thd, et, new_name)))
+ if (!(ret= db_repository->update_event(thd, et, new_name)))
{
Event_scheduler *scheduler= Event_scheduler::get_instance();
if (scheduler->initialized() &&
@@ -977,74 +357,9 @@ Events::update_event(THD *thd, Event_timed *et, sp_name *new_name,
Drops an event
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
-
- RETURN VALUE
- 0 OK
- !0 Error (my_error() called)
-*/
-
-int db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists,
- uint *rows_affected)
-{
- TABLE *table;
- Open_tables_state backup;
- int ret;
-
- DBUG_ENTER("db_drop_event");
- ret= EVEX_OPEN_TABLE_FAILED;
-
- thd->reset_n_backup_open_tables_state(&backup);
- if (Events::open_event_table(thd, TL_WRITE, &table))
- {
- my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
- goto done;
- }
-
- if (!(ret= evex_db_find_event_aux(thd, et, table)))
- {
- if ((ret= table->file->ha_delete_row(table->record[0])))
- {
- my_error(ER_EVENT_CANNOT_DELETE, MYF(0));
- goto done;
- }
- }
- 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;
- }
-
-
-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);
-}
-
-
-/*
- Drops an event
-
- SYNOPSIS
Events::drop_event()
thd THD
- et event's name
+ name 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
@@ -1054,19 +369,20 @@ done:
*/
int
-Events::drop_event(THD *thd, Event_timed *et, bool drop_if_exists,
- uint *rows_affected)
+Events::drop_event(THD *thd, sp_name *name, bool drop_if_exists,
+ uint *rows_affected)
{
int ret;
DBUG_ENTER("Events::drop_event");
- if (!(ret= db_drop_event(thd, et, drop_if_exists, rows_affected)))
+
+ if (!(ret= db_repository->drop_event(thd, name->m_db, name->m_name,
+ drop_if_exists, rows_affected)))
{
Event_scheduler *scheduler= Event_scheduler::get_instance();
- if (scheduler->initialized() && (ret= scheduler->drop_event(thd, et)))
+ if (scheduler->initialized() && (ret= scheduler->drop_event(thd, name)))
my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret);
}
-
DBUG_RETURN(ret);
}
@@ -1091,11 +407,11 @@ Events::show_create_event(THD *thd, sp_name *spn)
Event_timed *et= NULL;
Open_tables_state backup;
- DBUG_ENTER("evex_update_event");
+ DBUG_ENTER("Events::show_create_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);
+ ret= db_repository->find_event(thd, spn, &et, NULL, thd->mem_root);
thd->restore_backup_open_tables_state(&backup);
if (!ret)
@@ -1167,76 +483,14 @@ Events::drop_schema_events(THD *thd, char *db)
DBUG_PRINT("enter", ("dropping events from %s", db));
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);
+ ret= scheduler->drop_schema_events(thd, db_lex);
+ ret= db_repository->drop_schema_events(thd, db_lex);
DBUG_RETURN(ret);
}
/*
- Drops all events in the selected database, from mysql.event.
-
- SYNOPSIS
- evex_drop_db_events_from_table()
- thd Thread
- db Schema name
-
- RETURN VALUE
- 0 OK
- !0 Error from ha_delete_row
-*/
-
-int
-db_drop_events_from_table(THD *thd, LEX_STRING *db)
-{
- 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)))
- {
- if (my_errno != ENOENT)
- sql_print_error("Table mysql.event is damaged. Got error %d on open",
- my_errno);
- DBUG_RETURN(ret);
- }
- /* 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);
-}
-
-
-
-/*
Inits the scheduler's structures.
SYNOPSIS
@@ -1254,14 +508,16 @@ int
Events::init()
{
int ret= 0;
+ Event_db_repository *db_repo;
DBUG_ENTER("Events::init");
+ db_repository->init_repository();
/* it should be an assignment! */
if (opt_event_scheduler)
{
Event_scheduler *scheduler= Event_scheduler::get_instance();
DBUG_ASSERT(opt_event_scheduler == 1 || opt_event_scheduler == 2);
- DBUG_RETURN(scheduler->init() ||
+ DBUG_RETURN(scheduler->init(db_repository) ||
(opt_event_scheduler == 1? scheduler->start():
scheduler->start_suspended()));
}
@@ -1273,16 +529,17 @@ Events::init()
Cleans up scheduler's resources. Called at server shutdown.
SYNOPSIS
- Events::shutdown()
+ Events::deinit()
NOTES
This function is not synchronized.
*/
void
-Events::shutdown()
+Events::deinit()
{
- DBUG_ENTER("Events::shutdown");
+ DBUG_ENTER("Events::deinit");
+
Event_scheduler *scheduler= Event_scheduler::get_instance();
if (scheduler->initialized())
{
@@ -1290,26 +547,9 @@ Events::shutdown()
scheduler->destroy();
}
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Proxy for Event_scheduler::dump_internal_status
-
- SYNOPSIS
- Events::dump_internal_status()
- thd Thread
-
- RETURN VALUE
- 0 OK
- !0 Error
-*/
+ db_repository->deinit_repository();
-int
-Events::dump_internal_status(THD *thd)
-{
- return Event_scheduler::dump_internal_status(thd);
+ DBUG_VOID_RETURN;
}
@@ -1324,6 +564,7 @@ Events::dump_internal_status(THD *thd)
void
Events::init_mutexes()
{
+ db_repository= new Event_db_repository;
Event_scheduler::init_mutexes();
}
@@ -1339,4 +580,61 @@ void
Events::destroy_mutexes()
{
Event_scheduler::destroy_mutexes();
+ delete db_repository;
+ db_repository= NULL;
+}
+
+
+/*
+ Proxy for Event_scheduler::dump_internal_status
+
+ SYNOPSIS
+ Events::dump_internal_status()
+ thd Thread
+
+ RETURN VALUE
+ 0 OK
+ !0 Error
+*/
+
+int
+Events::dump_internal_status(THD *thd)
+{
+ return Event_scheduler::dump_internal_status(thd);
+}
+
+
+/*
+ Proxy for Event_db_repository::fill_schema_events.
+ Callback for I_S from sql_show.cc
+
+ SYNOPSIS
+ Events::fill_schema_events()
+ thd Thread
+ tables The schema table
+ cond Unused
+
+ RETURN VALUE
+ 0 OK
+ !0 Error
+*/
+
+int
+Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
+{
+ char *db= NULL;
+ DBUG_ENTER("Events::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->orig_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);
+ db= thd->lex->select_lex.db;
+ }
+ DBUG_RETURN(get_instance()->db_repository->fill_schema_events(thd, tables, db));
}
diff --git a/sql/events.h b/sql/events.h
index 66cce6e7777..45a0db13980 100644
--- a/sql/events.h
+++ b/sql/events.h
@@ -16,78 +16,96 @@
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_timed;
+class Event_parse_data;
+class Event_db_repository;
+
+/* 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 Events
{
public:
+ /*
+ 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_timed;
+
static ulong opt_event_scheduler;
static TYPELIB opt_typelib;
- enum enum_table_field
- {
- 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 :) */
- };
+ int
+ init();
+
+ void
+ deinit();
- static int
- create_event(THD *thd, Event_timed *et, uint create_options,
- uint *rows_affected);
+ void
+ init_mutexes();
+
+ void
+ destroy_mutexes();
- static int
- update_event(THD *thd, Event_timed *et, sp_name *new_name,
- uint *rows_affected);
+ static Events*
+ get_instance();
- static int
- drop_event(THD *thd, Event_timed *et, bool drop_if_exists,
- uint *rows_affected);
+ int
+ create_event(THD *thd, Event_timed *et, Event_parse_data *parse_data,
+ uint create_options, uint *rows_affected);
- static int
+ int
+ update_event(THD *thd, Event_timed *et, Event_parse_data *parse_data,
+ sp_name *new_name, uint *rows_affected);
+
+ int
+ drop_event(THD *thd, sp_name *name, bool drop_if_exists, uint *rows_affected);
+
+ int
+ drop_schema_events(THD *thd, char *db);
+
+ int
open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
- static int
+ int
show_create_event(THD *thd, sp_name *spn);
+ /* 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);
+ fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */);
- static int
+ int
dump_internal_status(THD *thd);
-
- static int
- init();
-
- static void
- shutdown();
-
- static void
- init_mutexes();
-
- static void
- destroy_mutexes();
+ Event_db_repository *db_repository;
private:
+ /* Singleton DP is used */
+ Events(){}
+ ~Events(){}
+
+ /* Singleton instance */
+ static Events singleton;
+
/* 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/mysqld.cc b/sql/mysqld.cc
index 4ab1b365f73..a27be384ee2 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -883,7 +883,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)
@@ -1321,7 +1321,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
@@ -2884,7 +2884,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,
@@ -3673,7 +3673,7 @@ we force server id to 2, but this MySQL server will not act as a slave.");
if (!opt_noacl)
{
- Events::init();
+ Events::get_instance()->init();
}
#if defined(__NT__) || defined(HAVE_SMEM)
handle_connections_methods();
diff --git a/sql/set_var.cc b/sql/set_var.cc
index b0ecc7eccef..6c3606c9150 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -3892,7 +3892,7 @@ 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;
+ int res;
Event_scheduler *scheduler= Event_scheduler::get_instance();
/* here start the thread if not running. */
DBUG_ENTER("sys_var_event_scheduler::update");
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index 476bc2f2f02..ac4f2dd9237 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -5839,3 +5839,6 @@ ER_CANT_ACTIVATE_LOG
eng "Cannot activate '%-.64s' log."
ER_RBR_NOT_AVAILABLE
eng "The server was not built with row-based replication"
+ER_EVENT_RECURSIVITY_FORBIDDEN
+ eng "Recursivity of EVENT DDL statements is forbidden when body is present"
+
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 7fa0173ddea..f69f830ba8c 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -2073,6 +2073,63 @@ bool Security_context::set_user(char *user_arg)
return user == 0;
}
+/*
+ Switches the security context
+ SYNOPSIS
+ THD::change_security_context()
+ 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
+ FALSE OK
+ TRUE Error (generates error too)
+*/
+
+bool
+THD::change_security_context(LEX_STRING user, LEX_STRING host,
+ LEX_STRING db, Security_context *s_ctx,
+ Security_context **backup)
+{
+ DBUG_ENTER("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))
+ {
+ my_error(ER_NO_SUCH_USER, MYF(0), user.str, host.str);
+ DBUG_RETURN(TRUE);
+ }
+ *backup= security_ctx;
+ security_ctx= s_ctx;
+#endif
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Restores the security context
+ SYNOPSIS
+ restore_security_context()
+ thd Thread
+ backup Context to switch to
+*/
+
+void
+THD::restore_security_context(Security_context *backup)
+{
+ DBUG_ENTER("restore_security_context");
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (backup)
+ security_ctx= backup;
+#endif
+ DBUG_VOID_RETURN;
+}
+
+
/****************************************************************************
Handling of open and locked tables states.
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 5222e75f309..d7c35ec545e 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -868,6 +868,14 @@ public:
char *db, *catalog;
Security_context main_security_ctx;
Security_context *security_ctx;
+
+ bool
+ change_security_context(LEX_STRING user, LEX_STRING host,
+ LEX_STRING db, Security_context *s_ctx,
+ Security_context **backup);
+
+ void
+ restore_security_context(Security_context *backup);
/* remote (peer) port */
uint16 peer_port;
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 8dd62fc8494..83516da38d4 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -904,7 +904,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);
+ error= 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 SELECT DATABASE()
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index a46aaa0bab7..e2cf213cd17 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -28,6 +28,7 @@ class sp_pcontext;
class st_alter_tablespace;
class partition_info;
class Event_timed;
+class Event_parse_data;
#ifdef MYSQL_SERVER
/*
@@ -1017,6 +1018,7 @@ typedef struct st_lex : public Query_tables_list
st_sp_chistics sp_chistics;
Event_timed *et;
+ Event_parse_data *event_parse_data;
bool et_compile_phase;
bool only_view; /* used for SHOW CREATE TABLE/VIEW */
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 1ecc7b78315..5ec4e7b3b68 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
/*
@@ -3831,7 +3831,6 @@ end_with_restore_list:
}
case SQLCOM_CREATE_EVENT:
case SQLCOM_ALTER_EVENT:
- case SQLCOM_DROP_EVENT:
{
uint rows_affected= 1;
DBUG_ASSERT(lex->et);
@@ -3860,17 +3859,15 @@ end_with_restore_list:
switch (lex->sql_command) {
case SQLCOM_CREATE_EVENT:
- res= Events::create_event(thd, lex->et,
- (uint) lex->create_info.options,
- &rows_affected);
+ res= Events::get_instance()->
+ create_event(thd, lex->et, lex->event_parse_data,
+ (uint) lex->create_info.options, &rows_affected);
break;
case SQLCOM_ALTER_EVENT:
- res= Events::update_event(thd, lex->et, lex->spname,
- &rows_affected);
+ res= Events::get_instance()->
+ update_event(thd, lex->et, lex->event_parse_data,
+ 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",
@@ -3889,10 +3886,10 @@ end_with_restore_list:
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));
@@ -3906,15 +3903,31 @@ 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);
+
+ if (lex->sql_command == SQLCOM_SHOW_CREATE_EVENT)
+ res= Events::get_instance()->show_create_event(thd, lex->spname);
+ else
+ {
+ uint rows_affected= 1;
+ if (end_active_trans(thd))
+ {
+ res= -1;
+ break;
+ }
+ if (!(res= Events::get_instance()->drop_event(thd, lex->spname,
+ lex->drop_if_exists,
+ &rows_affected)))
+ send_ok(thd, rows_affected);
+ }
break;
}
#ifndef DBUG_OFF
case SQLCOM_SHOW_SCHEDULER_STATUS:
{
- res= Events::dump_internal_status(thd);
+ res= Events::get_instance()->dump_internal_status(thd);
break;
}
#endif
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 8f8c84c2db5..86a9c380ee1 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
@@ -4166,7 +4166,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;
@@ -4301,183 +4301,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");
@@ -5574,7 +5397,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_yacc.yy b/sql/sql_yacc.yy
index 0632e2298cd..bda51faeeb8 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>
@@ -880,7 +880,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
@@ -1257,7 +1258,33 @@ 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 *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;
+ }
+ ;
+
+
+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
@@ -1268,18 +1295,19 @@ create:
if (lex->et)
{
/*
- Recursive events are not possible because recursive SPs
- are not also possible. lex->sp_head is not stacked.
+ Recursive CREATE EVENT statement 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()
YYABORT;
+ if (!(lex->event_parse_data= Event_parse_data::new_instance(YYTHD)))
+ YYABORT;
/*
We have to turn of CLIENT_MULTI_QUERIES while parsing a
@@ -1289,9 +1317,12 @@ create:
$<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES);
+
+ lex->event_parse_data->identifier= $3;
+
if (!lex->et_compile_phase)
{
- lex->et->init_name(YYTHD, $4);
+ lex->et->init_name(YYTHD, $3);
lex->et->init_definer(YYTHD);
}
}
@@ -1303,13 +1334,12 @@ 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
@@ -1317,33 +1347,12 @@ create:
*/
Lex->sql_command= SQLCOM_CREATE_EVENT;
}
- | 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->event_parse_data->item_expression= $2;
+ Lex->event_parse_data->interval= $3;
LEX *lex=Lex;
if (!lex->et_compile_phase)
{
@@ -1365,6 +1374,7 @@ ev_schedule_time: EVERY_SYM expr interval
ev_ends
| AT_SYM expr
{
+ Lex->event_parse_data->item_execute_at= $2;
LEX *lex=Lex;
if (!lex->et_compile_phase)
{
@@ -1395,6 +1405,7 @@ ev_schedule_time: EVERY_SYM expr interval
opt_ev_status: /* empty */ { $$= 0; }
| ENABLE_SYM
{
+ Lex->event_parse_data->status= Event_parse_data::ENABLED;
LEX *lex=Lex;
if (!lex->et_compile_phase)
lex->et->status= Event_timed::ENABLED;
@@ -1402,6 +1413,7 @@ opt_ev_status: /* empty */ { $$= 0; }
}
| DISABLE_SYM
{
+ Lex->event_parse_data->status= Event_parse_data::DISABLED;
LEX *lex=Lex;
if (!lex->et_compile_phase)
@@ -1412,10 +1424,12 @@ opt_ev_status: /* empty */ { $$= 0; }
ev_starts: /* empty */
{
+ Lex->event_parse_data->item_starts= new Item_func_now_local();
Lex->et->init_starts(YYTHD, new Item_func_now_local());
}
| STARTS_SYM expr
{
+ Lex->event_parse_data->item_starts= $2;
LEX *lex= Lex;
if (!lex->et_compile_phase)
{
@@ -1443,6 +1457,7 @@ ev_starts: /* empty */
ev_ends: /* empty */
| ENDS_SYM expr
{
+ Lex->event_parse_data->item_ends= $2;
LEX *lex= Lex;
if (!lex->et_compile_phase)
{
@@ -1467,6 +1482,8 @@ opt_ev_on_completion: /* empty */ { $$= 0; }
ev_on_completion:
ON COMPLETION_SYM PRESERVE_SYM
{
+ Lex->event_parse_data->on_completion=
+ Event_parse_data::ON_COMPLETION_PRESERVE;
LEX *lex=Lex;
if (!lex->et_compile_phase)
lex->et->on_completion= Event_timed::ON_COMPLETION_PRESERVE;
@@ -1474,6 +1491,8 @@ ev_on_completion:
}
| ON COMPLETION_SYM NOT_SYM PRESERVE_SYM
{
+ Lex->event_parse_data->on_completion=
+ Event_parse_data::ON_COMPLETION_DROP;
LEX *lex=Lex;
if (!lex->et_compile_phase)
lex->et->on_completion= Event_timed::ON_COMPLETION_DROP;
@@ -1484,6 +1503,7 @@ ev_on_completion:
opt_ev_comment: /* empty */ { $$= 0; }
| COMMENT_SYM TEXT_STRING_sys
{
+ Lex->comment= Lex->event_parse_data->comment= $2;
LEX *lex= Lex;
if (!lex->et_compile_phase)
{
@@ -1499,25 +1519,43 @@ 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= 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;
@@ -1526,18 +1564,16 @@ ev_sql_stmt:
{
LEX *lex=Lex;
- if (!$<sphead>1)
- {
- sp_head *sp= lex->sphead;
- // return back to the original memory root ASAP
- sp->init_strings(YYTHD, lex, NULL);
- sp->restore_thd_mem_root(YYTHD);
+ // return back to the original memory root ASAP
+ lex->sphead->init_strings(YYTHD, lex, NULL);
+ 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;
- }
+ lex->et->sphead= lex->sphead;
+ lex->sphead= NULL;
+
+ Lex->event_parse_data->init_body(YYTHD);
if (!lex->et_compile_phase)
{
lex->et->init_body(YYTHD);
@@ -4720,16 +4756,11 @@ alter:
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");
+ lex->spname= NULL;
+
+ if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD)))
YYABORT;
- }
- lex->spname= 0;//defensive programming
+ Lex->event_parse_data->identifier= $3;
if (!(et= new (YYTHD->mem_root) Event_timed()))// implicitly calls Event_timed::init()
YYABORT;
@@ -4742,9 +4773,9 @@ alter:
}
/*
- 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 of 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;
@@ -7664,29 +7695,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
{
@@ -8416,12 +8427,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;
}
;
@@ -10751,20 +10758,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
+ {}
;
/**************************************************************************