From e5f8163b88d019942c5eb205a023a24c3e9cee04 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 27 Jun 2006 08:48:50 +0200 Subject: [PATCH 1/7] first cut of WL#3337 (New event scheduler locking infrastructure). Infrastructure built. Added the foreseen files and change Makefile.am/CMakeLists.txt accordingly. libmysqld/Makefile.am: add new files. this is first cut of WL3337u sql/CMakeLists.txt: add more files to build sql/Makefile.am: add new files. this is first cut of WL3337 sql/event_scheduler.cc: event_timed.h -> event_data_objects.h (WL#3337) sql/events.cc: event_timed.h -> event_data_objects.h (WL#3337) sql/share/errmsg.txt: new error message sql/event_data_objects.cc: event_timed.h -> event_data_objects.h (WL#3337) sql/event_data_objects.h: event_timed.h -> event_data_objects.h (WL#3337) sql/sql_parse.cc: event_timed.h -> event_data_objects.h (WL#3337) sql/sql_show.cc: event_timed.h -> event_data_objects.h (WL#3337) sql/sql_yacc.yy: event_timed.h -> event_data_objects.h (WL#3337) --- libmysqld/Makefile.am | 3 +- sql/CMakeLists.txt | 3 +- sql/Makefile.am | 6 +- sql/{event_timed.cc => event_data_objects.cc} | 2 +- sql/{event_timed.h => event_data_objects.h} | 93 ++++++++++++++++++- sql/event_db_repository.cc | 19 ++++ sql/event_db_repository.h | 20 ++++ sql/event_queue.cc | 19 ++++ sql/event_queue.h | 20 ++++ sql/event_scheduler.cc | 2 +- sql/events.cc | 2 +- sql/share/errmsg.txt | 3 + sql/sql_parse.cc | 2 +- sql/sql_show.cc | 2 +- sql/sql_yacc.yy | 2 +- 15 files changed, 184 insertions(+), 14 deletions(-) rename sql/{event_timed.cc => event_data_objects.cc} (99%) rename sql/{event_timed.h => event_data_objects.h} (74%) create mode 100644 sql/event_db_repository.cc create mode 100644 sql/event_db_repository.h create mode 100644 sql/event_queue.cc create mode 100644 sql/event_queue.h diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index b91944ac258..2e03ba0f0a9 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -68,7 +68,8 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \ spatial.cc gstream.cc sql_help.cc tztime.cc sql_cursor.cc \ sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc \ parse_file.cc sql_view.cc sql_trigger.cc my_decimal.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 \ rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \ sql_tablespace.cc \ rpl_injector.cc my_user.c partition_info.cc diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index d80a51bb829..9c4d544233f 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 ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc diff --git a/sql/Makefile.am b/sql/Makefile.am index 387f18c2ae9..29645302ecc 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -65,7 +65,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.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_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 similarity index 99% rename from sql/event_timed.cc rename to sql/event_data_objects.cc index 4ec875f32a3..ebd45dd1a23 100644 --- a/sql/event_timed.cc +++ b/sql/event_data_objects.cc @@ -18,7 +18,7 @@ #include "mysql_priv.h" #include "events_priv.h" #include "events.h" -#include "event_timed.h" +#include "event_data_objects.h" #include "sp_head.h" diff --git a/sql/event_timed.h b/sql/event_data_objects.h similarity index 74% rename from sql/event_timed.h rename to sql/event_data_objects.h index 0652cece361..0c122211a9d 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 @@ -41,6 +41,7 @@ class sp_head; +class Sql_alloc; class Event_timed { @@ -213,5 +214,89 @@ 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; + + Item* item_expression; + Item* item_interval; + longlong expression; + interval_type interval; + +// ulonglong created; +// ulonglong modified; + + 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); + +}; + +#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..547e1e6887b --- /dev/null +++ b/sql/event_db_repository.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_db_repository.h" +#include "event_data_objects.h" diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h new file mode 100644 index 00000000000..d8a8784089e --- /dev/null +++ b/sql/event_db_repository.h @@ -0,0 +1,20 @@ +#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 */ + + +#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..fc2ad75b272 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -17,7 +17,7 @@ #include "mysql_priv.h" #include "events_priv.h" #include "events.h" -#include "event_timed.h" +#include "event_data_objects.h" #include "event_scheduler.h" #include "sp_head.h" diff --git a/sql/events.cc b/sql/events.cc index b4e50cdedee..391ecd6ba16 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -17,7 +17,7 @@ #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 "sp_head.h" 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_parse.cc b/sql/sql_parse.cc index 6e89b91ff4c..3ebf700b715 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 /* diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 8776189b493..33fa9758c6c 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 #ifdef WITH_PARTITION_STORAGE_ENGINE diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 5d89e8a618b..e9e00828b7e 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 #include From d2db48c69bf473101cceffe8305a5922dd1a2af2 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 27 Jun 2006 10:53:26 +0200 Subject: [PATCH 2/7] WL#3337 (Events new infrasctructure) Second cut of separating parsing phase from execution phase Separate Event_timed from parsing phase and introducing Event_parse_data. sql/event_data_objects.cc: second cut, copy Event_timed::init_body() to Event_parse_data::init_body() Init the body during parsing, everything else keep as a pointer to Item or some other pointer to use for later initialization. sql/event_data_objects.h: get the identifier as sp_name*, later will initialize our structures sql/events.cc: for easy transition add temporarily parse_data, later Event_timed *et will be removed. Do slow transition because Event_timed is so tightly integrated that a front-attack by removing things from this class was unsuccessful. Do things step by step by eliminating dependencies. Hence, the code could be checked with the current test suite too (early testing) sql/events.h: for easy transition add temporarily parse_data, later Event_timed *et will be removed. Do slow transition because Event_timed is so tightly integrated that a front-attack by removing things from this class was unsuccessful. Do things step by step by eliminating dependencies. Hence, the code could be checked with the current test suite too (early testing) BitKeeper/etc/ignore: Added libmysql/viosocket.o.6WmSJk libmysqld/event_data_objects.cc libmysqld/event_db_repository.cc libmysqld/event_queue.cc server-tools/instance-manager/net_serv.cc to the ignore list sql/share/errmsg.txt: remove this message, not used and needed for now sql/sql_lex.h: for easy transition add temporarily parse_data, later Event_timed *et will be removed. Do slow transition because Event_timed is so tightly integrated that a front-attack by removing things from this class was unsuccessful. Do things step by step by eliminating dependencies. Hence, the code could be checked with the current test suite too (early testing) sql/sql_parse.cc: for easy transition add temporarily parse_data, later Event_timed *et will be removed. Do slow transition because Event_timed is so tightly integrated that a front-attack by removing things from this class was unsuccessful. Do things step by step by eliminating dependencies. Hence, the code could be checked with the current test suite too (early testing) sql/sql_yacc.yy: for easy transition add temporarily parse_data, later Event_timed *et will be removed. Do slow transition because Event_timed is so tightly integrated that a front-attack by removing things from this class was unsuccessful. Do things step by step by eliminating dependencies. Hence, the code could be checked with the current test suite too (early testing) --- .bzrignore | 5 +++ sql/event_data_objects.cc | 89 +++++++++++++++++++++++++++++++++++++++ sql/event_data_objects.h | 9 ++-- sql/events.cc | 14 +++--- sql/events.h | 13 +++--- sql/share/errmsg.txt | 3 -- sql/sql_lex.h | 2 + sql/sql_parse.cc | 11 +++-- sql/sql_yacc.yy | 41 ++++++++++++++---- 9 files changed, 151 insertions(+), 36 deletions(-) diff --git a/.bzrignore b/.bzrignore index e07aa0cf86a..f9753c20fac 100644 --- a/.bzrignore +++ b/.bzrignore @@ -1778,3 +1778,8 @@ vio/viotest-sslconnect.cpp vio/viotest.cpp zlib/*.ds? zlib/*.vcproj +libmysql/viosocket.o.6WmSJk +libmysqld/event_data_objects.cc +libmysqld/event_db_repository.cc +libmysqld/event_queue.cc +server-tools/instance-manager/net_serv.cc diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index ebd45dd1a23..6d15ad66bcb 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -22,6 +22,95 @@ #include "sp_head.h" +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 diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h index 0c122211a9d..a9483465a8d 100644 --- a/sql/event_data_objects.h +++ b/sql/event_data_objects.h @@ -259,19 +259,16 @@ public: my_bool ends_null; my_bool execute_at_null; + sp_name *identifier; Item* item_expression; - Item* item_interval; longlong expression; interval_type interval; -// ulonglong created; -// ulonglong modified; - static Event_parse_data * new_instance(THD *thd); - Event_parse_data() {} - ~Event_parse_data() {} + Event_parse_data(); + ~Event_parse_data(); int init_definer(THD *thd); diff --git a/sql/events.cc b/sql/events.cc index 391ecd6ba16..679b571ff5c 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -904,8 +904,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; @@ -948,8 +948,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; @@ -1054,8 +1054,8 @@ done: */ int -Events::drop_event(THD *thd, Event_timed *et, bool drop_if_exists, - uint *rows_affected) +Events::drop_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, + bool drop_if_exists, uint *rows_affected) { int ret; @@ -1091,7 +1091,7 @@ 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); diff --git a/sql/events.h b/sql/events.h index 66cce6e7777..055c9a371a4 100644 --- a/sql/events.h +++ b/sql/events.h @@ -18,6 +18,7 @@ class Event_timed; +class Event_parse_data; class Events { @@ -47,16 +48,16 @@ public: }; static int - create_event(THD *thd, Event_timed *et, uint create_options, - uint *rows_affected); + create_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, + uint create_options, uint *rows_affected); static int - update_event(THD *thd, Event_timed *et, sp_name *new_name, - uint *rows_affected); + update_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, + sp_name *new_name, uint *rows_affected); static int - drop_event(THD *thd, Event_timed *et, bool drop_if_exists, - uint *rows_affected); + drop_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, + bool drop_if_exists, uint *rows_affected); static int open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index ac4f2dd9237..476bc2f2f02 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5839,6 +5839,3 @@ 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_lex.h b/sql/sql_lex.h index 15a94041d24..6e7456b61bc 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 3ebf700b715..bca140739f3 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3848,17 +3848,17 @@ end_with_restore_list: switch (lex->sql_command) { case SQLCOM_CREATE_EVENT: - res= Events::create_event(thd, lex->et, + res= Events::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::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); + res= Events::drop_event(thd, lex->et, lex->event_parse_data, + lex->drop_if_exists, &rows_affected); default:; } DBUG_PRINT("info", ("CREATE/ALTER/DROP returned error code=%d af_rows=%d", @@ -3880,7 +3880,6 @@ end_with_restore_list: 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)); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e9e00828b7e..a40500afa90 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1280,6 +1280,8 @@ create: 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,6 +1291,9 @@ create: $$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); + + lex->event_parse_data->identifier= $4; + if (!lex->et_compile_phase) { lex->et->init_name(YYTHD, $4); @@ -1344,6 +1349,8 @@ create: 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 +1372,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 +1403,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 +1411,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 +1422,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 +1455,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 +1480,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 +1489,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 +1501,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) { @@ -1518,6 +1536,8 @@ ev_sql_stmt: 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; @@ -1538,6 +1558,7 @@ ev_sql_stmt: 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); @@ -4728,6 +4749,10 @@ alter: } lex->spname= 0;//defensive programming + if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) + YYABORT; + Lex->event_parse_data->identifier= $3; + if (!(et= new (YYTHD->mem_root) Event_timed()))// implicitly calls Event_timed::init() YYABORT; lex->et = et; @@ -4739,9 +4764,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 ';'. */ $$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; @@ -7661,6 +7686,10 @@ drop: } | DROP EVENT_SYM if_exists sp_name { + if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) + YYABORT; + Lex->event_parse_data->identifier= $4; + LEX *lex=Lex; if (lex->et) @@ -8430,12 +8459,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; } ; From ef9a97e6856ec8128b66a47b124ce3c09f0fd1bc Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 27 Jun 2006 11:51:11 +0200 Subject: [PATCH 3/7] WL#3337 (Event scheduler new architecture) Third cut to simplify parsing phase. Now DROP EVENT works. Overloaded few functions to be able to use either sp_name or pass two LEX_STRINGs instead of a Event_timed pointer. This is transitional and eventually the old functions will be removed. For now DROP EVENT also works, does not need anymore a parsing object (Event_timed) and definer initialization because everyone who has EVENT_ACL can drop events, and this is checked on execution time in sql_parse.cc from the security context, as it should be. sql/event_data_objects.cc: overload few functions sql/event_scheduler.cc: Event_scheduler::drop_event() actually does not need Event_timed object but just an identifier, hence pass only sp_name. Overloaded Event_scheduler::find_event() to work with sp_name object. Eventually the old version will be removed. This is being done as transitional step to be able to test frequently code. sql/event_scheduler.h: Event_scheduler::drop_event() actually does not need Event_timed object but just an identifier, hence pass only sp_name. Overloaded Event_scheduler::find_event() to work with sp_name object. Eventually the old version will be removed. This is being done as transitional step to be able to test frequently code. sql/events.cc: Change db_drop_event() not to use Event_timed, either coming from parsing or from Event_timed::drop, but use LEX_STRINGs. sp_name is not convinient because in Event_timed::drop a temporary object has to be created. Hence, dereference the sp_name in Events::drop_event() and pass the LEX_STRINGs. sql/events.h: Change db_drop_event() not to use Event_timed, either coming from parsing or from Event_timed::drop, but use LEX_STRINGs. sp_name is not convinient because in Event_timed::drop a temporary object has to be created. Hence, dereference the sp_name in Events::drop_event() and pass the LEX_STRINGs. sql/events_priv.h: Change db_drop_event() not to use Event_timed, either coming from parsing or from Event_timed::drop, but use LEX_STRINGs. sp_name is not convinient because in Event_timed::drop a temporary object has to be created. Hence, dereference the sp_name in Events::drop_event() and pass the LEX_STRINGs. sql/sql_parse.cc: SQLCOM_DROP_EVENT does not need lex->event_parse_data object and is more like SQLCOM_SHOW_CREATE_EVENT. Therefore, move it to the block that handles the latter. sql/sql_yacc.yy: DROP EVENT does not need a parsing object, just a name. Store it as lex->spname. Pretty similar handling to the one of SHOW CREATE EVENT. --- sql/event_data_objects.cc | 60 +++++++++++++++++++++++++++++++++++++-- sql/event_scheduler.cc | 49 ++++++++++++++++++++++++++++++-- sql/event_scheduler.h | 6 +++- sql/events.cc | 27 ++++++++---------- sql/events.h | 5 ++-- sql/events_priv.h | 17 ++++++++++- sql/sql_parse.cc | 22 ++++++++++---- sql/sql_yacc.yy | 30 ++------------------ 8 files changed, 158 insertions(+), 58 deletions(-) diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 6d15ad66bcb..71739bf0f1c 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -1299,7 +1299,7 @@ Event_timed::drop(THD *thd) uint tmp= 0; DBUG_ENTER("Event_timed::drop"); - DBUG_RETURN(db_drop_event(thd, this, false, &tmp)); + DBUG_RETURN(db_drop_event(thd, dbname, name, false, &tmp)); } @@ -1903,8 +1903,62 @@ 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) && - event_timed_definer_equal(a, &b->definer); + event_timed_db_equal(a, &b->dbname); +} + + +/* + Checks whether two events have the same name + + SYNOPSIS + event_timed_name_equal() + + RETURN VALUE + TRUE names are equal + FALSE names are not equal +*/ + +bool +event_timed_name_equal(sp_name *name, LEX_STRING *event_name) +{ + 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); +} + + +/* + 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(sp_name *a, Event_timed *b) +{ + return event_timed_name_equal(a, &b->name) && + event_timed_db_equal(a, &b->dbname); } diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index fc2ad75b272..f83bc5f25a3 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -833,12 +833,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 +849,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(); @@ -1049,6 +1050,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 diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h index 5ae310bab2a..183ef450be2 100644 --- a/sql/event_scheduler.h +++ b/sql/event_scheduler.h @@ -16,6 +16,7 @@ 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 THD; @@ -73,7 +74,7 @@ public: LEX_STRING *new_name); bool - drop_event(THD *thd, Event_timed *et); + drop_event(THD *thd, sp_name *name); int @@ -136,6 +137,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(); diff --git a/sql/events.cc b/sql/events.cc index 679b571ff5c..c9d05994873 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -988,14 +988,15 @@ Events::update_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, !0 Error (my_error() called) */ -int db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists, - uint *rows_affected) +int db_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("db_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); @@ -1005,13 +1006,10 @@ int db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists, goto done; } - if (!(ret= evex_db_find_event_aux(thd, et, table))) + 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)); - goto done; - } } else if (ret == EVEX_KEY_NOT_FOUND) { @@ -1019,14 +1017,12 @@ int db_drop_event(THD *thd, Event_timed *et, bool 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); + "Event", name.str); ret= 0; } else - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str); - goto done; + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str); } - done: /* evex_drop_event() is used by Event_timed::drop therefore @@ -1044,7 +1040,7 @@ done: 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,16 +1050,17 @@ done: */ int -Events::drop_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, - 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_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); } diff --git a/sql/events.h b/sql/events.h index 055c9a371a4..d56a544493d 100644 --- a/sql/events.h +++ b/sql/events.h @@ -16,7 +16,7 @@ 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; @@ -56,8 +56,7 @@ public: sp_name *new_name, uint *rows_affected); static int - drop_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, - bool drop_if_exists, uint *rows_affected); + drop_event(THD *thd, sp_name *name, bool drop_if_exists, uint *rows_affected); static int open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); diff --git a/sql/events_priv.h b/sql/events_priv.h index ed02cb7055b..fe208ace280 100644 --- a/sql/events_priv.h +++ b/sql/events_priv.h @@ -25,6 +25,7 @@ #define EVEX_MAX_INTERVAL_VALUE 2147483647L class Event_timed; +class sp_name; int evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, @@ -32,7 +33,7 @@ evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, TABLE *table); int -db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists, +db_drop_event(THD *thd, LEX_STRING db, LEX_STRING name, bool drop_if_exists, uint *rows_affected); int db_find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl, @@ -68,6 +69,20 @@ bool event_timed_identifier_equal(Event_timed *a, Event_timed *b); + +/* Compares only the name part of the identifier */ +bool +event_timed_name_equal(sp_name *name, LEX_STRING *event_name); + +/* 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); + + bool change_security_context(THD *thd, LEX_STRING user, LEX_STRING host, LEX_STRING db, Security_context *s_ctx, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index bca140739f3..4f75339e5a2 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3819,7 +3819,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); @@ -3856,9 +3855,6 @@ end_with_restore_list: res= Events::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->event_parse_data, - lex->drop_if_exists, &rows_affected); default:; } DBUG_PRINT("info", ("CREATE/ALTER/DROP returned error code=%d af_rows=%d", @@ -3877,6 +3873,7 @@ end_with_restore_list: break; } + case SQLCOM_DROP_EVENT: case SQLCOM_SHOW_CREATE_EVENT: { DBUG_ASSERT(lex->spname); @@ -3893,9 +3890,24 @@ 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::show_create_event(thd, lex->spname); + else + { + uint rows_affected= 1; + if (end_active_trans(thd)) + { + res= -1; + break; + } + if (!(res= Events::drop_event(thd, lex->spname, lex->drop_if_exists, + &rows_affected))) + send_ok(thd, rows_affected); + } break; } #ifndef DBUG_OFF diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index a40500afa90..4dfabbf0c27 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -7686,33 +7686,9 @@ drop: } | DROP EVENT_SYM if_exists sp_name { - if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) - YYABORT; - Lex->event_parse_data->identifier= $4; - - 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 { From cace147c63a03b4dc9bd3e55ae3bda4f5e9db97b Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 27 Jun 2006 13:15:40 +0200 Subject: [PATCH 4/7] WL#3337 (Event scheduler new architecture) Fourth cut of refactoring the parsing. Next step will be to refactor of usage of Event_timed during Events::create_event() and Events::update_event(). Disallow: - CREATE EVENT ... DO CREATE EVENT ...; - ALTER EVENT ... DO CREATE EVENT ...; - CREATE EVENT ... DO ALTER EVENT DO ....; - CREATE PROCEDURE ... BEGIN CREATE EVENT ... END| Allowed: - 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| mysql-test/r/events.result: update results mysql-test/r/events_bugs.result: update results mysql-test/t/events.test: use number as error, mysql-test-run does not like the name. will come back to this later, now it's enough to pass the test. disable nested events / events in SP, when the nested event has a body. If no body is provided, namely DROP EVENT or ALTER that changes the characteristics, then these are allowed. mysql-test/t/events_bugs.test: use number as error, mysql-test-run does not like the name. will come back to this later, now it's enough to pass the test. disable nested events / events in SP, when the nested event has a body. If no body is provided, namely DROP EVENT or ALTER that changes the characteristics, then these are allowed. sql/share/errmsg.txt: new message sql/sql_yacc.yy: refactor CREATE EVENT parsing to fit into the structure CREATE DEFINER=xxx EVENT The actual definer part is not used, but parsed, for now. Disable nested CREATE EVENTS, CREATE EVENT inside CREATE PROCEDURE. And an event DDL with body inside an ALTER. 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| --- mysql-test/r/events.result | 16 ++- mysql-test/r/events_bugs.result | 13 +- mysql-test/t/events.test | 17 ++- mysql-test/t/events_bugs.test | 13 +- sql/share/errmsg.txt | 3 + sql/sql_yacc.yy | 222 +++++++++++++++++--------------- 6 files changed, 147 insertions(+), 137 deletions(-) diff --git a/mysql-test/r/events.result b/mysql-test/r/events.result index e115e077535..f95953cdc86 100644 --- a/mysql-test/r/events.result +++ b/mysql-test/r/events.result @@ -85,13 +85,25 @@ SHOW EVENTS; Db Name Definer Type Execute at Interval value Interval field Starts Ends Status events_test event_starts_test root@localhost RECURRING NULL 20 SECOND # # ENABLED DROP EVENT event_starts_test; +create table test_nested(a int); create event e_43 on schedule every 1 second do set @a = 5; set global event_scheduler = 1; alter event e_43 do alter event e_43 do set @a = 4; +ERROR HY000: Recursivity of EVENT DDL statements is forbidden when body is present +alter event e_43 do +begin +alter event e_43 on schedule every 5 minute; +insert into test_nested values(1); +end| +set global event_scheduler = 1; select db, name, body, status, interval_field, interval_value from mysql.event; db name body status interval_field interval_value -events_test e_43 set @a = 4 ENABLED SECOND 1 +events_test e_43 begin +alter event e_43 on schedule every 5 minute; +insert into test_nested values(1); +end ENABLED MINUTE 5 drop event e_43; +drop table test_nested; "Let's check whether we can use non-qualified names" create table non_qualif(a int); create event non_qualif_ev on schedule every 10 minute do insert into non_qualif values (800219); @@ -358,7 +370,7 @@ root localhost events_test Connect User lock select get_lock("test_lock2_1", 20) drop event закачка21; create table t_16 (s1 int); create trigger t_16_bi before insert on t_16 for each row create event e_16 on schedule every 1 second do set @a=5; -ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger. +ERROR HY000: Recursivity of EVENT DDL statements is forbidden when body is present drop table t_16; create event white_space on schedule every 10 hour diff --git a/mysql-test/r/events_bugs.result b/mysql-test/r/events_bugs.result index a7c2964a253..e1f8551c2fd 100644 --- a/mysql-test/r/events_bugs.result +++ b/mysql-test/r/events_bugs.result @@ -17,18 +17,7 @@ DROP EVENT ДОЛЕН_регистър_утф8; SET NAMES latin1; set @a=3; CREATE PROCEDURE p_16 () CREATE EVENT e_16 ON SCHEDULE EVERY @a SECOND DO SET @a=5; -call p_16(); -"Here we used to crash!" -call p_16(); -ERROR HY000: Event 'e_16' already exists -call p_16(); -ERROR HY000: Event 'e_16' already exists -DROP EVENT e_16; -CALL p_16(); -CALL p_16(); -ERROR HY000: Event 'e_16' already exists -DROP PROCEDURE p_16; -DROP EVENT e_16; +ERROR HY000: Recursivity of EVENT DDL statements is forbidden when body is present create event e_55 on schedule at 99990101000000 do drop table t; ERROR HY000: Incorrect AT value: '99990101000000' create event e_55 on schedule every 10 hour starts 99990101000000 do drop table t; diff --git a/mysql-test/t/events.test b/mysql-test/t/events.test index aac13a55dd3..1940016a7a1 100644 --- a/mysql-test/t/events.test +++ b/mysql-test/t/events.test @@ -81,14 +81,23 @@ SHOW EVENTS; DROP EVENT event_starts_test; # # +create table test_nested(a int); create event e_43 on schedule every 1 second do set @a = 5; set global event_scheduler = 1; ---sleep 2 +--error 1562 alter event e_43 do alter event e_43 do set @a = 4; ---sleep 2 +delimiter |; +alter event e_43 do +begin + alter event e_43 on schedule every 5 minute; + insert into test_nested values(1); +end| +delimiter ;| +set global event_scheduler = 1; +--sleep 1 select db, name, body, status, interval_field, interval_value from mysql.event; drop event e_43; ---sleep 1 +drop table test_nested; --echo "Let's check whether we can use non-qualified names" create table non_qualif(a int); @@ -315,7 +324,7 @@ drop event закачка21; # Bug #16410 Events: CREATE EVENT is legal in a CREATE TRIGGER statement # create table t_16 (s1 int); ---error 1422 +--error 1562 create trigger t_16_bi before insert on t_16 for each row create event e_16 on schedule every 1 second do set @a=5; drop table t_16; # diff --git a/mysql-test/t/events_bugs.test b/mysql-test/t/events_bugs.test index 9434de7be7d..e07707e7734 100644 --- a/mysql-test/t/events_bugs.test +++ b/mysql-test/t/events_bugs.test @@ -30,19 +30,8 @@ SET NAMES latin1; # START - BUG#16408: Events: crash for an event in a procedure # set @a=3; +--error 1562 CREATE PROCEDURE p_16 () CREATE EVENT e_16 ON SCHEDULE EVERY @a SECOND DO SET @a=5; -call p_16(); ---echo "Here we used to crash!" ---error ER_EVENT_ALREADY_EXISTS -call p_16(); ---error ER_EVENT_ALREADY_EXISTS -call p_16(); -DROP EVENT e_16; -CALL p_16(); ---error ER_EVENT_ALREADY_EXISTS -CALL p_16(); -DROP PROCEDURE p_16; -DROP EVENT e_16; # # END - BUG#16408: Events: crash for an event in a procedure # 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_yacc.yy b/sql/sql_yacc.yy index 4dfabbf0c27..23201a81f88 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -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,78 +1258,13 @@ create: lex->name=$4.str; lex->create_info.options=$3; } - | CREATE EVENT_SYM opt_if_not_exists sp_name - /* - BE CAREFUL when you add a new rule to update the block where - YYTHD->client_capabilities is set back to original value - */ - { - LEX *lex=Lex; - - if (lex->et) - { - /* - Recursive events are not possible because recursive SPs - are not also possible. lex->sp_head is not stacked. - */ - // ToDo Andrey : Change the error message - my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT"); - YYABORT; - } - - lex->create_info.options= $3; - - 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 - stored procedure, otherwise yylex will chop it into pieces - at each ';'. - */ - $$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; - YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); - - - lex->event_parse_data->identifier= $4; - - if (!lex->et_compile_phase) - { - lex->et->init_name(YYTHD, $4); - lex->et->init_definer(YYTHD); - } - } - ON SCHEDULE_SYM ev_schedule_time - opt_ev_on_completion - opt_ev_status - opt_ev_comment - DO_SYM ev_sql_stmt - { - /* - Restore flag if it was cleared above - $1 - CREATE - $2 - EVENT_SYM - $3 - opt_if_not_exists - $4 - sp_name - $5 - the block above - */ - YYTHD->client_capabilities |= $5; - - /* - sql_command is set here because some rules in ev_sql_stmt - can overwrite it - */ - 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 + view_or_trigger_or_sp_or_event {} | CREATE USER clear_privileges grant_list { @@ -1347,6 +1283,72 @@ create: ; +event_tail: + EVENT_SYM opt_if_not_exists sp_name + /* + BE CAREFUL when you add a new rule to update the block where + YYTHD->client_capabilities is set back to original value + */ + { + LEX *lex=Lex; + + if (lex->et) + { + /* + Recursive CREATE EVENT statement are not possible because + recursive SPs are not also possible. lex->sp_head is not stacked. + */ + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT"); + YYABORT; + } + + lex->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 + stored procedure, otherwise yylex will chop it into pieces + at each ';'. + */ + $$= 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, $3); + lex->et->init_definer(YYTHD); + } + } + ON SCHEDULE_SYM ev_schedule_time + opt_ev_on_completion + opt_ev_status + opt_ev_comment + DO_SYM ev_sql_stmt + { + /* + Restore flag if it was cleared above + $1 - EVENT_SYM + $2 - opt_if_not_exists + $3 - sp_name + $4 - the block above + */ + YYTHD->client_capabilities |= $4; + + /* + sql_command is set here because some rules in ev_sql_stmt + can overwrite it + */ + Lex->sql_command= SQLCOM_CREATE_EVENT; + } + + ev_schedule_time: EVERY_SYM expr interval { Lex->event_parse_data->item_expression= $2; @@ -1517,25 +1519,41 @@ ev_sql_stmt: LEX *lex= Lex; sp_head *sp; - $$= 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); - - sp->m_type= TYPE_ENUM_PROCEDURE; - - lex->sphead= sp; - - bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); - lex->sphead->m_chistics= &lex->sp_chistics; - - lex->sphead->m_body_begin= lex->ptr; + my_error(ER_EVENT_RECURSIVITY_FORBIDDEN, MYF(0)); + YYABORT; } + + if (!(lex->sphead= new sp_head())) + YYABORT; + + lex->sphead->reset_thd_mem_root(YYTHD); + lex->sphead->init(lex); + + lex->sphead->m_type= TYPE_ENUM_PROCEDURE; + + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + lex->sphead->m_chistics= &lex->sp_chistics; + + lex->sphead->m_body_begin= lex->ptr; Lex->event_parse_data->body_begin= lex->ptr; @@ -1546,18 +1564,15 @@ ev_sql_stmt: { LEX *lex=Lex; - if (!$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) { @@ -4738,16 +4753,7 @@ 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"); - YYABORT; - } - lex->spname= 0;//defensive programming + lex->spname= NULL; if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) YYABORT; @@ -10767,20 +10773,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 + {} ; /************************************************************************** From acefb78bc3fbf28376d8713e1dc9f056dc3cdbf6 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 28 Jun 2006 01:28:03 +0200 Subject: [PATCH 5/7] WL#3337 (Events new architecture) 5th cut, moved DB related code to Event_db_repository and updated accordingly the remanining code. Moved change/restore_security_context() to class THD Removed events_priv.h Next step is to reorganize create/update_event() and parsing for them. But probably some other refactoring could be done in the meanwhile. The changes so far pass the test suite. BitKeeper/deleted/.del-events_priv.h~2e8bce2cf35997df: Delete: sql/events_priv.h sql/Makefile.am: events_priv.h is no more sql/event_data_objects.cc: reorganize events code sql/event_data_objects.h: reorganize events code sql/event_db_repository.cc: reorganize events code sql/event_db_repository.h: reorganize events code sql/event_scheduler.cc: reorganize events code sql/event_scheduler.h: reorganize events code sql/events.cc: reorganize events code sql/events.h: reorganize events code sql/mysqld.cc: reorganize events code sql/set_var.cc: reorganize events code sql/sql_class.cc: add ::change_security_context() and restore_security_context() sql/sql_class.h: add ::change_security_context() and restore_security_context() sql/sql_db.cc: reorganize Events code sql/sql_parse.cc: reorganize Events code sql/sql_show.cc: reorganize Events code --- sql/Makefile.am | 2 +- sql/event_data_objects.cc | 88 ++-- sql/event_data_objects.h | 32 ++ sql/event_db_repository.cc | 854 +++++++++++++++++++++++++++++++++++++ sql/event_db_repository.h | 82 ++++ sql/event_scheduler.cc | 91 +--- sql/event_scheduler.h | 37 +- sql/events.cc | 812 ++--------------------------------- sql/events.h | 115 ++--- sql/events_priv.h | 94 ---- sql/mysqld.cc | 2 +- sql/set_var.cc | 2 +- sql/sql_class.cc | 57 +++ sql/sql_class.h | 8 + sql/sql_db.cc | 2 +- sql/sql_parse.cc | 20 +- sql/sql_show.cc | 4 +- 17 files changed, 1220 insertions(+), 1082 deletions(-) delete mode 100644 sql/events_priv.h diff --git a/sql/Makefile.am b/sql/Makefile.am index 29645302ecc..49f85b3921d 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -64,7 +64,7 @@ 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_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 \ diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 71739bf0f1c..4b72c7e55a7 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -16,12 +16,15 @@ #define MYSQL_LEX 1 #include "mysql_priv.h" -#include "events_priv.h" #include "events.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) { @@ -733,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); @@ -772,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; @@ -800,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)); @@ -823,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: @@ -1277,6 +1280,7 @@ Event_timed::mark_last_executed(THD *thd) } + /* Drops the event @@ -1299,7 +1303,8 @@ Event_timed::drop(THD *thd) uint tmp= 0; DBUG_ENTER("Event_timed::drop"); - DBUG_RETURN(db_drop_event(thd, dbname, name, false, &tmp)); + DBUG_RETURN(Events::get_instance()-> + db_repository.drop_event(thd, dbname, name, false, &tmp)); } @@ -1336,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; @@ -1352,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; } @@ -1630,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; @@ -1669,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; @@ -1870,23 +1875,6 @@ event_timed_db_equal(Event_timed *et, LEX_STRING *db) } -/* - Checks whether two events have the same definer - - SYNOPSIS - event_timed_definer_equal() - - Returns - TRUE definers are equal - FALSE definers are not equal -*/ - -bool -event_timed_definer_equal(Event_timed *et, LEX_STRING *definer) -{ - return !sortcmp_lex_string(et->definer, *definer, system_charset_info); -} - /* Checks whether two events are equal by identifiers diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h index a9483465a8d..ff547cebd5b 100644 --- a/sql/event_data_objects.h +++ b/sql/event_data_objects.h @@ -40,9 +40,35 @@ #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 { Event_timed(const Event_timed &); /* Prevent use of these */ @@ -296,4 +322,10 @@ public: }; + +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 index 547e1e6887b..249910c5f50 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -17,3 +17,857 @@ #include "mysql_priv.h" #include "event_db_repository.h" #include "event_data_objects.h" +#include "sp_head.h" +#include "sp.h" +#include "events.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); +} + + +/* + 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 +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 (Events::get_instance()->db_repository.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 index d8a8784089e..c0cfccf215a 100644 --- a/sql/event_db_repository.h +++ b/sql/event_db_repository.h @@ -16,5 +16,87 @@ 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); + +class Event_queue_element; + +class Event_db_repository +{ +public: + Event_db_repository(){} + ~Event_db_repository(){} + + int + init_repository(); + + void + deinit_repository(); + + int + open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); + + 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); +private: + + int + drop_events_by_field(THD *thd, enum enum_events_table_field field, + LEX_STRING field_value); + + 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_scheduler.cc b/sql/event_scheduler.cc index f83bc5f25a3..60c9cda0a56 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_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")); @@ -904,12 +905,12 @@ Event_scheduler::drop_event(THD *thd, sp_name *name) 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; @@ -947,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")); @@ -961,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 ??? @@ -1111,11 +1112,11 @@ Event_scheduler::find_event(sp_name *name, 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()) { @@ -1124,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 @@ -1179,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"); @@ -1187,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); @@ -1713,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; @@ -1778,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) { @@ -2116,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); -} /* @@ -2212,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= Events::get_instance()->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 183ef450be2..bd099d10839 100644 --- a/sql/event_scheduler.h +++ b/sql/event_scheduler.h @@ -18,6 +18,7 @@ class sp_name; class Event_timed; +class Event_db_repository; class THD; typedef bool * (*event_timed_identifier_comparator)(Event_timed*, Event_timed*); @@ -31,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 { @@ -66,10 +56,10 @@ 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); @@ -78,10 +68,10 @@ public: 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 @@ -92,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(); @@ -156,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 @@ -230,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 c9d05994873..52e814043d8 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_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. @@ -188,6 +89,14 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) } +Events * +Events::get_instance() +{ + DBUG_ENTER("Events::get_instance"); + DBUG_RETURN(&singleton); +} + + /* Reconstructs interval expression from interval type and expression value that is in form of a value of the smalles entity: @@ -207,9 +116,8 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) */ 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 +249,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); } @@ -913,9 +283,10 @@ Events::create_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, 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() && @@ -960,7 +331,7 @@ Events::update_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, 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() && @@ -973,67 +344,6 @@ Events::update_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, } -/* - 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, LEX_STRING db, LEX_STRING name, - bool drop_if_exists, uint *rows_affected) -{ - TABLE *table; - Open_tables_state backup; - int ret; - - DBUG_ENTER("db_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 (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_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); -} - - /* Drops an event @@ -1056,14 +366,14 @@ Events::drop_event(THD *thd, sp_name *name, bool drop_if_exists, int ret; DBUG_ENTER("Events::drop_event"); - if (!(ret= db_drop_event(thd, name->m_db, name->m_name, 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, name))) my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret); } - DBUG_RETURN(ret); } @@ -1092,7 +402,7 @@ Events::show_create_event(THD *thd, sp_name *spn) 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) @@ -1164,75 +474,13 @@ 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. @@ -1251,14 +499,17 @@ int Events::init() { int ret= 0; + Event_db_repository *db_repo; DBUG_ENTER("Events::init"); + db_repo= &get_instance()->db_repository; + db_repo->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_repo) || (opt_event_scheduler == 1? scheduler->start(): scheduler->start_suspended())); } @@ -1270,22 +521,23 @@ 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()) { scheduler->stop(); scheduler->destroy(); } + get_instance()->db_repository.deinit_repository(); DBUG_VOID_RETURN; } diff --git a/sql/events.h b/sql/events.h index d56a544493d..339f87c3806 100644 --- a/sql/events.h +++ b/sql/events.h @@ -20,65 +20,42 @@ class sp_name; class Event_timed; class Event_parse_data; +#include "event_db_repository.h" + +/* 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 :) */ - }; - - static int - create_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, - uint create_options, uint *rows_affected); - - static int - update_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, - sp_name *new_name, uint *rows_affected); - - static int - drop_event(THD *thd, sp_name *name, bool drop_if_exists, uint *rows_affected); - - static int - open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); - - static int - show_create_event(THD *thd, sp_name *spn); - - static int - reconstruct_interval_expression(String *buf, interval_type interval, - longlong expression); - - static int - drop_schema_events(THD *thd, char *db); - - static int - dump_internal_status(THD *thd); - static int init(); static void - shutdown(); + deinit(); static void init_mutexes(); @@ -86,8 +63,48 @@ public: static void destroy_mutexes(); + static Events* + get_instance(); + + int + create_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, + uint create_options, uint *rows_affected); + + 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 + open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); + + 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); + + int + drop_schema_events(THD *thd, char *db); + + int + dump_internal_status(THD *thd); + + 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 fe208ace280..00000000000 --- a/sql/events_priv.h +++ /dev/null @@ -1,94 +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; -class sp_name; - -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, LEX_STRING db, LEX_STRING name, 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); - - - -/* Compares only the name part of the identifier */ -bool -event_timed_name_equal(sp_name *name, LEX_STRING *event_name); - -/* 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); - - -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 3f93de00594..a82f66acd19 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -886,7 +886,7 @@ static void close_connections(void) } (void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list - Events::shutdown(); + Events::deinit(); end_slave(); if (thread_count) diff --git a/sql/set_var.cc b/sql/set_var.cc index e6b0625f097..98bcf3be6b2 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -3891,7 +3891,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/sql_class.cc b/sql/sql_class.cc index 23603afc038..97fc6feb5f7 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2048,6 +2048,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 fa4a29ff112..191ecc2ab18 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_parse.cc b/sql/sql_parse.cc index 4f75339e5a2..0d52bc41a48 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3847,13 +3847,14 @@ end_with_restore_list: switch (lex->sql_command) { case SQLCOM_CREATE_EVENT: - res= Events::create_event(thd, lex->et, lex->event_parse_data, - (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->event_parse_data, - lex->spname, &rows_affected); + res= Events::get_instance()-> + update_event(thd, lex->et, lex->event_parse_data, + lex->spname, &rows_affected); break; default:; } @@ -3895,7 +3896,7 @@ end_with_restore_list: } if (lex->sql_command == SQLCOM_SHOW_CREATE_EVENT) - res= Events::show_create_event(thd, lex->spname); + res= Events::get_instance()->show_create_event(thd, lex->spname); else { uint rows_affected= 1; @@ -3904,8 +3905,9 @@ end_with_restore_list: res= -1; break; } - if (!(res= Events::drop_event(thd, lex->spname, lex->drop_if_exists, - &rows_affected))) + if (!(res= Events::get_instance()->drop_event(thd, lex->spname, + lex->drop_if_exists, + &rows_affected))) send_ok(thd, rows_affected); } break; @@ -3913,7 +3915,7 @@ end_with_restore_list: #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 33fa9758c6c..77906973a38 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -4324,7 +4324,7 @@ int events_table_index_read_for_db(THD *thd, TABLE *schema_table, DBUG_PRINT("info", ("Using prefix scanning on PK")); event_table->file->ha_index_init(0, 1); - event_table->field[Events::FIELD_DB]-> + event_table->field[ET_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; @@ -4443,7 +4443,7 @@ int fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) thd->lex->select_lex.db:"(null)")); thd->reset_n_backup_open_tables_state(&backup); - if (Events::open_event_table(thd, TL_READ, &event_table)) + if (Events::get_instance()->open_event_table(thd, TL_READ, &event_table)) { sql_print_error("Table mysql.event is damaged."); thd->restore_backup_open_tables_state(&backup); From 8ca78787a54f5d86f23147ca734e8e167a56b7b4 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 28 Jun 2006 14:22:14 +0200 Subject: [PATCH 6/7] WL#3337 (Events new architecture) Cut number 6. Move code from sql_show.cc to event_db_repository.cc that more belongs to the latter. sql/event_db_repository.cc: move code that works with mysql.event from sql_show.cc to event_db_repository.cc . Route through Event_db_repository's interface which is proxied by class Events. The code relies on a function from sql_show.cc which does the actual storage in the schema table. I think it's better to leave the function there because the structure of I_S.EVENTS is defined in sql_show.cc sql/event_db_repository.h: I_S / SHOW EVENTS handling hooks sql/event_scheduler.cc: use the pointer to db_repository which Event_scheduler already has sql/events.cc: Put a comment to get_instance sql/events.h: callback for I_S (sql_show.cc) sql/sql_show.cc: move code that belongs more to Event_db_repository than to here. Use a callback of class Events. Only 1 function is left here, because it copies data into the actual rows of I_S.EVENTS and belongs to this file. sql/sql_show.h: export this function will be called from event_db_repository.cc --- sql/event_db_repository.cc | 173 ++++++++++++++++++++++++++++++++++- sql/event_db_repository.h | 28 +++++- sql/event_scheduler.cc | 2 +- sql/events.cc | 47 +++++++++- sql/events.h | 3 + sql/sql_show.cc | 181 +------------------------------------ sql/sql_show.h | 2 + 7 files changed, 247 insertions(+), 189 deletions(-) diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 249910c5f50..96d2b6c957f 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -20,6 +20,7 @@ #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 @@ -188,8 +189,7 @@ evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update) 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_INTERVAL_EXPR]->store((longlong)et->expression, true); table->field[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull(); /* @@ -276,16 +276,181 @@ evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, } +/* + 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 - db_find_event() + 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 @@ -308,7 +473,7 @@ Event_db_repository::find_event(THD *thd, sp_name *name, Event_timed **ett, if (tbl) table= tbl; - else if (Events::get_instance()->db_repository.open_event_table(thd, TL_READ, &table)) + else if (open_event_table(thd, TL_READ, &table)) { my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); ret= EVEX_GENERAL_ERROR; diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h index c0cfccf215a..1cbee96b68f 100644 --- a/sql/event_db_repository.h +++ b/sql/event_db_repository.h @@ -43,6 +43,17 @@ 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 @@ -57,9 +68,6 @@ public: void deinit_repository(); - int - open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); - int create_event(THD *thd, Event_timed *et, my_bool create_if_not, uint *rows_affected); @@ -86,11 +94,23 @@ public: int find_event_by_name(THD *thd, LEX_STRING db, LEX_STRING name, TABLE *table); -private: + 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; diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 60c9cda0a56..cb500de53b9 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -2159,7 +2159,7 @@ Event_scheduler::load_events_from_db(THD *thd) DBUG_RETURN(EVEX_GENERAL_ERROR); } - if ((ret= Events::get_instance()->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/events.cc b/sql/events.cc index 52e814043d8..a5af196af2a 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -88,6 +88,15 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) (unsigned char *) t.str,t.length, 0); } +/* + Accessor for the singleton instance. + + SYNOPSIS + Events::get_instance() + + RETURN VALUE + address +*/ Events * Events::get_instance() @@ -110,7 +119,7 @@ Events::get_instance() interval - the interval type (for instance YEAR_MONTH) expression - the value in the lowest entity - RETURNS + RETURN VALUE 0 - OK 1 - Error */ @@ -562,6 +571,42 @@ Events::dump_internal_status(THD *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)); +} + + /* Inits Events mutexes diff --git a/sql/events.h b/sql/events.h index 339f87c3806..3a7d1f8bc5e 100644 --- a/sql/events.h +++ b/sql/events.h @@ -90,6 +90,9 @@ public: int drop_schema_events(THD *thd, char *db); + + static int + fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */); int dump_internal_status(THD *thd); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 77906973a38..bf69d3a0986 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -4160,7 +4160,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; @@ -4295,183 +4295,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[ET_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->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); - } - - 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::get_instance()->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->orig_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"); @@ -5568,7 +5391,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 */ From 400276c2f577f63ffd3871a91bce207f65d5e682 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 28 Jun 2006 15:14:05 +0200 Subject: [PATCH 7/7] WL#3337 (Events new architecture) Cut 7 (refactoring) db_repository is no more embedded in the Events singleton. Therefore a change to Events_db_repository won't mean recompile of all files in the server which include events.h sql/event_data_objects.cc: db_repository is no more embedded in the Events singleton. Therefore a change to Events_db_repository won't mean recompile of all files in the server which include events.h sql/event_db_repository.cc: db_repository is no more embedded in the Events singleton. Therefore a change to Events_db_repository won't mean recompile of all files in the server which include events.h sql/events.cc: db_repository is no more embedded in the Events singleton. Therefore a change to Events_db_repository won't mean recompile of all files in the server which include events.h sql/events.h: db_repository is no more embedded in the Events singleton. Therefore a change to Events_db_repository won't mean recompile of all files in the server which include events.h sql/mysqld.cc: db_repository is no more embedded in the Events singleton. Therefore a change to Events_db_repository won't mean recompile of all files in the server which include events.h --- sql/event_data_objects.cc | 2 +- sql/event_db_repository.cc | 2 +- sql/events.cc | 84 ++++++++++++++++++++------------------ sql/events.h | 20 ++++----- sql/mysqld.cc | 8 ++-- 5 files changed, 59 insertions(+), 57 deletions(-) diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 4b72c7e55a7..228f1a4ff4a 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -1304,7 +1304,7 @@ Event_timed::drop(THD *thd) DBUG_ENTER("Event_timed::drop"); DBUG_RETURN(Events::get_instance()-> - db_repository.drop_event(thd, dbname, name, false, &tmp)); + db_repository->drop_event(thd, dbname, name, false, &tmp)); } diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 96d2b6c957f..fc771b6bac3 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -271,7 +271,7 @@ 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. + return Events::get_instance()->db_repository-> find_event_by_name(thd, dbname, ev_name, table); } diff --git a/sql/events.cc b/sql/events.cc index a5af196af2a..21da9a2fd8a 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -258,7 +258,7 @@ int Events::open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table) { - return db_repository.open_event_table(thd, lock_type, table); + return db_repository->open_event_table(thd, lock_type, table); } @@ -292,7 +292,7 @@ Events::create_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, DBUG_PRINT("enter", ("name: %*s options:%d", et->name.length, et->name.str, create_options)); - if (!(ret= db_repository. + if (!(ret= db_repository-> create_event(thd, et, create_options & HA_LEX_CREATE_IF_NOT_EXISTS, rows_affected))) @@ -340,7 +340,7 @@ Events::update_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, 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_repository.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() && @@ -376,7 +376,7 @@ Events::drop_event(THD *thd, sp_name *name, bool drop_if_exists, DBUG_ENTER("Events::drop_event"); - if (!(ret= db_repository.drop_event(thd, name->m_db, name->m_name, + if (!(ret= db_repository->drop_event(thd, name->m_db, name->m_name, drop_if_exists, rows_affected))) { Event_scheduler *scheduler= Event_scheduler::get_instance(); @@ -411,7 +411,7 @@ Events::show_create_event(THD *thd, sp_name *spn) DBUG_PRINT("enter", ("name: %*s", spn->m_name.length, spn->m_name.str)); thd->reset_n_backup_open_tables_state(&backup); - ret= db_repository.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) @@ -484,7 +484,7 @@ Events::drop_schema_events(THD *thd, char *db) Event_scheduler *scheduler= Event_scheduler::get_instance(); ret= scheduler->drop_schema_events(thd, db_lex); - ret= db_repository.drop_schema_events(thd, db_lex); + ret= db_repository->drop_schema_events(thd, db_lex); DBUG_RETURN(ret); } @@ -510,15 +510,14 @@ Events::init() int ret= 0; Event_db_repository *db_repo; DBUG_ENTER("Events::init"); - db_repo= &get_instance()->db_repository; - db_repo->init_repository(); + 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(db_repo) || + DBUG_RETURN(scheduler->init(db_repository) || (opt_event_scheduler == 1? scheduler->start(): scheduler->start_suspended())); } @@ -540,18 +539,52 @@ void Events::deinit() { DBUG_ENTER("Events::deinit"); + Event_scheduler *scheduler= Event_scheduler::get_instance(); if (scheduler->initialized()) { scheduler->stop(); scheduler->destroy(); } - get_instance()->db_repository.deinit_repository(); + + db_repository->deinit_repository(); DBUG_VOID_RETURN; } +/* + Inits Events mutexes + + SYNOPSIS + Events::init_mutexes() + thd Thread +*/ + +void +Events::init_mutexes() +{ + db_repository= new Event_db_repository; + Event_scheduler::init_mutexes(); +} + + +/* + Destroys Events mutexes + + SYNOPSIS + Events::destroy_mutexes() +*/ + +void +Events::destroy_mutexes() +{ + Event_scheduler::destroy_mutexes(); + delete db_repository; + db_repository= NULL; +} + + /* Proxy for Event_scheduler::dump_internal_status @@ -603,34 +636,5 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) DBUG_RETURN(1); db= thd->lex->select_lex.db; } - DBUG_RETURN(get_instance()->db_repository.fill_schema_events(thd, tables, db)); -} - - -/* - Inits Events mutexes - - SYNOPSIS - Events::init_mutexes() - thd Thread -*/ - -void -Events::init_mutexes() -{ - Event_scheduler::init_mutexes(); -} - - -/* - Destroys Events mutexes - - SYNOPSIS - Events::destroy_mutexes() -*/ - -void -Events::destroy_mutexes() -{ - Event_scheduler::destroy_mutexes(); + DBUG_RETURN(get_instance()->db_repository->fill_schema_events(thd, tables, db)); } diff --git a/sql/events.h b/sql/events.h index 3a7d1f8bc5e..45a0db13980 100644 --- a/sql/events.h +++ b/sql/events.h @@ -19,8 +19,7 @@ class sp_name; class Event_timed; class Event_parse_data; - -#include "event_db_repository.h" +class Event_db_repository; /* Return codes */ enum enum_events_error_code @@ -51,16 +50,16 @@ public: static ulong opt_event_scheduler; static TYPELIB opt_typelib; - static int + int init(); - static void + void deinit(); - static void + void init_mutexes(); - static void + void destroy_mutexes(); static Events* @@ -77,6 +76,9 @@ public: 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); @@ -88,16 +90,13 @@ public: reconstruct_interval_expression(String *buf, interval_type interval, longlong expression); - int - drop_schema_events(THD *thd, char *db); - static int fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */); int dump_internal_status(THD *thd); - Event_db_repository db_repository; + Event_db_repository *db_repository; private: /* Singleton DP is used */ @@ -107,7 +106,6 @@ private: /* Singleton instance */ static Events singleton; - /* Prevent use of these */ Events(const Events &); void operator=(Events &); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a82f66acd19..099779f91d0 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -886,7 +886,7 @@ static void close_connections(void) } (void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list - Events::deinit(); + 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();