summaryrefslogtreecommitdiff
path: root/sql/event.cc
diff options
context:
space:
mode:
authorunknown <andrey@lmy004.>2005-12-05 11:45:04 +0100
committerunknown <andrey@lmy004.>2005-12-05 11:45:04 +0100
commit7ff79771057678b8c3474963773abe3a591fb812 (patch)
treee98ba39876d161bb72b5a72a6122df08aecdfd33 /sql/event.cc
parente7f18c97b8528b283a06c76e7be319c3d8b5cc69 (diff)
downloadmariadb-git-7ff79771057678b8c3474963773abe3a591fb812.tar.gz
WL#1034 updated
- split into several files - forbid parallel execution (before analyse is done how to make it possible) because the same sp_head instance cannot be executed in parallel - added GPL headers - changed EVENT_ACL to be per DB variable - fixed minor problems mysql-test/lib/init_db.sql: WL#1034 updated - add Event_priv to mysql.user (update test) - add updated mysql.event table struct scripts/mysql_fix_privilege_tables.sql: WL#1034 updated add updated mysql.event structure sql/Makefile.am: split event.cc into - event.cc (create/alter/drop) - event_executor.cc (main and worker threads) - event_timed.cc (class event_timed) - event_priv.h (some definitions used internally by the module) sql/event.cc: - added GPL header - split into few files - fixed some issues after code review - now using SP routines for opening/traversing/closing tables (will be reverted) sql/event.h: - add GPL header - remove two methods - inline them in sql_yacc.yy - don't use absolute values for EVEX_ defines but the SP_ equivalents (have to move 100% to SP_ defines and as later step to not transfer messages upwards in the stack but report them at the place they occur) - updated reference table definition - move default mem_root param from event.cc to the header sql/mysqld.cc: WL#1034 rename --event-executor to --event-scheduler executor sounds a bit scary :) sql/set_var.cc: rename internal variable name from event_executor to event_scheduler (ppl won't be scarried anymore :) sql/share/errmsg.txt: omit one %s - not needed sql/sp_head.cc: disable this DBUG_PRINT for a bit. m_next_cached_sp is 0x0 and I get crash here... sql/sp_head.h: remove m_old_cmq, a temporal variable is used in sql_yacc.yy (Serg's idea) to keep the previous state. $<ulong_val>$ = .... YYTHD->client_capabilites != $<ulong_val>4; (the same is done also for class event_timed) sql/sql_acl.cc: handle pre-5.1.4 table and give the user EVENT_ACL if he had CREATE_ACL sql/sql_acl.h: fix the bitmask sql/sql_parse.cc: - move from EVENT_ACL being global to EVENT_ACL being per DB like CREATE_PROC_ACL - lex->m_qname dropped, because not needed, fix code therefore - add comment that SHOW CREATE EVENT has to be implemented sql/sql_yacc.yy: - use temporal variable of Bison to store a short lived value - fix indentation - inline 2 class event_timed methods in the parser sql/tztime.h: be more expressive - it's already extern
Diffstat (limited to 'sql/event.cc')
-rw-r--r--sql/event.cc1641
1 files changed, 75 insertions, 1566 deletions
diff --git a/sql/event.cc b/sql/event.cc
index 007391f76a0..82dbc2ebd76 100644
--- a/sql/event.cc
+++ b/sql/event.cc
@@ -1,77 +1,80 @@
+/* Copyright (C) 2000-2003 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 "sql_acl.h"
#include "event.h"
+#include "event_priv.h"
#include "sp.h"
/*
TODO list :
- 1. Move event_timed class to event_timed.cc as well as the part of the header
- 2. Do something aboute the events replication. SQL statements issued while
- executing an event should not be logged into the binary log.
- 3. Add a lock and use it for guarding access to events_array dynamic array.
- 4. Add checks everywhere where new instance of THD is created. NULL can be
+ - Remove m_ prefixes of member variables.
+
+ - Use timestamps instead of datetime.
+
+ - Don't use SP's functionality for opening and closing of tables
+
+ - CREATE EVENT should not go into binary log! Does it now? The SQL statements
+ issued by the EVENT are replicated.
+
+ - Add a lock and use it for guarding access to events_array dynamic array.
+
+ - Add checks everywhere where new instance of THD is created. NULL can be
returned and this will crash the server. The server will crash probably
later but should not be in this code! Add a global variable, and a lock
to guard it, that will specify an error in a worker thread so preventing
new threads from being spawned.
- 5. Move executor related code to event_executor.cc and .h
- 6. Maybe move all allocations during parsing to evex_mem_root thus saving
+
+ - Maybe move all allocations during parsing to evex_mem_root thus saving
double parsing in evex_create_event!
- 7. If the server is killed (stopping) try to kill executing events..
- 8. What happens if one renames an event in the DB while it is in memory?
+
+ - If the server is killed (stopping) try to kill executing events..
+
+ - What happens if one renames an event in the DB while it is in memory?
Or even deleting it?
- 9. created & modified in the table should be UTC?
- 10. Add a lock to event_timed to serialize execution of an event - do not
+
+ - created & modified in the table should be UTC?
+
+ - Add a lock to event_timed to serialize execution of an event - do not
allow parallel executions. Hmm, however how last_executed is marked
then? The call to event_timed::mark_last_executed() must be moved to
event_timed::execute()?
- 11. Consider using conditional variable when doing shutdown instead of
+
+ - Consider using conditional variable when doing shutdown instead of
waiting some time (tries < 5).
- 12. Fix event_timed::get_show_create_event.
- 13. Add logging to file.
- 14. Add function documentation whenever needed.
-*/
+ - Make event_timed::get_show_create_event() work
+ - Add function documentation whenever needed.
+ - Add logging to file
+
+Warning:
+ - For now parallel execution is not possible because the same sp_head cannot be
+ executed few times!!! There is still no lock attached to particular event.
+
+ */
+
+
+
+
+bool mysql_event_table_exists= 1;
+DYNAMIC_ARRAY events_array;
+DYNAMIC_ARRAY evex_executing_queue;
+MEM_ROOT evex_mem_root;
+
+
-enum
-{
- EVEX_FIELD_DB = 0,
- EVEX_FIELD_NAME,
- EVEX_FIELD_BODY,
- EVEX_FIELD_DEFINER,
- EVEX_FIELD_EXECUTE_AT,
- EVEX_FIELD_INTERVAL_EXPR,
- EVEX_FIELD_TRANSIENT_INTERVAL,
- EVEX_FIELD_CREATED,
- EVEX_FIELD_MODIFIED,
- EVEX_FIELD_LAST_EXECUTED,
- EVEX_FIELD_STARTS,
- EVEX_FIELD_ENDS,
- EVEX_FIELD_STATUS,
- EVEX_FIELD_ON_COMPLETION,
- EVEX_FIELD_COMMENT,
- EVEX_FIELD_COUNT /* a cool trick to count the number of fields :) */
-};
-
-
-#define EVEX_OPEN_TABLE_FOR_UPDATE() \
- open_proc_type_table_for_update(thd, "event", &mysql_event_table_exists)
-
-static bool mysql_event_table_exists= 1;
-static DYNAMIC_ARRAY events_array;
-static DYNAMIC_ARRAY evex_executing_queue;
-static MEM_ROOT evex_mem_root;
-static uint workers_count;
-static bool evex_is_running= false;
-static pthread_mutex_t LOCK_event_arrays,
- LOCK_workers_count,
- LOCK_evex_running;
-
-extern int yyparse(void *thd);
-
-ulong opt_event_executor;
-my_bool event_executor_running_global_var= false;
-
-extern ulong thread_created;
//extern volatile uint thread_running;
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
@@ -89,7 +92,7 @@ extern ulong thread_created;
a < b -> -1
*/
-static inline int
+inline int
my_time_compare(TIME *a, TIME *b)
{
/*
@@ -140,17 +143,10 @@ my_time_compare(TIME *a, TIME *b)
}
-static int
+inline int
event_timed_compare(event_timed **a, event_timed **b)
{
return my_time_compare(&(*a)->m_execute_at, &(*b)->m_execute_at);
-/*
- if (a->sort > b->sort)
- return -1;
- if (a->sort < b->sort)
- return 1;
- return 0;
-*/
}
@@ -210,15 +206,14 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et)
if (et->m_expr)
{
- // m_expr was fixed in init_interval
-// et->m_expr->save_in_field(table->field[EVEX_FIELD_INTERVAL_EXPR], (my_bool)TRUE);
-
table->field[EVEX_FIELD_INTERVAL_EXPR]->set_notnull();
table->field[EVEX_FIELD_INTERVAL_EXPR]->store((longlong)et->m_expr);
table->field[EVEX_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!
+ /*
+ In the enum (C) intervals start from 0 but in mysql enum valid values start
+ from 1. Thus +1 offset is needed!
+ */
table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->m_interval + 1);
}
else if (et->m_execute_at.year)
@@ -243,10 +238,6 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et)
store((et->m_comment).str, (et->m_comment).length, system_charset_info);
DBUG_RETURN(0);
-parse_error:
- DBUG_RETURN(EVEX_PARSE_ERROR);
-general_error:
- DBUG_RETURN(EVEX_GENERAL_ERROR);
get_field_failed:
DBUG_RETURN(EVEX_GET_FIELD_FAILED);
}
@@ -299,11 +290,6 @@ db_create_event(THD *thd, event_timed *et)
restore_record(table, s->default_values); // Get default values for fields
strxmov(definer, et->m_definer_user.str, "@", et->m_definer_host.str, NullS);
- if (table->s->fields != EVEX_FIELD_COUNT)
- {
- ret= EVEX_GET_FIELD_FAILED;
- goto done;
- }
/* TODO : Uncomment these and add handling in sql_parse.cc or here
if (sp->m_name.length > table->field[MYSQL_PROC_FIELD_NAME]->field_length)
@@ -477,6 +463,7 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock)
allocate on evex_mem_root. call without evex_mem_root and
and m_sphead will not be cleared!
*/
+
if ((ret= ett->compile(thd, &evex_mem_root)))
{
thd->mem_root= tmp_mem_root;
@@ -554,27 +541,23 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock)
//we are lucky the event is in the executing queue, no need of second pass
need_second_pass= false;
idx= get_index_dynamic(&events_array, (gptr) ett);
- if (idx != -1)
- {
- //destruct first and then remove. the destructor will delete sp_head
- ett->free_sp();
- delete_dynamic_element(&events_array, idx);
- delete_dynamic_element(&evex_executing_queue, i);
- }
- else
+ if (idx == -1)
{
//this should never happen
- DBUG_PRINT("error", ("Sth weird with get_index_dynamic. %d."
+ DBUG_PRINT("error", (" get_index_dynamic problem. %d."
"i=%d idx=%d evex_ex_queue.buf=%p evex_ex_queue.elements=%d ett=%p\n"
"events_array=%p events_array.elements=%d events_array.buf=%p\n"
"p_et=%p ett=%p",
__LINE__, i, idx, &evex_executing_queue.buffer,
evex_executing_queue.elements, ett, &events_array,
events_array.elements, events_array.buffer, p_et, ett));
- ret= EVEX_GENERAL_ERROR;
- goto done;
+ DBUG_ASSERT(0);
}
- //ok we have cleaned
+ //destruct first and then remove. the destructor will delete sp_head
+ ett->free_sp();
+ delete_dynamic_element(&events_array, idx);
+ delete_dynamic_element(&evex_executing_queue, i);
+ // ok, we have cleaned
}
}
@@ -768,41 +751,6 @@ done_no_evex:
/*
- Checks for existance of a specified event
-
- SYNOPSIS
- evex_event_exists()
- thd THD
- et event's name
-
- NOTES
- Currently unused
-*/
-
-bool
-evex_event_exists(THD *thd, event_timed *et)
-{
- TABLE *table;
- int ret;
- bool opened= FALSE;
- Open_tables_state open_tables_state_backup;
- DBUG_ENTER("evex_event_exists");
-
- if (!(table= open_proc_type_table_for_read(thd, &open_tables_state_backup,
- "event", &mysql_event_table_exists)))
- DBUG_RETURN(SP_OPEN_TABLE_FAILED);
-
- ret= sp_db_find_routine_aux(thd, 0/*notype*/, et->m_db, et->m_name, table);
-
- close_thread_tables(thd);
- thd->restore_backup_open_tables_state(&open_tables_state_backup);
- thd->clear_error();
-
- DBUG_RETURN(ret == SP_OK);
-}
-
-
-/*
Drops an event
SYNOPSIS
@@ -868,1443 +816,4 @@ done:
}
-/*
- !!! This one is executor related so maybe moving it to
- event_executor.cc is a good idea or ?
-*/
-static int
-evex_load_events_from_db(THD *thd)
-{
- TABLE *table;
- READ_RECORD read_record_info;
- MYSQL_LOCK *lock;
- Open_tables_state open_tables_state_backup;
- int ret= -1;
-
- DBUG_ENTER("evex_load_events_from_db");
-
- if (!(table= open_proc_type_table_for_read(thd, &open_tables_state_backup,
- "event", &mysql_event_table_exists)))
- DBUG_RETURN(SP_OPEN_TABLE_FAILED);
-
- VOID(pthread_mutex_lock(&LOCK_event_arrays));
-
- init_read_record(&read_record_info, thd, table ,NULL,1,0);
- while (!(read_record_info.read_record(&read_record_info)))
- {
- event_timed *et, *et_copy;
- if (!(et= new event_timed()))
- {
- DBUG_PRINT("evex_load_events_from_db", ("Out of memory"));
- ret= -1;
- goto end;
- }
- DBUG_PRINT("evex_load_events_from_db", ("Loading event from row."));
-
- if (et->load_from_row(&evex_mem_root, table))
- //error loading!
- continue;
-
- DBUG_PRINT("evex_load_events_from_db",
- ("Event %s loaded from row. Time to compile", et->m_name.str));
-
- if (et->compile(thd, &evex_mem_root))
- //problem during compile
- continue;
- // let's find when to be executed
- et->compute_next_execution_time();
-
- DBUG_PRINT("evex_load_events_from_db",
- ("Adding %s to the executor list.", et->m_name.str));
- VOID(push_dynamic(&events_array,(gptr) et));
- // we always add at the end so the number of elements - 1 is the place
- // in the buffer
- et_copy= dynamic_element(&events_array, events_array.elements - 1,
- event_timed*);
- VOID(push_dynamic(&evex_executing_queue,(gptr) &et_copy));
- et->m_free_sphead_on_delete= false;
- DBUG_PRINT("info", (""));
- delete et;
- }
- end_read_record(&read_record_info);
-
- qsort((gptr) dynamic_element(&evex_executing_queue, 0, event_timed**),
- evex_executing_queue.elements,
- sizeof(event_timed **),
- (qsort_cmp) event_timed_compare
- );
- VOID(pthread_mutex_unlock(&LOCK_event_arrays));
-
- thd->version--; // Force close to free memory
- ret= 0;
-
-end:
- close_thread_tables(thd);
- thd->restore_backup_open_tables_state(&open_tables_state_backup);
-
- DBUG_PRINT("evex_load_events_from_db",
- ("Events loaded from DB. Status code %d", ret));
- DBUG_RETURN(ret);
-}
-
-////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////
-//////////////// EVENT_TIMED class /////////////////////////////////
-////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////
-
-
-/*
- Init all member variables
-
- SYNOPSIS
- event_timed::init()
-*/
-
-void
-event_timed::init()
-{
- DBUG_ENTER("event_timed::init");
-
- m_qname.str= m_db.str= m_name.str= m_body.str= m_comment.str= 0;
- m_qname.length= m_db.length= m_name.length= m_body.length= m_comment.length= 0;
-
- set_zero_time(&m_starts, MYSQL_TIMESTAMP_DATETIME);
- set_zero_time(&m_ends, MYSQL_TIMESTAMP_DATETIME);
- set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME);
- set_zero_time(&m_last_executed, MYSQL_TIMESTAMP_DATETIME);
-
- m_definer_user.str= m_definer_host.str= 0;
- m_definer_user.length= m_definer_host.length= 0;
-
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Set a name of the event
-
- SYNOPSIS
- event_timed::init_name()
- thd THD
- name the name extracted in the parser
-*/
-
-void
-event_timed::init_name(THD *thd, sp_name *name)
-{
- DBUG_ENTER("event_timed::init_name");
- uint n; /* Counter for nul trimming */
- /* During parsing, we must use thd->mem_root */
- MEM_ROOT *root= thd->mem_root;
-
- /* We have to copy strings to get them into the right memroot */
- if (name)
- {
- m_db.length= name->m_db.length;
- if (name->m_db.length == 0)
- m_db.str= NULL;
- else
- m_db.str= strmake_root(root, name->m_db.str, name->m_db.length);
- m_name.length= name->m_name.length;
- m_name.str= strmake_root(root, name->m_name.str, name->m_name.length);
-
- if (name->m_qname.length == 0)
- name->init_qname(thd);
- m_qname.length= name->m_qname.length;
- m_qname.str= strmake_root(root, name->m_qname.str, m_qname.length);
- }
- else if (thd->db)
- {
- m_db.length= thd->db_length;
- m_db.str= strmake_root(root, thd->db, m_db.length);
- }
-
- DBUG_PRINT("m_db", ("len=%d db=%s",m_db.length, m_db.str));
- DBUG_PRINT("m_name", ("len=%d name=%s",m_name.length, m_name.str));
-
- DBUG_VOID_RETURN;
-}
-
-
-/*
- 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.
-*/
-
-void
-event_timed::init_body(THD *thd)
-{
- DBUG_ENTER("event_timed::init_body");
- MEM_ROOT *root= thd->mem_root;
-
- m_body.length= thd->lex->ptr - m_body_begin;
- // Trim nuls at the end
- while (m_body.length && m_body_begin[m_body.length-1] == '\0')
- m_body.length--;
-
- m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length);
-
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Set time for execution for one time events.
-
- SYNOPSIS
- event_timed::init_execute_at()
- expr when (datetime)
-
- RETURNS
- 0 - OK
- EVEX_PARSE_ERROR - fix_fields failed
- EVEX_BAD_PARAMS - datetime is in the past
-*/
-
-int
-event_timed::init_execute_at(THD *thd, Item *expr)
-{
- my_bool not_used;
- TIME ltime;
- my_time_t my_time_tmp;
-
- TIME time_tmp;
- DBUG_ENTER("event_timed::init_execute_at");
-
- if (expr->fix_fields(thd, &expr))
- DBUG_RETURN(EVEX_PARSE_ERROR);
-
- if (expr->val_int() == MYSQL_TIMESTAMP_ERROR)
- DBUG_RETURN(EVEX_BAD_PARAMS);
-
- // let's check whether time is in the past
- thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,
- (my_time_t) thd->query_start());
-
- if (expr->val_int() < TIME_to_ulonglong_datetime(&time_tmp))
- DBUG_RETURN(EVEX_BAD_PARAMS);
-
- if ((not_used= expr->get_date(&ltime, TIME_NO_ZERO_DATE)))
- DBUG_RETURN(EVEX_BAD_PARAMS);
-
- /*
- This may result in a 1970-01-01 date if ltime is > 2037-xx-xx
- CONVERT_TZ has similar problem
- */
- my_tz_UTC->gmt_sec_to_TIME(&ltime, TIME_to_timestamp(thd,&ltime, &not_used));
-
-
- m_execute_at= ltime;
- DBUG_RETURN(0);
-}
-
-
-/*
- Set time for execution for transient events.
-
- SYNOPSIS
- event_timed::init_interval()
- expr how much?
- interval what is the interval
-
- RETURNS
- 0 - OK
- EVEX_PARSE_ERROR - fix_fields failed
- EVEX_BAD_PARAMS - Interval is not positive
-*/
-
-int
-event_timed::init_interval(THD *thd, Item *expr, interval_type interval)
-{
- longlong tmp;
- DBUG_ENTER("event_timed::init_interval");
-
- if (expr->fix_fields(thd, &expr))
- DBUG_RETURN(EVEX_PARSE_ERROR);
-
- if ((tmp= expr->val_int()) <= 0)
- DBUG_RETURN(EVEX_BAD_PARAMS);
-
- m_expr= tmp;
- m_interval= interval;
- DBUG_RETURN(0);
-}
-
-
-/*
- Set activation time.
-
- SYNOPSIS
- event_timed::init_starts()
- expr how much?
- interval what is the interval
-
- NOTES
- Note that activation time is not execution time.
- EVERY 5 MINUTE STARTS "2004-12-12 10:00:00" means that
- the event will be executed every 5 minutes but this will
- start at the date shown above. Expressions are possible :
- DATE_ADD(NOW(), INTERVAL 1 DAY) -- start tommorow at
- same time.
-
- RETURNS
- 0 - OK
- EVEX_PARSE_ERROR - fix_fields failed
-*/
-
-int
-event_timed::init_starts(THD *thd, Item *starts)
-{
- my_bool not_used;
- TIME ltime;
- my_time_t my_time_tmp;
-
- DBUG_ENTER("event_timed::init_starts");
-
- if (starts->fix_fields(thd, &starts))
- DBUG_RETURN(EVEX_PARSE_ERROR);
-
- if (starts->val_int() == MYSQL_TIMESTAMP_ERROR)
- DBUG_RETURN(EVEX_BAD_PARAMS);
-
- if ((not_used= starts->get_date(&ltime, TIME_NO_ZERO_DATE)))
- DBUG_RETURN(EVEX_BAD_PARAMS);
-
- /*
- This may result in a 1970-01-01 date if ltime is > 2037-xx-xx
- CONVERT_TZ has similar problem
- */
- my_tz_UTC->gmt_sec_to_TIME(&ltime, TIME_to_timestamp(thd,&ltime, &not_used));
-
- m_starts= ltime;
- DBUG_RETURN(0);
-}
-
-
-/*
- Set deactivation time.
-
- SYNOPSIS
- event_timed::init_ends()
- thd THD
- ends when?
-
- NOTES
- Note that activation time is not execution time.
- EVERY 5 MINUTE ENDS "2004-12-12 10:00:00" means that
- the event will be executed every 5 minutes but this will
- end at the date shown above. Expressions are possible :
- DATE_ADD(NOW(), INTERVAL 1 DAY) -- end tommorow at
- same time.
-
- RETURNS
- 0 - OK
- EVEX_PARSE_ERROR - fix_fields failed
- EVEX_BAD_PARAMS - ENDS before STARTS
-*/
-
-int
-event_timed::init_ends(THD *thd, Item *ends)
-{
- TIME ltime;
- my_time_t my_time_tmp;
- my_bool not_used;
-
- DBUG_ENTER("event_timed::init_ends");
-
- if (ends->fix_fields(thd, &ends))
- DBUG_RETURN(EVEX_PARSE_ERROR);
-
- // the field was already fixed in init_ends
- if ((not_used= ends->get_date(&ltime, TIME_NO_ZERO_DATE)))
- DBUG_RETURN(EVEX_BAD_PARAMS);
-
- /*
- This may result in a 1970-01-01 date if ltime is > 2037-xx-xx
- CONVERT_TZ has similar problem
- */
- my_tz_UTC->gmt_sec_to_TIME(&ltime, TIME_to_timestamp(thd, &ltime, &not_used));
-
- if (m_starts.year && my_time_compare(&m_starts, &ltime) != -1)
- DBUG_RETURN(EVEX_BAD_PARAMS);
-
- m_ends= ltime;
- DBUG_RETURN(0);
-}
-
-
-/*
- Set behaviour when ENDS has been set and passed by.
-
- SYNOPSIS
- event_timed::init_interval()
- drop if set then drop the event otherwise preserve it.
-*/
-
-void
-event_timed::set_on_completion_drop(bool drop)
-{
- DBUG_ENTER("event_timed::set_on_completion");
- if (drop)
- m_on_completion= MYSQL_EVENT_ON_COMPLETION_DROP;
- else
- m_on_completion= MYSQL_EVENT_ON_COMPLETION_PRESERVE;
-
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Sets event's status. DISABLED - not executable even if
- everything else is ok (STARTS, ENDS, INTERVAL and so on).
-
- SYNOPSIS
- event_timed::set_event_status()
- enabled set whether enabled or not.
-*/
-
-void
-event_timed::set_event_status(bool enabled)
-{
- DBUG_ENTER("event_timed::set_on_completion");
-
- m_status_changed= true;
- if (enabled)
- m_status= MYSQL_EVENT_ENABLED;
- else
- m_status= MYSQL_EVENT_DISABLED;
-
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Sets comment.
-
- SYNOPSIS
- event_timed::init_comment()
- thd THD - used for memory allocation
- comment the string.
-*/
-
-void
-event_timed::init_comment(THD *thd, LEX_STRING *comment)
-{
- DBUG_ENTER("event_timed::init_comment");
-
- MEM_ROOT *root= thd->mem_root;
- m_comment.length= comment->length;
- m_comment.str= strmake_root(root, comment->str, comment->length);
- DBUG_PRINT("m_comment", ("len=%d",m_comment.length));
-
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Inits definer (m_definer_user and m_definer_host) during
- parsing.
-
- SYNOPSIS
- event_timed::init_definer()
-*/
-
-int
-event_timed::init_definer(THD *thd)
-{
- DBUG_ENTER("event_timed::init_definer");
-
- m_definer_user.str= strdup_root(thd->mem_root, thd->security_ctx->priv_user);
- m_definer_user.length= strlen(thd->security_ctx->priv_user);
-
- m_definer_host.str= strdup_root(thd->mem_root, thd->security_ctx->priv_host);
- m_definer_host.length= strlen(thd->security_ctx->priv_host);
-
- DBUG_RETURN(0);
-}
-
-
-/*
- Loads an event from a row from mysql.event
-
- SYNOPSIS
- event_timed::load_from_row()
-*/
-
-int
-event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
-{
- longlong created;
- longlong modified;
- char *ptr;
- event_timed *et;
- uint len;
- bool res1, res2;
-
- DBUG_ENTER("event_timed::load_from_row");
-
- if (!table)
- goto error;
-
- et= this;
-
- if (table->s->fields != EVEX_FIELD_COUNT)
- goto error;
-
- if ((et->m_db.str= get_field(mem_root,
- table->field[EVEX_FIELD_DB])) == NULL)
- goto error;
-
- et->m_db.length= strlen(et->m_db.str);
-
- if ((et->m_name.str= get_field(mem_root,
- table->field[EVEX_FIELD_NAME])) == NULL)
- goto error;
-
- et->m_name.length= strlen(et->m_name.str);
-
- if ((et->m_body.str= get_field(mem_root,
- table->field[EVEX_FIELD_BODY])) == NULL)
- goto error;
-
- et->m_body.length= strlen(et->m_body.str);
-
- if ((et->m_definer.str= get_field(mem_root,
- table->field[EVEX_FIELD_DEFINER])) == NullS)
- goto error;
- et->m_definer.length= strlen(et->m_definer.str);
-
- ptr= strchr(et->m_definer.str, '@');
-
- if (! ptr)
- ptr= et->m_definer.str; // Weird, isn't it?
-
- len= ptr - et->m_definer.str;
-
- et->m_definer_user.str= strmake_root(mem_root, et->m_definer.str, len);
- et->m_definer_user.length= len;
- len= et->m_definer.length - len - 1; //1 is because of @
- et->m_definer_host.str= strmake_root(mem_root, ptr + 1, len);//1: because of @
- et->m_definer_host.length= len;
-
-
- res1= table->field[EVEX_FIELD_STARTS]->
- get_date(&et->m_starts, TIME_NO_ZERO_DATE);
-
- res2= table->field[EVEX_FIELD_ENDS]->
- get_date(&et->m_ends, TIME_NO_ZERO_DATE);
-
- et->m_expr= table->field[EVEX_FIELD_INTERVAL_EXPR]->val_int();
-
- /*
- If res1 and res2 are true then both fields are empty.
- Hence if EVEX_FIELD_EXECUTE_AT is empty there is an error.
- */
- if (res1 && res2 && !et->m_expr && table->field[EVEX_FIELD_EXECUTE_AT]->
- get_date(&et->m_execute_at, TIME_NO_ZERO_DATE))
- goto error;
-
- /*
- In DB the values start from 1 but enum interval_type starts
- from 0
- */
- et->m_interval= (interval_type)
- ((ulonglong) table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->val_int() - 1);
-
- et->m_modified= table->field[EVEX_FIELD_CREATED]->val_int();
- et->m_created= table->field[EVEX_FIELD_MODIFIED]->val_int();
-
- /*
- ToDo Andrey : Ask PeterG & Serg what to do in this case.
- Whether on load last_executed_at should be loaded
- or it must be 0ed. If last_executed_at is loaded
- then an event can be scheduled for execution
- instantly. Let's say an event has to be executed
- every 15 mins. The server has been stopped for
- more than this time and then started. If L_E_AT
- is loaded from DB, execution at L_E_AT+15min
- will be scheduled. However this time is in the past.
- Hence immediate execution. Due to patch of
- ::mark_last_executed() m_last_executed gets time_now
- and not m_execute_at. If not like this a big
- queue can be scheduled for times which are still in
- the past (2, 3 and more executions which will be
- consequent).
- */
- set_zero_time(&m_last_executed, MYSQL_TIMESTAMP_DATETIME);
-#ifdef ANDREY_0
- table->field[EVEX_FIELD_LAST_EXECUTED]->
- get_date(&et->m_last_executed, TIME_NO_ZERO_DATE);
-#endif
- m_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[EVEX_FIELD_STATUS])) == NullS)
- goto error;
-
- DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->m_name.str, ptr));
- et->m_status= (ptr[0]=='E'? MYSQL_EVENT_ENABLED:
- MYSQL_EVENT_DISABLED);
-
- // ToDo : Andrey . Find a way not to allocate ptr on event_mem_root
- if ((ptr= get_field(mem_root,
- table->field[EVEX_FIELD_ON_COMPLETION])) == NullS)
- goto error;
-
- et->m_on_completion= (ptr[0]=='D'? MYSQL_EVENT_ON_COMPLETION_DROP:
- MYSQL_EVENT_ON_COMPLETION_PRESERVE);
-
- et->m_comment.str= get_field(mem_root, table->field[EVEX_FIELD_COMMENT]);
- if (et->m_comment.str != NullS)
- et->m_comment.length= strlen(et->m_comment.str);
- else
- et->m_comment.length= 0;
-
- DBUG_RETURN(0);
-error:
- DBUG_RETURN(EVEX_GET_FIELD_FAILED);
-}
-
-
-bool
-event_timed::compute_next_execution_time()
-{
- TIME time_now;
- my_time_t now;
- int tmp;
-
- DBUG_ENTER("event_timed::compute_next_execution_time");
-
- if (m_status == MYSQL_EVENT_DISABLED)
- {
- DBUG_PRINT("compute_next_execution_time",
- ("Event %s is DISABLED", m_name.str));
- goto ret;
- }
- //if one-time no need to do computation
- if (!m_expr)
- {
- //let's check whether it was executed
- if (m_last_executed.year)
- {
- DBUG_PRINT("compute_next_execution_time",
- ("One-time event %s was already executed", m_name.str));
- if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
- {
- DBUG_PRINT("compute_next_execution_time",
- ("One-time event will be dropped."));
- m_dropped= true;
- }
- m_status= MYSQL_EVENT_DISABLED;
- m_status_changed= true;
- }
- goto ret;
- }
- time(&now);
- my_tz_UTC->gmt_sec_to_TIME(&time_now, now);
-/*
- sql_print_information("[%s.%s]", m_db.str, m_name.str);
- sql_print_information("time_now : [%d-%d-%d %d:%d:%d ]", time_now.year, time_now.month, time_now.day, time_now.hour, time_now.minute, time_now.second);
- sql_print_information("m_starts : [%d-%d-%d %d:%d:%d ]", m_starts.year, m_starts.month, m_starts.day, m_starts.hour, m_starts.minute, m_starts.second);
- sql_print_information("m_ends : [%d-%d-%d %d:%d:%d ]", m_ends.year, m_ends.month, m_ends.day, m_ends.hour, m_ends.minute, m_ends.second);
- sql_print_information("m_last_ex: [%d-%d-%d %d:%d:%d ]", m_last_executed.year, m_last_executed.month, m_last_executed.day, m_last_executed.hour, m_last_executed.minute, m_last_executed.second);
-*/
- //if time_now is after m_ends don't execute anymore
- if (m_ends.year && (tmp= my_time_compare(&m_ends, &time_now)) == -1)
- {
- // time_now is after m_ends. don't execute anymore
- set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME);
- if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
- m_dropped= true;
- m_status= MYSQL_EVENT_DISABLED;
- m_status_changed= true;
-
- goto ret;
- }
-
- /*
- Here time_now is before or equals m_ends if the latter is set.
- Let's check whether time_now is before m_starts.
- If so schedule for m_starts
- */
- if (m_starts.year && (tmp= my_time_compare(&time_now, &m_starts)) < 1)
- {
- if (tmp == 0 && my_time_compare(&m_starts, &m_last_executed) == 0)
- {
- /*
- time_now = m_starts = m_last_executed
- do nothing or we will schedule for second time execution at m_starts.
- */
- }
- else
- {
- //m_starts is in the future
- //time_now before m_starts. Scheduling for m_starts
- m_execute_at= m_starts;
- goto ret;
- }
- }
-
- if (m_starts.year && m_ends.year)
- {
- /*
- Both m_starts and m_ends are set and time_now is between them (incl.)
- If m_last_executed is set then increase with m_expr. The new TIME is
- after m_ends set m_execute_at to 0. And check for m_on_completion
- If not set then schedule for now.
- */
- if (!m_last_executed.year)
- m_execute_at= time_now;
- else
- {
- my_time_t last, ll_ends;
-
- // There was previous execution
- last= sec_since_epoch_TIME(&m_last_executed) + m_expr;
- ll_ends= sec_since_epoch_TIME(&m_ends);
- //now convert back to TIME
- //ToDo Andrey: maybe check for error here?
- if (ll_ends < last)
- {
- // Next execution after ends. No more executions
- set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME);
- if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
- m_dropped= true;
- }
- else
- my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, last);
- }
- goto ret;
- }
- else if (!m_starts.year && !m_ends.year)
- {
- // both m_starts and m_ends are not set, se we schedule for the next
- // based on m_last_executed
- if (!m_last_executed.year)
- //m_last_executed not set. Schedule the event for now
- m_execute_at= time_now;
- else
- //ToDo Andrey: maybe check for error here?
- my_tz_UTC->gmt_sec_to_TIME(&m_execute_at,
- sec_since_epoch_TIME(&m_last_executed) + m_expr);
- goto ret;
- }
- else
- {
- //either m_starts or m_ends is set
- if (m_starts.year)
- {
- /*
- - m_starts is set.
- - m_starts is not in the future according to check made before
- Hence schedule for m_starts + m_expr in case m_last_executed
- is not set, otherwise to m_last_executed + m_expr
- */
- my_time_t last;
-
- //convert either m_last_executed or m_starts to seconds
- if (m_last_executed.year)
- last= sec_since_epoch_TIME(&m_last_executed) + m_expr;
- else
- last= sec_since_epoch_TIME(&m_starts);
-
- //now convert back to TIME
- //ToDo Andrey: maybe check for error here?
- my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, last);
- }
- else
- {
- /*
- - m_ends is set
- - m_ends is after time_now or is equal
- Hence check for m_last_execute and increment with m_expr.
- If m_last_executed is not set then schedule for now
- */
- my_time_t last, ll_ends;
-
- if (!m_last_executed.year)
- m_execute_at= time_now;
- else
- {
- last= sec_since_epoch_TIME(&m_last_executed);
- ll_ends= sec_since_epoch_TIME(&m_ends);
- last+= m_expr;
- //now convert back to TIME
- //ToDo Andrey: maybe check for error here?
- if (ll_ends < last)
- {
- set_zero_time(&m_execute_at, MYSQL_TIMESTAMP_DATETIME);
- if (m_on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
- m_dropped= true;
- }
- else
- my_tz_UTC->gmt_sec_to_TIME(&m_execute_at, last);
- }
- }
- goto ret;
- }
-ret:
-
- DBUG_RETURN(false);
-}
-
-
-void
-event_timed::mark_last_executed()
-{
- TIME time_now;
- my_time_t now;
-
- time(&now);
- my_tz_UTC->gmt_sec_to_TIME(&time_now, now);
-
- m_last_executed= time_now; // was m_execute_at
-#ifdef ANDREY_0
- m_last_executed= m_execute_at;
-#endif
- m_last_executed_changed= true;
-}
-
-
-bool
-event_timed::drop(THD *thd)
-{
- DBUG_ENTER("event_timed::drop");
-
- if (evex_drop_event(thd, this, false))
- DBUG_RETURN(true);
-
- DBUG_RETURN(false);
-}
-
-
-bool
-event_timed::update_fields(THD *thd)
-{
- TABLE *table;
- int ret= 0;
- bool opened;
-
- DBUG_ENTER("event_timed::update_time_fields");
-
- DBUG_PRINT("enter", ("name: %*s", m_name.length, m_name.str));
-
- //no need to update if nothing has changed
- if (!(m_status_changed || m_last_executed_changed))
- goto done;
-
- if (!(table= EVEX_OPEN_TABLE_FOR_UPDATE()))
- DBUG_RETURN(SP_OPEN_TABLE_FAILED);
-
- if ((ret= sp_db_find_routine_aux(thd, 0/*notype*/, m_db, m_name, table)))
- goto done;
-
- store_record(table,record[1]);
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; // Don't update create on row update.
-
- if (m_last_executed_changed)
- {
- table->field[EVEX_FIELD_LAST_EXECUTED]->set_notnull();
- table->field[EVEX_FIELD_LAST_EXECUTED]->store_time(&m_last_executed,
- MYSQL_TIMESTAMP_DATETIME);
- m_last_executed_changed= false;
- }
- if (m_status_changed)
- {
- table->field[EVEX_FIELD_STATUS]->set_notnull();
- table->field[EVEX_FIELD_STATUS]->store((longlong)m_status);
- m_status_changed= false;
- }
-
- if ((table->file->update_row(table->record[1],table->record[0])))
- ret= EVEX_WRITE_ROW_FAILED;
-
-done:
- close_thread_tables(thd);
-
- DBUG_RETURN(ret);
-}
-
-
-char *
-event_timed::get_show_create_event(THD *thd, uint *length)
-{
- char *dst, *ret;
- uint len, tmp_len;
-
- len = strlen("CREATE EVENT ") + m_db.length + strlen(".") + m_name.length +
- strlen(" ON SCHEDULE ") + strlen("EVERY 5 MINUTE ")
-/*
- + strlen("ON COMPLETION ")
- + (m_on_completion==MYSQL_EVENT_ON_COMPLETION_DROP?
- strlen("NOT PRESERVE "):strlen("PRESERVE "))
- + (m_status==MYSQL_EVENT_ENABLED?
- strlen("ENABLE "):strlen("DISABLE "))
- + strlen("COMMENT \"") + m_comment.length + strlen("\" ")
-*/
- + strlen("DO ") +
- + m_body.length + strlen(";");
-
- ret= dst= (char*) alloc_root(thd->mem_root, len);
- memcpy(dst, "CREATE EVENT ", tmp_len= strlen("CREATE EVENT "));
- dst+= tmp_len;
- memcpy(dst, m_db.str, tmp_len=m_db.length);
- dst+= tmp_len;
- memcpy(dst, ".", tmp_len= strlen("."));
- dst+= tmp_len;
- memcpy(dst, m_name.str, tmp_len= m_name.length);
- dst+= tmp_len;
- memcpy(dst, " ON SCHEDULE ", tmp_len= strlen(" ON SCHEDULE "));
- dst+= tmp_len;
- memcpy(dst, "EVERY 5 MINUTE ", tmp_len= strlen("EVERY 5 MINUTE "));
- dst+= tmp_len;
-/*
- memcpy(dst, "ON COMPLETION ", tmp_len =strlen("ON COMPLETION "));
- dst+= tmp_len;
- memcpy(dst, (m_on_completion==MYSQL_EVENT_ON_COMPLETION_DROP?
- "NOT PRESERVE ":"PRESERVE "),
- tmp_len =(m_on_completion==MYSQL_EVENT_ON_COMPLETION_DROP? 13:9));
- dst+= tmp_len;
-
- memcpy(dst, (m_status==MYSQL_EVENT_ENABLED?
- "ENABLE ":"DISABLE "),
- tmp_len= (m_status==MYSQL_EVENT_ENABLED? 8:9));
- dst+=tmp_len;
-
- memcpy(dst, "COMMENT \"", tmp_len= strlen("COMMENT \""));
- dst+= tmp_len;
- memcpy(dst, m_comment.str, tmp_len= m_comment.length);
- dst+= tmp_len;
- memcpy(dst, "\" ", tmp_len=2);
- dst+= tmp_len;
-*/
- memcpy(dst, "DO ", tmp_len=3);
- dst+= tmp_len;
-
- memcpy(dst, m_body.str, tmp_len= m_body.length);
- dst+= tmp_len;
- memcpy(dst, ";", 1);
- ++dst;
- *dst= '\0';
-
- *length= len;
-
- return ret;
-}
-
-
-int
-event_timed::execute(THD *thd, MEM_ROOT *mem_root= NULL)
-{
- List<Item> empty_item_list;
- int ret= 0;
-
- DBUG_ENTER("event_timed::execute");
-
- // TODO Andrey : make this as member variable and delete in destructor
- empty_item_list.empty();
-
- if (!m_sphead && (ret= compile(thd, mem_root)))
- goto done;
-
- ret= m_sphead->execute_procedure(thd, &empty_item_list);
-
-done:
- // Don't cache m_sphead if allocated on another mem_root
- if (mem_root && m_sphead)
- {
- delete m_sphead;
- m_sphead= 0;
- }
-
- DBUG_RETURN(ret);
-}
-
-
-int
-event_timed::compile(THD *thd, MEM_ROOT *mem_root= NULL)
-{
- MEM_ROOT *tmp_mem_root= 0;
- LEX *old_lex= thd->lex, lex;
- char *old_db;
- event_timed *ett;
- sp_name *spn;
- char *old_query;
- uint old_query_len;
- st_sp_chistics *p;
-
- DBUG_ENTER("event_timed::compile");
- // change the memory root for the execution time
- if (mem_root)
- {
- tmp_mem_root= thd->mem_root;
- thd->mem_root= mem_root;
- }
- old_query_len= thd->query_length;
- old_query= thd->query;
- old_db= thd->db;
- thd->db= m_db.str;
- thd->query= get_show_create_event(thd, &thd->query_length);
- DBUG_PRINT("event_timed::compile", ("query:%s",thd->query));
-
- thd->lex= &lex;
- lex_start(thd, (uchar*)thd->query, thd->query_length);
- lex.et_compile_phase= TRUE;
- if (yyparse((void *)thd) || thd->is_fatal_error)
- {
- // Free lex associated resources
- // QQ: Do we really need all this stuff here ?
- if (lex.sphead)
- {
- if (&lex != thd->lex)
- thd->lex->sphead->restore_lex(thd);
- delete lex.sphead;
- lex.sphead= 0;
- }
- // QQ: anything else ?
- lex_end(&lex);
- thd->lex= old_lex;
- DBUG_RETURN(-1);
- }
-
- m_sphead= lex.sphead;
- m_sphead->m_db= m_db;
- //copy also chistics since they will vanish otherwise we get 0x0 pointer
- // Todo : Handle sql_mode !!
- m_sphead->set_definer(m_definer.str, m_definer.length);
- m_sphead->set_info(0, 0, &lex.sp_chistics, 0/*sql_mode*/);
- m_sphead->optimize();
- lex_end(&lex);
- thd->lex= old_lex;
- thd->query= old_query;
- thd->query_length= old_query_len;
- thd->db= old_db;
- /*
- Change the memory root for the execution time.
- */
- if (mem_root)
- thd->mem_root= tmp_mem_root;
-
- DBUG_RETURN(0);
-}
-
-
-/******************************** EXECUTOR ************************************/
-
-
-//extern "C" pthread_handler_decl(event_executor_main, arg);
-//extern "C" pthread_handler_decl(event_executor_worker, arg);
-
-/*
- TODO Andrey: Check for command line option whether to start
- the main thread or not.
-*/
-
-pthread_handler_t event_executor_worker(void *arg);
-pthread_handler_t event_executor_main(void *arg);
-
-int
-init_events()
-{
- pthread_t th;
-
- DBUG_ENTER("init_events");
-
- DBUG_PRINT("info",("Starting events main thread"));
-
- pthread_mutex_init(&LOCK_event_arrays, MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&LOCK_workers_count, MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&LOCK_evex_running, MY_MUTEX_INIT_FAST);
-
- VOID(pthread_mutex_lock(&LOCK_evex_running));
- evex_is_running= false;
- event_executor_running_global_var= false;
- VOID(pthread_mutex_unlock(&LOCK_evex_running));
-
- //TODO Andrey: Change the error code returned!
- if (pthread_create(&th, NULL, event_executor_main, (void*)NULL))
- DBUG_RETURN(ER_SLAVE_THREAD);
-
- DBUG_RETURN(0);
-}
-
-
-void
-shutdown_events()
-{
- VOID(pthread_mutex_lock(&LOCK_evex_running));
- VOID(pthread_mutex_unlock(&LOCK_evex_running));
- pthread_mutex_destroy(&LOCK_event_arrays);
- pthread_mutex_destroy(&LOCK_workers_count);
- pthread_mutex_destroy(&LOCK_evex_running);
-}
-
-
-static int
-init_event_thread(THD* thd)
-{
- DBUG_ENTER("init_event_thread");
- thd->client_capabilities= 0;
- thd->security_ctx->skip_grants();
- my_net_init(&thd->net, 0);
- thd->net.read_timeout = slave_net_timeout;
- thd->slave_thread= 0;
- thd->options= OPTION_AUTO_IS_NULL;
- thd->client_capabilities= CLIENT_LOCAL_FILES;
- thd->real_id=pthread_self();
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- thd->thread_id= thread_id++;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
-
- if (init_thr_lock() || thd->store_globals())
- {
- thd->cleanup();
- delete thd;
- DBUG_RETURN(-1);
- }
-
-#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
- sigset_t set;
- VOID(sigemptyset(&set)); // Get mask in use
- VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
-#endif
-
- thd->proc_info= "Initialized";
- thd->version= refresh_version;
- thd->set_time();
- DBUG_RETURN(0);
-}
-
-
-pthread_handler_t event_executor_main(void *arg)
-{
- THD *thd; /* needs to be first for thread_stack */
- ulonglong iter_num= 0;
- uint i=0, j=0;
-
- DBUG_ENTER("event_executor_main");
- DBUG_PRINT("event_executor_main", ("EVEX thread started"));
-
- VOID(pthread_mutex_lock(&LOCK_evex_running));
- evex_is_running= true;
- event_executor_running_global_var= opt_event_executor;
- VOID(pthread_mutex_unlock(&LOCK_evex_running));
-
- // init memory root
- init_alloc_root(&evex_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
-
- // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff
- my_thread_init();
-
- //TODO Andrey: Check for NULL
- if (!(thd = new THD)) // note that contructor of THD uses DBUG_ !
- {
- sql_print_error("Cannot create THD for event_executor_main");
- goto err_no_thd;
- }
- thd->thread_stack = (char*)&thd; // remember where our stack is
-
- pthread_detach_this_thread();
-
- if (init_event_thread(thd))
- goto err;
-
- thd->init_for_queries();
-
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- threads.append(thd);
- thread_count++;
- thread_running++;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
-
- DBUG_PRINT("EVEX main thread", ("Initing events_array"));
-
- VOID(pthread_mutex_lock(&LOCK_event_arrays));
- /*
- my_malloc is used as underlying allocator which does not use a mem_root
- thus data should be freed at later stage.
- */
- VOID(my_init_dynamic_array(&events_array, sizeof(event_timed), 50, 100));
- VOID(my_init_dynamic_array(&evex_executing_queue, sizeof(event_timed *), 50, 100));
- VOID(pthread_mutex_unlock(&LOCK_event_arrays));
-
- if (evex_load_events_from_db(thd))
- goto err;
-
- THD_CHECK_SENTRY(thd);
- /* Read queries from the IO/THREAD until this thread is killed */
- while (!thd->killed)
- {
- TIME time_now;
- my_time_t now;
- my_ulonglong cnt;
-
- DBUG_PRINT("info", ("EVEX External Loop %d", ++cnt));
-// sql_print_information("[EVEX] External Loop!");
- my_sleep(500000);// sleep 0.5s
- if (!event_executor_running_global_var)
- continue;
- time(&now);
- my_tz_UTC->gmt_sec_to_TIME(&time_now, now);
-
-
- VOID(pthread_mutex_lock(&LOCK_event_arrays));
- for (i= 0; (i < evex_executing_queue.elements) && !thd->killed; ++i)
- {
- event_timed **p_et=dynamic_element(&evex_executing_queue,i,event_timed**);
- event_timed *et= *p_et;
-// sql_print_information("[EVEX] External Loop 2!");
-
- if (!event_executor_running_global_var)
- break;// soon we will do only continue (see the code a bit above)
-
- thd->proc_info = "Iterating";
- THD_CHECK_SENTRY(thd);
- /*
- if this is the first event which is after time_now then no
- more need to iterate over more elements since the array is sorted.
- */
- if (et->m_execute_at.year &&
- my_time_compare(&time_now, &et->m_execute_at) == -1)
- break;
-
- if (et->m_status == MYSQL_EVENT_ENABLED)
- {
- pthread_t th;
-
- DBUG_PRINT("info", (" Spawning a thread %d", ++iter_num));
-// sql_print_information(" Spawning a thread %d", ++iter_num);
-
- if (pthread_create(&th, NULL, event_executor_worker, (void*)et))
- {
- sql_print_error("Problem while trying to create a thread");
- VOID(pthread_mutex_unlock(&LOCK_event_arrays));
- goto err; // for now finish execution of the Executor
- }
-
- et->mark_last_executed();
- et->compute_next_execution_time();
- et->update_fields(thd);
- if ((et->m_execute_at.year && !et->m_expr)
- || TIME_to_ulonglong_datetime(&et->m_execute_at) == 0L)
- et->m_flags |= EVENT_EXEC_NO_MORE;
- }
- }
- /*
- Let's remove elements which won't be executed any more
- The number is "i" and it is <= up to evex_executing_queue.elements
- */
- j= 0;
- while (j < i && j < evex_executing_queue.elements)
- {
- event_timed **p_et= dynamic_element(&evex_executing_queue, j, event_timed**);
- event_timed *et= *p_et;
- if (et->m_flags & EVENT_EXEC_NO_MORE || et->m_status == MYSQL_EVENT_DISABLED)
- {
- delete_dynamic_element(&evex_executing_queue, j);
- DBUG_PRINT("", ("DELETING FROM EXECUTION QUEUE [%s.%s]",et->m_db.str, et->m_name.str));
- // nulling the position, will delete later
- if (et->m_dropped)
- {
- // we have to drop the event
- int idx;
- et->drop(thd);
- idx= get_index_dynamic(&events_array, (gptr) et);
- if (idx != -1)
- delete_dynamic_element(&events_array, idx);
- else
- sql_print_error("Something weird happened with events. %d", __LINE__);
- }
- continue;
- }
- ++j;
- }
- if (evex_executing_queue.elements)
- //ToDo Andrey : put a lock here
- qsort((gptr) dynamic_element(&evex_executing_queue, 0, event_timed**),
- evex_executing_queue.elements,
- sizeof(event_timed **),
- (qsort_cmp) event_timed_compare
- );
-
- VOID(pthread_mutex_unlock(&LOCK_event_arrays));
- }// while (!thd->killed)
-
-err:
- VOID(pthread_mutex_lock(&LOCK_evex_running));
- evex_is_running= false;
- VOID(pthread_mutex_unlock(&LOCK_evex_running));
-
- sql_print_information("Event executor stopping");
- // LEX_STRINGs reside in the memory root and will be destroyed with it.
- // Hence no need of delete but only freeing of SP
- for (i=0; i < events_array.elements; ++i)
- {
- event_timed *et= dynamic_element(&events_array, i, event_timed*);
- et->free_sp();
- }
- // TODO Andrey: USE lock here!
- delete_dynamic(&evex_executing_queue);
- delete_dynamic(&events_array);
-
- thd->proc_info = "Clearing";
- DBUG_ASSERT(thd->net.buff != 0);
- net_end(&thd->net); // destructor will not free it, because we are weird
- THD_CHECK_SENTRY(thd);
- pthread_mutex_lock(&LOCK_thread_count);
- thread_count--;
- thread_running--;
- THD_CHECK_SENTRY(thd);
- delete thd;
- pthread_mutex_unlock(&LOCK_thread_count);
-
- /*
- sleeping some time may help not crash the server. sleeping
- is done to wait for spawned threads to finish.
-
- TODO: A better will be with a conditional variable
- */
- {
- uint tries= 0;
- while (tries++ < 5)
- {
- VOID(pthread_mutex_lock(&LOCK_workers_count));
- if (!workers_count)
- {
- VOID(pthread_mutex_unlock(&LOCK_workers_count));
- break;
- }
- VOID(pthread_mutex_unlock(&LOCK_workers_count));
- DBUG_PRINT("info", ("Sleep %d", tries));
- my_sleep(1000000 * tries);// 1s
- }
- DBUG_PRINT("info", ("Maybe now it is ok to kill the thread and evex MRoot"));
- }
-
-err_no_thd:
- VOID(pthread_mutex_lock(&LOCK_evex_running));
- evex_is_running= false;
- VOID(pthread_mutex_unlock(&LOCK_evex_running));
-
- free_root(&evex_mem_root, MYF(0));
- sql_print_information("Event executor stopped");
-
- shutdown_events();
-
- my_thread_end();
- pthread_exit(0);
- DBUG_RETURN(0); // Can't return anything here
-}
-
-
-pthread_handler_t event_executor_worker(void *event_void)
-{
- THD *thd; /* needs to be first for thread_stack */
- List<Item> empty_item_list;
- event_timed *event = (event_timed *) event_void;
- MEM_ROOT mem_root;
- ulong save_options;
-
-
- DBUG_ENTER("event_executor_worker");
- VOID(pthread_mutex_lock(&LOCK_workers_count));
- ++workers_count;
- VOID(pthread_mutex_unlock(&LOCK_workers_count));
-
- init_alloc_root(&mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
-
- //we pass this empty list as parameter to the SP_HEAD of the event
- empty_item_list.empty();
-
- my_thread_init();
-
- //TODO Andrey: Check for NULL
- if (!(thd = new THD)) // note that contructor of THD uses DBUG_ !
- {
- sql_print_error("Cannot create a THD structure in worker thread");
- goto err_no_thd;
- }
- thd->thread_stack = (char*)&thd; // remember where our stack is
- thd->mem_root= &mem_root;
-// pthread_detach_this_thread();
- pthread_detach(pthread_self());
- if (init_event_thread(thd))
- goto err;
-
- thd->init_for_queries();
- save_options= thd->options;
- thd->options&= ~OPTION_BIN_LOG;
-
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- threads.append(thd);
- thread_count++;
- thread_running++;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
-
- //thd->security_ctx->priv_host is char[MAX_HOSTNAME]
-
- strxnmov(thd->security_ctx->priv_host, sizeof(thd->security_ctx->priv_host),
- event->m_definer_host.str, NullS);
-
- thd->security_ctx->priv_user= event->m_definer_user.str;
-
- thd->db= event->m_db.str;
- if (!check_global_access(thd, EVENT_ACL))
- {
- char exec_time[200];
- int ret;
- my_TIME_to_str(&event->m_execute_at, exec_time);
- DBUG_PRINT("info", (" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time));
-// sql_print_information(" EVEX EXECUTING event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time);
- ret= event->execute(thd);
-// sql_print_information(" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]. RetCode=%d", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time, ret);
- DBUG_PRINT("info", (" EVEX EXECUTED event for event %s.%s [EXPR:%d][EXECUTE_AT:%s]", event->m_db.str, event->m_name.str,(int) event->m_expr, exec_time));
- }
- thd->db= 0;
- //reenable (is it needed?)
- thd->options= save_options;
-err:
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- thread_count--;
- thread_running--;
- /*
- Some extra safety, which should not been needed (normally, event deletion
- should already have done these assignments (each event which sets these
- variables is supposed to set them to 0 before terminating)).
- */
- //thd->query= thd->db= thd->catalog= 0;
- //thd->query_length= thd->db_length= 0;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
-
- //thd->temporary_tables = 0; // remove tempation from destructor to close them
- thd->proc_info = "Clearing";
- DBUG_ASSERT(thd->net.buff != 0);
- net_end(&thd->net); // destructor will not free it, because we are weird
- THD_CHECK_SENTRY(thd);
-
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- THD_CHECK_SENTRY(thd);
- delete thd;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
-
-err_no_thd:
-
- free_root(&mem_root, MYF(0));
-// sql_print_information(" Worker thread exiting");
-
- VOID(pthread_mutex_lock(&LOCK_workers_count));
- --workers_count;
- VOID(pthread_mutex_unlock(&LOCK_workers_count));
- my_thread_end();
- pthread_exit(0);
- DBUG_RETURN(0); // Can't return anything here
-}