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
This commit is contained in:
parent
e7f18c97b8
commit
7ff7977105
@ -89,6 +89,7 @@ CREATE TABLE user (
|
||||
Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL,
|
||||
Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL,
|
||||
Create_user_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL,
|
||||
Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL,
|
||||
ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL,
|
||||
ssl_cipher BLOB NOT NULL,
|
||||
x509_issuer BLOB NOT NULL,
|
||||
@ -103,9 +104,9 @@ CHARACTER SET utf8 COLLATE utf8_bin
|
||||
comment='Users and global privileges';
|
||||
|
||||
|
||||
INSERT INTO user VALUES ('localhost' ,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
|
||||
INSERT INTO user VALUES ('@HOSTNAME@%' ,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
|
||||
REPLACE INTO user VALUES ('127.0.0.1' ,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
|
||||
INSERT INTO user VALUES ('localhost' ,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
|
||||
INSERT INTO user VALUES ('@HOSTNAME@%' ,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
|
||||
REPLACE INTO user VALUES ('127.0.0.1' ,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
|
||||
INSERT INTO user (host,user) VALUES ('localhost','');
|
||||
INSERT INTO user (host,user) VALUES ('@HOSTNAME@%','');
|
||||
|
||||
@ -566,3 +567,29 @@ CREATE TABLE proc (
|
||||
comment char(64) collate utf8_bin DEFAULT '' NOT NULL,
|
||||
PRIMARY KEY (db,name,type)
|
||||
) character set utf8 comment='Stored Procedures';
|
||||
|
||||
|
||||
CREATE TABLE event (
|
||||
'db' VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
'name' VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
'body' longblob NOT NULL,
|
||||
'definer' VARCHAR(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
'execute_at' DATETIME default NULL,
|
||||
'transient_expression' int(11) default NULL,
|
||||
'interval_type' 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') default NULL,
|
||||
'created' TIMESTAMP NOT NULL default '0000-00-00 00:00:00',
|
||||
'modified' TIMESTAMP NOT NULL default '0000-00-00 00:00:00',
|
||||
'last_executed' DATETIME default NULL,
|
||||
'starts' DATETIME default NULL,
|
||||
'ends' DATETIME default NULL,
|
||||
'status' ENUM('ENABLED','DISABLED') NOT NULL default 'ENABLED',
|
||||
'on_completion' ENUM('DROP','PRESERVE') NOT NULL default 'DROP',
|
||||
'comment' varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
PRIMARY KEY ('db','name')
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events';
|
||||
|
@ -526,3 +526,41 @@ ALTER TABLE proc MODIFY db
|
||||
char(77) collate utf8_bin DEFAULT '' NOT NULL,
|
||||
MODIFY comment
|
||||
char(64) collate utf8_bin DEFAULT '' NOT NULL;
|
||||
|
||||
#
|
||||
# EVENT table
|
||||
#
|
||||
|
||||
|
||||
CREATE TABLE event (
|
||||
'db' VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
'name' VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
'body' longblob NOT NULL,
|
||||
'definer' VARCHAR(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
'execute_at' DATETIME default NULL,
|
||||
'transient_expression' int(11) default NULL,
|
||||
'interval_type' 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') default NULL,
|
||||
'created' TIMESTAMP NOT NULL default '0000-00-00 00:00:00',
|
||||
'modified' TIMESTAMP NOT NULL default '0000-00-00 00:00:00',
|
||||
'last_executed' DATETIME default NULL,
|
||||
'starts' DATETIME default NULL,
|
||||
'ends' DATETIME default NULL,
|
||||
'status' ENUM('ENABLED','DISABLED') NOT NULL default 'ENABLED',
|
||||
'on_completion' ENUM('DROP','PRESERVE') NOT NULL default 'DROP',
|
||||
'comment' varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
PRIMARY KEY ('db','name')
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events';
|
||||
|
||||
|
||||
#
|
||||
# EVENT privilege
|
||||
#
|
||||
|
||||
ALTER TABLE mysql.user add Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL AFTER Create_user_priv;
|
||||
ALTER TABLE mysql.db add Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL;
|
||||
|
@ -61,7 +61,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 event.h \
|
||||
sql_array.h sql_cursor.h event.h event_priv.h \
|
||||
sql_plugin.h authors.h
|
||||
mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
|
||||
item.cc item_sum.cc item_buff.cc item_func.cc \
|
||||
@ -94,7 +94,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
|
||||
gstream.cc spatial.cc sql_help.cc sql_cursor.cc \
|
||||
tztime.cc my_time.c my_decimal.cc\
|
||||
sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \
|
||||
sp_cache.cc parse_file.cc sql_trigger.cc event.cc \
|
||||
sp_cache.cc parse_file.cc sql_trigger.cc \
|
||||
event_executor.cc event.cc event_timed.cc \
|
||||
sql_plugin.cc\
|
||||
handlerton.cc
|
||||
EXTRA_mysqld_SOURCES = ha_innodb.cc ha_berkeley.cc ha_archive.cc \
|
||||
|
1629
sql/event.cc
1629
sql/event.cc
File diff suppressed because it is too large
Load Diff
115
sql/event.h
115
sql/event.h
@ -1,27 +1,58 @@
|
||||
/* -*- C++ -*- */
|
||||
/* 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 */
|
||||
|
||||
#ifndef _EVENT_H_
|
||||
#define _EVENT_H_
|
||||
#include "sp_head.h"
|
||||
#include "sp.h"
|
||||
|
||||
|
||||
extern ulong opt_event_executor;
|
||||
|
||||
#define EVEX_OK 0
|
||||
#define EVEX_KEY_NOT_FOUND -1
|
||||
#define EVEX_OPEN_TABLE_FAILED -2
|
||||
#define EVEX_WRITE_ROW_FAILED -3
|
||||
#define EVEX_DELETE_ROW_FAILED -4
|
||||
#define EVEX_GET_FIELD_FAILED -5
|
||||
#define EVEX_PARSE_ERROR -6
|
||||
#define EVEX_INTERNAL_ERROR -7
|
||||
#define EVEX_NO_DB_ERROR -8
|
||||
#define EVEX_GENERAL_ERROR -9
|
||||
#define EVEX_BAD_PARAMS -10
|
||||
#define EVEX_NOT_RUNNING -11
|
||||
#define EVEX_OK SP_OK
|
||||
#define EVEX_KEY_NOT_FOUND SP_KEY_NOT_FOUND
|
||||
#define EVEX_OPEN_TABLE_FAILED SP_OPEN_TABLE_FAILED
|
||||
#define EVEX_WRITE_ROW_FAILED SP_WRITE_ROW_FAILED
|
||||
#define EVEX_DELETE_ROW_FAILED SP_DELETE_ROW_FAILED
|
||||
#define EVEX_GET_FIELD_FAILED SP_GET_FIELD_FAILED
|
||||
#define EVEX_PARSE_ERROR SP_PARSE_ERROR
|
||||
#define EVEX_INTERNAL_ERROR SP_INTERNAL_ERROR
|
||||
#define EVEX_NO_DB_ERROR SP_NO_DB_ERROR
|
||||
#define EVEX_GENERAL_ERROR -20
|
||||
#define EVEX_BAD_IDENTIFIER SP_BAD_IDENTIFIER
|
||||
#define EVEX_BODY_TOO_LONG SP_BODY_TOO_LONG
|
||||
#define EVEX_BAD_PARAMS -21
|
||||
#define EVEX_NOT_RUNNING -22
|
||||
|
||||
#define EVENT_EXEC_NO_MORE (1L << 0)
|
||||
#define EVENT_NOT_USED (1L << 1)
|
||||
#define EVENT_EXEC_NO_MORE (1L << 0)
|
||||
#define EVENT_NOT_USED (1L << 1)
|
||||
|
||||
#define SP_OK 0
|
||||
#define SP_KEY_NOT_FOUND -1
|
||||
#define SP_OPEN_TABLE_FAILED -2
|
||||
#define SP_WRITE_ROW_FAILED -3
|
||||
#define SP_DELETE_ROW_FAILED -4
|
||||
#define SP_GET_FIELD_FAILED -5
|
||||
#define SP_PARSE_ERROR -6
|
||||
#define SP_INTERNAL_ERROR -7
|
||||
#define SP_NO_DB_ERROR -8
|
||||
#define SP_BAD_IDENTIFIER -9
|
||||
#define SP_BODY_TOO_LONG -10
|
||||
|
||||
extern ulong opt_event_executor;
|
||||
|
||||
enum enum_event_on_completion
|
||||
{
|
||||
@ -40,11 +71,12 @@ class event_timed
|
||||
{
|
||||
event_timed(const event_timed &); /* Prevent use of these */
|
||||
void operator=(event_timed &);
|
||||
my_bool running;
|
||||
pthread_mutex_t LOCK_running;
|
||||
|
||||
public:
|
||||
LEX_STRING m_db;
|
||||
LEX_STRING m_name;
|
||||
LEX_STRING m_qname; // db.name
|
||||
LEX_STRING m_body;
|
||||
|
||||
LEX_STRING m_definer_user;
|
||||
@ -64,9 +96,6 @@ public:
|
||||
enum enum_event_status m_status;
|
||||
sp_head *m_sphead;
|
||||
|
||||
|
||||
|
||||
uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value
|
||||
const uchar *m_body_begin;
|
||||
|
||||
bool m_dropped;
|
||||
@ -75,15 +104,19 @@ public:
|
||||
bool m_last_executed_changed;
|
||||
bool m_status_changed;
|
||||
|
||||
event_timed():m_expr(0), m_created(0), m_modified(0),
|
||||
event_timed():running(0), m_expr(0), m_created(0), m_modified(0),
|
||||
m_on_completion(MYSQL_EVENT_ON_COMPLETION_DROP),
|
||||
m_status(MYSQL_EVENT_ENABLED), m_sphead(0), m_dropped(false),
|
||||
m_free_sphead_on_delete(true), m_flags(0),
|
||||
m_last_executed_changed(false), m_status_changed(false)
|
||||
{ init(); }
|
||||
{
|
||||
pthread_mutex_init(&LOCK_running, MY_MUTEX_INIT_FAST);
|
||||
init();
|
||||
}
|
||||
|
||||
~event_timed()
|
||||
{
|
||||
pthread_mutex_destroy(&LOCK_running);
|
||||
if (m_free_sphead_on_delete)
|
||||
free_sp();
|
||||
}
|
||||
@ -115,12 +148,6 @@ public:
|
||||
void
|
||||
init_comment(THD *thd, LEX_STRING *comment);
|
||||
|
||||
void
|
||||
set_on_completion_drop(bool drop);
|
||||
|
||||
void
|
||||
set_event_status(bool enabled);
|
||||
|
||||
int
|
||||
load_from_row(MEM_ROOT *mem_root, TABLE *table);
|
||||
|
||||
@ -140,10 +167,10 @@ public:
|
||||
get_show_create_event(THD *thd, uint *length);
|
||||
|
||||
int
|
||||
execute(THD *thd, MEM_ROOT *mem_root);
|
||||
execute(THD *thd, MEM_ROOT *mem_root= NULL);
|
||||
|
||||
int
|
||||
compile(THD *thd, MEM_ROOT *mem_root);
|
||||
compile(THD *thd, MEM_ROOT *mem_root= NULL);
|
||||
|
||||
void free_sp()
|
||||
{
|
||||
@ -172,29 +199,21 @@ init_events();
|
||||
void
|
||||
shutdown_events();
|
||||
|
||||
/*
|
||||
typedef struct st_event_item {
|
||||
my_time_t execute_at;
|
||||
sp_head *proc;
|
||||
char *definer_user;
|
||||
char *definer_host;
|
||||
} EVENT_ITEM;
|
||||
*/
|
||||
|
||||
// auxiliary
|
||||
int
|
||||
event_timed_compare(event_timed **a, event_timed **b);
|
||||
|
||||
|
||||
/*
|
||||
CREATE TABLE `event` (
|
||||
`db` varchar(64) character set latin1 collate latin1_bin NOT NULL default '',
|
||||
`name` varchar(64) NOT NULL default '',
|
||||
`body` blob NOT NULL,
|
||||
`definer` varchar(77) character set latin1 collate latin1_bin NOT NULL default '',
|
||||
`db` varchar(64) character set utf8 collate utf8_bin NOT NULL default '',
|
||||
`name` varchar(64) character set utf8 collate utf8_bin NOT NULL default '',
|
||||
`body` longblob NOT NULL,
|
||||
`definer` varchar(77) character set utf8 collate utf8_bin NOT NULL default '',
|
||||
`execute_at` datetime default NULL,
|
||||
`transient_expression` int(11) default NULL,
|
||||
`interval_type` 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') DEFAULT NULL,
|
||||
`interval_type` 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') default NULL,
|
||||
`created` timestamp NOT NULL default '0000-00-00 00:00:00',
|
||||
`modified` timestamp NOT NULL default '0000-00-00 00:00:00',
|
||||
`last_executed` datetime default NULL,
|
||||
@ -202,9 +221,9 @@ CREATE TABLE `event` (
|
||||
`ends` datetime default NULL,
|
||||
`status` enum('ENABLED','DISABLED') NOT NULL default 'ENABLED',
|
||||
`on_completion` enum('DROP','PRESERVE') NOT NULL default 'DROP',
|
||||
`comment` varchar(64) character set latin1 collate latin1_bin NOT NULL default '',
|
||||
`comment` varchar(64) character set utf8 collate utf8_bin NOT NULL default '',
|
||||
PRIMARY KEY (`db`,`name`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8
|
||||
*/
|
||||
|
||||
#endif /* _EVENT_H_ */
|
||||
|
533
sql/event_executor.cc
Normal file
533
sql/event_executor.cc
Normal file
@ -0,0 +1,533 @@
|
||||
/* 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 "event.h"
|
||||
#include "event_priv.h"
|
||||
#include "sp.h"
|
||||
|
||||
#define DBUG_FAULTY_THR2
|
||||
|
||||
static uint workers_count;
|
||||
|
||||
|
||||
pthread_mutex_t LOCK_event_arrays,
|
||||
LOCK_workers_count,
|
||||
LOCK_evex_running;
|
||||
|
||||
|
||||
bool evex_is_running= false;
|
||||
|
||||
ulong opt_event_executor;
|
||||
my_bool event_executor_running_global_var= false;
|
||||
|
||||
extern ulong thread_created;
|
||||
|
||||
|
||||
static int
|
||||
evex_load_events_from_db(THD *thd);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
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));
|
||||
|
||||
#ifndef DBUG_FAULTY_THR
|
||||
//TODO Andrey: Change the error code returned!
|
||||
if (pthread_create(&th, NULL, event_executor_main, (void*)NULL))
|
||||
DBUG_RETURN(ER_SLAVE_THREAD);
|
||||
#else
|
||||
event_executor_main(NULL);
|
||||
#endif
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
shutdown_events()
|
||||
{
|
||||
DBUG_ENTER("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);
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
// make this thread invisible it has no vio -> show processlist won't see
|
||||
thd->system_thread= 0;
|
||||
|
||||
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!");
|
||||
thd->proc_info = "Sleeping";
|
||||
my_sleep(1000000);// sleep 1s
|
||||
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 &&
|
||||
!check_access(thd, EVENT_ACL, et->m_db.str, 0, 0, 0,
|
||||
is_schema_db(et->m_db.str)))
|
||||
{
|
||||
pthread_t th;
|
||||
|
||||
DBUG_PRINT("info", (" Spawning a thread %d", ++iter_num));
|
||||
thd->proc_info = "Starting new thread";
|
||||
sql_print_information(" Spawning a thread %d", ++iter_num);
|
||||
#ifndef DBUG_FAULTY_THR
|
||||
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
|
||||
}
|
||||
#else
|
||||
event_executor_worker((void *) et);
|
||||
#endif
|
||||
et->mark_last_executed();
|
||||
thd->proc_info = "Computing next time";
|
||||
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);
|
||||
DBUG_ASSERT(idx != -1);
|
||||
delete_dynamic_element(&events_array, idx);
|
||||
}
|
||||
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;
|
||||
|
||||
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();
|
||||
|
||||
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(pthread_self());
|
||||
if (init_event_thread(thd))
|
||||
goto err;
|
||||
|
||||
thd->init_for_queries();
|
||||
|
||||
// make this thread visible it has no vio -> show processlist needs this flag
|
||||
thd->system_thread= 0;
|
||||
|
||||
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;
|
||||
{
|
||||
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, &mem_root);
|
||||
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;
|
||||
|
||||
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)).
|
||||
*/
|
||||
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
||||
|
||||
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));
|
||||
|
||||
#ifndef DBUG_FAULTY_THR
|
||||
my_thread_end();
|
||||
pthread_exit(0);
|
||||
#endif
|
||||
DBUG_RETURN(0); // Can't return anything here
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
57
sql/event_priv.h
Normal file
57
sql/event_priv.h
Normal file
@ -0,0 +1,57 @@
|
||||
/* 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 */
|
||||
|
||||
#ifndef _EVENT_PRIV_H_
|
||||
#define _EVENT_PRIV_H_
|
||||
|
||||
#define EVEX_OPEN_TABLE_FOR_UPDATE() \
|
||||
open_proc_type_table_for_update(thd, "event", &mysql_event_table_exists)
|
||||
|
||||
|
||||
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 :) */
|
||||
};
|
||||
|
||||
extern bool evex_is_running;
|
||||
extern bool mysql_event_table_exists;
|
||||
extern DYNAMIC_ARRAY events_array;
|
||||
extern DYNAMIC_ARRAY evex_executing_queue;
|
||||
extern MEM_ROOT evex_mem_root;
|
||||
extern pthread_mutex_t LOCK_event_arrays,
|
||||
LOCK_workers_count,
|
||||
LOCK_evex_running;
|
||||
|
||||
|
||||
int
|
||||
my_time_compare(TIME *a, TIME *b);
|
||||
|
||||
#endif /* _EVENT_PRIV_H_ */
|
944
sql/event_timed.cc
Normal file
944
sql/event_timed.cc
Normal file
@ -0,0 +1,944 @@
|
||||
/* 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 "event.h"
|
||||
#include "event_priv.h"
|
||||
#include "sp.h"
|
||||
|
||||
|
||||
|
||||
extern int yyparse(void *thd);
|
||||
|
||||
/*
|
||||
Init all member variables
|
||||
|
||||
SYNOPSIS
|
||||
event_timed::init()
|
||||
*/
|
||||
|
||||
void
|
||||
event_timed::init()
|
||||
{
|
||||
DBUG_ENTER("event_timed::init");
|
||||
|
||||
m_db.str= m_name.str= m_body.str= m_comment.str= 0;
|
||||
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);
|
||||
}
|
||||
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(<ime, 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(<ime, TIME_to_timestamp(thd,<ime, ¬_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(<ime, 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(<ime, TIME_to_timestamp(thd,<ime, ¬_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(<ime, 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(<ime, TIME_to_timestamp(thd, <ime, ¬_used));
|
||||
|
||||
if (m_starts.year && my_time_compare(&m_starts, <ime) != -1)
|
||||
DBUG_RETURN(EVEX_BAD_PARAMS);
|
||||
|
||||
m_ends= ltime;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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)
|
||||
{
|
||||
|
||||
return (bool) evex_drop_event(thd, this, 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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Executes the event (the underlying sp_head object);
|
||||
|
||||
SYNOPSIS
|
||||
evex_fill_row()
|
||||
thd THD
|
||||
mem_root If != NULL use it to compile the event on it
|
||||
|
||||
Returns
|
||||
0 - success
|
||||
-100 - event in execution (parallel execution is impossible)
|
||||
others - retcodes of sp_head::execute_procedure()
|
||||
|
||||
*/
|
||||
|
||||
int
|
||||
event_timed::execute(THD *thd, MEM_ROOT *mem_root)
|
||||
{
|
||||
List<Item> empty_item_list;
|
||||
int ret= 0;
|
||||
|
||||
DBUG_ENTER("event_timed::execute");
|
||||
|
||||
VOID(pthread_mutex_lock(&LOCK_running));
|
||||
if (running)
|
||||
{
|
||||
VOID(pthread_mutex_unlock(&LOCK_running));
|
||||
DBUG_RETURN(-100);
|
||||
}
|
||||
running= true;
|
||||
VOID(pthread_mutex_unlock(&LOCK_running));
|
||||
|
||||
// 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);
|
||||
|
||||
VOID(pthread_mutex_lock(&LOCK_running));
|
||||
running= false;
|
||||
VOID(pthread_mutex_unlock(&LOCK_running));
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
@ -4807,7 +4807,7 @@ Disable with --skip-bdb (will save memory).",
|
||||
(gptr*) &global_system_variables.engine_condition_pushdown,
|
||||
(gptr*) &global_system_variables.engine_condition_pushdown,
|
||||
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"event-executor", OPT_EVENT_EXECUTOR, "Print a symbolic stack trace on failure.",
|
||||
{"event-scheduler", OPT_EVENT_EXECUTOR, "Enable/disable the event scheduler.",
|
||||
(gptr*) &opt_event_executor, (gptr*) &opt_event_executor, 0, GET_BOOL, NO_ARG,
|
||||
1/*default*/, 0/*min-value*/, 1/*max-value*/, 0, 0, 0},
|
||||
{"exit-info", 'T', "Used for debugging; Use at your own risk!", 0, 0, 0,
|
||||
|
@ -207,7 +207,7 @@ sys_var_long_ptr sys_delayed_insert_timeout("delayed_insert_timeout",
|
||||
&delayed_insert_timeout);
|
||||
sys_var_long_ptr sys_delayed_queue_size("delayed_queue_size",
|
||||
&delayed_queue_size);
|
||||
sys_var_bool_ptr sys_event_executor("event_executor",
|
||||
sys_var_bool_ptr sys_event_executor("event_scheduler",
|
||||
&event_executor_running_global_var);
|
||||
sys_var_long_ptr sys_expire_logs_days("expire_logs_days",
|
||||
&expire_logs_days);
|
||||
|
@ -5730,7 +5730,7 @@ ER_EVENT_DOES_NOT_EXIST
|
||||
ER_EVENT_CANT_ALTER
|
||||
eng "Failed to alter event %s"
|
||||
ER_EVENT_DROP_FAILED
|
||||
eng "Failed to DROP %s %s"
|
||||
eng "Failed to drop %s"
|
||||
ER_EVENT_INTERVAL_NOT_POSITIVE
|
||||
eng "INTERVAL must be positive"
|
||||
ER_EVENT_ENDS_BEFORE_STARTS
|
||||
|
@ -958,10 +958,10 @@ int sp_head::execute(THD *thd)
|
||||
DBUG_ASSERT(!(m_flags & IS_INVOKED));
|
||||
m_flags|= IS_INVOKED;
|
||||
m_first_instance->m_first_free_instance= m_next_cached_sp;
|
||||
DBUG_PRINT("info", ("first free for 0x%lx ++: 0x%lx->0x%lx, level: %lu, flags %x",
|
||||
(ulong)m_first_instance, this, m_next_cached_sp,
|
||||
m_next_cached_sp->m_recursion_level,
|
||||
m_next_cached_sp->m_flags));
|
||||
// DBUG_PRINT("info", ("first free for 0x%lx ++: 0x%lx->0x%lx, level: %lu, flags %x",
|
||||
// (ulong)m_first_instance, this, m_next_cached_sp,
|
||||
// m_next_cached_sp->m_recursion_level,
|
||||
// m_next_cached_sp->m_flags));
|
||||
/*
|
||||
Check that if there are not any instances after this one then
|
||||
pointer to the last instance points on this instance or if there are
|
||||
|
@ -130,7 +130,6 @@ public:
|
||||
uint m_returns_len; // For FUNCTIONs only
|
||||
uint m_returns_pack; // For FUNCTIONs only
|
||||
const uchar *m_tmp_query; // Temporary pointer to sub query string
|
||||
uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value
|
||||
st_sp_chistics *m_chistics;
|
||||
ulong m_sql_mode; // For SHOW CREATE and execution
|
||||
LEX_STRING m_qname; // db.name
|
||||
|
@ -352,6 +352,14 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
|
||||
if (table->s->fields <= 36 && (user.access & GRANT_ACL))
|
||||
user.access|= CREATE_USER_ACL;
|
||||
|
||||
|
||||
/*
|
||||
if it is pre 5.1.4 privilege table then map CREATE privilege on
|
||||
CREATE|ALTER|DROP|EXECUTE EVENT
|
||||
*/
|
||||
if (table->s->fields <= 37 && (user.access & CREATE_ACL))
|
||||
user.access|= EVENT_ACL;
|
||||
|
||||
user.sort= get_sort(2,user.host.hostname,user.user);
|
||||
user.hostname_length= (user.host.hostname ?
|
||||
(uint) strlen(user.host.hostname) : 0);
|
||||
|
@ -57,7 +57,7 @@
|
||||
(UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
|
||||
GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | \
|
||||
LOCK_TABLES_ACL | EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | \
|
||||
CREATE_PROC_ACL | ALTER_PROC_ACL)
|
||||
CREATE_PROC_ACL | ALTER_PROC_ACL | EVENT_ACL)
|
||||
|
||||
#define TABLE_ACLS \
|
||||
(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
|
||||
|
@ -3675,9 +3675,6 @@ end_with_restore_list:
|
||||
}
|
||||
case SQLCOM_CREATE_EVENT:
|
||||
{
|
||||
if (check_global_access(thd, EVENT_ACL))
|
||||
break;
|
||||
|
||||
DBUG_ASSERT(lex->et);
|
||||
if (! lex->et->m_db.str)
|
||||
{
|
||||
@ -3686,7 +3683,9 @@ end_with_restore_list:
|
||||
lex->et= 0;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (check_access(thd, EVENT_ACL, lex->et->m_db.str, 0, 0, 0,
|
||||
is_schema_db(lex->et->m_db.str)))
|
||||
break;
|
||||
int result;
|
||||
uint create_options= lex->create_info.options;
|
||||
res= (result= evex_create_event(thd, lex->et, create_options));
|
||||
@ -3716,9 +3715,6 @@ end_with_restore_list:
|
||||
}
|
||||
case SQLCOM_ALTER_EVENT:
|
||||
{
|
||||
if (check_global_access(thd, EVENT_ACL))
|
||||
break;
|
||||
|
||||
DBUG_ASSERT(lex->et);
|
||||
if (! lex->et->m_db.str)
|
||||
{
|
||||
@ -3727,6 +3723,9 @@ end_with_restore_list:
|
||||
lex->et= 0;
|
||||
goto error;
|
||||
}
|
||||
if (check_access(thd, EVENT_ACL, lex->et->m_db.str, 0, 0, 0,
|
||||
is_schema_db(lex->et->m_db.str)))
|
||||
break;
|
||||
|
||||
int result;
|
||||
res= (result= evex_update_event(thd, lex->spname, lex->et));
|
||||
@ -3735,10 +3734,10 @@ end_with_restore_list:
|
||||
send_ok(thd, 1);
|
||||
break;
|
||||
case EVEX_KEY_NOT_FOUND:
|
||||
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), lex->et->m_qname.str);
|
||||
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), lex->et->m_name.str);
|
||||
break;
|
||||
default:
|
||||
my_error(ER_EVENT_CANT_ALTER, MYF(0), lex->et->m_qname.str);
|
||||
my_error(ER_EVENT_CANT_ALTER, MYF(0), lex->et->m_name.str);
|
||||
break;
|
||||
}
|
||||
delete lex->et;
|
||||
@ -3754,9 +3753,6 @@ end_with_restore_list:
|
||||
}
|
||||
case SQLCOM_DROP_EVENT:
|
||||
{
|
||||
if (check_global_access(thd, EVENT_ACL))
|
||||
break;
|
||||
|
||||
DBUG_ASSERT(lex->et);
|
||||
if (! lex->et->m_db.str)
|
||||
{
|
||||
@ -3765,6 +3761,9 @@ end_with_restore_list:
|
||||
lex->et= 0;
|
||||
goto error;
|
||||
}
|
||||
if (check_access(thd, EVENT_ACL, lex->et->m_db.str, 0, 0, 0,
|
||||
is_schema_db(lex->et->m_db.str)))
|
||||
break;
|
||||
|
||||
int result;
|
||||
res= (result= evex_drop_event(thd, lex->et, lex->drop_if_exists));
|
||||
@ -3773,10 +3772,10 @@ end_with_restore_list:
|
||||
send_ok(thd, 1);
|
||||
break;
|
||||
case EVEX_KEY_NOT_FOUND:
|
||||
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), lex->et->m_qname.str);
|
||||
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), lex->et->m_name.str);
|
||||
break;
|
||||
default:
|
||||
my_error(ER_EVENT_DROP_FAILED, MYF(0), lex->et->m_qname.str);
|
||||
my_error(ER_EVENT_DROP_FAILED, MYF(0), lex->et->m_name.str);
|
||||
break;
|
||||
}
|
||||
delete lex->et;
|
||||
@ -3786,7 +3785,8 @@ end_with_restore_list:
|
||||
}
|
||||
case SQLCOM_SHOW_CREATE_EVENT:
|
||||
{
|
||||
if (check_global_access(thd, EVENT_ACL))
|
||||
if (check_access(thd, EVENT_ACL, lex->spname->m_db.str, 0, 0, 0,
|
||||
is_schema_db(lex->spname->m_db.str)))
|
||||
break;
|
||||
|
||||
if (lex->spname->m_name.length > NAME_LEN)
|
||||
@ -3794,6 +3794,7 @@ end_with_restore_list:
|
||||
my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
|
||||
goto error;
|
||||
}
|
||||
/* TODO : Implement it */
|
||||
send_ok(thd, 1);
|
||||
break;
|
||||
}
|
||||
|
105
sql/sql_yacc.yy
105
sql/sql_yacc.yy
@ -1261,7 +1261,7 @@ create:
|
||||
* stored procedure, otherwise yylex will chop it into pieces
|
||||
* at each ';'.
|
||||
*/
|
||||
sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
$<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES);
|
||||
}
|
||||
'('
|
||||
@ -1294,9 +1294,9 @@ create:
|
||||
YYABORT;
|
||||
sp->init_strings(YYTHD, lex, $3);
|
||||
lex->sql_command= SQLCOM_CREATE_PROCEDURE;
|
||||
|
||||
/* Restore flag if it was cleared above */
|
||||
if (sp->m_old_cmq)
|
||||
YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
|
||||
YYTHD->client_capabilities |= $<ulong_num>3;
|
||||
sp->restore_thd_mem_root(YYTHD);
|
||||
}
|
||||
| CREATE
|
||||
@ -1314,32 +1314,35 @@ create:
|
||||
| CREATE EVENT_SYM opt_if_not_exists sp_name
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
event_timed *et;
|
||||
|
||||
if (lex->et)
|
||||
{
|
||||
/*
|
||||
Recursive events are not possible because recursive SPs
|
||||
are not also possible. lex->sp_head is not stacked.
|
||||
*/
|
||||
// ToDo Andrey : Change the error message
|
||||
my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT");
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
lex->create_info.options=$3;
|
||||
lex->create_info.options= $3;
|
||||
|
||||
et= new event_timed();// implicitly calls event_timed::init()
|
||||
lex->et = et;
|
||||
if (!(lex->et= new event_timed())) // implicitly calls event_timed::init()
|
||||
YYABORT;
|
||||
|
||||
if (!lex->et_compile_phase)
|
||||
et->init_name(YYTHD, $4);
|
||||
|
||||
/*
|
||||
We have to turn of CLIENT_MULTI_QUERIES while parsing a
|
||||
stored procedure, otherwise yylex will chop it into pieces
|
||||
at each ';'.
|
||||
*/
|
||||
et->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
|
||||
|
||||
lex->sphead= 0;
|
||||
$<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES);
|
||||
|
||||
if (!lex->et_compile_phase)
|
||||
lex->et->init_name(YYTHD, $4);
|
||||
|
||||
lex->sphead= 0;//defensive programming
|
||||
}
|
||||
ON SCHEDULE_SYM ev_schedule_time
|
||||
ev_on_completion
|
||||
@ -1347,9 +1350,14 @@ create:
|
||||
ev_comment
|
||||
DO_SYM ev_sql_stmt
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
/*
|
||||
sql_command is set here because some rules in ev_sql_stmt
|
||||
can overwrite it
|
||||
*/
|
||||
// Restore flag if it was cleared above
|
||||
YYTHD->client_capabilities |= $<ulong_num>5;
|
||||
|
||||
lex->sql_command= SQLCOM_CREATE_EVENT;
|
||||
Lex->sql_command= SQLCOM_CREATE_EVENT;
|
||||
}
|
||||
;
|
||||
|
||||
@ -1396,14 +1404,14 @@ ev_status: /* empty */
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
if (!lex->et_compile_phase)
|
||||
lex->et->set_event_status(1);
|
||||
lex->et->m_status= MYSQL_EVENT_ENABLED;
|
||||
}
|
||||
| DISABLE_SYM
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
|
||||
if (!lex->et_compile_phase)
|
||||
lex->et->set_event_status(0);
|
||||
lex->et->m_status= MYSQL_EVENT_DISABLED;
|
||||
}
|
||||
;
|
||||
ev_starts: /* empty */
|
||||
@ -1438,13 +1446,13 @@ ev_on_completion: /* empty */
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
if (!lex->et_compile_phase)
|
||||
lex->et->set_on_completion_drop(false);
|
||||
lex->et->m_on_completion= MYSQL_EVENT_ON_COMPLETION_PRESERVE;
|
||||
}
|
||||
| ON COMPLETION_SYM NOT_SYM PRESERVE_SYM
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
if (!lex->et_compile_phase)
|
||||
lex->et->set_on_completion_drop(true);
|
||||
lex->et->m_on_completion= MYSQL_EVENT_ON_COMPLETION_DROP;
|
||||
}
|
||||
;
|
||||
ev_comment: /* empty */
|
||||
@ -1489,10 +1497,6 @@ ev_sql_stmt:
|
||||
sp->restore_thd_mem_root(YYTHD);
|
||||
|
||||
lex->sp_chistics.suid= SP_IS_SUID;//always the definer!
|
||||
|
||||
// Restore flag if it was cleared above
|
||||
if (lex->et->m_old_cmq)
|
||||
YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
|
||||
|
||||
if (!lex->et_compile_phase)
|
||||
{
|
||||
@ -1577,7 +1581,7 @@ create_function_tail:
|
||||
* stored procedure, otherwise yylex will chop it into pieces
|
||||
* at each ';'.
|
||||
*/
|
||||
sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
$<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
|
||||
lex->sphead->m_param_begin= lex->tok_start+1;
|
||||
}
|
||||
@ -1656,9 +1660,9 @@ create_function_tail:
|
||||
YYABORT;
|
||||
lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
|
||||
sp->init_strings(YYTHD, lex, lex->spname);
|
||||
|
||||
/* Restore flag if it was cleared above */
|
||||
if (sp->m_old_cmq)
|
||||
YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
|
||||
YYTHD->client_capabilities |= $<ulong_num>2;
|
||||
sp->restore_thd_mem_root(YYTHD);
|
||||
}
|
||||
;
|
||||
@ -4194,11 +4198,15 @@ alter:
|
||||
|
||||
if (lex->et)
|
||||
{
|
||||
/*
|
||||
Recursive events are not possible because recursive SPs
|
||||
are not also possible. lex->sp_head is not stacked.
|
||||
*/
|
||||
// ToDo Andrey : Change the error message
|
||||
my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT");
|
||||
YYABORT;
|
||||
}
|
||||
lex->spname= 0;//defensive
|
||||
lex->spname= 0;//defensive programming
|
||||
|
||||
et= new event_timed();// implicitly calls event_timed::init()
|
||||
lex->et = et;
|
||||
@ -4209,7 +4217,7 @@ alter:
|
||||
stored procedure, otherwise yylex will chop it into pieces
|
||||
at each ';'.
|
||||
*/
|
||||
et->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
$<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
|
||||
|
||||
/*
|
||||
@ -4225,8 +4233,12 @@ alter:
|
||||
ev_comment
|
||||
ev_opt_sql_stmt
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
lex->sql_command= SQLCOM_ALTER_EVENT;
|
||||
/*
|
||||
sql_command is set here because some rules in ev_sql_stmt
|
||||
can overwrite it
|
||||
*/
|
||||
YYTHD->client_capabilities |= $<ulong_num>3;
|
||||
Lex->sql_command= SQLCOM_ALTER_EVENT;
|
||||
}
|
||||
;
|
||||
|
||||
@ -6953,18 +6965,21 @@ drop:
|
||||
| DROP EVENT_SYM if_exists sp_name
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
event_timed *et;
|
||||
|
||||
if (lex->et)
|
||||
{
|
||||
// ToDo Andrey : Change the error message
|
||||
/*
|
||||
Recursive events are not possible because recursive SPs
|
||||
are not also possible. lex->sp_head is not stacked.
|
||||
*/
|
||||
my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT");
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
et= new event_timed;
|
||||
lex->et = et;
|
||||
et->init_name(YYTHD, $4);
|
||||
if (!(lex->et= new event_timed()))
|
||||
YYABORT;
|
||||
lex->et->init_name(YYTHD, $4);
|
||||
|
||||
lex->sql_command = SQLCOM_DROP_EVENT;
|
||||
lex->drop_if_exists= $3;
|
||||
@ -7623,10 +7638,8 @@ show_param:
|
||||
}
|
||||
| CREATE EVENT_SYM sp_name
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
|
||||
lex->sql_command = SQLCOM_SHOW_CREATE_EVENT;
|
||||
lex->spname= $3;
|
||||
Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT;
|
||||
Lex->spname= $3;
|
||||
};
|
||||
;
|
||||
|
||||
@ -8515,7 +8528,7 @@ keyword_sp:
|
||||
| AGGREGATE_SYM {}
|
||||
| ALGORITHM_SYM {}
|
||||
| ANY_SYM {}
|
||||
| AT_SYM {}
|
||||
| AT_SYM {}
|
||||
| AUTO_INC {}
|
||||
| AVG_ROW_LENGTH {}
|
||||
| AVG_SYM {}
|
||||
@ -8561,8 +8574,8 @@ keyword_sp:
|
||||
| ESCAPE_SYM {}
|
||||
| EVENT_SYM {}
|
||||
| EVENTS_SYM {}
|
||||
| EVERY_SYM {}
|
||||
| EXPANSION_SYM {}
|
||||
| EVERY_SYM {}
|
||||
| EXPANSION_SYM {}
|
||||
| EXTENDED_SYM {}
|
||||
| FAST_SYM {}
|
||||
| FOUND_SYM {}
|
||||
@ -8684,7 +8697,7 @@ keyword_sp:
|
||||
| ROW_FORMAT_SYM {}
|
||||
| ROW_SYM {}
|
||||
| RTREE_SYM {}
|
||||
| SCHEDULE_SYM {}
|
||||
| SCHEDULE_SYM {}
|
||||
| SECOND_SYM {}
|
||||
| SERIAL_SYM {}
|
||||
| SERIALIZABLE_SYM {}
|
||||
@ -10076,7 +10089,7 @@ trigger_tail:
|
||||
stored procedure, otherwise yylex will chop it into pieces
|
||||
at each ';'.
|
||||
*/
|
||||
sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
$<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
|
||||
|
||||
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
|
||||
@ -10091,8 +10104,8 @@ trigger_tail:
|
||||
lex->sql_command= SQLCOM_CREATE_TRIGGER;
|
||||
sp->init_strings(YYTHD, lex, $3);
|
||||
/* Restore flag if it was cleared above */
|
||||
if (sp->m_old_cmq)
|
||||
YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
|
||||
|
||||
YYTHD->client_capabilities |= $<ulong_num>11;
|
||||
sp->restore_thd_mem_root(YYTHD);
|
||||
|
||||
if (sp->is_not_allowed_in_function("trigger"))
|
||||
|
@ -64,7 +64,7 @@ extern Time_zone * my_tz_find(const String *name, TABLE_LIST *tz_tables);
|
||||
extern Time_zone * my_tz_find_with_opening_tz_tables(THD *thd, const String *name);
|
||||
extern my_bool my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap);
|
||||
extern void my_tz_free();
|
||||
my_time_t sec_since_epoch_TIME(TIME *t);
|
||||
extern my_time_t sec_since_epoch_TIME(TIME *t);
|
||||
|
||||
extern TABLE_LIST fake_time_zone_tables_list;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user