Commit 9309fae9 authored by andrey@lmy004's avatar andrey@lmy004

WL#3337 (Events new architecture)

5th cut, moved DB related code to Event_db_repository and
updated accordingly the remanining code.
Moved change/restore_security_context() to class THD
Removed events_priv.h
Next step is to reorganize create/update_event() and parsing for them.
But probably some other refactoring could be done in the meanwhile.
The changes so far pass the test suite.
parent 0c439c9f
......@@ -64,7 +64,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
tztime.h my_decimal.h\
sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \
parse_file.h sql_view.h sql_trigger.h \
sql_array.h sql_cursor.h events.h events_priv.h \
sql_array.h sql_cursor.h events.h \
sql_plugin.h authors.h sql_partition.h event_data_objects.h \
event_queue.h event_db_repository.h \
partition_info.h partition_element.h event_scheduler.h \
......
......@@ -16,12 +16,15 @@
#define MYSQL_LEX 1
#include "mysql_priv.h"
#include "events_priv.h"
#include "events.h"
#include "event_data_objects.h"
#include "event_db_repository.h"
#include "sp_head.h"
#define EVEX_MAX_INTERVAL_VALUE 2147483647L
Event_parse_data *
Event_parse_data::new_instance(THD *thd)
{
......@@ -733,29 +736,29 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
et= this;
if (table->s->fields != Events::FIELD_COUNT)
if (table->s->fields != ET_FIELD_COUNT)
goto error;
if ((et->dbname.str= get_field(mem_root,
table->field[Events::FIELD_DB])) == NULL)
table->field[ET_FIELD_DB])) == NULL)
goto error;
et->dbname.length= strlen(et->dbname.str);
if ((et->name.str= get_field(mem_root,
table->field[Events::FIELD_NAME])) == NULL)
table->field[ET_FIELD_NAME])) == NULL)
goto error;
et->name.length= strlen(et->name.str);
if ((et->body.str= get_field(mem_root,
table->field[Events::FIELD_BODY])) == NULL)
table->field[ET_FIELD_BODY])) == NULL)
goto error;
et->body.length= strlen(et->body.str);
if ((et->definer.str= get_field(mem_root,
table->field[Events::FIELD_DEFINER])) == NullS)
table->field[ET_FIELD_DEFINER])) == NullS)
goto error;
et->definer.length= strlen(et->definer.str);
......@@ -772,27 +775,27 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
et->definer_host.str= strmake_root(mem_root, ptr + 1, len);/* 1:because of @*/
et->definer_host.length= len;
et->starts_null= table->field[Events::FIELD_STARTS]->is_null();
res1= table->field[Events::FIELD_STARTS]->
et->starts_null= table->field[ET_FIELD_STARTS]->is_null();
res1= table->field[ET_FIELD_STARTS]->
get_date(&et->starts,TIME_NO_ZERO_DATE);
et->ends_null= table->field[Events::FIELD_ENDS]->is_null();
res2= table->field[Events::FIELD_ENDS]->get_date(&et->ends, TIME_NO_ZERO_DATE);
et->ends_null= table->field[ET_FIELD_ENDS]->is_null();
res2= table->field[ET_FIELD_ENDS]->get_date(&et->ends, TIME_NO_ZERO_DATE);
if (!table->field[Events::FIELD_INTERVAL_EXPR]->is_null())
et->expression= table->field[Events::FIELD_INTERVAL_EXPR]->val_int();
if (!table->field[ET_FIELD_INTERVAL_EXPR]->is_null())
et->expression= table->field[ET_FIELD_INTERVAL_EXPR]->val_int();
else
et->expression= 0;
/*
If res1 and res2 are true then both fields are empty.
Hence if Events::FIELD_EXECUTE_AT is empty there is an error.
Hence if ET_FIELD_EXECUTE_AT is empty there is an error.
*/
et->execute_at_null=
table->field[Events::FIELD_EXECUTE_AT]->is_null();
table->field[ET_FIELD_EXECUTE_AT]->is_null();
DBUG_ASSERT(!(et->starts_null && et->ends_null && !et->expression &&
et->execute_at_null));
if (!et->expression &&
table->field[Events::FIELD_EXECUTE_AT]-> get_date(&et->execute_at,
table->field[ET_FIELD_EXECUTE_AT]-> get_date(&et->execute_at,
TIME_NO_ZERO_DATE))
goto error;
......@@ -800,22 +803,22 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
In DB the values start from 1 but enum interval_type starts
from 0
*/
if (!table->field[Events::FIELD_TRANSIENT_INTERVAL]->is_null())
if (!table->field[ET_FIELD_TRANSIENT_INTERVAL]->is_null())
et->interval= (interval_type) ((ulonglong)
table->field[Events::FIELD_TRANSIENT_INTERVAL]->val_int() - 1);
table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_int() - 1);
else
et->interval= (interval_type) 0;
et->created= table->field[Events::FIELD_CREATED]->val_int();
et->modified= table->field[Events::FIELD_MODIFIED]->val_int();
et->created= table->field[ET_FIELD_CREATED]->val_int();
et->modified= table->field[ET_FIELD_MODIFIED]->val_int();
table->field[Events::FIELD_LAST_EXECUTED]->
table->field[ET_FIELD_LAST_EXECUTED]->
get_date(&et->last_executed, TIME_NO_ZERO_DATE);
last_executed_changed= false;
/* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
if ((ptr= get_field(mem_root, table->field[Events::FIELD_STATUS])) == NullS)
if ((ptr= get_field(mem_root, table->field[ET_FIELD_STATUS])) == NullS)
goto error;
DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->name.str, ptr));
......@@ -823,20 +826,20 @@ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
/* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
if ((ptr= get_field(mem_root,
table->field[Events::FIELD_ON_COMPLETION])) == NullS)
table->field[ET_FIELD_ON_COMPLETION])) == NullS)
goto error;
et->on_completion= (ptr[0]=='D'? Event_timed::ON_COMPLETION_DROP:
Event_timed::ON_COMPLETION_PRESERVE);
et->comment.str= get_field(mem_root, table->field[Events::FIELD_COMMENT]);
et->comment.str= get_field(mem_root, table->field[ET_FIELD_COMMENT]);
if (et->comment.str != NullS)
et->comment.length= strlen(et->comment.str);
else
et->comment.length= 0;
et->sql_mode= (ulong) table->field[Events::FIELD_SQL_MODE]->val_int();
et->sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
DBUG_RETURN(0);
error:
......@@ -1277,6 +1280,7 @@ Event_timed::mark_last_executed(THD *thd)
}
/*
Drops the event
......@@ -1299,7 +1303,8 @@ Event_timed::drop(THD *thd)
uint tmp= 0;
DBUG_ENTER("Event_timed::drop");
DBUG_RETURN(db_drop_event(thd, dbname, name, false, &tmp));
DBUG_RETURN(Events::get_instance()->
db_repository.drop_event(thd, dbname, name, false, &tmp));
}
......@@ -1336,7 +1341,7 @@ Event_timed::update_fields(THD *thd)
thd->reset_n_backup_open_tables_state(&backup);
if (Events::open_event_table(thd, TL_WRITE, &table))
if (Events::get_instance()->open_event_table(thd, TL_WRITE, &table))
{
ret= EVEX_OPEN_TABLE_FAILED;
goto done;
......@@ -1352,15 +1357,15 @@ Event_timed::update_fields(THD *thd)
if (last_executed_changed)
{
table->field[Events::FIELD_LAST_EXECUTED]->set_notnull();
table->field[Events::FIELD_LAST_EXECUTED]->store_time(&last_executed,
table->field[ET_FIELD_LAST_EXECUTED]->set_notnull();
table->field[ET_FIELD_LAST_EXECUTED]->store_time(&last_executed,
MYSQL_TIMESTAMP_DATETIME);
last_executed_changed= false;
}
if (status_changed)
{
table->field[Events::FIELD_STATUS]->set_notnull();
table->field[Events::FIELD_STATUS]->store((longlong)status, true);
table->field[ET_FIELD_STATUS]->set_notnull();
table->field[ET_FIELD_STATUS]->store((longlong)status, true);
status_changed= false;
}
......@@ -1630,8 +1635,8 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
thd->query_length= show_create.length();
DBUG_PRINT("info", ("query:%s",thd->query));
change_security_context(thd, definer_user, definer_host, dbname,
&security_ctx, &save_ctx);
thd->change_security_context(definer_user, definer_host, dbname,
&security_ctx, &save_ctx);
thd->lex= &lex;
lex_start(thd, (uchar*)thd->query, thd->query_length);
lex.et_compile_phase= TRUE;
......@@ -1669,7 +1674,7 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
lex.et->deinit_mutexes();
lex_end(&lex);
restore_security_context(thd, save_ctx);
thd->restore_security_context(save_ctx);
DBUG_PRINT("note", ("return old data on its place. set back NAMES"));
thd->lex= old_lex;
......@@ -1870,23 +1875,6 @@ event_timed_db_equal(Event_timed *et, LEX_STRING *db)
}
/*
Checks whether two events have the same definer
SYNOPSIS
event_timed_definer_equal()
Returns
TRUE definers are equal
FALSE definers are not equal
*/
bool
event_timed_definer_equal(Event_timed *et, LEX_STRING *definer)
{
return !sortcmp_lex_string(et->definer, *definer, system_charset_info);
}
/*
Checks whether two events are equal by identifiers
......
......@@ -40,9 +40,35 @@
#define EVENT_FREE_WHEN_FINISHED (1L << 2)
#define EVENT_EXEC_STARTED 0
#define EVENT_EXEC_ALREADY_EXEC 1
#define EVENT_EXEC_CANT_FORK 2
class sp_head;
class Sql_alloc;
class Event_timed;
/* Compares only the schema part of the identifier */
bool
event_timed_db_equal(Event_timed *et, LEX_STRING *db);
/* Compares the whole identifier*/
bool
event_timed_identifier_equal(Event_timed *a, Event_timed *b);
/* Compares only the schema part of the identifier */
bool
event_timed_db_equal(sp_name *name, LEX_STRING *db);
/* Compares the whole identifier*/
bool
event_timed_identifier_equal(sp_name *a, Event_timed *b);
class Event_timed
{
Event_timed(const Event_timed &); /* Prevent use of these */
......@@ -296,4 +322,10 @@ class Event_parse_data : public Sql_alloc
};
class Event_queue_element : public Event_timed
{
};
#endif /* _EVENT_DATA_OBJECTS_H_ */
......@@ -17,3 +17,857 @@
#include "mysql_priv.h"
#include "event_db_repository.h"
#include "event_data_objects.h"
#include "sp_head.h"
#include "sp.h"
#include "events.h"
#define EVEX_DB_FIELD_LEN 64
#define EVEX_NAME_FIELD_LEN 64
time_t mysql_event_last_create_time= 0L;
static
TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] = {
{
{(char *) STRING_WITH_LEN("db")},
{(char *) STRING_WITH_LEN("char(64)")},
{(char *) STRING_WITH_LEN("utf8")}
},
{
{(char *) STRING_WITH_LEN("name")},
{(char *) STRING_WITH_LEN("char(64)")},
{(char *) STRING_WITH_LEN("utf8")}
},
{
{(char *) STRING_WITH_LEN("body")},
{(char *) STRING_WITH_LEN("longblob")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("definer")},
{(char *) STRING_WITH_LEN("char(77)")},
{(char *) STRING_WITH_LEN("utf8")}
},
{
{(char *) STRING_WITH_LEN("execute_at")},
{(char *) STRING_WITH_LEN("datetime")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("interval_value")},
{(char *) STRING_WITH_LEN("int(11)")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("interval_field")},
{(char *) STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY',"
"'HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR',"
"'DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND',"
"'DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND',"
"'SECOND_MICROSECOND')")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("created")},
{(char *) STRING_WITH_LEN("timestamp")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("modified")},
{(char *) STRING_WITH_LEN("timestamp")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("last_executed")},
{(char *) STRING_WITH_LEN("datetime")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("starts")},
{(char *) STRING_WITH_LEN("datetime")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("ends")},
{(char *) STRING_WITH_LEN("datetime")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("status")},
{(char *) STRING_WITH_LEN("enum('ENABLED','DISABLED')")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("on_completion")},
{(char *) STRING_WITH_LEN("enum('DROP','PRESERVE')")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("sql_mode")},
{(char *) STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES',"
"'IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION',"
"'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB',"
"'NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40',"
"'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES',"
"'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES',"
"'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER',"
"'HIGH_NOT_PRECEDENCE')")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("comment")},
{(char *) STRING_WITH_LEN("char(64)")},
{(char *) STRING_WITH_LEN("utf8")}
}
};
/*
Puts some data common to CREATE and ALTER EVENT into a row.
SYNOPSIS
evex_fill_row()
thd THD
table the row to fill out
et Event's data
RETURN VALUE
0 - OK
EVEX_GENERAL_ERROR - bad data
EVEX_GET_FIELD_FAILED - field count does not match. table corrupted?
DESCRIPTION
Used both when an event is created and when it is altered.
*/
static int
evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update)
{
CHARSET_INFO *scs= system_charset_info;
enum enum_events_table_field field_num;
DBUG_ENTER("evex_fill_row");
DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str));
DBUG_PRINT("info", ("name =[%s]", et->name.str));
DBUG_PRINT("info", ("body =[%s]", et->body.str));
if (table->field[field_num= ET_FIELD_DEFINER]->
store(et->definer.str, et->definer.length, scs))
goto err_truncate;
if (table->field[field_num= ET_FIELD_DB]->
store(et->dbname.str, et->dbname.length, scs))
goto err_truncate;
if (table->field[field_num= ET_FIELD_NAME]->
store(et->name.str, et->name.length, scs))
goto err_truncate;
/* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/
table->field[ET_FIELD_ON_COMPLETION]->
store((longlong)et->on_completion, true);
table->field[ET_FIELD_STATUS]->store((longlong)et->status, true);
/*
Change the SQL_MODE only if body was present in an ALTER EVENT and of course
always during CREATE EVENT.
*/
if (et->body.str)
{
table->field[ET_FIELD_SQL_MODE]->
store((longlong)thd->variables.sql_mode, true);
if (table->field[field_num= ET_FIELD_BODY]->
store(et->body.str, et->body.length, scs))
goto err_truncate;
}
if (et->expression)
{
table->field[ET_FIELD_INTERVAL_EXPR]->set_notnull();
table->field[ET_FIELD_INTERVAL_EXPR]->
store((longlong)et->expression, true);
table->field[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull();
/*
In the enum (C) intervals start from 0 but in mysql enum valid values start
from 1. Thus +1 offset is needed!
*/
table->field[ET_FIELD_TRANSIENT_INTERVAL]->
store((longlong)et->interval+1, true);
table->field[ET_FIELD_EXECUTE_AT]->set_null();
if (!et->starts_null)
{
table->field[ET_FIELD_STARTS]->set_notnull();
table->field[ET_FIELD_STARTS]->
store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME);
}
if (!et->ends_null)
{
table->field[ET_FIELD_ENDS]->set_notnull();
table->field[ET_FIELD_ENDS]->
store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME);
}
}
else if (et->execute_at.year)
{
table->field[ET_FIELD_INTERVAL_EXPR]->set_null();
table->field[ET_FIELD_TRANSIENT_INTERVAL]->set_null();
table->field[ET_FIELD_STARTS]->set_null();
table->field[ET_FIELD_ENDS]->set_null();
table->field[ET_FIELD_EXECUTE_AT]->set_notnull();
table->field[ET_FIELD_EXECUTE_AT]->
store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME);
}
else
{
DBUG_ASSERT(is_update);
/*
it is normal to be here when the action is update
this is an error if the action is create. something is borked
*/
}
((Field_timestamp *)table->field[ET_FIELD_MODIFIED])->set_time();
if (et->comment.str)
{
if (table->field[field_num= ET_FIELD_COMMENT]->
store(et->comment.str, et->comment.length, scs))
goto err_truncate;
}
DBUG_RETURN(0);
err_truncate:
my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), table->field[field_num]->field_name);
DBUG_RETURN(EVEX_GENERAL_ERROR);
}
/*
Find row in open mysql.event table representing event
SYNOPSIS
evex_db_find_event_by_name()
thd Thread context
dbname Name of event's database
rname Name of the event inside the db
table TABLE object for open mysql.event table.
RETURN VALUE
0 - Routine found
EVEX_KEY_NOT_FOUND - No routine with given name
*/
int
evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname,
const LEX_STRING ev_name,
TABLE *table)
{
return Events::get_instance()->db_repository.
find_event_by_name(thd, dbname, ev_name, table);
}
/*
Looks for a named event in mysql.event and in case of success returns
an object will data loaded from the table.
SYNOPSIS
db_find_event()
thd THD
name the name of the event to find
ett event's data if event is found
tbl TABLE object to use when not NULL
NOTES
1) Use sp_name for look up, return in **ett if found
2) tbl is not closed at exit
RETURN VALUE
0 ok In this case *ett is set to the event
# error *ett == 0
*/
int
Event_db_repository::find_event(THD *thd, sp_name *name, Event_timed **ett,
TABLE *tbl, MEM_ROOT *root)
{
TABLE *table;
int ret;
Event_timed *et= NULL;
DBUG_ENTER("db_find_event");
DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
if (tbl)
table= tbl;
else if (Events::get_instance()->db_repository.open_event_table(thd, TL_READ, &table))
{
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
ret= EVEX_GENERAL_ERROR;
goto done;
}
if ((ret= evex_db_find_event_by_name(thd, name->m_db, name->m_name, table)))
{
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str);
goto done;
}
et= new Event_timed;
/*
1)The table should not be closed beforehand. ::load_from_row() only loads
and does not compile
2)::load_from_row() is silent on error therefore we emit error msg here
*/
if ((ret= et->load_from_row(root, table)))
{
my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0));
goto done;
}
done:
if (ret)
{
delete et;
et= 0;
}
/* don't close the table if we haven't opened it ourselves */
if (!tbl && table)
close_thread_tables(thd);
*ett= et;
DBUG_RETURN(ret);
}
int
Event_db_repository::init_repository()
{
init_alloc_root(&repo_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
return 0;
}
void
Event_db_repository::deinit_repository()
{
free_root(&repo_root, MYF(0));
}
/*
Open mysql.event table for read
SYNOPSIS
Events::open_event_table()
thd Thread context
lock_type How to lock the table
table We will store the open table here
RETURN VALUE
1 Cannot lock table
2 The table is corrupted - different number of fields
0 OK
*/
int
Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
TABLE **table)
{
TABLE_LIST tables;
DBUG_ENTER("Event_db_repository::open_event_table");
bzero((char*) &tables, sizeof(tables));
tables.db= (char*) "mysql";
tables.table_name= tables.alias= (char*) "event";
tables.lock_type= lock_type;
if (simple_open_n_lock_tables(thd, &tables))
DBUG_RETURN(1);
if (table_check_intact(tables.table, ET_FIELD_COUNT,
event_table_fields,
&mysql_event_last_create_time,
ER_CANNOT_LOAD_FROM_TABLE))
{
close_thread_tables(thd);
DBUG_RETURN(2);
}
*table= tables.table;
tables.table->use_all_columns();
DBUG_RETURN(0);
}
/*
Creates an event in mysql.event
SYNOPSIS
Event_db_repository::create_event()
thd THD
et Event_timed object containing information for the event
create_if_not If an warning should be generated in case event exists
rows_affected How many rows were affected
RETURN VALUE
0 - OK
EVEX_GENERAL_ERROR - Failure
DESCRIPTION
Creates an event. Relies on evex_fill_row which is shared with
db_update_event. The name of the event is inside "et".
*/
int
Event_db_repository::create_event(THD *thd, Event_timed *et,
my_bool create_if_not, uint *rows_affected)
{
int ret= 0;
CHARSET_INFO *scs= system_charset_info;
TABLE *table;
char olddb[128];
bool dbchanged= false;
DBUG_ENTER("Event_db_repository::create_event");
DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str));
*rows_affected= 0;
DBUG_PRINT("info", ("open mysql.event for update"));
if (open_event_table(thd, TL_WRITE, &table))
{
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
goto err;
}
DBUG_PRINT("info", ("check existance of an event with the same name"));
if (!evex_db_find_event_by_name(thd, et->dbname, et->name, table))
{
if (create_if_not)
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
et->name.str);
goto ok;
}
my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), et->name.str);
goto err;
}
DBUG_PRINT("info", ("non-existant, go forward"));
if ((ret= sp_use_new_db(thd, et->dbname.str,olddb, sizeof(olddb),0,
&dbchanged)))
{
my_error(ER_BAD_DB_ERROR, MYF(0));
goto err;
}
restore_record(table, s->default_values); // Get default values for fields
if (system_charset_info->cset->numchars(system_charset_info, et->dbname.str,
et->dbname.str + et->dbname.length)
> EVEX_DB_FIELD_LEN)
{
my_error(ER_TOO_LONG_IDENT, MYF(0), et->dbname.str);
goto err;
}
if (system_charset_info->cset->numchars(system_charset_info, et->name.str,
et->name.str + et->name.length)
> EVEX_DB_FIELD_LEN)
{
my_error(ER_TOO_LONG_IDENT, MYF(0), et->name.str);
goto err;
}
if (et->body.length > table->field[ET_FIELD_BODY]->field_length)
{
my_error(ER_TOO_LONG_BODY, MYF(0), et->name.str);
goto err;
}
if (!(et->expression) && !(et->execute_at.year))
{
DBUG_PRINT("error", ("neither expression nor execute_at are set!"));
my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0));
goto err;
}
((Field_timestamp *)table->field[ET_FIELD_CREATED])->set_time();
/*
evex_fill_row() calls my_error() in case of error so no need to
handle it here
*/
if ((ret= evex_fill_row(thd, table, et, false)))
goto err;
if (table->file->ha_write_row(table->record[0]))
{
my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
goto err;
}
#ifdef USE_THIS_CODE_AS_TEMPLATE_WHEN_EVENT_REPLICATION_IS_AGREED
if (mysql_bin_log.is_open())
{
thd->clear_error();
/* Such a statement can always go directly to binlog, no trans cache */
thd->binlog_query(THD::MYSQL_QUERY_TYPE, thd->query, thd->query_length,
FALSE, FALSE);
}
#endif
*rows_affected= 1;
ok:
if (dbchanged)
(void) mysql_change_db(thd, olddb, 1);
if (table)
close_thread_tables(thd);
DBUG_RETURN(EVEX_OK);
err:
if (dbchanged)
(void) mysql_change_db(thd, olddb, 1);
if (table)
close_thread_tables(thd);
DBUG_RETURN(EVEX_GENERAL_ERROR);
}
/*
Used to execute ALTER EVENT. Pendant to Events::update_event().
SYNOPSIS
Event_db_repository::update_event()
thd THD
sp_name the name of the event to alter
et event's data
RETURN VALUE
0 OK
EVEX_GENERAL_ERROR Error occured (my_error() called)
NOTES
sp_name is passed since this is the name of the event to
alter in case of RENAME TO.
*/
int
Event_db_repository::update_event(THD *thd, Event_timed *et, sp_name *new_name)
{
CHARSET_INFO *scs= system_charset_info;
TABLE *table;
int ret= EVEX_OPEN_TABLE_FAILED;
DBUG_ENTER("Event_db_repository::update_event");
DBUG_PRINT("enter", ("dbname: %.*s", et->dbname.length, et->dbname.str));
DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str));
DBUG_PRINT("enter", ("user: %.*s", et->definer.length, et->definer.str));
if (new_name)
DBUG_PRINT("enter", ("rename to: %.*s", new_name->m_name.length,
new_name->m_name.str));
if (open_event_table(thd, TL_WRITE, &table))
{
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
goto err;
}
/* first look whether we overwrite */
if (new_name)
{
if (!sortcmp_lex_string(et->name, new_name->m_name, scs) &&
!sortcmp_lex_string(et->dbname, new_name->m_db, scs))
{
my_error(ER_EVENT_SAME_NAME, MYF(0), et->name.str);
goto err;
}
if (!evex_db_find_event_by_name(thd,new_name->m_db,new_name->m_name,table))
{
my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str);
goto err;
}
}
/*
...and then if there is such an event. Don't exchange the blocks
because you will get error 120 from table handler because new_name will
overwrite the key and SE will tell us that it cannot find the already found
row (copied into record[1] later
*/
if (EVEX_KEY_NOT_FOUND == find_event_by_name(thd, et->dbname, et->name, table))
{
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str);
goto err;
}
store_record(table,record[1]);
/* Don't update create on row update. */
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
/*
evex_fill_row() calls my_error() in case of error so no need to
handle it here
*/
if ((ret= evex_fill_row(thd, table, et, true)))
goto err;
if (new_name)
{
table->field[ET_FIELD_DB]->
store(new_name->m_db.str, new_name->m_db.length, scs);
table->field[ET_FIELD_NAME]->
store(new_name->m_name.str, new_name->m_name.length, scs);
}
if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
{
my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
goto err;
}
/* close mysql.event or we crash later when loading the event from disk */
close_thread_tables(thd);
DBUG_RETURN(0);
err:
if (table)
close_thread_tables(thd);
DBUG_RETURN(EVEX_GENERAL_ERROR);
}
/*
Drops an event
SYNOPSIS
Event_db_repository::drop_event()
thd THD
db database name
name event's name
drop_if_exists if set and the event not existing => warning onto the stack
rows_affected affected number of rows is returned heres
RETURN VALUE
0 OK
!0 Error (my_error() called)
*/
int
Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
bool drop_if_exists, uint *rows_affected)
{
TABLE *table;
Open_tables_state backup;
int ret;
DBUG_ENTER("Event_db_repository::drop_event");
DBUG_PRINT("enter", ("db=%s name=%s", db.str, name.str));
ret= EVEX_OPEN_TABLE_FAILED;
thd->reset_n_backup_open_tables_state(&backup);
if (open_event_table(thd, TL_WRITE, &table))
{
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
goto done;
}
if (!(ret= evex_db_find_event_by_name(thd, db, name, table)))
{
if ((ret= table->file->ha_delete_row(table->record[0])))
my_error(ER_EVENT_CANNOT_DELETE, MYF(0));
}
else if (ret == EVEX_KEY_NOT_FOUND)
{
if (drop_if_exists)
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
"Event", name.str);
ret= 0;
} else
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
}
done:
/*
evex_drop_event() is used by Event_timed::drop therefore
we have to close our thread tables.
*/
close_thread_tables(thd);
thd->restore_backup_open_tables_state(&backup);
DBUG_RETURN(ret);
}
int
Event_db_repository::find_event_by_name(THD *thd, LEX_STRING db,
LEX_STRING name, TABLE *table)
{
byte key[MAX_KEY_LENGTH];
DBUG_ENTER("Event_db_repository::find_event_by_name");
DBUG_PRINT("enter", ("name: %.*s", name.length, name.str));
/*
Create key to find row. We have to use field->store() to be able to
handle VARCHAR and CHAR fields.
Assumption here is that the two first fields in the table are
'db' and 'name' and the first key is the primary key over the
same fields.
*/
if (db.length > table->field[ET_FIELD_DB]->field_length ||
name.length > table->field[ET_FIELD_NAME]->field_length)
DBUG_RETURN(EVEX_KEY_NOT_FOUND);
table->field[ET_FIELD_DB]->store(db.str, db.length, &my_charset_bin);
table->field[ET_FIELD_NAME]->store(name.str, name.length, &my_charset_bin);
key_copy(key, table->record[0], table->key_info,
table->key_info->key_length);
if (table->file->index_read_idx(table->record[0], 0, key,
table->key_info->key_length,
HA_READ_KEY_EXACT))
{
DBUG_PRINT("info", ("Row not found"));
DBUG_RETURN(EVEX_KEY_NOT_FOUND);
}
DBUG_PRINT("info", ("Row found!"));
DBUG_RETURN(0);
}
int
Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema)
{
return drop_events_by_field(thd, ET_FIELD_DB, schema);
}
int
Event_db_repository::drop_user_events(THD *thd, LEX_STRING definer)
{
return drop_events_by_field(thd, ET_FIELD_DEFINER, definer);
}
/*
Drops all events in the selected database, from mysql.event.
SYNOPSIS
drop_events_from_table_by_field()
thd Thread
table mysql.event TABLE
field Which field of the row to use for matching
field_value The value that should match
RETURN VALUE
0 OK
!0 Error from ha_delete_row
*/
int
Event_db_repository::drop_events_by_field(THD *thd,
enum enum_events_table_field field,
LEX_STRING field_value)
{
int ret= 0;
TABLE *table;
Open_tables_state backup;
READ_RECORD read_record_info;
DBUG_ENTER("drop_events_from_table_by_field");
DBUG_PRINT("enter", ("field=%d field_value=%s", field, field_value.str));
if (open_event_table(thd, TL_WRITE, &table))
{
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
DBUG_RETURN(1);
}
/* only enabled events are in memory, so we go now and delete the rest */
init_read_record(&read_record_info, thd, table, NULL, 1, 0);
while (!ret && !(read_record_info.read_record(&read_record_info)) )
{
char *et_field= get_field(thd->mem_root, table->field[field]);
LEX_STRING et_field_lex= {et_field, strlen(et_field)};
DBUG_PRINT("info", ("Current event %s name=%s", et_field,
get_field(thd->mem_root, table->field[ET_FIELD_NAME])));
if (!sortcmp_lex_string(et_field_lex, field_value, system_charset_info))
{
DBUG_PRINT("info", ("Dropping"));
if ((ret= table->file->ha_delete_row(table->record[0])))
my_error(ER_EVENT_DROP_FAILED, MYF(0),
get_field(thd->mem_root, table->field[ET_FIELD_NAME]));
}
}
end_read_record(&read_record_info);
thd->version--; /* Force close to free memory */
DBUG_RETURN(ret);
}
/*
Looks for a named event in mysql.event and then loads it from
the table, compiles and inserts it into the cache.
SYNOPSIS
Event_scheduler::load_named_event()
thd THD
etn The name of the event to load and compile on scheduler's root
etn_new The loaded event
RETURN VALUE
NULL Error during compile or the event is non-enabled.
otherwise Address
*/
int
Event_db_repository::load_named_event(THD *thd, Event_timed *etn,
Event_timed **etn_new)
{
int ret= 0;
MEM_ROOT *tmp_mem_root;
Event_timed *et_loaded= NULL;
Open_tables_state backup;
DBUG_ENTER("Event_scheduler::load_and_compile_event");
DBUG_PRINT("enter",("thd=%p name:%*s",thd, etn->name.length, etn->name.str));
thd->reset_n_backup_open_tables_state(&backup);
/* No need to use my_error() here because db_find_event() has done it */
{
sp_name spn(etn->dbname, etn->name);
ret= find_event(thd, &spn, &et_loaded, NULL, &repo_root);
}
thd->restore_backup_open_tables_state(&backup);
/* In this case no memory was allocated so we don't need to clean */
if (ret)
DBUG_RETURN(OP_LOAD_ERROR);
if (et_loaded->status != Event_timed::ENABLED)
{
/*
We don't load non-enabled events.
In db_find_event() `et_new` was allocated on the heap and not on
scheduler_root therefore we delete it here.
*/
delete et_loaded;
DBUG_RETURN(OP_DISABLED_EVENT);
}
et_loaded->compute_next_execution_time();
*etn_new= et_loaded;
DBUG_RETURN(OP_OK);
}
......@@ -16,5 +16,87 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
enum enum_events_table_field
{
ET_FIELD_DB = 0,
ET_FIELD_NAME,
ET_FIELD_BODY,
ET_FIELD_DEFINER,
ET_FIELD_EXECUTE_AT,
ET_FIELD_INTERVAL_EXPR,
ET_FIELD_TRANSIENT_INTERVAL,
ET_FIELD_CREATED,
ET_FIELD_MODIFIED,
ET_FIELD_LAST_EXECUTED,
ET_FIELD_STARTS,
ET_FIELD_ENDS,
ET_FIELD_STATUS,
ET_FIELD_ON_COMPLETION,
ET_FIELD_SQL_MODE,
ET_FIELD_COMMENT,
ET_FIELD_COUNT /* a cool trick to count the number of fields :) */
};
int
evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname,
const LEX_STRING ev_name,
TABLE *table);
class Event_queue_element;
class Event_db_repository
{
public:
Event_db_repository(){}
~Event_db_repository(){}
int
init_repository();
void
deinit_repository();
int
open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
int
create_event(THD *thd, Event_timed *et, my_bool create_if_not,
uint *rows_affected);
int
update_event(THD *thd, Event_timed *et, sp_name *new_name);
int
drop_event(THD *thd, LEX_STRING db, LEX_STRING name, bool drop_if_exists,
uint *rows_affected);
int
drop_schema_events(THD *thd, LEX_STRING schema);
int
drop_user_events(THD *thd, LEX_STRING definer);
int
find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl,
MEM_ROOT *root);
int
load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new);
int
find_event_by_name(THD *thd, LEX_STRING db, LEX_STRING name, TABLE *table);
private:
int
drop_events_by_field(THD *thd, enum enum_events_table_field field,
LEX_STRING field_value);
MEM_ROOT repo_root;
/* Prevent use of these */
Event_db_repository(const Event_db_repository &);
void operator=(Event_db_repository &);
};
#endif /* _EVENT_DB_REPOSITORY_H_ */
......@@ -15,10 +15,10 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "mysql_priv.h"
#include "events_priv.h"
#include "events.h"
#include "event_data_objects.h"
#include "event_scheduler.h"
#include "event_db_repository.h"
#include "sp_head.h"
/*
......@@ -574,8 +574,8 @@ event_worker_thread(void *arg)
to change the context before sending the signal. We are under
LOCK_scheduler_data being held by Event_scheduler::run() -> ::execute_top().
*/
change_security_context(thd, event->definer_user, event->definer_host,
event->dbname, &security_ctx, &save_ctx);
thd->change_security_context(event->definer_user, event->definer_host,
event->dbname, &security_ctx, &save_ctx);
DBUG_PRINT("info", ("master_access=%d db_access=%d",
thd->security_ctx->master_access, thd->security_ctx->db_access));
......@@ -687,7 +687,7 @@ Event_scheduler::get_instance()
*/
bool
Event_scheduler::init()
Event_scheduler::init(Event_db_repository *db_repo)
{
int i= 0;
bool ret= FALSE;
......@@ -695,6 +695,7 @@ Event_scheduler::init()
DBUG_PRINT("enter", ("this=%p", this));
LOCK_SCHEDULER_DATA();
db_repository= db_repo;
for (;i < COND_LAST; i++)
if (pthread_cond_init(&cond_vars[i], NULL))
{
......@@ -783,10 +784,10 @@ Event_scheduler::destroy()
OP_LOAD_ERROR Error during loading from disk
*/
enum Event_scheduler::enum_error_code
int
Event_scheduler::create_event(THD *thd, Event_timed *et, bool check_existence)
{
enum enum_error_code res;
int res;
Event_timed *et_new;
DBUG_ENTER("Event_scheduler::create_event");
DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et,&LOCK_scheduler_data));
......@@ -805,7 +806,7 @@ Event_scheduler::create_event(THD *thd, Event_timed *et, bool check_existence)
}
/* We need to load the event on scheduler_root */
if (!(res= load_named_event(thd, et, &et_new)))
if (!(res= db_repository->load_named_event(thd, et, &et_new)))
{
queue_insert_safe(&queue, (byte *) et_new);
DBUG_PRINT("info", ("Sending COND_new_work"));
......@@ -904,12 +905,12 @@ Event_scheduler::drop_event(THD *thd, sp_name *name)
OP_ALREADY_EXISTS Event already in the queue
*/
enum Event_scheduler::enum_error_code
int
Event_scheduler::update_event(THD *thd, Event_timed *et,
LEX_STRING *new_schema,
LEX_STRING *new_name)
{
enum enum_error_code res;
int res= OP_OK;
Event_timed *et_old, *et_new= NULL;
LEX_STRING old_schema, old_name;
......@@ -947,7 +948,7 @@ Event_scheduler::update_event(THD *thd, Event_timed *et,
1. Error occured
2. If the replace is DISABLED, we don't load it into the queue.
*/
if (!(res= load_named_event(thd, et, &et_new)))
if (!(res= db_repository->load_named_event(thd, et, &et_new)))
{
queue_insert_safe(&queue, (byte *) et_new);
DBUG_PRINT("info", ("Sending COND_new_work"));
......@@ -961,7 +962,7 @@ Event_scheduler::update_event(THD *thd, Event_timed *et,
et->dbname= old_schema;
et->name= old_name;
}
DBUG_PRINT("info", ("res=%d", res));
UNLOCK_SCHEDULER_DATA();
/*
Andrey: Is this comment still truthful ???
......@@ -1111,11 +1112,11 @@ Event_scheduler::find_event(sp_name *name, bool remove_from_q)
*/
void
Event_scheduler::drop_matching_events(THD *thd, LEX_STRING *pattern,
Event_scheduler::drop_matching_events(THD *thd, LEX_STRING pattern,
bool (*comparator)(Event_timed *,LEX_STRING *))
{
DBUG_ENTER("Event_scheduler::drop_matching_events");
DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern->length, pattern->str,
DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern.length, pattern.str,
state));
if (is_running_or_suspended())
{
......@@ -1124,7 +1125,7 @@ Event_scheduler::drop_matching_events(THD *thd, LEX_STRING *pattern,
{
Event_timed *et= (Event_timed *) queue_element(&queue, i);
DBUG_PRINT("info", ("[%s.%s]?", et->dbname.str, et->name.str));
if (comparator(et, pattern))
if (comparator(et, &pattern))
{
/*
The queue is ordered. If we remove an element, then all elements after
......@@ -1179,7 +1180,7 @@ Event_scheduler::drop_matching_events(THD *thd, LEX_STRING *pattern,
*/
int
Event_scheduler::drop_schema_events(THD *thd, LEX_STRING *schema)
Event_scheduler::drop_schema_events(THD *thd, LEX_STRING schema)
{
int ret;
DBUG_ENTER("Event_scheduler::drop_schema_events");
......@@ -1187,7 +1188,6 @@ Event_scheduler::drop_schema_events(THD *thd, LEX_STRING *schema)
if (is_running_or_suspended())
drop_matching_events(thd, schema, event_timed_db_equal);
ret= db_drop_events_from_table(thd, schema);
UNLOCK_SCHEDULER_DATA();
DBUG_RETURN(ret);
......@@ -1713,7 +1713,7 @@ Event_scheduler::stop_all_running_events(THD *thd)
The caller must have acquited LOCK_scheduler_data.
*/
enum Event_scheduler::enum_error_code
int
Event_scheduler::stop()
{
THD *thd= current_thd;
......@@ -1778,7 +1778,7 @@ Event_scheduler::stop()
OP_OK OK
*/
enum Event_scheduler::enum_error_code
int
Event_scheduler::suspend_or_resume(
enum Event_scheduler::enum_suspend_or_resume action)
{
......@@ -2116,59 +2116,6 @@ Event_scheduler::events_count()
}
/*
Looks for a named event in mysql.event and then loads it from
the table, compiles and inserts it into the cache.
SYNOPSIS
Event_scheduler::load_named_event()
thd THD
etn The name of the event to load and compile on scheduler's root
etn_new The loaded event
RETURN VALUE
NULL Error during compile or the event is non-enabled.
otherwise Address
*/
enum Event_scheduler::enum_error_code
Event_scheduler::load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new)
{
int ret= 0;
MEM_ROOT *tmp_mem_root;
Event_timed *et_loaded= NULL;
Open_tables_state backup;
DBUG_ENTER("Event_scheduler::load_and_compile_event");
DBUG_PRINT("enter",("thd=%p name:%*s",thd, etn->name.length, etn->name.str));
thd->reset_n_backup_open_tables_state(&backup);
/* No need to use my_error() here because db_find_event() has done it */
{
sp_name spn(etn->dbname, etn->name);
ret= db_find_event(thd, &spn, &et_loaded, NULL, &scheduler_root);
}
thd->restore_backup_open_tables_state(&backup);
/* In this case no memory was allocated so we don't need to clean */
if (ret)
DBUG_RETURN(OP_LOAD_ERROR);
if (et_loaded->status != Event_timed::ENABLED)
{
/*
We don't load non-enabled events.
In db_find_event() `et_new` was allocated on the heap and not on
scheduler_root therefore we delete it here.
*/
delete et_loaded;
DBUG_RETURN(OP_DISABLED_EVENT);
}
et_loaded->compute_next_execution_time();
*etn_new= et_loaded;
DBUG_RETURN(OP_OK);
}
/*
......@@ -2212,7 +2159,7 @@ Event_scheduler::load_events_from_db(THD *thd)
DBUG_RETURN(EVEX_GENERAL_ERROR);
}
if ((ret= Events::open_event_table(thd, TL_READ, &table)))
if ((ret= Events::get_instance()->open_event_table(thd, TL_READ, &table)))
{
sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open.");
DBUG_RETURN(EVEX_OPEN_TABLE_FAILED);
......
......@@ -18,6 +18,7 @@
class sp_name;
class Event_timed;
class Event_db_repository;
class THD;
typedef bool * (*event_timed_identifier_comparator)(Event_timed*, Event_timed*);
......@@ -31,17 +32,6 @@ events_shutdown();
class Event_scheduler
{
public:
/* Return codes */
enum enum_error_code
{
OP_OK= 0,
OP_NOT_RUNNING,
OP_CANT_KILL,
OP_CANT_INIT,
OP_DISABLED_EVENT,
OP_LOAD_ERROR,
OP_ALREADY_EXISTS
};
enum enum_state
{
......@@ -66,10 +56,10 @@ class Event_scheduler
/* Methods for queue management follow */
enum enum_error_code
int
create_event(THD *thd, Event_timed *et, bool check_existence);
enum enum_error_code
int
update_event(THD *thd, Event_timed *et, LEX_STRING *new_schema,
LEX_STRING *new_name);
......@@ -78,10 +68,10 @@ class Event_scheduler
int
drop_schema_events(THD *thd, LEX_STRING *schema);
drop_schema_events(THD *thd, LEX_STRING schema);
int
drop_user_events(THD *thd, LEX_STRING *definer, uint *dropped_num)
drop_user_events(THD *thd, LEX_STRING *definer)
{ DBUG_ASSERT(0); return 0;}
uint
......@@ -92,20 +82,24 @@ class Event_scheduler
bool
start();
enum enum_error_code
int
stop();
bool
start_suspended();
/*
Need to be public because has to be called from the function
passed to pthread_create.
*/
bool
run(THD *thd);
enum enum_error_code
int
suspend_or_resume(enum enum_suspend_or_resume action);
bool
init();
init(Event_db_repository *db_repo);
void
destroy();
......@@ -156,14 +150,11 @@ class Event_scheduler
void
stop_all_running_events(THD *thd);
enum enum_error_code
load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new);
int
load_events_from_db(THD *thd);
void
drop_matching_events(THD *thd, LEX_STRING *pattern,
drop_matching_events(THD *thd, LEX_STRING pattern,
bool (*)(Event_timed *,LEX_STRING *));
bool
......@@ -230,6 +221,8 @@ class Event_scheduler
/* 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;
......
......@@ -15,11 +15,10 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "mysql_priv.h"
#include "events_priv.h"
#include "events.h"
#include "event_data_objects.h"
#include "event_scheduler.h"
#include "sp.h"
#include "event_db_repository.h"
#include "sp_head.h"
/*
......@@ -48,10 +47,6 @@
*/
MEM_ROOT evex_mem_root;
time_t mysql_event_last_create_time= 0L;
const char *event_scheduler_state_names[]=
{ "OFF", "0", "ON", "1", "SUSPEND", "2", NullS };
......@@ -63,104 +58,10 @@ TYPELIB Events::opt_typelib=
NULL
};
Events Events::singleton;
ulong Events::opt_event_scheduler= 2;
static
TABLE_FIELD_W_TYPE event_table_fields[Events::FIELD_COUNT] = {
{
{(char *) STRING_WITH_LEN("db")},
{(char *) STRING_WITH_LEN("char(64)")},
{(char *) STRING_WITH_LEN("utf8")}
},
{
{(char *) STRING_WITH_LEN("name")},
{(char *) STRING_WITH_LEN("char(64)")},
{(char *) STRING_WITH_LEN("utf8")}
},
{
{(char *) STRING_WITH_LEN("body")},
{(char *) STRING_WITH_LEN("longblob")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("definer")},
{(char *) STRING_WITH_LEN("char(77)")},
{(char *) STRING_WITH_LEN("utf8")}
},
{
{(char *) STRING_WITH_LEN("execute_at")},
{(char *) STRING_WITH_LEN("datetime")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("interval_value")},
{(char *) STRING_WITH_LEN("int(11)")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("interval_field")},
{(char *) STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY',"
"'HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR',"
"'DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND',"
"'DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND',"
"'SECOND_MICROSECOND')")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("created")},
{(char *) STRING_WITH_LEN("timestamp")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("modified")},
{(char *) STRING_WITH_LEN("timestamp")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("last_executed")},
{(char *) STRING_WITH_LEN("datetime")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("starts")},
{(char *) STRING_WITH_LEN("datetime")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("ends")},
{(char *) STRING_WITH_LEN("datetime")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("status")},
{(char *) STRING_WITH_LEN("enum('ENABLED','DISABLED')")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("on_completion")},
{(char *) STRING_WITH_LEN("enum('DROP','PRESERVE')")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("sql_mode")},
{(char *) STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES',"
"'IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION',"
"'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB',"
"'NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40',"
"'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES',"
"'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES',"
"'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER',"
"'HIGH_NOT_PRECEDENCE')")},
{NULL, 0}
},
{
{(char *) STRING_WITH_LEN("comment")},
{(char *) STRING_WITH_LEN("char(64)")},
{(char *) STRING_WITH_LEN("utf8")}
}
};
/*
Compares 2 LEX strings regarding case.
......@@ -188,6 +89,14 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
}
Events *
Events::get_instance()
{
DBUG_ENTER("Events::get_instance");
DBUG_RETURN(&singleton);
}
/*
Reconstructs interval expression from interval type and expression
value that is in form of a value of the smalles entity:
......@@ -207,9 +116,8 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
*/
int
Events::reconstruct_interval_expression(String *buf,
interval_type interval,
longlong expression)
Events::reconstruct_interval_expression(String *buf, interval_type interval,
longlong expression)
{
ulonglong expr= expression;
char tmp_buff[128], *end;
......@@ -341,545 +249,7 @@ int
Events::open_event_table(THD *thd, enum thr_lock_type lock_type,
TABLE **table)
{
TABLE_LIST tables;
DBUG_ENTER("open_events_table");
bzero((char*) &tables, sizeof(tables));
tables.db= (char*) "mysql";
tables.table_name= tables.alias= (char*) "event";
tables.lock_type= lock_type;
if (simple_open_n_lock_tables(thd, &tables))
DBUG_RETURN(1);
if (table_check_intact(tables.table, Events::FIELD_COUNT,
event_table_fields,
&mysql_event_last_create_time,
ER_CANNOT_LOAD_FROM_TABLE))
{
close_thread_tables(thd);
DBUG_RETURN(2);
}
*table= tables.table;
tables.table->use_all_columns();
DBUG_RETURN(0);
}
/*
Find row in open mysql.event table representing event
SYNOPSIS
evex_db_find_event_aux()
thd Thread context
et event_timed object containing dbname & name
table TABLE object for open mysql.event table.
RETURN VALUE
0 - Routine found
EVEX_KEY_NOT_FOUND - No routine with given name
*/
inline int
evex_db_find_event_aux(THD *thd, Event_timed *et, TABLE *table)
{
return evex_db_find_event_by_name(thd, et->dbname, et->name, table);
}
/*
Find row in open mysql.event table representing event
SYNOPSIS
evex_db_find_event_by_name()
thd Thread context
dbname Name of event's database
rname Name of the event inside the db
table TABLE object for open mysql.event table.
RETURN VALUE
0 - Routine found
EVEX_KEY_NOT_FOUND - No routine with given name
*/
int
evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname,
const LEX_STRING ev_name,
TABLE *table)
{
byte key[MAX_KEY_LENGTH];
DBUG_ENTER("evex_db_find_event_by_name");
DBUG_PRINT("enter", ("name: %.*s", ev_name.length, ev_name.str));
/*
Create key to find row. We have to use field->store() to be able to
handle VARCHAR and CHAR fields.
Assumption here is that the two first fields in the table are
'db' and 'name' and the first key is the primary key over the
same fields.
*/
if (dbname.length > table->field[Events::FIELD_DB]->field_length ||
ev_name.length > table->field[Events::FIELD_NAME]->field_length)
DBUG_RETURN(EVEX_KEY_NOT_FOUND);
table->field[Events::FIELD_DB]->store(dbname.str, dbname.length,
&my_charset_bin);
table->field[Events::FIELD_NAME]->store(ev_name.str, ev_name.length,
&my_charset_bin);
key_copy(key, table->record[0], table->key_info,
table->key_info->key_length);
if (table->file->index_read_idx(table->record[0], 0, key,
table->key_info->key_length,
HA_READ_KEY_EXACT))
{
DBUG_PRINT("info", ("Row not found"));
DBUG_RETURN(EVEX_KEY_NOT_FOUND);
}
DBUG_PRINT("info", ("Row found!"));
DBUG_RETURN(0);
}
/*
Puts some data common to CREATE and ALTER EVENT into a row.
SYNOPSIS
evex_fill_row()
thd THD
table the row to fill out
et Event's data
RETURN VALUE
0 - OK
EVEX_GENERAL_ERROR - bad data
EVEX_GET_FIELD_FAILED - field count does not match. table corrupted?
DESCRIPTION
Used both when an event is created and when it is altered.
*/
static int
evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update)
{
CHARSET_INFO *scs= system_charset_info;
enum Events::enum_table_field field_num;
DBUG_ENTER("evex_fill_row");
DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str));
DBUG_PRINT("info", ("name =[%s]", et->name.str));
DBUG_PRINT("info", ("body =[%s]", et->body.str));
if (table->field[field_num= Events::FIELD_DEFINER]->
store(et->definer.str, et->definer.length, scs))
goto err_truncate;
if (table->field[field_num= Events::FIELD_DB]->
store(et->dbname.str, et->dbname.length, scs))
goto err_truncate;
if (table->field[field_num= Events::FIELD_NAME]->
store(et->name.str, et->name.length, scs))
goto err_truncate;
/* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/
table->field[Events::FIELD_ON_COMPLETION]->
store((longlong)et->on_completion, true);
table->field[Events::FIELD_STATUS]->store((longlong)et->status, true);
/*
Change the SQL_MODE only if body was present in an ALTER EVENT and of course
always during CREATE EVENT.
*/
if (et->body.str)
{
table->field[Events::FIELD_SQL_MODE]->
store((longlong)thd->variables.sql_mode, true);
if (table->field[field_num= Events::FIELD_BODY]->
store(et->body.str, et->body.length, scs))
goto err_truncate;
}
if (et->expression)
{
table->field[Events::FIELD_INTERVAL_EXPR]->set_notnull();
table->field[Events::FIELD_INTERVAL_EXPR]->
store((longlong)et->expression, true);
table->field[Events::FIELD_TRANSIENT_INTERVAL]->set_notnull();
/*
In the enum (C) intervals start from 0 but in mysql enum valid values start
from 1. Thus +1 offset is needed!
*/
table->field[Events::FIELD_TRANSIENT_INTERVAL]->
store((longlong)et->interval+1, true);
table->field[Events::FIELD_EXECUTE_AT]->set_null();
if (!et->starts_null)
{
table->field[Events::FIELD_STARTS]->set_notnull();
table->field[Events::FIELD_STARTS]->
store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME);
}
if (!et->ends_null)
{
table->field[Events::FIELD_ENDS]->set_notnull();
table->field[Events::FIELD_ENDS]->
store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME);
}
}
else if (et->execute_at.year)
{
table->field[Events::FIELD_INTERVAL_EXPR]->set_null();
table->field[Events::FIELD_TRANSIENT_INTERVAL]->set_null();
table->field[Events::FIELD_STARTS]->set_null();
table->field[Events::FIELD_ENDS]->set_null();
table->field[Events::FIELD_EXECUTE_AT]->set_notnull();
table->field[Events::FIELD_EXECUTE_AT]->
store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME);
}
else
{
DBUG_ASSERT(is_update);
/*
it is normal to be here when the action is update
this is an error if the action is create. something is borked
*/
}
((Field_timestamp *)table->field[Events::FIELD_MODIFIED])->set_time();
if (et->comment.str)
{
if (table->field[field_num= Events::FIELD_COMMENT]->
store(et->comment.str, et->comment.length, scs))
goto err_truncate;
}
DBUG_RETURN(0);
err_truncate:
my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), table->field[field_num]->field_name);
DBUG_RETURN(EVEX_GENERAL_ERROR);
}
/*
Creates an event in mysql.event
SYNOPSIS
db_create_event()
thd THD
et Event_timed object containing information for the event
create_if_not If an warning should be generated in case event exists
rows_affected How many rows were affected
RETURN VALUE
0 - OK
EVEX_GENERAL_ERROR - Failure
DESCRIPTION
Creates an event. Relies on evex_fill_row which is shared with
db_update_event. The name of the event is inside "et".
*/
int
db_create_event(THD *thd, Event_timed *et, my_bool create_if_not,
uint *rows_affected)
{
int ret= 0;
CHARSET_INFO *scs= system_charset_info;
TABLE *table;
char olddb[128];
bool dbchanged= false;
DBUG_ENTER("db_create_event");
DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str));
*rows_affected= 0;
DBUG_PRINT("info", ("open mysql.event for update"));
if (Events::open_event_table(thd, TL_WRITE, &table))
{
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
goto err;
}
DBUG_PRINT("info", ("check existance of an event with the same name"));
if (!evex_db_find_event_aux(thd, et, table))
{
if (create_if_not)
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
et->name.str);
goto ok;
}
my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), et->name.str);
goto err;
}
DBUG_PRINT("info", ("non-existant, go forward"));
if ((ret= sp_use_new_db(thd, et->dbname.str,olddb, sizeof(olddb),0,
&dbchanged)))
{
my_error(ER_BAD_DB_ERROR, MYF(0));
goto err;
}
restore_record(table, s->default_values); // Get default values for fields
if (system_charset_info->cset->numchars(system_charset_info, et->dbname.str,
et->dbname.str + et->dbname.length)
> EVEX_DB_FIELD_LEN)
{
my_error(ER_TOO_LONG_IDENT, MYF(0), et->dbname.str);
goto err;
}
if (system_charset_info->cset->numchars(system_charset_info, et->name.str,
et->name.str + et->name.length)
> EVEX_DB_FIELD_LEN)
{
my_error(ER_TOO_LONG_IDENT, MYF(0), et->name.str);
goto err;
}
if (et->body.length > table->field[Events::FIELD_BODY]->field_length)
{
my_error(ER_TOO_LONG_BODY, MYF(0), et->name.str);
goto err;
}
if (!(et->expression) && !(et->execute_at.year))
{
DBUG_PRINT("error", ("neither expression nor execute_at are set!"));
my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0));
goto err;
}
((Field_timestamp *)table->field[Events::FIELD_CREATED])->set_time();
/*
evex_fill_row() calls my_error() in case of error so no need to
handle it here
*/
if ((ret= evex_fill_row(thd, table, et, false)))
goto err;
if (table->file->ha_write_row(table->record[0]))
{
my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
goto err;
}
#ifdef USE_THIS_CODE_AS_TEMPLATE_WHEN_EVENT_REPLICATION_IS_AGREED
if (mysql_bin_log.is_open())
{
thd->clear_error();
/* Such a statement can always go directly to binlog, no trans cache */
thd->binlog_query(THD::MYSQL_QUERY_TYPE, thd->query, thd->query_length,
FALSE, FALSE);
}
#endif
*rows_affected= 1;
ok:
if (dbchanged)
(void) mysql_change_db(thd, olddb, 1);
if (table)
close_thread_tables(thd);
DBUG_RETURN(EVEX_OK);
err:
if (dbchanged)
(void) mysql_change_db(thd, olddb, 1);
if (table)
close_thread_tables(thd);
DBUG_RETURN(EVEX_GENERAL_ERROR);
}
/*
Used to execute ALTER EVENT. Pendant to Events::update_event().
SYNOPSIS
db_update_event()
thd THD
sp_name the name of the event to alter
et event's data
RETURN VALUE
0 OK
EVEX_GENERAL_ERROR Error occured (my_error() called)
NOTES
sp_name is passed since this is the name of the event to
alter in case of RENAME TO.
*/
static int
db_update_event(THD *thd, Event_timed *et, sp_name *new_name)
{
CHARSET_INFO *scs= system_charset_info;
TABLE *table;
int ret= EVEX_OPEN_TABLE_FAILED;
DBUG_ENTER("db_update_event");
DBUG_PRINT("enter", ("dbname: %.*s", et->dbname.length, et->dbname.str));
DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str));
DBUG_PRINT("enter", ("user: %.*s", et->definer.length, et->definer.str));
if (new_name)
DBUG_PRINT("enter", ("rename to: %.*s", new_name->m_name.length,
new_name->m_name.str));
if (Events::open_event_table(thd, TL_WRITE, &table))
{
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
goto err;
}
/* first look whether we overwrite */
if (new_name)
{
if (!sortcmp_lex_string(et->name, new_name->m_name, scs) &&
!sortcmp_lex_string(et->dbname, new_name->m_db, scs))
{
my_error(ER_EVENT_SAME_NAME, MYF(0), et->name.str);
goto err;
}
if (!evex_db_find_event_by_name(thd,new_name->m_db,new_name->m_name,table))
{
my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str);
goto err;
}
}
/*
...and then if there is such an event. Don't exchange the blocks
because you will get error 120 from table handler because new_name will
overwrite the key and SE will tell us that it cannot find the already found
row (copied into record[1] later
*/
if (EVEX_KEY_NOT_FOUND == evex_db_find_event_aux(thd, et, table))
{
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str);
goto err;
}
store_record(table,record[1]);
/* Don't update create on row update. */
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
/*
evex_fill_row() calls my_error() in case of error so no need to
handle it here
*/
if ((ret= evex_fill_row(thd, table, et, true)))
goto err;
if (new_name)
{
table->field[Events::FIELD_DB]->
store(new_name->m_db.str, new_name->m_db.length, scs);
table->field[Events::FIELD_NAME]->
store(new_name->m_name.str, new_name->m_name.length, scs);
}
if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
{
my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
goto err;
}
/* close mysql.event or we crash later when loading the event from disk */
close_thread_tables(thd);
DBUG_RETURN(0);
err:
if (table)
close_thread_tables(thd);
DBUG_RETURN(EVEX_GENERAL_ERROR);
}
/*
Looks for a named event in mysql.event and in case of success returns
an object will data loaded from the table.
SYNOPSIS
db_find_event()
thd THD
name the name of the event to find
ett event's data if event is found
tbl TABLE object to use when not NULL
NOTES
1) Use sp_name for look up, return in **ett if found
2) tbl is not closed at exit
RETURN VALUE
0 ok In this case *ett is set to the event
# error *ett == 0
*/
int
db_find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl,
MEM_ROOT *root)
{
TABLE *table;
int ret;
Event_timed *et= NULL;
DBUG_ENTER("db_find_event");
DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
if (!root)
root= &evex_mem_root;
if (tbl)
table= tbl;
else if (Events::open_event_table(thd, TL_READ, &table))
{
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
ret= EVEX_GENERAL_ERROR;
goto done;
}
if ((ret= evex_db_find_event_by_name(thd, name->m_db, name->m_name, table)))
{
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str);
goto done;
}
et= new Event_timed;
/*
1)The table should not be closed beforehand. ::load_from_row() only loads
and does not compile
2)::load_from_row() is silent on error therefore we emit error msg here
*/
if ((ret= et->load_from_row(root, table)))
{
my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0));
goto done;
}
done:
if (ret)
{
delete et;
et= 0;
}
/* don't close the table if we haven't opened it ourselves */
if (!tbl && table)
close_thread_tables(thd);
*ett= et;
DBUG_RETURN(ret);
return db_repository.open_event_table(thd, lock_type, table);
}
......@@ -913,9 +283,10 @@ Events::create_event(THD *thd, Event_timed *et, Event_parse_data *parse_data,
DBUG_PRINT("enter", ("name: %*s options:%d", et->name.length,
et->name.str, create_options));
if (!(ret = db_create_event(thd, et,
create_options & HA_LEX_CREATE_IF_NOT_EXISTS,
rows_affected)))
if (!(ret= db_repository.
create_event(thd, et,
create_options & HA_LEX_CREATE_IF_NOT_EXISTS,
rows_affected)))
{
Event_scheduler *scheduler= Event_scheduler::get_instance();
if (scheduler->initialized() &&
......@@ -960,7 +331,7 @@ Events::update_event(THD *thd, Event_timed *et, Event_parse_data *parse_data,
crash later in the code when loading and compiling the new definition.
Also on error conditions my_error() is called so no need to handle here
*/
if (!(ret= db_update_event(thd, et, new_name)))
if (!(ret= db_repository.update_event(thd, et, new_name)))
{
Event_scheduler *scheduler= Event_scheduler::get_instance();
if (scheduler->initialized() &&
......@@ -973,67 +344,6 @@ Events::update_event(THD *thd, Event_timed *et, Event_parse_data *parse_data,
}
/*
Drops an event
SYNOPSIS
db_drop_event()
thd THD
et event's name
drop_if_exists if set and the event not existing => warning onto the stack
rows_affected affected number of rows is returned heres
RETURN VALUE
0 OK
!0 Error (my_error() called)
*/
int db_drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
bool drop_if_exists, uint *rows_affected)
{
TABLE *table;
Open_tables_state backup;
int ret;
DBUG_ENTER("db_drop_event");
DBUG_PRINT("enter", ("db=%s name=%s", db.str, name.str));
ret= EVEX_OPEN_TABLE_FAILED;
thd->reset_n_backup_open_tables_state(&backup);
if (Events::open_event_table(thd, TL_WRITE, &table))
{
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
goto done;
}
if (!(ret= evex_db_find_event_by_name(thd, db, name, table)))
{
if ((ret= table->file->ha_delete_row(table->record[0])))
my_error(ER_EVENT_CANNOT_DELETE, MYF(0));
}
else if (ret == EVEX_KEY_NOT_FOUND)
{
if (drop_if_exists)
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
"Event", name.str);
ret= 0;
} else
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
}
done:
/*
evex_drop_event() is used by Event_timed::drop therefore
we have to close our thread tables.
*/
close_thread_tables(thd);
thd->restore_backup_open_tables_state(&backup);
DBUG_RETURN(ret);
}
/*
Drops an event
......@@ -1056,14 +366,14 @@ Events::drop_event(THD *thd, sp_name *name, bool drop_if_exists,
int ret;
DBUG_ENTER("Events::drop_event");
if (!(ret= db_drop_event(thd, name->m_db, name->m_name, drop_if_exists,
rows_affected)))
if (!(ret= db_repository.drop_event(thd, name->m_db, name->m_name,
drop_if_exists, rows_affected)))
{
Event_scheduler *scheduler= Event_scheduler::get_instance();
if (scheduler->initialized() && (ret= scheduler->drop_event(thd, name)))
my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret);
}
DBUG_RETURN(ret);
}
......@@ -1092,7 +402,7 @@ Events::show_create_event(THD *thd, sp_name *spn)
DBUG_PRINT("enter", ("name: %*s", spn->m_name.length, spn->m_name.str));
thd->reset_n_backup_open_tables_state(&backup);
ret= db_find_event(thd, spn, &et, NULL, thd->mem_root);
ret= db_repository.find_event(thd, spn, &et, NULL, thd->mem_root);
thd->restore_backup_open_tables_state(&backup);
if (!ret)
......@@ -1164,75 +474,13 @@ Events::drop_schema_events(THD *thd, char *db)
DBUG_PRINT("enter", ("dropping events from %s", db));
Event_scheduler *scheduler= Event_scheduler::get_instance();
if (scheduler->initialized())
ret= scheduler->drop_schema_events(thd, &db_lex);
else
ret= db_drop_events_from_table(thd, &db_lex);
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);
ret= scheduler->drop_schema_events(thd, db_lex);
ret= db_repository.drop_schema_events(thd, db_lex);
DBUG_RETURN(ret);
}
/*
Inits the scheduler's structures.
......@@ -1251,14 +499,17 @@ int
Events::init()
{
int ret= 0;
Event_db_repository *db_repo;
DBUG_ENTER("Events::init");
db_repo= &get_instance()->db_repository;
db_repo->init_repository();
/* it should be an assignment! */
if (opt_event_scheduler)
{
Event_scheduler *scheduler= Event_scheduler::get_instance();
DBUG_ASSERT(opt_event_scheduler == 1 || opt_event_scheduler == 2);
DBUG_RETURN(scheduler->init() ||
DBUG_RETURN(scheduler->init(db_repo) ||
(opt_event_scheduler == 1? scheduler->start():
scheduler->start_suspended()));
}
......@@ -1270,22 +521,23 @@ Events::init()
Cleans up scheduler's resources. Called at server shutdown.
SYNOPSIS
Events::shutdown()
Events::deinit()
NOTES
This function is not synchronized.
*/
void
Events::shutdown()
Events::deinit()
{
DBUG_ENTER("Events::shutdown");
DBUG_ENTER("Events::deinit");
Event_scheduler *scheduler= Event_scheduler::get_instance();
if (scheduler->initialized())
{
scheduler->stop();
scheduler->destroy();
}
get_instance()->db_repository.deinit_repository();
DBUG_VOID_RETURN;
}
......
......@@ -20,74 +20,91 @@ class sp_name;
class Event_timed;
class Event_parse_data;
#include "event_db_repository.h"
/* Return codes */
enum enum_events_error_code
{
OP_OK= 0,
OP_NOT_RUNNING,
OP_CANT_KILL,
OP_CANT_INIT,
OP_DISABLED_EVENT,
OP_LOAD_ERROR,
OP_ALREADY_EXISTS
};
int
sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs);
class Events
{
public:
/*
Quite NOT the best practice and will be removed once
Event_timed::drop() and Event_timed is fixed not do drop directly
or other scheme will be found.
*/
friend class Event_timed;
static ulong opt_event_scheduler;
static TYPELIB opt_typelib;
enum enum_table_field
{
FIELD_DB = 0,
FIELD_NAME,
FIELD_BODY,
FIELD_DEFINER,
FIELD_EXECUTE_AT,
FIELD_INTERVAL_EXPR,
FIELD_TRANSIENT_INTERVAL,
FIELD_CREATED,
FIELD_MODIFIED,
FIELD_LAST_EXECUTED,
FIELD_STARTS,
FIELD_ENDS,
FIELD_STATUS,
FIELD_ON_COMPLETION,
FIELD_SQL_MODE,
FIELD_COMMENT,
FIELD_COUNT /* a cool trick to count the number of fields :) */
};
static int
init();
static void
deinit();
static void
init_mutexes();
static void
destroy_mutexes();
static Events*
get_instance();
int
create_event(THD *thd, Event_timed *et, Event_parse_data *parse_data,
uint create_options, uint *rows_affected);
static int
int
update_event(THD *thd, Event_timed *et, Event_parse_data *parse_data,
sp_name *new_name, uint *rows_affected);
static int
int
drop_event(THD *thd, sp_name *name, bool drop_if_exists, uint *rows_affected);
static int
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
int
drop_schema_events(THD *thd, char *db);
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 &);
......
#ifndef _EVENT_PRIV_H_
#define _EVENT_PRIV_H_
/* Copyright (C) 2004-2006 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#define EVENT_EXEC_STARTED 0
#define EVENT_EXEC_ALREADY_EXEC 1
#define EVENT_EXEC_CANT_FORK 2
#define EVEX_DB_FIELD_LEN 64
#define EVEX_NAME_FIELD_LEN 64
#define EVEX_MAX_INTERVAL_VALUE 2147483647L
class Event_timed;
class sp_name;
int
evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname,
const LEX_STRING ev_name,
TABLE *table);
int
db_drop_event(THD *thd, LEX_STRING db, LEX_STRING name, bool drop_if_exists,
uint *rows_affected);
int
db_find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl,
MEM_ROOT *root);
int
db_create_event(THD *thd, Event_timed *et, my_bool create_if_not,
uint *rows_affected);
int
db_drop_events_from_table(THD *thd, LEX_STRING *db);
int
sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs);
/* Compares only the name part of the identifier */
bool
event_timed_name_equal(Event_timed *et, LEX_STRING *name);
/* Compares only the schema part of the identifier */
bool
event_timed_db_equal(Event_timed *et, LEX_STRING *db);
/*
Compares only the definer part of the identifier. Use during DROP USER
to drop user's events. (Still not implemented)
*/
bool
event_timed_definer_equal(Event_timed *et, LEX_STRING *definer);
/* Compares the whole identifier*/
bool
event_timed_identifier_equal(Event_timed *a, Event_timed *b);
/* Compares only the name part of the identifier */
bool
event_timed_name_equal(sp_name *name, LEX_STRING *event_name);
/* Compares only the schema part of the identifier */
bool
event_timed_db_equal(sp_name *name, LEX_STRING *db);
/* Compares the whole identifier*/
bool
event_timed_identifier_equal(sp_name *a, Event_timed *b);
bool
change_security_context(THD *thd, LEX_STRING user, LEX_STRING host,
LEX_STRING db, Security_context *s_ctx,
Security_context **backup);
void
restore_security_context(THD *thd, Security_context *backup);
#endif /* _EVENT_PRIV_H_ */
......@@ -886,7 +886,7 @@ static void close_connections(void)
}
(void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list
Events::shutdown();
Events::deinit();
end_slave();
if (thread_count)
......
......@@ -3891,7 +3891,7 @@ byte *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b)
bool
sys_var_event_scheduler::update(THD *thd, set_var *var)
{
enum Event_scheduler::enum_error_code res;
int res;
Event_scheduler *scheduler= Event_scheduler::get_instance();
/* here start the thread if not running. */
DBUG_ENTER("sys_var_event_scheduler::update");
......
......@@ -2048,6 +2048,63 @@ bool Security_context::set_user(char *user_arg)
return user == 0;
}
/*
Switches the security context
SYNOPSIS
THD::change_security_context()
user The user
host The host of the user
db The schema for which the security_ctx will be loaded
s_ctx Security context to load state into
backup Where to store the old context
RETURN VALUE
FALSE OK
TRUE Error (generates error too)
*/
bool
THD::change_security_context(LEX_STRING user, LEX_STRING host,
LEX_STRING db, Security_context *s_ctx,
Security_context **backup)
{
DBUG_ENTER("change_security_context");
DBUG_PRINT("info",("%s@%s@%s", user.str, host.str, db.str));
#ifndef NO_EMBEDDED_ACCESS_CHECKS
s_ctx->init();
*backup= 0;
if (acl_getroot_no_password(s_ctx, user.str, host.str, host.str, db.str))
{
my_error(ER_NO_SUCH_USER, MYF(0), user.str, host.str);
DBUG_RETURN(TRUE);
}
*backup= security_ctx;
security_ctx= s_ctx;
#endif
DBUG_RETURN(FALSE);
}
/*
Restores the security context
SYNOPSIS
restore_security_context()
thd Thread
backup Context to switch to
*/
void
THD::restore_security_context(Security_context *backup)
{
DBUG_ENTER("restore_security_context");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (backup)
security_ctx= backup;
#endif
DBUG_VOID_RETURN;
}
/****************************************************************************
Handling of open and locked tables states.
......
......@@ -868,6 +868,14 @@ class THD :public Statement,
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;
......
......@@ -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()
......
......@@ -3847,13 +3847,14 @@ mysql_execute_command(THD *thd)
switch (lex->sql_command) {
case SQLCOM_CREATE_EVENT:
res= Events::create_event(thd, lex->et, lex->event_parse_data,
(uint) lex->create_info.options,
&rows_affected);
res= Events::get_instance()->
create_event(thd, lex->et, lex->event_parse_data,
(uint) lex->create_info.options, &rows_affected);
break;
case SQLCOM_ALTER_EVENT:
res= Events::update_event(thd, lex->et, lex->event_parse_data,
lex->spname, &rows_affected);
res= Events::get_instance()->
update_event(thd, lex->et, lex->event_parse_data,
lex->spname, &rows_affected);
break;
default:;
}
......@@ -3895,7 +3896,7 @@ mysql_execute_command(THD *thd)
}
if (lex->sql_command == SQLCOM_SHOW_CREATE_EVENT)
res= Events::show_create_event(thd, lex->spname);
res= Events::get_instance()->show_create_event(thd, lex->spname);
else
{
uint rows_affected= 1;
......@@ -3904,8 +3905,9 @@ mysql_execute_command(THD *thd)
res= -1;
break;
}
if (!(res= Events::drop_event(thd, lex->spname, lex->drop_if_exists,
&rows_affected)))
if (!(res= Events::get_instance()->drop_event(thd, lex->spname,
lex->drop_if_exists,
&rows_affected)))
send_ok(thd, rows_affected);
}
break;
......@@ -3913,7 +3915,7 @@ mysql_execute_command(THD *thd)
#ifndef DBUG_OFF
case SQLCOM_SHOW_SCHEDULER_STATUS:
{
res= Events::dump_internal_status(thd);
res= Events::get_instance()->dump_internal_status(thd);
break;
}
#endif
......
......@@ -4324,7 +4324,7 @@ int events_table_index_read_for_db(THD *thd, TABLE *schema_table,
DBUG_PRINT("info", ("Using prefix scanning on PK"));
event_table->file->ha_index_init(0, 1);
event_table->field[Events::FIELD_DB]->
event_table->field[ET_FIELD_DB]->
store(thd->lex->select_lex.db, strlen(thd->lex->select_lex.db), scs);
key_info= event_table->key_info;
key_len= key_info->key_part[0].store_length;
......@@ -4443,7 +4443,7 @@ int fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
thd->lex->select_lex.db:"(null)"));
thd->reset_n_backup_open_tables_state(&backup);
if (Events::open_event_table(thd, TL_READ, &event_table))
if (Events::get_instance()->open_event_table(thd, TL_READ, &event_table))
{
sql_print_error("Table mysql.event is damaged.");
thd->restore_backup_open_tables_state(&backup);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment