diff options
author | unknown <andrey@lmy004.> | 2006-06-28 15:54:09 +0200 |
---|---|---|
committer | unknown <andrey@lmy004.> | 2006-06-28 15:54:09 +0200 |
commit | 98211beaee1cffd0ecf28a2157fbb0de3692cc3e (patch) | |
tree | eca43a35436b43e490c1554f13bbcd02616e7fa2 /sql | |
parent | 57f3f2568ee27b30f0062b0175faf9f0a1a854e1 (diff) | |
parent | 400276c2f577f63ffd3871a91bce207f65d5e682 (diff) | |
download | mariadb-git-98211beaee1cffd0ecf28a2157fbb0de3692cc3e.tar.gz |
Merge
sql/CMakeLists.txt:
Auto merged
sql/events.cc:
Auto merged
sql/mysqld.cc:
Auto merged
sql/set_var.cc:
Auto merged
sql/sql_class.cc:
Auto merged
sql/sql_class.h:
Auto merged
sql/sql_lex.h:
Auto merged
sql/sql_parse.cc:
Auto merged
sql/sql_yacc.yy:
Auto merged
sql/sql_show.cc:
SCCS merged
Diffstat (limited to 'sql')
-rw-r--r-- | sql/CMakeLists.txt | 3 | ||||
-rw-r--r-- | sql/Makefile.am | 8 | ||||
-rw-r--r-- | sql/event_data_objects.cc (renamed from sql/event_timed.cc) | 219 | ||||
-rw-r--r-- | sql/event_data_objects.h (renamed from sql/event_timed.h) | 122 | ||||
-rw-r--r-- | sql/event_db_repository.cc | 1038 | ||||
-rw-r--r-- | sql/event_db_repository.h | 122 | ||||
-rw-r--r-- | sql/event_queue.cc | 19 | ||||
-rw-r--r-- | sql/event_queue.h | 20 | ||||
-rw-r--r-- | sql/event_scheduler.cc | 142 | ||||
-rw-r--r-- | sql/event_scheduler.h | 43 | ||||
-rw-r--r-- | sql/events.cc | 924 | ||||
-rw-r--r-- | sql/events.h | 110 | ||||
-rw-r--r-- | sql/events_priv.h | 79 | ||||
-rw-r--r-- | sql/mysqld.cc | 8 | ||||
-rw-r--r-- | sql/set_var.cc | 2 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 3 | ||||
-rw-r--r-- | sql/sql_class.cc | 57 | ||||
-rw-r--r-- | sql/sql_class.h | 8 | ||||
-rw-r--r-- | sql/sql_db.cc | 2 | ||||
-rw-r--r-- | sql/sql_lex.h | 2 | ||||
-rw-r--r-- | sql/sql_parse.cc | 39 | ||||
-rw-r--r-- | sql/sql_show.cc | 183 | ||||
-rw-r--r-- | sql/sql_show.h | 2 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 217 |
24 files changed, 1980 insertions, 1392 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 9a97b79813b..58f3fb4409e 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -51,7 +51,8 @@ ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.cc sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc time.cc tztime.cc uniques.cc unireg.cc item_xmlfunc.cc - rpl_tblmap.cc sql_binlog.cc event_scheduler.cc event_timed.cc + rpl_tblmap.cc sql_binlog.cc event_scheduler.cc event_data_objects.cc + event_queue.cc event_db_repository.cc sql_tablespace.cc events.cc ../sql-common/my_user.c partition_info.cc rpl_injector.cc ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc diff --git a/sql/Makefile.am b/sql/Makefile.am index 387f18c2ae9..49f85b3921d 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -64,8 +64,9 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ tztime.h my_decimal.h\ sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \ parse_file.h sql_view.h sql_trigger.h \ - sql_array.h sql_cursor.h events.h events_priv.h \ - sql_plugin.h authors.h sql_partition.h event_timed.h \ + sql_array.h sql_cursor.h events.h \ + sql_plugin.h authors.h sql_partition.h event_data_objects.h \ + event_queue.h event_db_repository.h \ partition_info.h partition_element.h event_scheduler.h \ contributors.h mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ @@ -104,7 +105,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ tztime.cc my_time.c my_user.c my_decimal.cc\ sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \ sp_cache.cc parse_file.cc sql_trigger.cc \ - event_scheduler.cc events.cc event_timed.cc \ + event_scheduler.cc events.cc event_data_objects.cc \ + event_queue.cc event_db_repository.cc \ sql_plugin.cc sql_binlog.cc \ sql_builtin.cc sql_tablespace.cc partition_info.cc diff --git a/sql/event_timed.cc b/sql/event_data_objects.cc index 4ec875f32a3..228f1a4ff4a 100644 --- a/sql/event_timed.cc +++ b/sql/event_data_objects.cc @@ -16,12 +16,104 @@ #define MYSQL_LEX 1 #include "mysql_priv.h" -#include "events_priv.h" #include "events.h" -#include "event_timed.h" +#include "event_data_objects.h" +#include "event_db_repository.h" #include "sp_head.h" +#define EVEX_MAX_INTERVAL_VALUE 2147483647L + + +Event_parse_data * +Event_parse_data::new_instance(THD *thd) +{ + return new (thd->mem_root) Event_parse_data; +} + + +Event_parse_data::Event_parse_data() +{ + item_execute_at= item_expression= item_starts= item_ends= NULL; +} + + +/* + Set body of the event - what should be executed. + + SYNOPSIS + Event_timed::init_body() + thd THD + + NOTE + The body is extracted by copying all data between the + start of the body set by another method and the current pointer in Lex. + + Some questionable removal of characters is done in here, and that part + should be refactored when the parser is smarter. +*/ + +void +Event_parse_data::init_body(THD *thd) +{ + DBUG_ENTER("Event_parse_data::init_body"); + DBUG_PRINT("info", ("body=[%s] body_begin=0x%ld end=0x%ld", body_begin, + body_begin, thd->lex->ptr)); + + body.length= thd->lex->ptr - body_begin; + const uchar *body_end= body_begin + body.length - 1; + + /* Trim nuls or close-comments ('*'+'/') or spaces at the end */ + while (body_begin < body_end) + { + + if ((*body_end == '\0') || + (my_isspace(thd->variables.character_set_client, *body_end))) + { /* consume NULs and meaningless whitespace */ + --body.length; + --body_end; + continue; + } + + /* + consume closing comments + + This is arguably wrong, but it's the best we have until the parser is + changed to be smarter. FIXME PARSER + + See also the sp_head code, where something like this is done also. + + One idea is to keep in the lexer structure the count of the number of + open-comments we've entered, and scan left-to-right looking for a + closing comment IFF the count is greater than zero. + + Another idea is to remove the closing comment-characters wholly in the + parser, since that's where it "removes" the opening characters. + */ + if ((*(body_end - 1) == '*') && (*body_end == '/')) + { + DBUG_PRINT("info", ("consumend one '*" "/' comment in the query '%s'", + body_begin)); + body.length-= 2; + body_end-= 2; + continue; + } + + break; /* none were found, so we have excised all we can. */ + } + + /* the first is always whitespace which I cannot skip in the parser */ + while (my_isspace(thd->variables.character_set_client, *body_begin)) + { + ++body_begin; + --body.length; + } + body.str= thd->strmake((char *)body_begin, body.length); + + DBUG_VOID_RETURN; +} + + /* Constructor @@ -644,29 +736,29 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) et= this; - if (table->s->fields != Events::FIELD_COUNT) + if (table->s->fields != ET_FIELD_COUNT) goto error; if ((et->dbname.str= get_field(mem_root, - table->field[Events::FIELD_DB])) == NULL) + table->field[ET_FIELD_DB])) == NULL) goto error; et->dbname.length= strlen(et->dbname.str); if ((et->name.str= get_field(mem_root, - table->field[Events::FIELD_NAME])) == NULL) + table->field[ET_FIELD_NAME])) == NULL) goto error; et->name.length= strlen(et->name.str); if ((et->body.str= get_field(mem_root, - table->field[Events::FIELD_BODY])) == NULL) + table->field[ET_FIELD_BODY])) == NULL) goto error; et->body.length= strlen(et->body.str); if ((et->definer.str= get_field(mem_root, - table->field[Events::FIELD_DEFINER])) == NullS) + table->field[ET_FIELD_DEFINER])) == NullS) goto error; et->definer.length= strlen(et->definer.str); @@ -683,27 +775,27 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) et->definer_host.str= strmake_root(mem_root, ptr + 1, len);/* 1:because of @*/ et->definer_host.length= len; - et->starts_null= table->field[Events::FIELD_STARTS]->is_null(); - res1= table->field[Events::FIELD_STARTS]-> + et->starts_null= table->field[ET_FIELD_STARTS]->is_null(); + res1= table->field[ET_FIELD_STARTS]-> get_date(&et->starts,TIME_NO_ZERO_DATE); - et->ends_null= table->field[Events::FIELD_ENDS]->is_null(); - res2= table->field[Events::FIELD_ENDS]->get_date(&et->ends, TIME_NO_ZERO_DATE); + et->ends_null= table->field[ET_FIELD_ENDS]->is_null(); + res2= table->field[ET_FIELD_ENDS]->get_date(&et->ends, TIME_NO_ZERO_DATE); - if (!table->field[Events::FIELD_INTERVAL_EXPR]->is_null()) - et->expression= table->field[Events::FIELD_INTERVAL_EXPR]->val_int(); + if (!table->field[ET_FIELD_INTERVAL_EXPR]->is_null()) + et->expression= table->field[ET_FIELD_INTERVAL_EXPR]->val_int(); else et->expression= 0; /* If res1 and res2 are true then both fields are empty. - Hence if Events::FIELD_EXECUTE_AT is empty there is an error. + Hence if ET_FIELD_EXECUTE_AT is empty there is an error. */ et->execute_at_null= - table->field[Events::FIELD_EXECUTE_AT]->is_null(); + table->field[ET_FIELD_EXECUTE_AT]->is_null(); DBUG_ASSERT(!(et->starts_null && et->ends_null && !et->expression && et->execute_at_null)); if (!et->expression && - table->field[Events::FIELD_EXECUTE_AT]-> get_date(&et->execute_at, + table->field[ET_FIELD_EXECUTE_AT]-> get_date(&et->execute_at, TIME_NO_ZERO_DATE)) goto error; @@ -711,22 +803,22 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) In DB the values start from 1 but enum interval_type starts from 0 */ - if (!table->field[Events::FIELD_TRANSIENT_INTERVAL]->is_null()) + if (!table->field[ET_FIELD_TRANSIENT_INTERVAL]->is_null()) et->interval= (interval_type) ((ulonglong) - table->field[Events::FIELD_TRANSIENT_INTERVAL]->val_int() - 1); + table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_int() - 1); else et->interval= (interval_type) 0; - et->created= table->field[Events::FIELD_CREATED]->val_int(); - et->modified= table->field[Events::FIELD_MODIFIED]->val_int(); + et->created= table->field[ET_FIELD_CREATED]->val_int(); + et->modified= table->field[ET_FIELD_MODIFIED]->val_int(); - table->field[Events::FIELD_LAST_EXECUTED]-> + table->field[ET_FIELD_LAST_EXECUTED]-> get_date(&et->last_executed, TIME_NO_ZERO_DATE); last_executed_changed= false; /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */ - if ((ptr= get_field(mem_root, table->field[Events::FIELD_STATUS])) == NullS) + if ((ptr= get_field(mem_root, table->field[ET_FIELD_STATUS])) == NullS) goto error; DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->name.str, ptr)); @@ -734,20 +826,20 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */ if ((ptr= get_field(mem_root, - table->field[Events::FIELD_ON_COMPLETION])) == NullS) + table->field[ET_FIELD_ON_COMPLETION])) == NullS) goto error; et->on_completion= (ptr[0]=='D'? Event_timed::ON_COMPLETION_DROP: Event_timed::ON_COMPLETION_PRESERVE); - et->comment.str= get_field(mem_root, table->field[Events::FIELD_COMMENT]); + et->comment.str= get_field(mem_root, table->field[ET_FIELD_COMMENT]); if (et->comment.str != NullS) et->comment.length= strlen(et->comment.str); else et->comment.length= 0; - et->sql_mode= (ulong) table->field[Events::FIELD_SQL_MODE]->val_int(); + et->sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int(); DBUG_RETURN(0); error: @@ -1188,6 +1280,7 @@ Event_timed::mark_last_executed(THD *thd) } + /* Drops the event @@ -1210,7 +1303,8 @@ Event_timed::drop(THD *thd) uint tmp= 0; DBUG_ENTER("Event_timed::drop"); - DBUG_RETURN(db_drop_event(thd, this, false, &tmp)); + DBUG_RETURN(Events::get_instance()-> + db_repository->drop_event(thd, dbname, name, false, &tmp)); } @@ -1247,7 +1341,7 @@ Event_timed::update_fields(THD *thd) thd->reset_n_backup_open_tables_state(&backup); - if (Events::open_event_table(thd, TL_WRITE, &table)) + if (Events::get_instance()->open_event_table(thd, TL_WRITE, &table)) { ret= EVEX_OPEN_TABLE_FAILED; goto done; @@ -1263,15 +1357,15 @@ Event_timed::update_fields(THD *thd) if (last_executed_changed) { - table->field[Events::FIELD_LAST_EXECUTED]->set_notnull(); - table->field[Events::FIELD_LAST_EXECUTED]->store_time(&last_executed, + table->field[ET_FIELD_LAST_EXECUTED]->set_notnull(); + table->field[ET_FIELD_LAST_EXECUTED]->store_time(&last_executed, MYSQL_TIMESTAMP_DATETIME); last_executed_changed= false; } if (status_changed) { - table->field[Events::FIELD_STATUS]->set_notnull(); - table->field[Events::FIELD_STATUS]->store((longlong)status, true); + table->field[ET_FIELD_STATUS]->set_notnull(); + table->field[ET_FIELD_STATUS]->store((longlong)status, true); status_changed= false; } @@ -1541,8 +1635,8 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root) thd->query_length= show_create.length(); DBUG_PRINT("info", ("query:%s",thd->query)); - change_security_context(thd, definer_user, definer_host, dbname, - &security_ctx, &save_ctx); + thd->change_security_context(definer_user, definer_host, dbname, + &security_ctx, &save_ctx); thd->lex= &lex; lex_start(thd, (uchar*)thd->query, thd->query_length); lex.et_compile_phase= TRUE; @@ -1580,7 +1674,7 @@ done: lex.et->deinit_mutexes(); lex_end(&lex); - restore_security_context(thd, save_ctx); + thd->restore_security_context(save_ctx); DBUG_PRINT("note", ("return old data on its place. set back NAMES")); thd->lex= old_lex; @@ -1781,21 +1875,59 @@ event_timed_db_equal(Event_timed *et, LEX_STRING *db) } + +/* + Checks whether two events are equal by identifiers + + SYNOPSIS + event_timed_identifier_equal() + + RETURN VALUE + TRUE equal + FALSE not equal +*/ + +bool +event_timed_identifier_equal(Event_timed *a, Event_timed *b) +{ + return event_timed_name_equal(a, &b->name) && + event_timed_db_equal(a, &b->dbname); +} + + /* - Checks whether two events have the same definer + Checks whether two events have the same name SYNOPSIS - event_timed_definer_equal() + event_timed_name_equal() - Returns - TRUE definers are equal - FALSE definers are not equal + RETURN VALUE + TRUE names are equal + FALSE names are not equal */ bool -event_timed_definer_equal(Event_timed *et, LEX_STRING *definer) +event_timed_name_equal(sp_name *name, LEX_STRING *event_name) { - return !sortcmp_lex_string(et->definer, *definer, system_charset_info); + return !sortcmp_lex_string(name->m_name, *event_name, system_charset_info); +} + + +/* + Checks whether two events are in the same schema + + SYNOPSIS + event_timed_db_equal() + + RETURN VALUE + TRUE schemas are equal + FALSE schemas are not equal +*/ + +bool +event_timed_db_equal(sp_name *name, LEX_STRING *db) +{ + return !sortcmp_lex_string(name->m_db, *db, system_charset_info); } @@ -1811,11 +1943,10 @@ event_timed_definer_equal(Event_timed *et, LEX_STRING *definer) */ bool -event_timed_identifier_equal(Event_timed *a, Event_timed *b) +event_timed_identifier_equal(sp_name *a, Event_timed *b) { return event_timed_name_equal(a, &b->name) && - event_timed_db_equal(a, &b->dbname) && - event_timed_definer_equal(a, &b->definer); + event_timed_db_equal(a, &b->dbname); } diff --git a/sql/event_timed.h b/sql/event_data_objects.h index 0652cece361..ff547cebd5b 100644 --- a/sql/event_timed.h +++ b/sql/event_data_objects.h @@ -1,5 +1,5 @@ -#ifndef _EVENT_TIMED_H_ -#define _EVENT_TIMED_H_ +#ifndef _EVENT_DATA_OBJECTS_H_ +#define _EVENT_DATA_OBJECTS_H_ /* Copyright (C) 2004-2006 MySQL AB This program is free software; you can redistribute it and/or modify @@ -40,7 +40,34 @@ #define EVENT_FREE_WHEN_FINISHED (1L << 2) +#define EVENT_EXEC_STARTED 0 +#define EVENT_EXEC_ALREADY_EXEC 1 +#define EVENT_EXEC_CANT_FORK 2 + + class sp_head; +class Sql_alloc; + +class Event_timed; + +/* Compares only the schema part of the identifier */ +bool +event_timed_db_equal(Event_timed *et, LEX_STRING *db); + + +/* Compares the whole identifier*/ +bool +event_timed_identifier_equal(Event_timed *a, Event_timed *b); + +/* Compares only the schema part of the identifier */ +bool +event_timed_db_equal(sp_name *name, LEX_STRING *db); + + +/* Compares the whole identifier*/ +bool +event_timed_identifier_equal(sp_name *a, Event_timed *b); + class Event_timed { @@ -213,5 +240,92 @@ public: void set_thread_id(ulong tid) { thread_id= tid; } }; - -#endif /* _EVENT_H_ */ + + +class Event_parse_data : public Sql_alloc +{ + Event_parse_data(const Event_parse_data &); /* Prevent use of these */ + void operator=(Event_parse_data &); + +public: + enum enum_status + { + ENABLED = 1, + DISABLED + }; + + enum enum_on_completion + { + ON_COMPLETION_DROP = 1, + ON_COMPLETION_PRESERVE + }; + + enum enum_on_completion on_completion; + enum enum_status status; + + const uchar *body_begin; + + LEX_STRING dbname; + LEX_STRING name; + LEX_STRING body; + + LEX_STRING definer_user; + LEX_STRING definer_host; + LEX_STRING definer;// combination of user and host + + LEX_STRING comment; + Item* item_starts; + Item* item_ends; + Item* item_execute_at; + + TIME starts; + TIME ends; + TIME execute_at; + my_bool starts_null; + my_bool ends_null; + my_bool execute_at_null; + + sp_name *identifier; + Item* item_expression; + longlong expression; + interval_type interval; + + static Event_parse_data * + new_instance(THD *thd); + + Event_parse_data(); + ~Event_parse_data(); + + int + init_definer(THD *thd); + + int + init_execute_at(THD *thd, Item *expr); + + int + init_interval(THD *thd, Item *expr, interval_type new_interval); + + void + init_name(THD *thd, sp_name *spn); + + int + init_starts(THD *thd, Item *starts); + + int + init_ends(THD *thd, Item *ends); + + void + init_body(THD *thd); + + void + init_comment(THD *thd, LEX_STRING *set_comment); + +}; + + +class Event_queue_element : public Event_timed +{ + +}; + +#endif /* _EVENT_DATA_OBJECTS_H_ */ diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc new file mode 100644 index 00000000000..fc771b6bac3 --- /dev/null +++ b/sql/event_db_repository.cc @@ -0,0 +1,1038 @@ +/* Copyright (C) 2004-2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysql_priv.h" +#include "event_db_repository.h" +#include "event_data_objects.h" +#include "sp_head.h" +#include "sp.h" +#include "events.h" +#include "sql_show.h" + +#define EVEX_DB_FIELD_LEN 64 +#define EVEX_NAME_FIELD_LEN 64 + + +time_t mysql_event_last_create_time= 0L; + +static +TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] = { + { + {(char *) STRING_WITH_LEN("db")}, + {(char *) STRING_WITH_LEN("char(64)")}, + {(char *) STRING_WITH_LEN("utf8")} + }, + { + {(char *) STRING_WITH_LEN("name")}, + {(char *) STRING_WITH_LEN("char(64)")}, + {(char *) STRING_WITH_LEN("utf8")} + }, + { + {(char *) STRING_WITH_LEN("body")}, + {(char *) STRING_WITH_LEN("longblob")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("definer")}, + {(char *) STRING_WITH_LEN("char(77)")}, + {(char *) STRING_WITH_LEN("utf8")} + }, + { + {(char *) STRING_WITH_LEN("execute_at")}, + {(char *) STRING_WITH_LEN("datetime")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("interval_value")}, + {(char *) STRING_WITH_LEN("int(11)")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("interval_field")}, + {(char *) STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY'," + "'HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR'," + "'DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND'," + "'DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND'," + "'SECOND_MICROSECOND')")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("created")}, + {(char *) STRING_WITH_LEN("timestamp")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("modified")}, + {(char *) STRING_WITH_LEN("timestamp")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("last_executed")}, + {(char *) STRING_WITH_LEN("datetime")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("starts")}, + {(char *) STRING_WITH_LEN("datetime")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("ends")}, + {(char *) STRING_WITH_LEN("datetime")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("status")}, + {(char *) STRING_WITH_LEN("enum('ENABLED','DISABLED')")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("on_completion")}, + {(char *) STRING_WITH_LEN("enum('DROP','PRESERVE')")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("sql_mode")}, + {(char *) STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES'," + "'IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION'," + "'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB'," + "'NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40'," + "'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES'," + "'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES'," + "'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER'," + "'HIGH_NOT_PRECEDENCE')")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("comment")}, + {(char *) STRING_WITH_LEN("char(64)")}, + {(char *) STRING_WITH_LEN("utf8")} + } +}; + + +/* + Puts some data common to CREATE and ALTER EVENT into a row. + + SYNOPSIS + evex_fill_row() + thd THD + table the row to fill out + et Event's data + + RETURN VALUE + 0 - OK + EVEX_GENERAL_ERROR - bad data + EVEX_GET_FIELD_FAILED - field count does not match. table corrupted? + + DESCRIPTION + Used both when an event is created and when it is altered. +*/ + +static int +evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update) +{ + CHARSET_INFO *scs= system_charset_info; + enum enum_events_table_field field_num; + + DBUG_ENTER("evex_fill_row"); + + DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str)); + DBUG_PRINT("info", ("name =[%s]", et->name.str)); + DBUG_PRINT("info", ("body =[%s]", et->body.str)); + + if (table->field[field_num= ET_FIELD_DEFINER]-> + store(et->definer.str, et->definer.length, scs)) + goto err_truncate; + + if (table->field[field_num= ET_FIELD_DB]-> + store(et->dbname.str, et->dbname.length, scs)) + goto err_truncate; + + if (table->field[field_num= ET_FIELD_NAME]-> + store(et->name.str, et->name.length, scs)) + goto err_truncate; + + /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/ + table->field[ET_FIELD_ON_COMPLETION]-> + store((longlong)et->on_completion, true); + + table->field[ET_FIELD_STATUS]->store((longlong)et->status, true); + + /* + Change the SQL_MODE only if body was present in an ALTER EVENT and of course + always during CREATE EVENT. + */ + if (et->body.str) + { + table->field[ET_FIELD_SQL_MODE]-> + store((longlong)thd->variables.sql_mode, true); + + if (table->field[field_num= ET_FIELD_BODY]-> + store(et->body.str, et->body.length, scs)) + goto err_truncate; + } + + if (et->expression) + { + table->field[ET_FIELD_INTERVAL_EXPR]->set_notnull(); + table->field[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, true); + + table->field[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull(); + /* + In the enum (C) intervals start from 0 but in mysql enum valid values start + from 1. Thus +1 offset is needed! + */ + table->field[ET_FIELD_TRANSIENT_INTERVAL]-> + store((longlong)et->interval+1, true); + + table->field[ET_FIELD_EXECUTE_AT]->set_null(); + + if (!et->starts_null) + { + table->field[ET_FIELD_STARTS]->set_notnull(); + table->field[ET_FIELD_STARTS]-> + store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME); + } + + if (!et->ends_null) + { + table->field[ET_FIELD_ENDS]->set_notnull(); + table->field[ET_FIELD_ENDS]-> + store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME); + } + } + else if (et->execute_at.year) + { + table->field[ET_FIELD_INTERVAL_EXPR]->set_null(); + table->field[ET_FIELD_TRANSIENT_INTERVAL]->set_null(); + table->field[ET_FIELD_STARTS]->set_null(); + table->field[ET_FIELD_ENDS]->set_null(); + + table->field[ET_FIELD_EXECUTE_AT]->set_notnull(); + table->field[ET_FIELD_EXECUTE_AT]-> + store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME); + } + else + { + DBUG_ASSERT(is_update); + /* + it is normal to be here when the action is update + this is an error if the action is create. something is borked + */ + } + + ((Field_timestamp *)table->field[ET_FIELD_MODIFIED])->set_time(); + + if (et->comment.str) + { + if (table->field[field_num= ET_FIELD_COMMENT]-> + store(et->comment.str, et->comment.length, scs)) + goto err_truncate; + } + + DBUG_RETURN(0); +err_truncate: + my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), table->field[field_num]->field_name); + DBUG_RETURN(EVEX_GENERAL_ERROR); +} + + +/* + Find row in open mysql.event table representing event + + SYNOPSIS + evex_db_find_event_by_name() + thd Thread context + dbname Name of event's database + rname Name of the event inside the db + table TABLE object for open mysql.event table. + + RETURN VALUE + 0 - Routine found + EVEX_KEY_NOT_FOUND - No routine with given name +*/ + +int +evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, + const LEX_STRING ev_name, + TABLE *table) +{ + return Events::get_instance()->db_repository-> + find_event_by_name(thd, dbname, ev_name, table); +} + + +/* + Performs an index scan of event_table (mysql.event) and fills schema_table. + + Synopsis + Event_db_repository::index_read_for_db_for_i_s() + thd Thread + schema_table The I_S.EVENTS table + event_table The event table to use for loading (mysql.event) + + Returns + 0 OK + 1 Error +*/ + +int +Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table, + TABLE *event_table, char *db) +{ + int ret=0; + CHARSET_INFO *scs= system_charset_info; + KEY *key_info; + uint key_len; + byte *key_buf= NULL; + LINT_INIT(key_buf); + + DBUG_ENTER("Event_db_repository::index_read_for_db_for_i_s"); + + DBUG_PRINT("info", ("Using prefix scanning on PK")); + event_table->file->ha_index_init(0, 1); + event_table->field[ET_FIELD_DB]->store(db, strlen(db), scs); + key_info= event_table->key_info; + key_len= key_info->key_part[0].store_length; + + if (!(key_buf= (byte *)alloc_root(thd->mem_root, key_len))) + { + ret= 1; + /* don't send error, it would be done by sql_alloc_error_handler() */ + } + else + { + key_copy(key_buf, event_table->record[0], key_info, key_len); + if (!(ret= event_table->file->index_read(event_table->record[0], key_buf, + key_len, HA_READ_PREFIX))) + { + DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret)); + do + { + ret= copy_event_to_schema_table(thd, schema_table, event_table); + if (ret == 0) + ret= event_table->file->index_next_same(event_table->record[0], + key_buf, key_len); + } while (ret == 0); + } + DBUG_PRINT("info", ("Scan finished. ret=%d", ret)); + } + event_table->file->ha_index_end(); + /* ret is guaranteed to be != 0 */ + if (ret == HA_ERR_END_OF_FILE || ret == HA_ERR_KEY_NOT_FOUND) + DBUG_RETURN(0); + DBUG_RETURN(1); +} + + +/* + Performs a table scan of event_table (mysql.event) and fills schema_table. + + Synopsis + Events_db_repository::table_scan_all_for_i_s() + thd Thread + schema_table The I_S.EVENTS in memory table + event_table The event table to use for loading. + + Returns + 0 OK + 1 Error +*/ + +int +Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table, + TABLE *event_table) +{ + int ret; + READ_RECORD read_record_info; + + DBUG_ENTER("Event_db_repository::table_scan_all_for_i_s"); + init_read_record(&read_record_info, thd, event_table, NULL, 1, 0); + + /* + rr_sequential, in read_record(), returns 137==HA_ERR_END_OF_FILE, + but rr_handle_error returns -1 for that reason. Thus, read_record() + returns -1 eventually. + */ + do + { + ret= read_record_info.read_record(&read_record_info); + if (ret == 0) + ret= copy_event_to_schema_table(thd, schema_table, event_table); + } + while (ret == 0); + + DBUG_PRINT("info", ("Scan finished. ret=%d", ret)); + end_read_record(&read_record_info); + + /* ret is guaranteed to be != 0 */ + DBUG_RETURN(ret == -1? 0:1); +} + + +/* + Fills I_S.EVENTS with data loaded from mysql.event. Also used by + SHOW EVENTS + + Synopsis + Event_db_repository::fill_schema_events() + thd Thread + tables The schema table + db If not NULL then get events only from this schema + + Returns + 0 OK + 1 Error +*/ + +int +Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables, char *db) +{ + TABLE *schema_table= tables->table; + TABLE *event_table= NULL; + Open_tables_state backup; + int ret= 0; + + DBUG_ENTER("Event_db_repository::fill_schema_events"); + DBUG_PRINT("info",("db=%s", db? db:"(null)")); + + thd->reset_n_backup_open_tables_state(&backup); + if (open_event_table(thd, TL_READ, &event_table)) + { + sql_print_error("Table mysql.event is damaged."); + thd->restore_backup_open_tables_state(&backup); + DBUG_RETURN(1); + } + + /* + 1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order + thus we won't order it. OTOH, SHOW EVENTS will be + ordered. + 2. SHOW EVENTS => PRIMARY KEY with prefix scanning on (db) + Reasoning: Events are per schema, therefore a scan over an index + will save use from doing a table scan and comparing + every single row's `db` with the schema which we show. + */ + if (db) + ret= index_read_for_db_for_i_s(thd, schema_table, event_table, db); + else + ret= table_scan_all_for_i_s(thd, schema_table, event_table); + + close_thread_tables(thd); + thd->restore_backup_open_tables_state(&backup); + + DBUG_PRINT("info", ("Return code=%d", ret)); + DBUG_RETURN(ret); +} + + +/* + Looks for a named event in mysql.event and in case of success returns + an object will data loaded from the table. + + SYNOPSIS + Event_db_repository::find_event() + thd THD + name the name of the event to find + ett event's data if event is found + tbl TABLE object to use when not NULL + root On which root to load the event + + NOTES + 1) Use sp_name for look up, return in **ett if found + 2) tbl is not closed at exit + + RETURN VALUE + 0 ok In this case *ett is set to the event + # error *ett == 0 +*/ + +int +Event_db_repository::find_event(THD *thd, sp_name *name, Event_timed **ett, + TABLE *tbl, MEM_ROOT *root) +{ + TABLE *table; + int ret; + Event_timed *et= NULL; + DBUG_ENTER("db_find_event"); + DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); + + if (tbl) + table= tbl; + else if (open_event_table(thd, TL_READ, &table)) + { + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + ret= EVEX_GENERAL_ERROR; + goto done; + } + + if ((ret= evex_db_find_event_by_name(thd, name->m_db, name->m_name, table))) + { + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str); + goto done; + } + et= new Event_timed; + + /* + 1)The table should not be closed beforehand. ::load_from_row() only loads + and does not compile + + 2)::load_from_row() is silent on error therefore we emit error msg here + */ + if ((ret= et->load_from_row(root, table))) + { + my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0)); + goto done; + } + +done: + if (ret) + { + delete et; + et= 0; + } + /* don't close the table if we haven't opened it ourselves */ + if (!tbl && table) + close_thread_tables(thd); + *ett= et; + DBUG_RETURN(ret); +} + + +int +Event_db_repository::init_repository() +{ + init_alloc_root(&repo_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); + return 0; +} + + +void +Event_db_repository::deinit_repository() +{ + free_root(&repo_root, MYF(0)); +} + + +/* + Open mysql.event table for read + + SYNOPSIS + Events::open_event_table() + thd Thread context + lock_type How to lock the table + table We will store the open table here + + RETURN VALUE + 1 Cannot lock table + 2 The table is corrupted - different number of fields + 0 OK +*/ + +int +Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type, + TABLE **table) +{ + TABLE_LIST tables; + DBUG_ENTER("Event_db_repository::open_event_table"); + + bzero((char*) &tables, sizeof(tables)); + tables.db= (char*) "mysql"; + tables.table_name= tables.alias= (char*) "event"; + tables.lock_type= lock_type; + + if (simple_open_n_lock_tables(thd, &tables)) + DBUG_RETURN(1); + + if (table_check_intact(tables.table, ET_FIELD_COUNT, + event_table_fields, + &mysql_event_last_create_time, + ER_CANNOT_LOAD_FROM_TABLE)) + { + close_thread_tables(thd); + DBUG_RETURN(2); + } + *table= tables.table; + tables.table->use_all_columns(); + DBUG_RETURN(0); +} + + +/* + Creates an event in mysql.event + + SYNOPSIS + Event_db_repository::create_event() + thd THD + et Event_timed object containing information for the event + create_if_not If an warning should be generated in case event exists + rows_affected How many rows were affected + + RETURN VALUE + 0 - OK + EVEX_GENERAL_ERROR - Failure + + DESCRIPTION + Creates an event. Relies on evex_fill_row which is shared with + db_update_event. The name of the event is inside "et". +*/ + +int +Event_db_repository::create_event(THD *thd, Event_timed *et, + my_bool create_if_not, uint *rows_affected) +{ + int ret= 0; + CHARSET_INFO *scs= system_charset_info; + TABLE *table; + char olddb[128]; + bool dbchanged= false; + DBUG_ENTER("Event_db_repository::create_event"); + DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str)); + + *rows_affected= 0; + DBUG_PRINT("info", ("open mysql.event for update")); + if (open_event_table(thd, TL_WRITE, &table)) + { + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + goto err; + } + + DBUG_PRINT("info", ("check existance of an event with the same name")); + if (!evex_db_find_event_by_name(thd, et->dbname, et->name, table)) + { + if (create_if_not) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS), + et->name.str); + goto ok; + } + my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), et->name.str); + goto err; + } + + DBUG_PRINT("info", ("non-existant, go forward")); + if ((ret= sp_use_new_db(thd, et->dbname.str,olddb, sizeof(olddb),0, + &dbchanged))) + { + my_error(ER_BAD_DB_ERROR, MYF(0)); + goto err; + } + + restore_record(table, s->default_values); // Get default values for fields + + if (system_charset_info->cset->numchars(system_charset_info, et->dbname.str, + et->dbname.str + et->dbname.length) + > EVEX_DB_FIELD_LEN) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), et->dbname.str); + goto err; + } + if (system_charset_info->cset->numchars(system_charset_info, et->name.str, + et->name.str + et->name.length) + > EVEX_DB_FIELD_LEN) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), et->name.str); + goto err; + } + + if (et->body.length > table->field[ET_FIELD_BODY]->field_length) + { + my_error(ER_TOO_LONG_BODY, MYF(0), et->name.str); + goto err; + } + + if (!(et->expression) && !(et->execute_at.year)) + { + DBUG_PRINT("error", ("neither expression nor execute_at are set!")); + my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0)); + goto err; + } + + ((Field_timestamp *)table->field[ET_FIELD_CREATED])->set_time(); + + /* + evex_fill_row() calls my_error() in case of error so no need to + handle it here + */ + if ((ret= evex_fill_row(thd, table, et, false))) + goto err; + + if (table->file->ha_write_row(table->record[0])) + { + my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret); + goto err; + } + +#ifdef USE_THIS_CODE_AS_TEMPLATE_WHEN_EVENT_REPLICATION_IS_AGREED + if (mysql_bin_log.is_open()) + { + thd->clear_error(); + /* Such a statement can always go directly to binlog, no trans cache */ + thd->binlog_query(THD::MYSQL_QUERY_TYPE, thd->query, thd->query_length, + FALSE, FALSE); + } +#endif + + *rows_affected= 1; +ok: + if (dbchanged) + (void) mysql_change_db(thd, olddb, 1); + if (table) + close_thread_tables(thd); + DBUG_RETURN(EVEX_OK); + +err: + if (dbchanged) + (void) mysql_change_db(thd, olddb, 1); + if (table) + close_thread_tables(thd); + DBUG_RETURN(EVEX_GENERAL_ERROR); +} + + +/* + Used to execute ALTER EVENT. Pendant to Events::update_event(). + + SYNOPSIS + Event_db_repository::update_event() + thd THD + sp_name the name of the event to alter + et event's data + + RETURN VALUE + 0 OK + EVEX_GENERAL_ERROR Error occured (my_error() called) + + NOTES + sp_name is passed since this is the name of the event to + alter in case of RENAME TO. +*/ + +int +Event_db_repository::update_event(THD *thd, Event_timed *et, sp_name *new_name) +{ + CHARSET_INFO *scs= system_charset_info; + TABLE *table; + int ret= EVEX_OPEN_TABLE_FAILED; + DBUG_ENTER("Event_db_repository::update_event"); + DBUG_PRINT("enter", ("dbname: %.*s", et->dbname.length, et->dbname.str)); + DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str)); + DBUG_PRINT("enter", ("user: %.*s", et->definer.length, et->definer.str)); + if (new_name) + DBUG_PRINT("enter", ("rename to: %.*s", new_name->m_name.length, + new_name->m_name.str)); + + if (open_event_table(thd, TL_WRITE, &table)) + { + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + goto err; + } + + /* first look whether we overwrite */ + if (new_name) + { + if (!sortcmp_lex_string(et->name, new_name->m_name, scs) && + !sortcmp_lex_string(et->dbname, new_name->m_db, scs)) + { + my_error(ER_EVENT_SAME_NAME, MYF(0), et->name.str); + goto err; + } + + if (!evex_db_find_event_by_name(thd,new_name->m_db,new_name->m_name,table)) + { + my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str); + goto err; + } + } + /* + ...and then if there is such an event. Don't exchange the blocks + because you will get error 120 from table handler because new_name will + overwrite the key and SE will tell us that it cannot find the already found + row (copied into record[1] later + */ + if (EVEX_KEY_NOT_FOUND == find_event_by_name(thd, et->dbname, et->name, table)) + { + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str); + goto err; + } + + store_record(table,record[1]); + + /* Don't update create on row update. */ + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; + + /* + evex_fill_row() calls my_error() in case of error so no need to + handle it here + */ + if ((ret= evex_fill_row(thd, table, et, true))) + goto err; + + if (new_name) + { + table->field[ET_FIELD_DB]-> + store(new_name->m_db.str, new_name->m_db.length, scs); + table->field[ET_FIELD_NAME]-> + store(new_name->m_name.str, new_name->m_name.length, scs); + } + + if ((ret= table->file->ha_update_row(table->record[1], table->record[0]))) + { + my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret); + goto err; + } + + /* close mysql.event or we crash later when loading the event from disk */ + close_thread_tables(thd); + DBUG_RETURN(0); + +err: + if (table) + close_thread_tables(thd); + DBUG_RETURN(EVEX_GENERAL_ERROR); +} + + +/* + Drops an event + + SYNOPSIS + Event_db_repository::drop_event() + thd THD + db database name + name event's name + drop_if_exists if set and the event not existing => warning onto the stack + rows_affected affected number of rows is returned heres + + RETURN VALUE + 0 OK + !0 Error (my_error() called) +*/ + +int +Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name, + bool drop_if_exists, uint *rows_affected) +{ + TABLE *table; + Open_tables_state backup; + int ret; + + DBUG_ENTER("Event_db_repository::drop_event"); + DBUG_PRINT("enter", ("db=%s name=%s", db.str, name.str)); + ret= EVEX_OPEN_TABLE_FAILED; + + thd->reset_n_backup_open_tables_state(&backup); + if (open_event_table(thd, TL_WRITE, &table)) + { + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + goto done; + } + + if (!(ret= evex_db_find_event_by_name(thd, db, name, table))) + { + if ((ret= table->file->ha_delete_row(table->record[0]))) + my_error(ER_EVENT_CANNOT_DELETE, MYF(0)); + } + else if (ret == EVEX_KEY_NOT_FOUND) + { + if (drop_if_exists) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), + "Event", name.str); + ret= 0; + } else + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str); + } + +done: + /* + evex_drop_event() is used by Event_timed::drop therefore + we have to close our thread tables. + */ + close_thread_tables(thd); + thd->restore_backup_open_tables_state(&backup); + DBUG_RETURN(ret); +} + + +int +Event_db_repository::find_event_by_name(THD *thd, LEX_STRING db, + LEX_STRING name, TABLE *table) +{ + byte key[MAX_KEY_LENGTH]; + DBUG_ENTER("Event_db_repository::find_event_by_name"); + DBUG_PRINT("enter", ("name: %.*s", name.length, name.str)); + + /* + Create key to find row. We have to use field->store() to be able to + handle VARCHAR and CHAR fields. + Assumption here is that the two first fields in the table are + 'db' and 'name' and the first key is the primary key over the + same fields. + */ + if (db.length > table->field[ET_FIELD_DB]->field_length || + name.length > table->field[ET_FIELD_NAME]->field_length) + + DBUG_RETURN(EVEX_KEY_NOT_FOUND); + + table->field[ET_FIELD_DB]->store(db.str, db.length, &my_charset_bin); + table->field[ET_FIELD_NAME]->store(name.str, name.length, &my_charset_bin); + + key_copy(key, table->record[0], table->key_info, + table->key_info->key_length); + + if (table->file->index_read_idx(table->record[0], 0, key, + table->key_info->key_length, + HA_READ_KEY_EXACT)) + { + DBUG_PRINT("info", ("Row not found")); + DBUG_RETURN(EVEX_KEY_NOT_FOUND); + } + + DBUG_PRINT("info", ("Row found!")); + DBUG_RETURN(0); +} + + +int +Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema) +{ + return drop_events_by_field(thd, ET_FIELD_DB, schema); +} + + +int +Event_db_repository::drop_user_events(THD *thd, LEX_STRING definer) +{ + return drop_events_by_field(thd, ET_FIELD_DEFINER, definer); +} + + +/* + Drops all events in the selected database, from mysql.event. + + SYNOPSIS + drop_events_from_table_by_field() + thd Thread + table mysql.event TABLE + field Which field of the row to use for matching + field_value The value that should match + + RETURN VALUE + 0 OK + !0 Error from ha_delete_row +*/ + +int +Event_db_repository::drop_events_by_field(THD *thd, + enum enum_events_table_field field, + LEX_STRING field_value) +{ + int ret= 0; + TABLE *table; + Open_tables_state backup; + READ_RECORD read_record_info; + DBUG_ENTER("drop_events_from_table_by_field"); + DBUG_PRINT("enter", ("field=%d field_value=%s", field, field_value.str)); + + if (open_event_table(thd, TL_WRITE, &table)) + { + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + DBUG_RETURN(1); + } + + /* only enabled events are in memory, so we go now and delete the rest */ + init_read_record(&read_record_info, thd, table, NULL, 1, 0); + while (!ret && !(read_record_info.read_record(&read_record_info)) ) + { + char *et_field= get_field(thd->mem_root, table->field[field]); + + LEX_STRING et_field_lex= {et_field, strlen(et_field)}; + DBUG_PRINT("info", ("Current event %s name=%s", et_field, + get_field(thd->mem_root, table->field[ET_FIELD_NAME]))); + + if (!sortcmp_lex_string(et_field_lex, field_value, system_charset_info)) + { + DBUG_PRINT("info", ("Dropping")); + if ((ret= table->file->ha_delete_row(table->record[0]))) + my_error(ER_EVENT_DROP_FAILED, MYF(0), + get_field(thd->mem_root, table->field[ET_FIELD_NAME])); + } + } + end_read_record(&read_record_info); + thd->version--; /* Force close to free memory */ + + DBUG_RETURN(ret); +} + + +/* + Looks for a named event in mysql.event and then loads it from + the table, compiles and inserts it into the cache. + + SYNOPSIS + Event_scheduler::load_named_event() + thd THD + etn The name of the event to load and compile on scheduler's root + etn_new The loaded event + + RETURN VALUE + NULL Error during compile or the event is non-enabled. + otherwise Address +*/ + +int +Event_db_repository::load_named_event(THD *thd, Event_timed *etn, + Event_timed **etn_new) +{ + int ret= 0; + MEM_ROOT *tmp_mem_root; + Event_timed *et_loaded= NULL; + Open_tables_state backup; + + DBUG_ENTER("Event_scheduler::load_and_compile_event"); + DBUG_PRINT("enter",("thd=%p name:%*s",thd, etn->name.length, etn->name.str)); + + thd->reset_n_backup_open_tables_state(&backup); + /* No need to use my_error() here because db_find_event() has done it */ + { + sp_name spn(etn->dbname, etn->name); + ret= find_event(thd, &spn, &et_loaded, NULL, &repo_root); + } + thd->restore_backup_open_tables_state(&backup); + /* In this case no memory was allocated so we don't need to clean */ + if (ret) + DBUG_RETURN(OP_LOAD_ERROR); + + if (et_loaded->status != Event_timed::ENABLED) + { + /* + We don't load non-enabled events. + In db_find_event() `et_new` was allocated on the heap and not on + scheduler_root therefore we delete it here. + */ + delete et_loaded; + DBUG_RETURN(OP_DISABLED_EVENT); + } + + et_loaded->compute_next_execution_time(); + *etn_new= et_loaded; + + DBUG_RETURN(OP_OK); +} diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h new file mode 100644 index 00000000000..1cbee96b68f --- /dev/null +++ b/sql/event_db_repository.h @@ -0,0 +1,122 @@ +#ifndef _EVENT_DB_REPOSITORY_H_ +#define _EVENT_DB_REPOSITORY_H_ +/* Copyright (C) 2004-2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +enum enum_events_table_field +{ + ET_FIELD_DB = 0, + ET_FIELD_NAME, + ET_FIELD_BODY, + ET_FIELD_DEFINER, + ET_FIELD_EXECUTE_AT, + ET_FIELD_INTERVAL_EXPR, + ET_FIELD_TRANSIENT_INTERVAL, + ET_FIELD_CREATED, + ET_FIELD_MODIFIED, + ET_FIELD_LAST_EXECUTED, + ET_FIELD_STARTS, + ET_FIELD_ENDS, + ET_FIELD_STATUS, + ET_FIELD_ON_COMPLETION, + ET_FIELD_SQL_MODE, + ET_FIELD_COMMENT, + ET_FIELD_COUNT /* a cool trick to count the number of fields :) */ +}; + + +int +evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, + const LEX_STRING ev_name, + TABLE *table); + +int +events_table_index_read_for_db(THD *thd, TABLE *schema_table, + TABLE *event_table); + +int +events_table_scan_all(THD *thd, TABLE *schema_table, TABLE *event_table); + +int +fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */); + + +class Event_queue_element; + +class Event_db_repository +{ +public: + Event_db_repository(){} + ~Event_db_repository(){} + + int + init_repository(); + + void + deinit_repository(); + + int + create_event(THD *thd, Event_timed *et, my_bool create_if_not, + uint *rows_affected); + + int + update_event(THD *thd, Event_timed *et, sp_name *new_name); + + int + drop_event(THD *thd, LEX_STRING db, LEX_STRING name, bool drop_if_exists, + uint *rows_affected); + + int + drop_schema_events(THD *thd, LEX_STRING schema); + + int + drop_user_events(THD *thd, LEX_STRING definer); + + int + find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl, + MEM_ROOT *root); + + int + load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new); + + int + find_event_by_name(THD *thd, LEX_STRING db, LEX_STRING name, TABLE *table); + + int + open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); + + int + fill_schema_events(THD *thd, TABLE_LIST *tables, char *db); + +private: + int + drop_events_by_field(THD *thd, enum enum_events_table_field field, + LEX_STRING field_value); + int + index_read_for_db_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table, + char *db); + + int + table_scan_all_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table); + + MEM_ROOT repo_root; + + /* Prevent use of these */ + Event_db_repository(const Event_db_repository &); + void operator=(Event_db_repository &); +}; + +#endif /* _EVENT_DB_REPOSITORY_H_ */ diff --git a/sql/event_queue.cc b/sql/event_queue.cc new file mode 100644 index 00000000000..46f965678c6 --- /dev/null +++ b/sql/event_queue.cc @@ -0,0 +1,19 @@ +/* Copyright (C) 2004-2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysql_priv.h" +#include "event_queue.h" +#include "event_data_objects.h" diff --git a/sql/event_queue.h b/sql/event_queue.h new file mode 100644 index 00000000000..b3aa6133840 --- /dev/null +++ b/sql/event_queue.h @@ -0,0 +1,20 @@ +#ifndef _EVENT_QUEUE_H_ +#define _EVENT_QUEUE_H_ +/* Copyright (C) 2004-2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#endif /* _EVENT_QUEUE_H_ */ diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 1b4a0d290e6..cb500de53b9 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -15,10 +15,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mysql_priv.h" -#include "events_priv.h" #include "events.h" -#include "event_timed.h" +#include "event_data_objects.h" #include "event_scheduler.h" +#include "event_db_repository.h" #include "sp_head.h" /* @@ -574,8 +574,8 @@ event_worker_thread(void *arg) to change the context before sending the signal. We are under LOCK_scheduler_data being held by Event_scheduler::run() -> ::execute_top(). */ - change_security_context(thd, event->definer_user, event->definer_host, - event->dbname, &security_ctx, &save_ctx); + thd->change_security_context(event->definer_user, event->definer_host, + event->dbname, &security_ctx, &save_ctx); DBUG_PRINT("info", ("master_access=%d db_access=%d", thd->security_ctx->master_access, thd->security_ctx->db_access)); @@ -687,7 +687,7 @@ Event_scheduler::get_instance() */ bool -Event_scheduler::init() +Event_scheduler::init(Event_db_repository *db_repo) { int i= 0; bool ret= FALSE; @@ -695,6 +695,7 @@ Event_scheduler::init() DBUG_PRINT("enter", ("this=%p", this)); LOCK_SCHEDULER_DATA(); + db_repository= db_repo; for (;i < COND_LAST; i++) if (pthread_cond_init(&cond_vars[i], NULL)) { @@ -783,10 +784,10 @@ Event_scheduler::destroy() OP_LOAD_ERROR Error during loading from disk */ -enum Event_scheduler::enum_error_code +int Event_scheduler::create_event(THD *thd, Event_timed *et, bool check_existence) { - enum enum_error_code res; + int res; Event_timed *et_new; DBUG_ENTER("Event_scheduler::create_event"); DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et,&LOCK_scheduler_data)); @@ -805,7 +806,7 @@ Event_scheduler::create_event(THD *thd, Event_timed *et, bool check_existence) } /* We need to load the event on scheduler_root */ - if (!(res= load_named_event(thd, et, &et_new))) + if (!(res= db_repository->load_named_event(thd, et, &et_new))) { queue_insert_safe(&queue, (byte *) et_new); DBUG_PRINT("info", ("Sending COND_new_work")); @@ -833,12 +834,13 @@ end: */ bool -Event_scheduler::drop_event(THD *thd, Event_timed *et) +Event_scheduler::drop_event(THD *thd, sp_name *name) { int res; Event_timed *et_old; DBUG_ENTER("Event_scheduler::drop_event"); - DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et,&LOCK_scheduler_data)); + DBUG_PRINT("enter", ("thd=%p name=%p lock=%p", thd, name, + &LOCK_scheduler_data)); LOCK_SCHEDULER_DATA(); if (!is_running_or_suspended()) @@ -848,7 +850,7 @@ Event_scheduler::drop_event(THD *thd, Event_timed *et) DBUG_RETURN(OP_OK); } - if (!(et_old= find_event(et, TRUE))) + if (!(et_old= find_event(name, TRUE))) DBUG_PRINT("info", ("No such event found, probably DISABLED")); UNLOCK_SCHEDULER_DATA(); @@ -903,12 +905,12 @@ Event_scheduler::drop_event(THD *thd, Event_timed *et) OP_ALREADY_EXISTS Event already in the queue */ -enum Event_scheduler::enum_error_code +int Event_scheduler::update_event(THD *thd, Event_timed *et, LEX_STRING *new_schema, LEX_STRING *new_name) { - enum enum_error_code res; + int res= OP_OK; Event_timed *et_old, *et_new= NULL; LEX_STRING old_schema, old_name; @@ -946,7 +948,7 @@ Event_scheduler::update_event(THD *thd, Event_timed *et, 1. Error occured 2. If the replace is DISABLED, we don't load it into the queue. */ - if (!(res= load_named_event(thd, et, &et_new))) + if (!(res= db_repository->load_named_event(thd, et, &et_new))) { queue_insert_safe(&queue, (byte *) et_new); DBUG_PRINT("info", ("Sending COND_new_work")); @@ -960,7 +962,7 @@ Event_scheduler::update_event(THD *thd, Event_timed *et, et->dbname= old_schema; et->name= old_name; } - + DBUG_PRINT("info", ("res=%d", res)); UNLOCK_SCHEDULER_DATA(); /* Andrey: Is this comment still truthful ??? @@ -1050,6 +1052,48 @@ Event_scheduler::find_event(Event_timed *etn, bool remove_from_q) /* + Searches for an event in the scheduler queue + + SYNOPSIS + Event_scheduler::find_event() + name The event to find + comparator The function to use for comparing + remove_from_q If found whether to remove from the Q + + RETURN VALUE + NULL Not found + otherwise Address + + NOTE + The caller should do the locking also the caller is responsible for + actual signalling in case an event is removed from the queue + (signalling COND_new_work for instance). +*/ + +Event_timed * +Event_scheduler::find_event(sp_name *name, bool remove_from_q) +{ + uint i; + DBUG_ENTER("Event_scheduler::find_event"); + + for (i= 0; i < queue.elements; ++i) + { + Event_timed *et= (Event_timed *) queue_element(&queue, i); + DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?", name->m_db.str, name->m_name.str, + et->dbname.str, et->name.str)); + if (event_timed_identifier_equal(name, et)) + { + if (remove_from_q) + queue_remove(&queue, i); + DBUG_RETURN(et); + } + } + + DBUG_RETURN(NULL); +} + + +/* Drops all events from the in-memory queue and disk that match certain pattern evaluated by a comparator function @@ -1068,11 +1112,11 @@ Event_scheduler::find_event(Event_timed *etn, bool remove_from_q) */ void -Event_scheduler::drop_matching_events(THD *thd, LEX_STRING *pattern, +Event_scheduler::drop_matching_events(THD *thd, LEX_STRING pattern, bool (*comparator)(Event_timed *,LEX_STRING *)) { DBUG_ENTER("Event_scheduler::drop_matching_events"); - DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern->length, pattern->str, + DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern.length, pattern.str, state)); if (is_running_or_suspended()) { @@ -1081,7 +1125,7 @@ Event_scheduler::drop_matching_events(THD *thd, LEX_STRING *pattern, { Event_timed *et= (Event_timed *) queue_element(&queue, i); DBUG_PRINT("info", ("[%s.%s]?", et->dbname.str, et->name.str)); - if (comparator(et, pattern)) + if (comparator(et, &pattern)) { /* The queue is ordered. If we remove an element, then all elements after @@ -1136,7 +1180,7 @@ Event_scheduler::drop_matching_events(THD *thd, LEX_STRING *pattern, */ int -Event_scheduler::drop_schema_events(THD *thd, LEX_STRING *schema) +Event_scheduler::drop_schema_events(THD *thd, LEX_STRING schema) { int ret; DBUG_ENTER("Event_scheduler::drop_schema_events"); @@ -1144,7 +1188,6 @@ Event_scheduler::drop_schema_events(THD *thd, LEX_STRING *schema) if (is_running_or_suspended()) drop_matching_events(thd, schema, event_timed_db_equal); - ret= db_drop_events_from_table(thd, schema); UNLOCK_SCHEDULER_DATA(); DBUG_RETURN(ret); @@ -1670,7 +1713,7 @@ Event_scheduler::stop_all_running_events(THD *thd) The caller must have acquited LOCK_scheduler_data. */ -enum Event_scheduler::enum_error_code +int Event_scheduler::stop() { THD *thd= current_thd; @@ -1735,7 +1778,7 @@ Event_scheduler::stop() OP_OK OK */ -enum Event_scheduler::enum_error_code +int Event_scheduler::suspend_or_resume( enum Event_scheduler::enum_suspend_or_resume action) { @@ -2073,59 +2116,6 @@ Event_scheduler::events_count() } -/* - Looks for a named event in mysql.event and then loads it from - the table, compiles and inserts it into the cache. - - SYNOPSIS - Event_scheduler::load_named_event() - thd THD - etn The name of the event to load and compile on scheduler's root - etn_new The loaded event - - RETURN VALUE - NULL Error during compile or the event is non-enabled. - otherwise Address -*/ - -enum Event_scheduler::enum_error_code -Event_scheduler::load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new) -{ - int ret= 0; - MEM_ROOT *tmp_mem_root; - Event_timed *et_loaded= NULL; - Open_tables_state backup; - - DBUG_ENTER("Event_scheduler::load_and_compile_event"); - DBUG_PRINT("enter",("thd=%p name:%*s",thd, etn->name.length, etn->name.str)); - - thd->reset_n_backup_open_tables_state(&backup); - /* No need to use my_error() here because db_find_event() has done it */ - { - sp_name spn(etn->dbname, etn->name); - ret= db_find_event(thd, &spn, &et_loaded, NULL, &scheduler_root); - } - thd->restore_backup_open_tables_state(&backup); - /* In this case no memory was allocated so we don't need to clean */ - if (ret) - DBUG_RETURN(OP_LOAD_ERROR); - - if (et_loaded->status != Event_timed::ENABLED) - { - /* - We don't load non-enabled events. - In db_find_event() `et_new` was allocated on the heap and not on - scheduler_root therefore we delete it here. - */ - delete et_loaded; - DBUG_RETURN(OP_DISABLED_EVENT); - } - - et_loaded->compute_next_execution_time(); - *etn_new= et_loaded; - - DBUG_RETURN(OP_OK); -} /* @@ -2169,7 +2159,7 @@ Event_scheduler::load_events_from_db(THD *thd) DBUG_RETURN(EVEX_GENERAL_ERROR); } - if ((ret= Events::open_event_table(thd, TL_READ, &table))) + if ((ret= db_repository->open_event_table(thd, TL_READ, &table))) { sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open."); DBUG_RETURN(EVEX_OPEN_TABLE_FAILED); diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h index 5ae310bab2a..bd099d10839 100644 --- a/sql/event_scheduler.h +++ b/sql/event_scheduler.h @@ -16,7 +16,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +class sp_name; class Event_timed; +class Event_db_repository; class THD; typedef bool * (*event_timed_identifier_comparator)(Event_timed*, Event_timed*); @@ -30,17 +32,6 @@ events_shutdown(); class Event_scheduler { public: - /* Return codes */ - enum enum_error_code - { - OP_OK= 0, - OP_NOT_RUNNING, - OP_CANT_KILL, - OP_CANT_INIT, - OP_DISABLED_EVENT, - OP_LOAD_ERROR, - OP_ALREADY_EXISTS - }; enum enum_state { @@ -65,22 +56,22 @@ public: /* Methods for queue management follow */ - enum enum_error_code + int create_event(THD *thd, Event_timed *et, bool check_existence); - enum enum_error_code + int update_event(THD *thd, Event_timed *et, LEX_STRING *new_schema, LEX_STRING *new_name); bool - drop_event(THD *thd, Event_timed *et); + drop_event(THD *thd, sp_name *name); int - drop_schema_events(THD *thd, LEX_STRING *schema); + drop_schema_events(THD *thd, LEX_STRING schema); int - drop_user_events(THD *thd, LEX_STRING *definer, uint *dropped_num) + drop_user_events(THD *thd, LEX_STRING *definer) { DBUG_ASSERT(0); return 0;} uint @@ -91,20 +82,24 @@ public: bool start(); - enum enum_error_code + int stop(); bool start_suspended(); + /* + Need to be public because has to be called from the function + passed to pthread_create. + */ bool run(THD *thd); - enum enum_error_code + int suspend_or_resume(enum enum_suspend_or_resume action); bool - init(); + init(Event_db_repository *db_repo); void destroy(); @@ -136,6 +131,9 @@ private: Event_timed * find_event(Event_timed *etn, bool remove_from_q); + Event_timed * + find_event(sp_name *name, bool remove_from_q); + uint workers_count(); @@ -152,14 +150,11 @@ private: void stop_all_running_events(THD *thd); - enum enum_error_code - load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new); - int load_events_from_db(THD *thd); void - drop_matching_events(THD *thd, LEX_STRING *pattern, + drop_matching_events(THD *thd, LEX_STRING pattern, bool (*)(Event_timed *,LEX_STRING *)); bool @@ -226,6 +221,8 @@ private: /* The MEM_ROOT of the object */ MEM_ROOT scheduler_root; + Event_db_repository *db_repository; + /* The sorted queue with the Event_timed objects */ QUEUE queue; diff --git a/sql/events.cc b/sql/events.cc index d67c42326e3..cd4d3dff244 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -15,11 +15,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mysql_priv.h" -#include "events_priv.h" #include "events.h" -#include "event_timed.h" +#include "event_data_objects.h" #include "event_scheduler.h" -#include "sp.h" +#include "event_db_repository.h" #include "sp_head.h" /* @@ -48,10 +47,6 @@ Warning: */ -MEM_ROOT evex_mem_root; -time_t mysql_event_last_create_time= 0L; - - const char *event_scheduler_state_names[]= { "OFF", "0", "ON", "1", "SUSPEND", "2", NullS }; @@ -63,104 +58,10 @@ TYPELIB Events::opt_typelib= NULL }; +Events Events::singleton; ulong Events::opt_event_scheduler= 2; -static -TABLE_FIELD_W_TYPE event_table_fields[Events::FIELD_COUNT] = { - { - {(char *) STRING_WITH_LEN("db")}, - {(char *) STRING_WITH_LEN("char(64)")}, - {(char *) STRING_WITH_LEN("utf8")} - }, - { - {(char *) STRING_WITH_LEN("name")}, - {(char *) STRING_WITH_LEN("char(64)")}, - {(char *) STRING_WITH_LEN("utf8")} - }, - { - {(char *) STRING_WITH_LEN("body")}, - {(char *) STRING_WITH_LEN("longblob")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("definer")}, - {(char *) STRING_WITH_LEN("char(77)")}, - {(char *) STRING_WITH_LEN("utf8")} - }, - { - {(char *) STRING_WITH_LEN("execute_at")}, - {(char *) STRING_WITH_LEN("datetime")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("interval_value")}, - {(char *) STRING_WITH_LEN("int(11)")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("interval_field")}, - {(char *) STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY'," - "'HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR'," - "'DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND'," - "'DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND'," - "'SECOND_MICROSECOND')")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("created")}, - {(char *) STRING_WITH_LEN("timestamp")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("modified")}, - {(char *) STRING_WITH_LEN("timestamp")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("last_executed")}, - {(char *) STRING_WITH_LEN("datetime")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("starts")}, - {(char *) STRING_WITH_LEN("datetime")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("ends")}, - {(char *) STRING_WITH_LEN("datetime")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("status")}, - {(char *) STRING_WITH_LEN("enum('ENABLED','DISABLED')")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("on_completion")}, - {(char *) STRING_WITH_LEN("enum('DROP','PRESERVE')")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("sql_mode")}, - {(char *) STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES'," - "'IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION'," - "'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB'," - "'NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40'," - "'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES'," - "'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES'," - "'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER'," - "'HIGH_NOT_PRECEDENCE')")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("comment")}, - {(char *) STRING_WITH_LEN("char(64)")}, - {(char *) STRING_WITH_LEN("utf8")} - } -}; - /* Compares 2 LEX strings regarding case. @@ -187,6 +88,23 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) (uchar *) t.str,t.length, 0); } +/* + Accessor for the singleton instance. + + SYNOPSIS + Events::get_instance() + + RETURN VALUE + address +*/ + +Events * +Events::get_instance() +{ + DBUG_ENTER("Events::get_instance"); + DBUG_RETURN(&singleton); +} + /* Reconstructs interval expression from interval type and expression @@ -201,15 +119,14 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) interval - the interval type (for instance YEAR_MONTH) expression - the value in the lowest entity - RETURNS + RETURN VALUE 0 - OK 1 - Error */ int -Events::reconstruct_interval_expression(String *buf, - interval_type interval, - longlong expression) +Events::reconstruct_interval_expression(String *buf, interval_type interval, + longlong expression) { ulonglong expr= expression; char tmp_buff[128], *end; @@ -341,545 +258,7 @@ int Events::open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table) { - TABLE_LIST tables; - DBUG_ENTER("open_events_table"); - - bzero((char*) &tables, sizeof(tables)); - tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*) "event"; - tables.lock_type= lock_type; - - if (simple_open_n_lock_tables(thd, &tables)) - DBUG_RETURN(1); - - if (table_check_intact(tables.table, Events::FIELD_COUNT, - event_table_fields, - &mysql_event_last_create_time, - ER_CANNOT_LOAD_FROM_TABLE)) - { - close_thread_tables(thd); - DBUG_RETURN(2); - } - *table= tables.table; - tables.table->use_all_columns(); - DBUG_RETURN(0); -} - - -/* - Find row in open mysql.event table representing event - - SYNOPSIS - evex_db_find_event_aux() - thd Thread context - et event_timed object containing dbname & name - table TABLE object for open mysql.event table. - - RETURN VALUE - 0 - Routine found - EVEX_KEY_NOT_FOUND - No routine with given name -*/ - -inline int -evex_db_find_event_aux(THD *thd, Event_timed *et, TABLE *table) -{ - return evex_db_find_event_by_name(thd, et->dbname, et->name, table); -} - - -/* - Find row in open mysql.event table representing event - - SYNOPSIS - evex_db_find_event_by_name() - thd Thread context - dbname Name of event's database - rname Name of the event inside the db - table TABLE object for open mysql.event table. - - RETURN VALUE - 0 - Routine found - EVEX_KEY_NOT_FOUND - No routine with given name -*/ - -int -evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, - const LEX_STRING ev_name, - TABLE *table) -{ - byte key[MAX_KEY_LENGTH]; - DBUG_ENTER("evex_db_find_event_by_name"); - DBUG_PRINT("enter", ("name: %.*s", ev_name.length, ev_name.str)); - - /* - Create key to find row. We have to use field->store() to be able to - handle VARCHAR and CHAR fields. - Assumption here is that the two first fields in the table are - 'db' and 'name' and the first key is the primary key over the - same fields. - */ - if (dbname.length > table->field[Events::FIELD_DB]->field_length || - ev_name.length > table->field[Events::FIELD_NAME]->field_length) - - DBUG_RETURN(EVEX_KEY_NOT_FOUND); - - table->field[Events::FIELD_DB]->store(dbname.str, dbname.length, - &my_charset_bin); - table->field[Events::FIELD_NAME]->store(ev_name.str, ev_name.length, - &my_charset_bin); - - key_copy(key, table->record[0], table->key_info, - table->key_info->key_length); - - if (table->file->index_read_idx(table->record[0], 0, key, - table->key_info->key_length, - HA_READ_KEY_EXACT)) - { - DBUG_PRINT("info", ("Row not found")); - DBUG_RETURN(EVEX_KEY_NOT_FOUND); - } - - DBUG_PRINT("info", ("Row found!")); - DBUG_RETURN(0); -} - - -/* - Puts some data common to CREATE and ALTER EVENT into a row. - - SYNOPSIS - evex_fill_row() - thd THD - table the row to fill out - et Event's data - - RETURN VALUE - 0 - OK - EVEX_GENERAL_ERROR - bad data - EVEX_GET_FIELD_FAILED - field count does not match. table corrupted? - - DESCRIPTION - Used both when an event is created and when it is altered. -*/ - -static int -evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update) -{ - CHARSET_INFO *scs= system_charset_info; - enum Events::enum_table_field field_num; - - DBUG_ENTER("evex_fill_row"); - - DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str)); - DBUG_PRINT("info", ("name =[%s]", et->name.str)); - DBUG_PRINT("info", ("body =[%s]", et->body.str)); - - if (table->field[field_num= Events::FIELD_DEFINER]-> - store(et->definer.str, et->definer.length, scs)) - goto err_truncate; - - if (table->field[field_num= Events::FIELD_DB]-> - store(et->dbname.str, et->dbname.length, scs)) - goto err_truncate; - - if (table->field[field_num= Events::FIELD_NAME]-> - store(et->name.str, et->name.length, scs)) - goto err_truncate; - - /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/ - table->field[Events::FIELD_ON_COMPLETION]-> - store((longlong)et->on_completion, true); - - table->field[Events::FIELD_STATUS]->store((longlong)et->status, true); - - /* - Change the SQL_MODE only if body was present in an ALTER EVENT and of course - always during CREATE EVENT. - */ - if (et->body.str) - { - table->field[Events::FIELD_SQL_MODE]-> - store((longlong)thd->variables.sql_mode, true); - - if (table->field[field_num= Events::FIELD_BODY]-> - store(et->body.str, et->body.length, scs)) - goto err_truncate; - } - - if (et->expression) - { - table->field[Events::FIELD_INTERVAL_EXPR]->set_notnull(); - table->field[Events::FIELD_INTERVAL_EXPR]-> - store((longlong)et->expression, true); - - table->field[Events::FIELD_TRANSIENT_INTERVAL]->set_notnull(); - /* - In the enum (C) intervals start from 0 but in mysql enum valid values start - from 1. Thus +1 offset is needed! - */ - table->field[Events::FIELD_TRANSIENT_INTERVAL]-> - store((longlong)et->interval+1, true); - - table->field[Events::FIELD_EXECUTE_AT]->set_null(); - - if (!et->starts_null) - { - table->field[Events::FIELD_STARTS]->set_notnull(); - table->field[Events::FIELD_STARTS]-> - store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME); - } - - if (!et->ends_null) - { - table->field[Events::FIELD_ENDS]->set_notnull(); - table->field[Events::FIELD_ENDS]-> - store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME); - } - } - else if (et->execute_at.year) - { - table->field[Events::FIELD_INTERVAL_EXPR]->set_null(); - table->field[Events::FIELD_TRANSIENT_INTERVAL]->set_null(); - table->field[Events::FIELD_STARTS]->set_null(); - table->field[Events::FIELD_ENDS]->set_null(); - - table->field[Events::FIELD_EXECUTE_AT]->set_notnull(); - table->field[Events::FIELD_EXECUTE_AT]-> - store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME); - } - else - { - DBUG_ASSERT(is_update); - /* - it is normal to be here when the action is update - this is an error if the action is create. something is borked - */ - } - - ((Field_timestamp *)table->field[Events::FIELD_MODIFIED])->set_time(); - - if (et->comment.str) - { - if (table->field[field_num= Events::FIELD_COMMENT]-> - store(et->comment.str, et->comment.length, scs)) - goto err_truncate; - } - - DBUG_RETURN(0); -err_truncate: - my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), table->field[field_num]->field_name); - DBUG_RETURN(EVEX_GENERAL_ERROR); -} - - -/* - Creates an event in mysql.event - - SYNOPSIS - db_create_event() - thd THD - et Event_timed object containing information for the event - create_if_not If an warning should be generated in case event exists - rows_affected How many rows were affected - - RETURN VALUE - 0 - OK - EVEX_GENERAL_ERROR - Failure - - DESCRIPTION - Creates an event. Relies on evex_fill_row which is shared with - db_update_event. The name of the event is inside "et". -*/ - -int -db_create_event(THD *thd, Event_timed *et, my_bool create_if_not, - uint *rows_affected) -{ - int ret= 0; - CHARSET_INFO *scs= system_charset_info; - TABLE *table; - char olddb[128]; - bool dbchanged= false; - DBUG_ENTER("db_create_event"); - DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str)); - - *rows_affected= 0; - DBUG_PRINT("info", ("open mysql.event for update")); - if (Events::open_event_table(thd, TL_WRITE, &table)) - { - my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - goto err; - } - - DBUG_PRINT("info", ("check existance of an event with the same name")); - if (!evex_db_find_event_aux(thd, et, table)) - { - if (create_if_not) - { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS), - et->name.str); - goto ok; - } - my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), et->name.str); - goto err; - } - - DBUG_PRINT("info", ("non-existant, go forward")); - if ((ret= sp_use_new_db(thd, et->dbname.str,olddb, sizeof(olddb),0, - &dbchanged))) - { - my_error(ER_BAD_DB_ERROR, MYF(0)); - goto err; - } - - restore_record(table, s->default_values); // Get default values for fields - - if (system_charset_info->cset->numchars(system_charset_info, et->dbname.str, - et->dbname.str + et->dbname.length) - > EVEX_DB_FIELD_LEN) - { - my_error(ER_TOO_LONG_IDENT, MYF(0), et->dbname.str); - goto err; - } - if (system_charset_info->cset->numchars(system_charset_info, et->name.str, - et->name.str + et->name.length) - > EVEX_DB_FIELD_LEN) - { - my_error(ER_TOO_LONG_IDENT, MYF(0), et->name.str); - goto err; - } - - if (et->body.length > table->field[Events::FIELD_BODY]->field_length) - { - my_error(ER_TOO_LONG_BODY, MYF(0), et->name.str); - goto err; - } - - if (!(et->expression) && !(et->execute_at.year)) - { - DBUG_PRINT("error", ("neither expression nor execute_at are set!")); - my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0)); - goto err; - } - - ((Field_timestamp *)table->field[Events::FIELD_CREATED])->set_time(); - - /* - evex_fill_row() calls my_error() in case of error so no need to - handle it here - */ - if ((ret= evex_fill_row(thd, table, et, false))) - goto err; - - if (table->file->ha_write_row(table->record[0])) - { - my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret); - goto err; - } - -#ifdef USE_THIS_CODE_AS_TEMPLATE_WHEN_EVENT_REPLICATION_IS_AGREED - if (mysql_bin_log.is_open()) - { - thd->clear_error(); - /* Such a statement can always go directly to binlog, no trans cache */ - thd->binlog_query(THD::MYSQL_QUERY_TYPE, thd->query, thd->query_length, - FALSE, FALSE); - } -#endif - - *rows_affected= 1; -ok: - if (dbchanged) - (void) mysql_change_db(thd, olddb, 1); - if (table) - close_thread_tables(thd); - DBUG_RETURN(EVEX_OK); - -err: - if (dbchanged) - (void) mysql_change_db(thd, olddb, 1); - if (table) - close_thread_tables(thd); - DBUG_RETURN(EVEX_GENERAL_ERROR); -} - - -/* - Used to execute ALTER EVENT. Pendant to Events::update_event(). - - SYNOPSIS - db_update_event() - thd THD - sp_name the name of the event to alter - et event's data - - RETURN VALUE - 0 OK - EVEX_GENERAL_ERROR Error occured (my_error() called) - - NOTES - sp_name is passed since this is the name of the event to - alter in case of RENAME TO. -*/ - -static int -db_update_event(THD *thd, Event_timed *et, sp_name *new_name) -{ - CHARSET_INFO *scs= system_charset_info; - TABLE *table; - int ret= EVEX_OPEN_TABLE_FAILED; - DBUG_ENTER("db_update_event"); - DBUG_PRINT("enter", ("dbname: %.*s", et->dbname.length, et->dbname.str)); - DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str)); - DBUG_PRINT("enter", ("user: %.*s", et->definer.length, et->definer.str)); - if (new_name) - DBUG_PRINT("enter", ("rename to: %.*s", new_name->m_name.length, - new_name->m_name.str)); - - if (Events::open_event_table(thd, TL_WRITE, &table)) - { - my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - goto err; - } - - /* first look whether we overwrite */ - if (new_name) - { - if (!sortcmp_lex_string(et->name, new_name->m_name, scs) && - !sortcmp_lex_string(et->dbname, new_name->m_db, scs)) - { - my_error(ER_EVENT_SAME_NAME, MYF(0), et->name.str); - goto err; - } - - if (!evex_db_find_event_by_name(thd,new_name->m_db,new_name->m_name,table)) - { - my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str); - goto err; - } - } - /* - ...and then if there is such an event. Don't exchange the blocks - because you will get error 120 from table handler because new_name will - overwrite the key and SE will tell us that it cannot find the already found - row (copied into record[1] later - */ - if (EVEX_KEY_NOT_FOUND == evex_db_find_event_aux(thd, et, table)) - { - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str); - goto err; - } - - store_record(table,record[1]); - - /* Don't update create on row update. */ - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - - /* - evex_fill_row() calls my_error() in case of error so no need to - handle it here - */ - if ((ret= evex_fill_row(thd, table, et, true))) - goto err; - - if (new_name) - { - table->field[Events::FIELD_DB]-> - store(new_name->m_db.str, new_name->m_db.length, scs); - table->field[Events::FIELD_NAME]-> - store(new_name->m_name.str, new_name->m_name.length, scs); - } - - if ((ret= table->file->ha_update_row(table->record[1], table->record[0]))) - { - my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret); - goto err; - } - - /* close mysql.event or we crash later when loading the event from disk */ - close_thread_tables(thd); - DBUG_RETURN(0); - -err: - if (table) - close_thread_tables(thd); - DBUG_RETURN(EVEX_GENERAL_ERROR); -} - - -/* - Looks for a named event in mysql.event and in case of success returns - an object will data loaded from the table. - - SYNOPSIS - db_find_event() - thd THD - name the name of the event to find - ett event's data if event is found - tbl TABLE object to use when not NULL - - NOTES - 1) Use sp_name for look up, return in **ett if found - 2) tbl is not closed at exit - - RETURN VALUE - 0 ok In this case *ett is set to the event - # error *ett == 0 -*/ - -int -db_find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl, - MEM_ROOT *root) -{ - TABLE *table; - int ret; - Event_timed *et= NULL; - DBUG_ENTER("db_find_event"); - DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - - if (!root) - root= &evex_mem_root; - - if (tbl) - table= tbl; - else if (Events::open_event_table(thd, TL_READ, &table)) - { - my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - ret= EVEX_GENERAL_ERROR; - goto done; - } - - if ((ret= evex_db_find_event_by_name(thd, name->m_db, name->m_name, table))) - { - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str); - goto done; - } - et= new Event_timed; - - /* - 1)The table should not be closed beforehand. ::load_from_row() only loads - and does not compile - - 2)::load_from_row() is silent on error therefore we emit error msg here - */ - if ((ret= et->load_from_row(root, table))) - { - my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0)); - goto done; - } - -done: - if (ret) - { - delete et; - et= 0; - } - /* don't close the table if we haven't opened it ourselves */ - if (!tbl && table) - close_thread_tables(thd); - *ett= et; - DBUG_RETURN(ret); + return db_repository->open_event_table(thd, lock_type, table); } @@ -904,8 +283,8 @@ done: */ int -Events::create_event(THD *thd, Event_timed *et, uint create_options, - uint *rows_affected) +Events::create_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, + uint create_options, uint *rows_affected) { int ret; @@ -913,9 +292,10 @@ Events::create_event(THD *thd, Event_timed *et, uint create_options, DBUG_PRINT("enter", ("name: %*s options:%d", et->name.length, et->name.str, create_options)); - if (!(ret = db_create_event(thd, et, - create_options & HA_LEX_CREATE_IF_NOT_EXISTS, - rows_affected))) + if (!(ret= db_repository-> + create_event(thd, et, + create_options & HA_LEX_CREATE_IF_NOT_EXISTS, + rows_affected))) { Event_scheduler *scheduler= Event_scheduler::get_instance(); if (scheduler->initialized() && @@ -948,8 +328,8 @@ Events::create_event(THD *thd, Event_timed *et, uint create_options, */ int -Events::update_event(THD *thd, Event_timed *et, sp_name *new_name, - uint *rows_affected) +Events::update_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, + sp_name *new_name, uint *rows_affected) { int ret; @@ -960,7 +340,7 @@ Events::update_event(THD *thd, Event_timed *et, sp_name *new_name, crash later in the code when loading and compiling the new definition. Also on error conditions my_error() is called so no need to handle here */ - if (!(ret= db_update_event(thd, et, new_name))) + if (!(ret= db_repository->update_event(thd, et, new_name))) { Event_scheduler *scheduler= Event_scheduler::get_instance(); if (scheduler->initialized() && @@ -977,74 +357,9 @@ Events::update_event(THD *thd, Event_timed *et, sp_name *new_name, Drops an event SYNOPSIS - db_drop_event() - thd THD - et event's name - drop_if_exists if set and the event not existing => warning onto the stack - rows_affected affected number of rows is returned heres - - RETURN VALUE - 0 OK - !0 Error (my_error() called) -*/ - -int db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists, - uint *rows_affected) -{ - TABLE *table; - Open_tables_state backup; - int ret; - - DBUG_ENTER("db_drop_event"); - ret= EVEX_OPEN_TABLE_FAILED; - - thd->reset_n_backup_open_tables_state(&backup); - if (Events::open_event_table(thd, TL_WRITE, &table)) - { - my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - goto done; - } - - if (!(ret= evex_db_find_event_aux(thd, et, table))) - { - if ((ret= table->file->ha_delete_row(table->record[0]))) - { - my_error(ER_EVENT_CANNOT_DELETE, MYF(0)); - goto done; - } - } - else if (ret == EVEX_KEY_NOT_FOUND) - { - if (drop_if_exists) - { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), - "Event", et->name.str); - ret= 0; - } else - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str); - goto done; - } - - -done: - /* - evex_drop_event() is used by Event_timed::drop therefore - we have to close our thread tables. - */ - close_thread_tables(thd); - thd->restore_backup_open_tables_state(&backup); - DBUG_RETURN(ret); -} - - -/* - Drops an event - - SYNOPSIS Events::drop_event() thd THD - et event's name + name event's name drop_if_exists if set and the event not existing => warning onto the stack rows_affected affected number of rows is returned heres @@ -1054,19 +369,20 @@ done: */ int -Events::drop_event(THD *thd, Event_timed *et, bool drop_if_exists, - uint *rows_affected) +Events::drop_event(THD *thd, sp_name *name, bool drop_if_exists, + uint *rows_affected) { int ret; DBUG_ENTER("Events::drop_event"); - if (!(ret= db_drop_event(thd, et, drop_if_exists, rows_affected))) + + if (!(ret= db_repository->drop_event(thd, name->m_db, name->m_name, + drop_if_exists, rows_affected))) { Event_scheduler *scheduler= Event_scheduler::get_instance(); - if (scheduler->initialized() && (ret= scheduler->drop_event(thd, et))) + if (scheduler->initialized() && (ret= scheduler->drop_event(thd, name))) my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret); } - DBUG_RETURN(ret); } @@ -1091,11 +407,11 @@ Events::show_create_event(THD *thd, sp_name *spn) Event_timed *et= NULL; Open_tables_state backup; - DBUG_ENTER("evex_update_event"); + DBUG_ENTER("Events::show_create_event"); DBUG_PRINT("enter", ("name: %*s", spn->m_name.length, spn->m_name.str)); thd->reset_n_backup_open_tables_state(&backup); - ret= db_find_event(thd, spn, &et, NULL, thd->mem_root); + ret= db_repository->find_event(thd, spn, &et, NULL, thd->mem_root); thd->restore_backup_open_tables_state(&backup); if (!ret) @@ -1167,76 +483,14 @@ Events::drop_schema_events(THD *thd, char *db) DBUG_PRINT("enter", ("dropping events from %s", db)); Event_scheduler *scheduler= Event_scheduler::get_instance(); - if (scheduler->initialized()) - ret= scheduler->drop_schema_events(thd, &db_lex); - else - ret= db_drop_events_from_table(thd, &db_lex); + ret= scheduler->drop_schema_events(thd, db_lex); + ret= db_repository->drop_schema_events(thd, db_lex); DBUG_RETURN(ret); } /* - Drops all events in the selected database, from mysql.event. - - SYNOPSIS - evex_drop_db_events_from_table() - thd Thread - db Schema name - - RETURN VALUE - 0 OK - !0 Error from ha_delete_row -*/ - -int -db_drop_events_from_table(THD *thd, LEX_STRING *db) -{ - int ret; - TABLE *table; - READ_RECORD read_record_info; - DBUG_ENTER("db_drop_events_from_table"); - DBUG_PRINT("info", ("dropping events from %s", db->str)); - - if ((ret= Events::open_event_table(thd, TL_WRITE, &table))) - { - if (my_errno != ENOENT) - sql_print_error("Table mysql.event is damaged. Got error %d on open", - my_errno); - DBUG_RETURN(ret); - } - /* only enabled events are in memory, so we go now and delete the rest */ - init_read_record(&read_record_info, thd, table, NULL, 1, 0); - while (!(read_record_info.read_record(&read_record_info)) && !ret) - { - char *et_db= get_field(thd->mem_root, - table->field[Events::FIELD_DB]); - - LEX_STRING et_db_lex= {et_db, strlen(et_db)}; - DBUG_PRINT("info", ("Current event %s.%s", et_db, - get_field(thd->mem_root, - table->field[Events::FIELD_NAME]))); - - if (!sortcmp_lex_string(et_db_lex, *db, system_charset_info)) - { - DBUG_PRINT("info", ("Dropping")); - if ((ret= table->file->ha_delete_row(table->record[0]))) - my_error(ER_EVENT_DROP_FAILED, MYF(0), - get_field(thd->mem_root, - table->field[Events::FIELD_NAME])); - } - } - end_read_record(&read_record_info); - thd->version--; /* Force close to free memory */ - - close_thread_tables(thd); - - DBUG_RETURN(ret); -} - - - -/* Inits the scheduler's structures. SYNOPSIS @@ -1254,14 +508,16 @@ int Events::init() { int ret= 0; + Event_db_repository *db_repo; DBUG_ENTER("Events::init"); + db_repository->init_repository(); /* it should be an assignment! */ if (opt_event_scheduler) { Event_scheduler *scheduler= Event_scheduler::get_instance(); DBUG_ASSERT(opt_event_scheduler == 1 || opt_event_scheduler == 2); - DBUG_RETURN(scheduler->init() || + DBUG_RETURN(scheduler->init(db_repository) || (opt_event_scheduler == 1? scheduler->start(): scheduler->start_suspended())); } @@ -1273,16 +529,17 @@ Events::init() Cleans up scheduler's resources. Called at server shutdown. SYNOPSIS - Events::shutdown() + Events::deinit() NOTES This function is not synchronized. */ void -Events::shutdown() +Events::deinit() { - DBUG_ENTER("Events::shutdown"); + DBUG_ENTER("Events::deinit"); + Event_scheduler *scheduler= Event_scheduler::get_instance(); if (scheduler->initialized()) { @@ -1290,26 +547,9 @@ Events::shutdown() scheduler->destroy(); } - DBUG_VOID_RETURN; -} - - -/* - Proxy for Event_scheduler::dump_internal_status - - SYNOPSIS - Events::dump_internal_status() - thd Thread - - RETURN VALUE - 0 OK - !0 Error -*/ + db_repository->deinit_repository(); -int -Events::dump_internal_status(THD *thd) -{ - return Event_scheduler::dump_internal_status(thd); + DBUG_VOID_RETURN; } @@ -1324,6 +564,7 @@ Events::dump_internal_status(THD *thd) void Events::init_mutexes() { + db_repository= new Event_db_repository; Event_scheduler::init_mutexes(); } @@ -1339,4 +580,61 @@ void Events::destroy_mutexes() { Event_scheduler::destroy_mutexes(); + delete db_repository; + db_repository= NULL; +} + + +/* + Proxy for Event_scheduler::dump_internal_status + + SYNOPSIS + Events::dump_internal_status() + thd Thread + + RETURN VALUE + 0 OK + !0 Error +*/ + +int +Events::dump_internal_status(THD *thd) +{ + return Event_scheduler::dump_internal_status(thd); +} + + +/* + Proxy for Event_db_repository::fill_schema_events. + Callback for I_S from sql_show.cc + + SYNOPSIS + Events::fill_schema_events() + thd Thread + tables The schema table + cond Unused + + RETURN VALUE + 0 OK + !0 Error +*/ + +int +Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) +{ + char *db= NULL; + DBUG_ENTER("Events::fill_schema_events"); + /* + If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to + be NULL. Let's do an assert anyway. + */ + if (thd->lex->orig_sql_command == SQLCOM_SHOW_EVENTS) + { + DBUG_ASSERT(thd->lex->select_lex.db); + if (check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0, + is_schema_db(thd->lex->select_lex.db))) + DBUG_RETURN(1); + db= thd->lex->select_lex.db; + } + DBUG_RETURN(get_instance()->db_repository->fill_schema_events(thd, tables, db)); } diff --git a/sql/events.h b/sql/events.h index 66cce6e7777..45a0db13980 100644 --- a/sql/events.h +++ b/sql/events.h @@ -16,78 +16,96 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - +class sp_name; class Event_timed; +class Event_parse_data; +class Event_db_repository; + +/* Return codes */ +enum enum_events_error_code +{ + OP_OK= 0, + OP_NOT_RUNNING, + OP_CANT_KILL, + OP_CANT_INIT, + OP_DISABLED_EVENT, + OP_LOAD_ERROR, + OP_ALREADY_EXISTS +}; + +int +sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs); + class Events { public: + /* + Quite NOT the best practice and will be removed once + Event_timed::drop() and Event_timed is fixed not do drop directly + or other scheme will be found. + */ + friend class Event_timed; + static ulong opt_event_scheduler; static TYPELIB opt_typelib; - enum enum_table_field - { - FIELD_DB = 0, - FIELD_NAME, - FIELD_BODY, - FIELD_DEFINER, - FIELD_EXECUTE_AT, - FIELD_INTERVAL_EXPR, - FIELD_TRANSIENT_INTERVAL, - FIELD_CREATED, - FIELD_MODIFIED, - FIELD_LAST_EXECUTED, - FIELD_STARTS, - FIELD_ENDS, - FIELD_STATUS, - FIELD_ON_COMPLETION, - FIELD_SQL_MODE, - FIELD_COMMENT, - FIELD_COUNT /* a cool trick to count the number of fields :) */ - }; + int + init(); + + void + deinit(); - static int - create_event(THD *thd, Event_timed *et, uint create_options, - uint *rows_affected); + void + init_mutexes(); + + void + destroy_mutexes(); - static int - update_event(THD *thd, Event_timed *et, sp_name *new_name, - uint *rows_affected); + static Events* + get_instance(); - static int - drop_event(THD *thd, Event_timed *et, bool drop_if_exists, - uint *rows_affected); + int + create_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, + uint create_options, uint *rows_affected); - static int + int + update_event(THD *thd, Event_timed *et, Event_parse_data *parse_data, + sp_name *new_name, uint *rows_affected); + + int + drop_event(THD *thd, sp_name *name, bool drop_if_exists, uint *rows_affected); + + int + drop_schema_events(THD *thd, char *db); + + int open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); - static int + int show_create_event(THD *thd, sp_name *spn); + /* Needed for both SHOW CREATE EVENT and INFORMATION_SCHEMA */ static int reconstruct_interval_expression(String *buf, interval_type interval, longlong expression); static int - drop_schema_events(THD *thd, char *db); + fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */); - static int + int dump_internal_status(THD *thd); - - static int - init(); - - static void - shutdown(); - - static void - init_mutexes(); - - static void - destroy_mutexes(); + Event_db_repository *db_repository; private: + /* Singleton DP is used */ + Events(){} + ~Events(){} + + /* Singleton instance */ + static Events singleton; + /* Prevent use of these */ Events(const Events &); void operator=(Events &); diff --git a/sql/events_priv.h b/sql/events_priv.h deleted file mode 100644 index ed02cb7055b..00000000000 --- a/sql/events_priv.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef _EVENT_PRIV_H_ -#define _EVENT_PRIV_H_ -/* Copyright (C) 2004-2006 MySQL AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#define EVENT_EXEC_STARTED 0 -#define EVENT_EXEC_ALREADY_EXEC 1 -#define EVENT_EXEC_CANT_FORK 2 - -#define EVEX_DB_FIELD_LEN 64 -#define EVEX_NAME_FIELD_LEN 64 -#define EVEX_MAX_INTERVAL_VALUE 2147483647L - -class Event_timed; - -int -evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, - const LEX_STRING ev_name, - TABLE *table); - -int -db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists, - uint *rows_affected); -int -db_find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl, - MEM_ROOT *root); - -int -db_create_event(THD *thd, Event_timed *et, my_bool create_if_not, - uint *rows_affected); - -int -db_drop_events_from_table(THD *thd, LEX_STRING *db); - -int -sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs); - -/* Compares only the name part of the identifier */ -bool -event_timed_name_equal(Event_timed *et, LEX_STRING *name); - -/* Compares only the schema part of the identifier */ -bool -event_timed_db_equal(Event_timed *et, LEX_STRING *db); - -/* - Compares only the definer part of the identifier. Use during DROP USER - to drop user's events. (Still not implemented) -*/ -bool -event_timed_definer_equal(Event_timed *et, LEX_STRING *definer); - -/* Compares the whole identifier*/ -bool -event_timed_identifier_equal(Event_timed *a, Event_timed *b); - - -bool -change_security_context(THD *thd, LEX_STRING user, LEX_STRING host, - LEX_STRING db, Security_context *s_ctx, - Security_context **backup); - -void -restore_security_context(THD *thd, Security_context *backup); - -#endif /* _EVENT_PRIV_H_ */ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 4ab1b365f73..a27be384ee2 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -883,7 +883,7 @@ static void close_connections(void) } (void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list - Events::shutdown(); + Events::get_instance()->deinit(); end_slave(); if (thread_count) @@ -1321,7 +1321,7 @@ static void clean_up_mutexes() (void) pthread_mutex_destroy(&LOCK_bytes_sent); (void) pthread_mutex_destroy(&LOCK_bytes_received); (void) pthread_mutex_destroy(&LOCK_user_conn); - Events::destroy_mutexes(); + Events::get_instance()->destroy_mutexes(); #ifdef HAVE_OPENSSL (void) pthread_mutex_destroy(&LOCK_des_key_file); #ifndef HAVE_YASSL @@ -2884,7 +2884,7 @@ static int init_thread_environment() (void) pthread_mutex_init(&LOCK_server_started, MY_MUTEX_INIT_FAST); (void) pthread_cond_init(&COND_server_started,NULL); sp_cache_init(); - Events::init_mutexes(); + Events::get_instance()->init_mutexes(); /* Parameter for threads created for connections */ (void) pthread_attr_init(&connection_attrib); (void) pthread_attr_setdetachstate(&connection_attrib, @@ -3673,7 +3673,7 @@ we force server id to 2, but this MySQL server will not act as a slave."); if (!opt_noacl) { - Events::init(); + Events::get_instance()->init(); } #if defined(__NT__) || defined(HAVE_SMEM) handle_connections_methods(); diff --git a/sql/set_var.cc b/sql/set_var.cc index b0ecc7eccef..6c3606c9150 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -3892,7 +3892,7 @@ byte *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b) bool sys_var_event_scheduler::update(THD *thd, set_var *var) { - enum Event_scheduler::enum_error_code res; + int res; Event_scheduler *scheduler= Event_scheduler::get_instance(); /* here start the thread if not running. */ DBUG_ENTER("sys_var_event_scheduler::update"); diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 476bc2f2f02..ac4f2dd9237 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5839,3 +5839,6 @@ ER_CANT_ACTIVATE_LOG eng "Cannot activate '%-.64s' log." ER_RBR_NOT_AVAILABLE eng "The server was not built with row-based replication" +ER_EVENT_RECURSIVITY_FORBIDDEN + eng "Recursivity of EVENT DDL statements is forbidden when body is present" + diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 7fa0173ddea..f69f830ba8c 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2073,6 +2073,63 @@ bool Security_context::set_user(char *user_arg) return user == 0; } +/* + Switches the security context + SYNOPSIS + THD::change_security_context() + user The user + host The host of the user + db The schema for which the security_ctx will be loaded + s_ctx Security context to load state into + backup Where to store the old context + + RETURN VALUE + FALSE OK + TRUE Error (generates error too) +*/ + +bool +THD::change_security_context(LEX_STRING user, LEX_STRING host, + LEX_STRING db, Security_context *s_ctx, + Security_context **backup) +{ + DBUG_ENTER("change_security_context"); + DBUG_PRINT("info",("%s@%s@%s", user.str, host.str, db.str)); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + s_ctx->init(); + *backup= 0; + if (acl_getroot_no_password(s_ctx, user.str, host.str, host.str, db.str)) + { + my_error(ER_NO_SUCH_USER, MYF(0), user.str, host.str); + DBUG_RETURN(TRUE); + } + *backup= security_ctx; + security_ctx= s_ctx; +#endif + DBUG_RETURN(FALSE); +} + + +/* + Restores the security context + SYNOPSIS + restore_security_context() + thd Thread + backup Context to switch to +*/ + +void +THD::restore_security_context(Security_context *backup) +{ + DBUG_ENTER("restore_security_context"); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (backup) + security_ctx= backup; +#endif + DBUG_VOID_RETURN; +} + + /**************************************************************************** Handling of open and locked tables states. diff --git a/sql/sql_class.h b/sql/sql_class.h index 5222e75f309..d7c35ec545e 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -868,6 +868,14 @@ public: char *db, *catalog; Security_context main_security_ctx; Security_context *security_ctx; + + bool + change_security_context(LEX_STRING user, LEX_STRING host, + LEX_STRING db, Security_context *s_ctx, + Security_context **backup); + + void + restore_security_context(Security_context *backup); /* remote (peer) port */ uint16 peer_port; diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 8dd62fc8494..83516da38d4 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -904,7 +904,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) exit: (void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */ - error= Events::drop_schema_events(thd, db); + error= Events::get_instance()->drop_schema_events(thd, db); /* If this database was the client's selected database, we silently change the client's selected database to nothing (to have an empty SELECT DATABASE() diff --git a/sql/sql_lex.h b/sql/sql_lex.h index a46aaa0bab7..e2cf213cd17 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -28,6 +28,7 @@ class sp_pcontext; class st_alter_tablespace; class partition_info; class Event_timed; +class Event_parse_data; #ifdef MYSQL_SERVER /* @@ -1017,6 +1018,7 @@ typedef struct st_lex : public Query_tables_list st_sp_chistics sp_chistics; Event_timed *et; + Event_parse_data *event_parse_data; bool et_compile_phase; bool only_view; /* used for SHOW CREATE TABLE/VIEW */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1ecc7b78315..5ec4e7b3b68 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -27,7 +27,7 @@ #include "sp.h" #include "sp_cache.h" #include "events.h" -#include "event_timed.h" +#include "event_data_objects.h" #ifdef HAVE_OPENSSL /* @@ -3831,7 +3831,6 @@ end_with_restore_list: } case SQLCOM_CREATE_EVENT: case SQLCOM_ALTER_EVENT: - case SQLCOM_DROP_EVENT: { uint rows_affected= 1; DBUG_ASSERT(lex->et); @@ -3860,17 +3859,15 @@ end_with_restore_list: switch (lex->sql_command) { case SQLCOM_CREATE_EVENT: - res= Events::create_event(thd, lex->et, - (uint) lex->create_info.options, - &rows_affected); + res= Events::get_instance()-> + create_event(thd, lex->et, lex->event_parse_data, + (uint) lex->create_info.options, &rows_affected); break; case SQLCOM_ALTER_EVENT: - res= Events::update_event(thd, lex->et, lex->spname, - &rows_affected); + res= Events::get_instance()-> + update_event(thd, lex->et, lex->event_parse_data, + lex->spname, &rows_affected); break; - case SQLCOM_DROP_EVENT: - res= Events::drop_event(thd, lex->et, lex->drop_if_exists, - &rows_affected); default:; } DBUG_PRINT("info", ("CREATE/ALTER/DROP returned error code=%d af_rows=%d", @@ -3889,10 +3886,10 @@ end_with_restore_list: break; } + case SQLCOM_DROP_EVENT: case SQLCOM_SHOW_CREATE_EVENT: { DBUG_ASSERT(lex->spname); - DBUG_ASSERT(lex->et); if (! lex->spname->m_db.str) { my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); @@ -3906,15 +3903,31 @@ end_with_restore_list: if (lex->spname->m_name.length > NAME_LEN) { my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str); + /* this jumps to the end of the function and skips own messaging */ goto error; } - res= Events::show_create_event(thd, lex->spname); + + if (lex->sql_command == SQLCOM_SHOW_CREATE_EVENT) + res= Events::get_instance()->show_create_event(thd, lex->spname); + else + { + uint rows_affected= 1; + if (end_active_trans(thd)) + { + res= -1; + break; + } + if (!(res= Events::get_instance()->drop_event(thd, lex->spname, + lex->drop_if_exists, + &rows_affected))) + send_ok(thd, rows_affected); + } break; } #ifndef DBUG_OFF case SQLCOM_SHOW_SCHEDULER_STATUS: { - res= Events::dump_internal_status(thd); + res= Events::get_instance()->dump_internal_status(thd); break; } #endif diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 8f8c84c2db5..86a9c380ee1 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -27,7 +27,7 @@ #include "authors.h" #include "contributors.h" #include "events.h" -#include "event_timed.h" +#include "event_data_objects.h" #include <my_dir.h> #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -4166,7 +4166,7 @@ extern LEX_STRING interval_type_to_name[]; 1 Error */ -static int +int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) { const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS; @@ -4301,183 +4301,6 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) } -/* - Performs an index scan of event_table (mysql.event) and fills schema_table. - - Synopsis - events_table_index_read_for_db() - thd Thread - schema_table The I_S.EVENTS table - event_table The event table to use for loading (mysql.event) - - Returns - 0 OK - 1 Error -*/ - -static -int events_table_index_read_for_db(THD *thd, TABLE *schema_table, - TABLE *event_table) -{ - int ret=0; - CHARSET_INFO *scs= system_charset_info; - KEY *key_info; - uint key_len; - byte *key_buf= NULL; - LINT_INIT(key_buf); - - DBUG_ENTER("schema_events_do_index_scan"); - - DBUG_PRINT("info", ("Using prefix scanning on PK")); - event_table->file->ha_index_init(0, 1); - event_table->field[Events::FIELD_DB]-> - store(thd->lex->select_lex.db, strlen(thd->lex->select_lex.db), scs); - key_info= event_table->key_info; - key_len= key_info->key_part[0].store_length; - - if (!(key_buf= (byte *)alloc_root(thd->mem_root, key_len))) - { - ret= 1; - /* don't send error, it would be done by sql_alloc_error_handler() */ - } - else - { - key_copy(key_buf, event_table->record[0], key_info, key_len); - if (!(ret= event_table->file->index_read(event_table->record[0], key_buf, - key_len, HA_READ_PREFIX))) - { - DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret)); - do - { - ret= copy_event_to_schema_table(thd, schema_table, event_table); - if (ret == 0) - ret= event_table->file->index_next_same(event_table->record[0], - key_buf, key_len); - } while (ret == 0); - } - DBUG_PRINT("info", ("Scan finished. ret=%d", ret)); - } - event_table->file->ha_index_end(); - /* ret is guaranteed to be != 0 */ - if (ret == HA_ERR_END_OF_FILE || ret == HA_ERR_KEY_NOT_FOUND) - DBUG_RETURN(0); - DBUG_RETURN(1); -} - - -/* - Performs a table scan of event_table (mysql.event) and fills schema_table. - - Synopsis - events_table_scan_all() - thd Thread - schema_table The I_S.EVENTS in memory table - event_table The event table to use for loading. - - Returns - 0 OK - 1 Error -*/ - -static -int events_table_scan_all(THD *thd, TABLE *schema_table, - TABLE *event_table) -{ - int ret; - READ_RECORD read_record_info; - - DBUG_ENTER("schema_events_do_table_scan"); - init_read_record(&read_record_info, thd, event_table, NULL, 1, 0); - - /* - rr_sequential, in read_record(), returns 137==HA_ERR_END_OF_FILE, - but rr_handle_error returns -1 for that reason. Thus, read_record() - returns -1 eventually. - */ - do - { - ret= read_record_info.read_record(&read_record_info); - if (ret == 0) - ret= copy_event_to_schema_table(thd, schema_table, event_table); - } - while (ret == 0); - - DBUG_PRINT("info", ("Scan finished. ret=%d", ret)); - end_read_record(&read_record_info); - - /* ret is guaranteed to be != 0 */ - DBUG_RETURN(ret == -1? 0:1); -} - - -/* - Fills I_S.EVENTS with data loaded from mysql.event. Also used by - SHOW EVENTS - - Synopsis - fill_schema_events() - thd Thread - tables The schema table - cond Unused - - Returns - 0 OK - 1 Error -*/ - -int fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) -{ - TABLE *schema_table= tables->table; - TABLE *event_table= NULL; - Open_tables_state backup; - int ret= 0; - - DBUG_ENTER("fill_schema_events"); - /* - If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to - be NULL. Let's do an assert anyway. - */ - if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS) - { - DBUG_ASSERT(thd->lex->select_lex.db); - if (check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0, - is_schema_db(thd->lex->select_lex.db))) - DBUG_RETURN(1); - } - - DBUG_PRINT("info",("db=%s", thd->lex->select_lex.db? - thd->lex->select_lex.db:"(null)")); - - thd->reset_n_backup_open_tables_state(&backup); - if (Events::open_event_table(thd, TL_READ, &event_table)) - { - sql_print_error("Table mysql.event is damaged."); - thd->restore_backup_open_tables_state(&backup); - DBUG_RETURN(1); - } - - /* - 1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order - thus we won't order it. OTOH, SHOW EVENTS will be - ordered. - 2. SHOW EVENTS => PRIMARY KEY with prefix scanning on (db) - Reasoning: Events are per schema, therefore a scan over an index - will save use from doing a table scan and comparing - every single row's `db` with the schema which we show. - */ - if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS) - ret= events_table_index_read_for_db(thd, schema_table, event_table); - else - ret= events_table_scan_all(thd, schema_table, event_table); - - close_thread_tables(thd); - thd->restore_backup_open_tables_state(&backup); - - DBUG_PRINT("info", ("Return code=%d", ret)); - DBUG_RETURN(ret); -} - - int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond) { DBUG_ENTER("fill_open_tables"); @@ -5574,7 +5397,7 @@ ST_SCHEMA_TABLE schema_tables[]= {"ENGINES", engines_fields_info, create_schema_table, fill_schema_engines, make_old_format, 0, -1, -1, 0}, {"EVENTS", events_fields_info, create_schema_table, - fill_schema_events, make_old_format, 0, -1, -1, 0}, + Events::fill_schema_events, make_old_format, 0, -1, -1, 0}, {"FILES", files_fields_info, create_schema_table, fill_schema_files, 0, 0, -1, -1, 0}, {"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table, diff --git a/sql/sql_show.h b/sql/sql_show.h index 6fce5e94ca3..681d1232b39 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -14,4 +14,6 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, HA_CREATE_INFO *create_info_arg); int view_store_create_info(THD *thd, TABLE_LIST *table, String *buff); +int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table); + #endif /* SQL_SHOW_H */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 0632e2298cd..bda51faeeb8 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -38,7 +38,7 @@ #include "sp_pcontext.h" #include "sp_rcontext.h" #include "sp.h" -#include "event_timed.h" +#include "event_data_objects.h" #include <myisam.h> #include <myisammrg.h> @@ -880,7 +880,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa load_data opt_field_or_var_spec fields_or_vars opt_load_data_set_spec definer view_replace_or_algorithm view_replace view_algorithm_opt - view_algorithm view_or_trigger_or_sp view_or_trigger_or_sp_tail + view_algorithm view_or_trigger_or_sp_or_event + view_or_trigger_or_sp_or_event_tail view_suid view_tail view_list_opt view_list view_select view_check_option trigger_tail sp_tail install uninstall partition_entry binlog_base64_event @@ -1257,7 +1258,33 @@ create: lex->name=$4.str; lex->create_info.options=$3; } - | CREATE EVENT_SYM opt_if_not_exists sp_name + | CREATE + { + Lex->create_view_mode= VIEW_CREATE_NEW; + Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; + Lex->create_view_suid= TRUE; + } + view_or_trigger_or_sp_or_event + {} + | CREATE USER clear_privileges grant_list + { + Lex->sql_command = SQLCOM_CREATE_USER; + } + | CREATE LOGFILE_SYM GROUP logfile_group_info + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP; + } + | CREATE TABLESPACE tablespace_info + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE; + } + ; + + +event_tail: + EVENT_SYM opt_if_not_exists sp_name /* BE CAREFUL when you add a new rule to update the block where YYTHD->client_capabilities is set back to original value @@ -1268,18 +1295,19 @@ create: if (lex->et) { /* - Recursive events are not possible because recursive SPs - are not also possible. lex->sp_head is not stacked. + Recursive CREATE EVENT statement are not possible because + recursive SPs are not also possible. lex->sp_head is not stacked. */ - // ToDo Andrey : Change the error message my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT"); YYABORT; } - lex->create_info.options= $3; + lex->create_info.options= $2; if (!(lex->et= new(YYTHD->mem_root) Event_timed())) // implicitly calls Event_timed::init() YYABORT; + if (!(lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) + YYABORT; /* We have to turn of CLIENT_MULTI_QUERIES while parsing a @@ -1289,9 +1317,12 @@ create: $<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); + + lex->event_parse_data->identifier= $3; + if (!lex->et_compile_phase) { - lex->et->init_name(YYTHD, $4); + lex->et->init_name(YYTHD, $3); lex->et->init_definer(YYTHD); } } @@ -1303,13 +1334,12 @@ create: { /* Restore flag if it was cleared above - $1 - CREATE - $2 - EVENT_SYM - $3 - opt_if_not_exists - $4 - sp_name - $5 - the block above + $1 - EVENT_SYM + $2 - opt_if_not_exists + $3 - sp_name + $4 - the block above */ - YYTHD->client_capabilities |= $<ulong_num>5; + YYTHD->client_capabilities |= $<ulong_num>4; /* sql_command is set here because some rules in ev_sql_stmt @@ -1317,33 +1347,12 @@ create: */ Lex->sql_command= SQLCOM_CREATE_EVENT; } - | CREATE - { - Lex->create_view_mode= VIEW_CREATE_NEW; - Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; - Lex->create_view_suid= TRUE; - } - view_or_trigger_or_sp - {} - | CREATE USER clear_privileges grant_list - { - Lex->sql_command = SQLCOM_CREATE_USER; - } - | CREATE LOGFILE_SYM GROUP logfile_group_info - { - LEX *lex= Lex; - lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP; - } - | CREATE TABLESPACE tablespace_info - { - LEX *lex= Lex; - lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE; - } - ; ev_schedule_time: EVERY_SYM expr interval { + Lex->event_parse_data->item_expression= $2; + Lex->event_parse_data->interval= $3; LEX *lex=Lex; if (!lex->et_compile_phase) { @@ -1365,6 +1374,7 @@ ev_schedule_time: EVERY_SYM expr interval ev_ends | AT_SYM expr { + Lex->event_parse_data->item_execute_at= $2; LEX *lex=Lex; if (!lex->et_compile_phase) { @@ -1395,6 +1405,7 @@ ev_schedule_time: EVERY_SYM expr interval opt_ev_status: /* empty */ { $$= 0; } | ENABLE_SYM { + Lex->event_parse_data->status= Event_parse_data::ENABLED; LEX *lex=Lex; if (!lex->et_compile_phase) lex->et->status= Event_timed::ENABLED; @@ -1402,6 +1413,7 @@ opt_ev_status: /* empty */ { $$= 0; } } | DISABLE_SYM { + Lex->event_parse_data->status= Event_parse_data::DISABLED; LEX *lex=Lex; if (!lex->et_compile_phase) @@ -1412,10 +1424,12 @@ opt_ev_status: /* empty */ { $$= 0; } ev_starts: /* empty */ { + Lex->event_parse_data->item_starts= new Item_func_now_local(); Lex->et->init_starts(YYTHD, new Item_func_now_local()); } | STARTS_SYM expr { + Lex->event_parse_data->item_starts= $2; LEX *lex= Lex; if (!lex->et_compile_phase) { @@ -1443,6 +1457,7 @@ ev_starts: /* empty */ ev_ends: /* empty */ | ENDS_SYM expr { + Lex->event_parse_data->item_ends= $2; LEX *lex= Lex; if (!lex->et_compile_phase) { @@ -1467,6 +1482,8 @@ opt_ev_on_completion: /* empty */ { $$= 0; } ev_on_completion: ON COMPLETION_SYM PRESERVE_SYM { + Lex->event_parse_data->on_completion= + Event_parse_data::ON_COMPLETION_PRESERVE; LEX *lex=Lex; if (!lex->et_compile_phase) lex->et->on_completion= Event_timed::ON_COMPLETION_PRESERVE; @@ -1474,6 +1491,8 @@ ev_on_completion: } | ON COMPLETION_SYM NOT_SYM PRESERVE_SYM { + Lex->event_parse_data->on_completion= + Event_parse_data::ON_COMPLETION_DROP; LEX *lex=Lex; if (!lex->et_compile_phase) lex->et->on_completion= Event_timed::ON_COMPLETION_DROP; @@ -1484,6 +1503,7 @@ ev_on_completion: opt_ev_comment: /* empty */ { $$= 0; } | COMMENT_SYM TEXT_STRING_sys { + Lex->comment= Lex->event_parse_data->comment= $2; LEX *lex= Lex; if (!lex->et_compile_phase) { @@ -1499,25 +1519,43 @@ ev_sql_stmt: LEX *lex= Lex; sp_head *sp; - $<sphead>$= lex->sphead; - - if (!lex->sphead) + /* + This stops the following : + - CREATE EVENT ... DO CREATE EVENT ...; + - ALTER EVENT ... DO CREATE EVENT ...; + - CREATE EVENT ... DO ALTER EVENT DO ....; + - CREATE PROCEDURE ... BEGIN CREATE EVENT ... END| + This allows: + - CREATE EVENT ... DO DROP EVENT yyy; + - CREATE EVENT ... DO ALTER EVENT yyy; + (the nested ALTER EVENT can have anything but DO clause) + - ALTER EVENT ... DO ALTER EVENT yyy; + (the nested ALTER EVENT can have anything but DO clause) + - ALTER EVENT ... DO DROP EVENT yyy; + - CREATE PROCEDURE ... BEGIN ALTER EVENT ... END| + (the nested ALTER EVENT can have anything but DO clause) + - CREATE PROCEDURE ... BEGIN DROP EVENT ... END| + */ + if (lex->sphead) { - if (!(sp= new sp_head())) - YYABORT; - - sp->reset_thd_mem_root(YYTHD); - sp->init(lex); + my_error(ER_EVENT_RECURSIVITY_FORBIDDEN, MYF(0)); + YYABORT; + } + + if (!(lex->sphead= new sp_head())) + YYABORT; - sp->m_type= TYPE_ENUM_PROCEDURE; + lex->sphead->reset_thd_mem_root(YYTHD); + lex->sphead->init(lex); - lex->sphead= sp; + lex->sphead->m_type= TYPE_ENUM_PROCEDURE; - bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); - lex->sphead->m_chistics= &lex->sp_chistics; + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lex->ptr; - } + lex->sphead->m_body_begin= lex->ptr; + + Lex->event_parse_data->body_begin= lex->ptr; if (!lex->et_compile_phase) lex->et->body_begin= lex->ptr; @@ -1526,18 +1564,16 @@ ev_sql_stmt: { LEX *lex=Lex; - if (!$<sphead>1) - { - sp_head *sp= lex->sphead; - // return back to the original memory root ASAP - sp->init_strings(YYTHD, lex, NULL); - sp->restore_thd_mem_root(YYTHD); + // return back to the original memory root ASAP + lex->sphead->init_strings(YYTHD, lex, NULL); + lex->sphead->restore_thd_mem_root(YYTHD); - lex->sp_chistics.suid= SP_IS_SUID;//always the definer! + lex->sp_chistics.suid= SP_IS_SUID;//always the definer! - lex->et->sphead= lex->sphead; - lex->sphead= NULL; - } + lex->et->sphead= lex->sphead; + lex->sphead= NULL; + + Lex->event_parse_data->init_body(YYTHD); if (!lex->et_compile_phase) { lex->et->init_body(YYTHD); @@ -4720,16 +4756,11 @@ alter: LEX *lex=Lex; Event_timed *et; - if (lex->et) - { - /* - Recursive events are not possible because recursive SPs - are not also possible. lex->sp_head is not stacked. - */ - my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT"); + lex->spname= NULL; + + if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) YYABORT; - } - lex->spname= 0;//defensive programming + Lex->event_parse_data->identifier= $3; if (!(et= new (YYTHD->mem_root) Event_timed()))// implicitly calls Event_timed::init() YYABORT; @@ -4742,9 +4773,9 @@ alter: } /* - We have to turn of CLIENT_MULTI_QUERIES while parsing a - stored procedure, otherwise yylex will chop it into pieces - at each ';'. + We have to turn of CLIENT_MULTI_QUERIES while parsing a + stored procedure, otherwise yylex will chop it into pieces + at each ';'. */ $<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; @@ -7664,29 +7695,9 @@ drop: } | DROP EVENT_SYM if_exists sp_name { - LEX *lex=Lex; - - if (lex->et) - { - /* - Recursive events are not possible because recursive SPs - are not also possible. lex->sp_head is not stacked. - */ - my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT"); - YYABORT; - } - - if (!(lex->et= new (YYTHD->mem_root) Event_timed())) - YYABORT; - - if (!lex->et_compile_phase) - { - lex->et->init_name(YYTHD, $4); - lex->et->init_definer(YYTHD); - } - - lex->sql_command = SQLCOM_DROP_EVENT; - lex->drop_if_exists= $3; + Lex->drop_if_exists= $3; + Lex->spname= $4; + Lex->sql_command = SQLCOM_DROP_EVENT; } | DROP TRIGGER_SYM sp_name { @@ -8416,12 +8427,8 @@ show_param: } | CREATE EVENT_SYM sp_name { - Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT; Lex->spname= $3; - Lex->et= new (YYTHD->mem_root) Event_timed(); - if (!Lex->et) - YYABORT; - Lex->et->init_definer(YYTHD); + Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT; } ; @@ -10751,20 +10758,22 @@ subselect_end: **************************************************************************/ -view_or_trigger_or_sp: - definer view_or_trigger_or_sp_tail +view_or_trigger_or_sp_or_event: + definer view_or_trigger_or_sp_or_event_tail {} | view_replace_or_algorithm definer view_tail {} ; -view_or_trigger_or_sp_tail: +view_or_trigger_or_sp_or_event_tail: view_tail {} | trigger_tail {} | sp_tail {} + | event_tail + {} ; /************************************************************************** |