mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
commit
7c049782fa
39 changed files with 6690 additions and 2818 deletions
|
@ -79,7 +79,7 @@ fast_cflags="-O3 -fno-omit-frame-pointer"
|
|||
reckless_cflags="-O3 -fomit-frame-pointer "
|
||||
|
||||
debug_cflags="-DUNIV_MUST_NOT_INLINE -DEXTRA_DEBUG -DFORCE_INIT_OF_VARS -DSAFEMALLOC -DPEDANTIC_SAFEMALLOC -DSAFE_MUTEX"
|
||||
debug_extra_cflags="-O1 -Wuninitialized"
|
||||
debug_extra_cflags="-O0"
|
||||
|
||||
base_cxxflags="-felide-constructors -fno-exceptions -fno-rtti"
|
||||
amd64_cxxflags="" # If dropping '--with-big-tables', add here "-DBIG_TABLES"
|
||||
|
|
|
@ -753,6 +753,7 @@ extern void get_dynamic(DYNAMIC_ARRAY *array,gptr element,uint array_index);
|
|||
extern void delete_dynamic(DYNAMIC_ARRAY *array);
|
||||
extern void delete_dynamic_element(DYNAMIC_ARRAY *array, uint array_index);
|
||||
extern void freeze_size(DYNAMIC_ARRAY *array);
|
||||
extern int get_index_dynamic(DYNAMIC_ARRAY *array, gptr element);
|
||||
#define dynamic_array_ptr(array,array_index) ((array)->buffer+(array_index)*(array)->size_of_element)
|
||||
#define dynamic_element(array,array_index,type) ((type)((array)->buffer) +(array_index))
|
||||
#define push_dynamic(A,B) insert_dynamic((A),(B))
|
||||
|
|
|
@ -35,6 +35,7 @@ typedef struct st_queue {
|
|||
uint offset_to_key; /* compare is done on element+offset */
|
||||
int max_at_top; /* Set if queue_top gives max */
|
||||
int (*compare)(void *, byte *,byte *);
|
||||
uint auto_extent;
|
||||
} QUEUE;
|
||||
|
||||
#define queue_top(queue) ((queue)->root[1])
|
||||
|
@ -49,14 +50,19 @@ typedef int (*queue_compare)(void *,byte *, byte *);
|
|||
int init_queue(QUEUE *queue,uint max_elements,uint offset_to_key,
|
||||
pbool max_at_top, queue_compare compare,
|
||||
void *first_cmp_arg);
|
||||
int init_queue_ex(QUEUE *queue,uint max_elements,uint offset_to_key,
|
||||
pbool max_at_top, queue_compare compare,
|
||||
void *first_cmp_arg, uint auto_extent);
|
||||
int reinit_queue(QUEUE *queue,uint max_elements,uint offset_to_key,
|
||||
pbool max_at_top, queue_compare compare,
|
||||
void *first_cmp_arg);
|
||||
int resize_queue(QUEUE *queue, uint max_elements);
|
||||
void delete_queue(QUEUE *queue);
|
||||
void queue_insert(QUEUE *queue,byte *element);
|
||||
int queue_insert_safe(QUEUE *queue, byte *element);
|
||||
byte *queue_remove(QUEUE *queue,uint idx);
|
||||
#define queue_remove_all(queue) { (queue)->elements= 0; }
|
||||
#define queue_is_full(queue) (queue->elements == queue->max_elements)
|
||||
void _downheap(QUEUE *queue,uint idx);
|
||||
void queue_fix(QUEUE *queue);
|
||||
#define is_queue_inited(queue) ((queue)->root != 0)
|
||||
|
|
|
@ -63,9 +63,8 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \
|
|||
unireg.cc uniques.cc stacktrace.c sql_union.cc hash_filo.cc \
|
||||
spatial.cc gstream.cc sql_help.cc tztime.cc sql_cursor.cc \
|
||||
sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc \
|
||||
parse_file.cc sql_view.cc sql_trigger.cc my_decimal.cc \
|
||||
item_xmlfunc.cc \
|
||||
rpl_filter.cc sql_partition.cc handlerton.cc sql_plugin.cc
|
||||
parse_file.cc sql_view.cc sql_trigger.cc my_decimal.cc event_executor.cc event.cc event_timed.cc \
|
||||
rpl_filter.cc sql_partition.cc handlerton.cc sql_plugin.cc
|
||||
|
||||
libmysqld_int_a_SOURCES= $(libmysqld_sources) $(libmysqlsources) $(sqlsources)
|
||||
EXTRA_libmysqld_a_SOURCES = ha_innodb.cc ha_berkeley.cc ha_archive.cc \
|
||||
|
|
210
libmysqld/event.h
Normal file
210
libmysqld/event.h
Normal file
|
@ -0,0 +1,210 @@
|
|||
/* -*- C++ -*- */
|
||||
#ifndef _EVENT_H_
|
||||
#define _EVENT_H_
|
||||
#include "sp_head.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 EVENT_EXEC_NO_MORE (1L << 0)
|
||||
#define EVENT_NOT_USED (1L << 1)
|
||||
|
||||
|
||||
enum enum_event_on_completion
|
||||
{
|
||||
MYSQL_EVENT_ON_COMPLETION_DROP = 1,
|
||||
MYSQL_EVENT_ON_COMPLETION_PRESERVE
|
||||
};
|
||||
|
||||
enum enum_event_status
|
||||
{
|
||||
MYSQL_EVENT_ENABLED = 1,
|
||||
MYSQL_EVENT_DISABLED
|
||||
};
|
||||
|
||||
|
||||
class event_timed
|
||||
{
|
||||
event_timed(const event_timed &); /* Prevent use of these */
|
||||
void operator=(event_timed &);
|
||||
|
||||
public:
|
||||
LEX_STRING m_db;
|
||||
LEX_STRING m_name;
|
||||
LEX_STRING m_qname; // db.name
|
||||
LEX_STRING m_body;
|
||||
|
||||
LEX_STRING m_definer_user;
|
||||
LEX_STRING m_definer_host;
|
||||
LEX_STRING m_definer;// combination of user and host
|
||||
|
||||
LEX_STRING m_comment;
|
||||
TIME m_starts;
|
||||
TIME m_ends;
|
||||
TIME m_execute_at;
|
||||
longlong m_expr;
|
||||
interval_type m_interval;
|
||||
longlong m_created;
|
||||
longlong m_modified;
|
||||
TIME m_last_executed;
|
||||
enum enum_event_on_completion m_on_completion;
|
||||
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;
|
||||
bool m_free_sphead_on_delete;
|
||||
uint m_flags;//all kind of purposes
|
||||
bool m_last_executed_changed;
|
||||
bool m_status_changed;
|
||||
|
||||
event_timed():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(); }
|
||||
|
||||
~event_timed()
|
||||
{
|
||||
if (m_free_sphead_on_delete)
|
||||
free_sp();
|
||||
}
|
||||
|
||||
void
|
||||
init();
|
||||
|
||||
int
|
||||
init_definer(THD *thd);
|
||||
|
||||
int
|
||||
init_execute_at(THD *thd, Item *expr);
|
||||
|
||||
int
|
||||
init_interval(THD *thd, Item *expr, interval_type interval);
|
||||
|
||||
void
|
||||
init_name(THD *thd, sp_name *name);
|
||||
|
||||
int
|
||||
init_starts(THD *thd, Item *starts);
|
||||
|
||||
int
|
||||
init_ends(THD *thd, Item *ends);
|
||||
|
||||
void
|
||||
event_timed::init_body(THD *thd);
|
||||
|
||||
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);
|
||||
|
||||
bool
|
||||
compute_next_execution_time();
|
||||
|
||||
void
|
||||
mark_last_executed();
|
||||
|
||||
bool
|
||||
drop(THD *thd);
|
||||
|
||||
bool
|
||||
update_fields(THD *thd);
|
||||
|
||||
char *
|
||||
get_show_create_event(THD *thd, uint *length);
|
||||
|
||||
int
|
||||
execute(THD *thd, MEM_ROOT *mem_root);
|
||||
|
||||
int
|
||||
compile(THD *thd, MEM_ROOT *mem_root);
|
||||
|
||||
void free_sp()
|
||||
{
|
||||
if (m_sphead)
|
||||
{
|
||||
delete m_sphead;
|
||||
m_sphead= 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int
|
||||
evex_create_event(THD *thd, event_timed *et, uint create_options);
|
||||
|
||||
int
|
||||
evex_update_event(THD *thd, sp_name *name, event_timed *et);
|
||||
|
||||
int
|
||||
evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists);
|
||||
|
||||
|
||||
int
|
||||
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;
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
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 '',
|
||||
`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 latin1 collate latin1_bin NOT NULL default '',
|
||||
PRIMARY KEY (`db`,`name`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||
*/
|
||||
|
||||
#endif /* _EVENT_H_ */
|
|
@ -22,6 +22,7 @@ CREATE TABLE db (
|
|||
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,
|
||||
Execute_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,
|
||||
PRIMARY KEY Host (Host,Db,User),
|
||||
KEY User (User)
|
||||
) engine=MyISAM
|
||||
|
@ -29,8 +30,8 @@ CHARACTER SET utf8 COLLATE utf8_bin
|
|||
comment='Database privileges';
|
||||
|
||||
|
||||
INSERT INTO db VALUES ('%','test','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N');
|
||||
INSERT INTO db VALUES ('%','test\_%','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N');
|
||||
INSERT INTO db VALUES ('%','test','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N','Y');
|
||||
INSERT INTO db VALUES ('%','test\_%','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N','Y');
|
||||
|
||||
|
||||
CREATE TABLE host (
|
||||
|
@ -89,6 +90,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 +105,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 +568,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 char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
name char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
body longblob NOT NULL,
|
||||
definer char(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
execute_at DATETIME default NULL,
|
||||
interval_value int(11) default NULL,
|
||||
interval_field 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,
|
||||
modified TIMESTAMP NOT NULL,
|
||||
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';
|
||||
|
|
|
@ -3,6 +3,7 @@ show tables;
|
|||
Tables_in_mysql
|
||||
columns_priv
|
||||
db
|
||||
event
|
||||
func
|
||||
help_category
|
||||
help_keyword
|
||||
|
@ -31,6 +32,7 @@ show tables;
|
|||
Tables_in_mysql
|
||||
columns_priv
|
||||
db
|
||||
event
|
||||
func
|
||||
help_category
|
||||
help_keyword
|
||||
|
@ -67,6 +69,7 @@ show tables;
|
|||
Tables_in_mysql
|
||||
columns_priv
|
||||
db
|
||||
event
|
||||
func
|
||||
help_category
|
||||
help_keyword
|
||||
|
|
21
mysql-test/r/events.result
Normal file
21
mysql-test/r/events.result
Normal file
|
@ -0,0 +1,21 @@
|
|||
use test;
|
||||
drop event if exists event1;
|
||||
Warnings:
|
||||
Note 1305 Event event1 does not exist
|
||||
create event event1 on schedule every 15 minute starts now() ends date_add(now(), interval 5 hour) DO begin end;
|
||||
alter event event1 rename to event2;
|
||||
alter event event2 disabled;
|
||||
drop event event2;
|
||||
create event event2 on schedule every 2 second starts now() ends date_add(now(), interval 5 hour) comment "some" DO begin end;
|
||||
drop event event2;
|
||||
create table t_event3 (a int, b float);
|
||||
drop event if exists event3;
|
||||
Warnings:
|
||||
Note 1305 Event event3 does not exist
|
||||
create event event3 on schedule every 50 + 10 minute starts date_add("20010101", interval 5 minute) ends date_add("20151010", interval 5 day) comment "portokala_comment" DO insert into t_event3 values (unix_timestamp(), rand());
|
||||
set max_allowed_packet=128000000;
|
||||
select count(*) from t_event3;
|
||||
count(*)
|
||||
0
|
||||
drop event event3;
|
||||
drop table t_event3;
|
|
@ -11,8 +11,8 @@ GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3
|
|||
GRANT SELECT ON `mysqltest`.* TO 'mysqltest_1'@'localhost'
|
||||
grant delete on mysqltest.* to mysqltest_1@localhost;
|
||||
select * from mysql.user where user="mysqltest_1";
|
||||
Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
|
||||
localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N SPECIFIED EDH-RSA-DES-CBC3-SHA 0 0 0 0
|
||||
Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
|
||||
localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N N SPECIFIED EDH-RSA-DES-CBC3-SHA 0 0 0 0
|
||||
show grants for mysqltest_1@localhost;
|
||||
Grants for mysqltest_1@localhost
|
||||
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3-SHA'
|
||||
|
@ -42,15 +42,15 @@ delete from mysql.user where user='mysqltest_1';
|
|||
flush privileges;
|
||||
grant usage on *.* to mysqltest_1@localhost with max_queries_per_hour 10;
|
||||
select * from mysql.user where user="mysqltest_1";
|
||||
Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
|
||||
localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N 10 0 0 0
|
||||
Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
|
||||
localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N N 10 0 0 0
|
||||
show grants for mysqltest_1@localhost;
|
||||
Grants for mysqltest_1@localhost
|
||||
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10
|
||||
grant usage on *.* to mysqltest_1@localhost with max_updates_per_hour 20 max_connections_per_hour 30;
|
||||
select * from mysql.user where user="mysqltest_1";
|
||||
Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
|
||||
localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N 10 20 30 0
|
||||
Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
|
||||
localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N N 10 20 30 0
|
||||
show grants for mysqltest_1@localhost;
|
||||
Grants for mysqltest_1@localhost
|
||||
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 20 MAX_CONNECTIONS_PER_HOUR 30
|
||||
|
@ -85,7 +85,7 @@ revoke LOCK TABLES, ALTER on mysqltest.* from mysqltest_1@localhost;
|
|||
show grants for mysqltest_1@localhost;
|
||||
Grants for mysqltest_1@localhost
|
||||
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost'
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, CREATE TEMPORARY TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE ON `mysqltest`.* TO 'mysqltest_1'@'localhost' WITH GRANT OPTION
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, CREATE TEMPORARY TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, EVENT ON `mysqltest`.* TO 'mysqltest_1'@'localhost' WITH GRANT OPTION
|
||||
revoke all privileges on mysqltest.* from mysqltest_1@localhost;
|
||||
delete from mysql.user where user='mysqltest_1';
|
||||
flush privileges;
|
||||
|
@ -448,6 +448,7 @@ Create view Tables To create new views
|
|||
Create user Server Admin To create new users
|
||||
Delete Tables To delete existing rows
|
||||
Drop Databases,Tables To drop databases, tables, and views
|
||||
Event Server Admin Creation, alteration, deletion and execution of events.
|
||||
Execute Functions,Procedures To execute stored routines
|
||||
File File access on server To read and write files on the server
|
||||
Grant option Databases,Tables,Functions,Procedures To give to other users those privileges you possess
|
||||
|
@ -593,7 +594,7 @@ delete from tables_priv where host = '' and user = 'mysqltest_1';
|
|||
flush privileges;
|
||||
set @user123="non-existent";
|
||||
select * from mysql.db where user=@user123;
|
||||
Host Db User Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Grant_priv References_priv Index_priv Alter_priv Create_tmp_table_priv Lock_tables_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Execute_priv
|
||||
Host Db User Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Grant_priv References_priv Index_priv Alter_priv Create_tmp_table_priv Lock_tables_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Execute_priv Event_priv
|
||||
set names koi8r;
|
||||
create database ÂÄ;
|
||||
grant select on ÂÄ.* to root@localhost;
|
||||
|
|
|
@ -57,6 +57,7 @@ VIEWS
|
|||
USER_PRIVILEGES
|
||||
columns_priv
|
||||
db
|
||||
event
|
||||
func
|
||||
help_category
|
||||
help_keyword
|
||||
|
@ -397,6 +398,7 @@ GRANTEE TABLE_CATALOG TABLE_SCHEMA PRIVILEGE_TYPE IS_GRANTABLE
|
|||
'mysqltest_1'@'localhost' NULL test SHOW VIEW YES
|
||||
'mysqltest_1'@'localhost' NULL test CREATE ROUTINE YES
|
||||
'mysqltest_1'@'localhost' NULL test ALTER ROUTINE YES
|
||||
'mysqltest_1'@'localhost' NULL test EVENT YES
|
||||
select * from information_schema.TABLE_PRIVILEGES where grantee like '%mysqltest_1%';
|
||||
GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PRIVILEGE_TYPE IS_GRANTABLE
|
||||
'mysqltest_1'@'localhost' NULL test t1 SELECT NO
|
||||
|
@ -757,6 +759,10 @@ TABLES CREATE_TIME datetime
|
|||
TABLES UPDATE_TIME datetime
|
||||
TABLES CHECK_TIME datetime
|
||||
TRIGGERS CREATED datetime
|
||||
event execute_at datetime
|
||||
event last_executed datetime
|
||||
event starts datetime
|
||||
event ends datetime
|
||||
SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES A
|
||||
WHERE NOT EXISTS
|
||||
(SELECT * FROM INFORMATION_SCHEMA.COLUMNS B
|
||||
|
|
|
@ -6,8 +6,8 @@ Grants for mysqltest_1@localhost
|
|||
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost'
|
||||
GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_1'@'localhost'
|
||||
select * from db where user = 'mysqltest_1';
|
||||
Host Db User Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Grant_priv References_priv Index_priv Alter_priv Create_tmp_table_priv Lock_tables_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Execute_priv
|
||||
localhost mysqltest mysqltest_1 Y Y Y Y Y Y N Y Y Y Y Y Y Y Y Y Y
|
||||
Host Db User Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Grant_priv References_priv Index_priv Alter_priv Create_tmp_table_priv Lock_tables_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Execute_priv Event_priv
|
||||
localhost mysqltest mysqltest_1 Y Y Y Y Y Y N Y Y Y Y Y Y Y Y Y Y Y
|
||||
update db set db = 'MYSQLtest' where db = 'mysqltest' and user = 'mysqltest_1' and host = 'localhost';
|
||||
flush privileges;
|
||||
show grants for mysqltest_1@localhost;
|
||||
|
@ -15,8 +15,8 @@ Grants for mysqltest_1@localhost
|
|||
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost'
|
||||
GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_1'@'localhost'
|
||||
select * from db where user = 'mysqltest_1';
|
||||
Host Db User Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Grant_priv References_priv Index_priv Alter_priv Create_tmp_table_priv Lock_tables_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Execute_priv
|
||||
localhost MYSQLtest mysqltest_1 Y Y Y Y Y Y N Y Y Y Y Y Y Y Y Y Y
|
||||
Host Db User Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Grant_priv References_priv Index_priv Alter_priv Create_tmp_table_priv Lock_tables_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Execute_priv Event_priv
|
||||
localhost MYSQLtest mysqltest_1 Y Y Y Y Y Y N Y Y Y Y Y Y Y Y Y Y Y
|
||||
delete from db where db = 'MYSQLtest' and user = 'mysqltest_1' and host = 'localhost';
|
||||
flush privileges;
|
||||
drop user mysqltest_1@localhost;
|
||||
|
|
|
@ -533,13 +533,13 @@ SET @aux= "SELECT COUNT(*)
|
|||
prepare my_stmt from @aux;
|
||||
execute my_stmt;
|
||||
COUNT(*)
|
||||
37
|
||||
38
|
||||
execute my_stmt;
|
||||
COUNT(*)
|
||||
37
|
||||
38
|
||||
execute my_stmt;
|
||||
COUNT(*)
|
||||
37
|
||||
38
|
||||
deallocate prepare my_stmt;
|
||||
drop procedure if exists p1|
|
||||
drop table if exists t1|
|
||||
|
|
|
@ -3,6 +3,7 @@ show tables;
|
|||
Tables_in_db
|
||||
columns_priv
|
||||
db
|
||||
event
|
||||
func
|
||||
help_category
|
||||
help_keyword
|
||||
|
@ -42,6 +43,7 @@ db CREATE TABLE `db` (
|
|||
`Create_routine_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
|
||||
`Alter_routine_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
|
||||
`Execute_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
|
||||
`Event_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
|
||||
PRIMARY KEY (`Host`,`Db`,`User`),
|
||||
KEY `User` (`User`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Database privileges'
|
||||
|
@ -101,6 +103,7 @@ user CREATE TABLE `user` (
|
|||
`Create_routine_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
|
||||
`Alter_routine_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
|
||||
`Create_user_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
|
||||
`Event_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
|
||||
`ssl_type` enum('','ANY','X509','SPECIFIED') character set utf8 NOT NULL default '',
|
||||
`ssl_cipher` blob NOT NULL,
|
||||
`x509_issuer` blob NOT NULL,
|
||||
|
|
17
mysql-test/t/events.test
Normal file
17
mysql-test/t/events.test
Normal file
|
@ -0,0 +1,17 @@
|
|||
use test;
|
||||
drop event if exists event1;
|
||||
create event event1 on schedule every 15 minute starts now() ends date_add(now(), interval 5 hour) DO begin end;
|
||||
alter event event1 rename to event2;
|
||||
alter event event2 disabled;
|
||||
|
||||
drop event event2;
|
||||
create event event2 on schedule every 2 second starts now() ends date_add(now(), interval 5 hour) comment "some" DO begin end;
|
||||
drop event event2;
|
||||
|
||||
create table t_event3 (a int, b float);
|
||||
drop event if exists event3;
|
||||
create event event3 on schedule every 50 + 10 minute starts date_add("20010101", interval 5 minute) ends date_add("20151010", interval 5 day) comment "portokala_comment" DO insert into t_event3 values (unix_timestamp(), rand());
|
||||
set max_allowed_packet=128000000;
|
||||
select count(*) from t_event3;
|
||||
drop event event3;
|
||||
drop table t_event3;
|
|
@ -278,3 +278,28 @@ void freeze_size(DYNAMIC_ARRAY *array)
|
|||
array->max_element=elements;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Get the index of a dynamic element
|
||||
|
||||
SYNOPSIS
|
||||
get_index_dynamic()
|
||||
array Array
|
||||
element Whose element index
|
||||
|
||||
*/
|
||||
|
||||
int get_index_dynamic(DYNAMIC_ARRAY *array, gptr element)
|
||||
{
|
||||
uint ret;
|
||||
if (array->buffer > element)
|
||||
return -1;
|
||||
|
||||
ret= (element - array->buffer) / array->size_of_element;
|
||||
if (ret > array->elements)
|
||||
return -1;
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
Implemention of queues from "Algoritms in C" by Robert Sedgewick.
|
||||
An optimisation of _downheap suggested in Exercise 7.51 in "Data
|
||||
Structures & Algorithms in C++" by Mark Allen Weiss, Second Edition
|
||||
was implemented by Mikael Ronström 2005. Also the O(N) algorithm
|
||||
was implemented by Mikael Ronstrom 2005. Also the O(N) algorithm
|
||||
of queue_fix was implemented.
|
||||
*/
|
||||
|
||||
|
@ -67,6 +67,46 @@ int init_queue(QUEUE *queue, uint max_elements, uint offset_to_key,
|
|||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Init queue, uses init_queue internally for init work but also accepts
|
||||
auto_extent as parameter
|
||||
|
||||
SYNOPSIS
|
||||
init_queue_ex()
|
||||
queue Queue to initialise
|
||||
max_elements Max elements that will be put in queue
|
||||
offset_to_key Offset to key in element stored in queue
|
||||
Used when sending pointers to compare function
|
||||
max_at_top Set to 1 if you want biggest element on top.
|
||||
compare Compare function for elements, takes 3 arguments.
|
||||
first_cmp_arg First argument to compare function
|
||||
auto_extent When the queue is full and there is insert operation
|
||||
extend the queue.
|
||||
|
||||
NOTES
|
||||
Will allocate max_element pointers for queue array
|
||||
|
||||
RETURN
|
||||
0 ok
|
||||
1 Could not allocate memory
|
||||
*/
|
||||
|
||||
int init_queue_ex(QUEUE *queue, uint max_elements, uint offset_to_key,
|
||||
pbool max_at_top, int (*compare) (void *, byte *, byte *),
|
||||
void *first_cmp_arg, uint auto_extent)
|
||||
{
|
||||
int ret;
|
||||
DBUG_ENTER("init_queue_ex");
|
||||
|
||||
if ((ret= init_queue(queue, max_elements, offset_to_key, max_at_top, compare,
|
||||
first_cmp_arg)))
|
||||
DBUG_RETURN(ret);
|
||||
|
||||
queue->auto_extent= auto_extent;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
Reinitialize queue for other usage
|
||||
|
||||
|
@ -192,6 +232,31 @@ void queue_insert(register QUEUE *queue, byte *element)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Does safe insert. If no more space left on the queue resize it.
|
||||
Return codes:
|
||||
0 - OK
|
||||
1 - Cannot allocate more memory
|
||||
2 - auto_extend is 0, the operation would
|
||||
|
||||
*/
|
||||
|
||||
int queue_insert_safe(register QUEUE *queue, byte *element)
|
||||
{
|
||||
|
||||
if (queue->elements == queue->max_elements)
|
||||
{
|
||||
if (!queue->auto_extent)
|
||||
return 2;
|
||||
else if (resize_queue(queue, queue->max_elements + queue->auto_extent))
|
||||
return 1;
|
||||
}
|
||||
|
||||
queue_insert(queue, element);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Remove item from queue */
|
||||
/* Returns pointer to removed element */
|
||||
|
||||
|
|
|
@ -526,3 +526,42 @@ 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 char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
name char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
body longblob NOT NULL,
|
||||
definer char(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
execute_at DATETIME default NULL,
|
||||
interval_value int(11) default NULL,
|
||||
interval_field 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,
|
||||
modified TIMESTAMP NOT NULL,
|
||||
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') character set utf8 DEFAULT 'N' NOT NULL AFTER Create_user_priv;
|
||||
ALTER TABLE mysql.db add Event_priv enum('N','Y') character set utf8 DEFAULT 'N' NOT NULL;
|
||||
|
||||
|
|
|
@ -63,7 +63,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 \
|
||||
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 \
|
||||
|
@ -97,6 +97,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.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_executor.cc event.cc event_timed.cc \
|
||||
sql_plugin.cc sql_binlog.cc \
|
||||
handlerton.cc
|
||||
EXTRA_mysqld_SOURCES = ha_innodb.cc ha_berkeley.cc ha_archive.cc \
|
||||
|
|
889
sql/event.cc
Normal file
889
sql/event.cc
Normal file
|
@ -0,0 +1,889 @@
|
|||
/* Copyright (C) 2004-2005 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 "event_priv.h"
|
||||
#include "event.h"
|
||||
#include "sp.h"
|
||||
|
||||
/*
|
||||
TODO list :
|
||||
- The default value of created/modified should not be 0000-00-00 because of
|
||||
STRICT mode restricions.
|
||||
|
||||
- CREATE EVENT should not go into binary log! Does it now? The SQL statements
|
||||
issued by the EVENT are replicated.
|
||||
I have an idea how to solve the problem at failover. So the status field
|
||||
will be ENUM('DISABLED', 'ENABLED', 'SLAVESIDE_DISABLED').
|
||||
In this case when CREATE EVENT is replicated it should go into the binary
|
||||
as SLAVESIDE_DISABLED if it is ENABLED, when it's created as DISABLEd it
|
||||
should be replicated as disabled. If an event is ALTERed as DISABLED the
|
||||
query should go untouched into the binary log, when ALTERed as enable then
|
||||
it should go as SLAVESIDE_DISABLED. This is regarding the SQL interface.
|
||||
TT routines however modify mysql.event internally and this does not go the log
|
||||
so in this case queries has to be injected into the log...somehow... or
|
||||
maybe a solution is RBR for this case, because the event may go only from
|
||||
ENABLED to DISABLED status change and this is safe for replicating. As well
|
||||
an event may be deleted which is also safe for RBR.
|
||||
|
||||
- Maybe move all allocations during parsing to evex_mem_root thus saving
|
||||
double parsing in evex_create_event!
|
||||
|
||||
- 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?
|
||||
|
||||
- Consider using conditional variable when doing shutdown instead of
|
||||
waiting till all worker threads end.
|
||||
|
||||
- Make event_timed::get_show_create_event() work
|
||||
|
||||
- Add function documentation whenever needed.
|
||||
|
||||
- Add logging to file
|
||||
|
||||
- Move comparison code to class event_timed
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
QUEUE EVEX_EQ_NAME;
|
||||
MEM_ROOT evex_mem_root;
|
||||
|
||||
|
||||
void
|
||||
evex_queue_init(EVEX_QUEUE_TYPE *queue)
|
||||
{
|
||||
if (init_queue_ex(queue, 30 /*num_el*/, 0 /*offset*/, 0 /*smallest_on_top*/,
|
||||
event_timed_compare_q, NULL, 30 /*auto_extent*/))
|
||||
sql_print_error("Insufficient memory to initialize executing queue.");
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
|
||||
{
|
||||
return cs->coll->strnncollsp(cs, (unsigned char *) s.str,s.length,
|
||||
(unsigned char *) t.str,t.length, 0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
my_time_compare(TIME *a, TIME *b)
|
||||
{
|
||||
my_ulonglong a_t= TIME_to_ulonglong_datetime(a)*100L + a->second_part;
|
||||
my_ulonglong b_t= TIME_to_ulonglong_datetime(b)*100L + b->second_part;
|
||||
|
||||
if (a_t > b_t)
|
||||
return 1;
|
||||
else if (a_t < b_t)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
event_timed_compare(event_timed *a, event_timed *b)
|
||||
{
|
||||
return my_time_compare(&a->execute_at, &b->execute_at);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Callback for the prio queue
|
||||
*/
|
||||
|
||||
int
|
||||
event_timed_compare_q(void *vptr, byte* a, byte *b)
|
||||
{
|
||||
return event_timed_compare((event_timed *)a, (event_timed *)b);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Open mysql.event table for read
|
||||
|
||||
SYNOPSIS
|
||||
evex_open_event_table_for_read()
|
||||
thd Thread context
|
||||
lock_type How to lock the table
|
||||
table The table pointer
|
||||
RETURN
|
||||
1 Cannot lock table
|
||||
2 The table is corrupted - different number of fields
|
||||
0 OK
|
||||
*/
|
||||
|
||||
int
|
||||
evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table)
|
||||
{
|
||||
TABLE_LIST tables;
|
||||
bool not_used;
|
||||
DBUG_ENTER("open_proc_table");
|
||||
|
||||
bzero((char*) &tables, sizeof(tables));
|
||||
tables.db= (char*) "mysql";
|
||||
tables.table_name= tables.alias= (char*) "event";
|
||||
tables.lock_type= lock_type;
|
||||
|
||||
if (simple_open_n_lock_tables(thd, &tables))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
if (tables.table->s->fields != EVEX_FIELD_COUNT)
|
||||
{
|
||||
my_error(ER_EVENT_COL_COUNT_DOESNT_MATCH, MYF(0), "mysql", "event");
|
||||
close_thread_tables(thd);
|
||||
DBUG_RETURN(2);
|
||||
}
|
||||
*table= tables.table;
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Find row in open mysql.event table representing event
|
||||
|
||||
SYNOPSIS
|
||||
evex_db_find_event_aux()
|
||||
thd Thread context
|
||||
dbname Name of event's database
|
||||
rname Name of the event inside the db
|
||||
table TABLE object for open mysql.event table.
|
||||
|
||||
RETURN VALUE
|
||||
0 - Routine found
|
||||
EVEX_KEY_NOT_FOUND - No routine with given name
|
||||
*/
|
||||
|
||||
int
|
||||
evex_db_find_event_aux(THD *thd, const LEX_STRING dbname,
|
||||
const LEX_STRING ev_name, TABLE *table)
|
||||
{
|
||||
byte key[MAX_KEY_LENGTH];
|
||||
DBUG_ENTER("evex_db_find_event_aux");
|
||||
DBUG_PRINT("enter", ("name: %.*s", ev_name.length, ev_name.str));
|
||||
|
||||
/*
|
||||
Create key to find row. We have to use field->store() to be able to
|
||||
handle VARCHAR and CHAR fields.
|
||||
Assumption here is that the two first fields in the table are
|
||||
'db' and 'name' and the first key is the primary key over the
|
||||
same fields.
|
||||
*/
|
||||
if (dbname.length > table->field[EVEX_FIELD_DB]->field_length ||
|
||||
ev_name.length > table->field[EVEX_FIELD_NAME]->field_length)
|
||||
DBUG_RETURN(EVEX_KEY_NOT_FOUND);
|
||||
|
||||
table->field[0]->store(dbname.str, dbname.length, &my_charset_bin);
|
||||
table->field[1]->store(ev_name.str, ev_name.length, &my_charset_bin);
|
||||
key_copy(key, table->record[0], table->key_info, table->key_info->key_length);
|
||||
|
||||
if (table->file->index_read_idx(table->record[0], 0, key,
|
||||
table->key_info->key_length,HA_READ_KEY_EXACT))
|
||||
DBUG_RETURN(EVEX_KEY_NOT_FOUND);
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Puts some data common to CREATE and ALTER EVENT into a row.
|
||||
|
||||
SYNOPSIS
|
||||
evex_fill_row()
|
||||
thd THD
|
||||
table the row to fill out
|
||||
et Event's data
|
||||
|
||||
Returns
|
||||
0 - ok
|
||||
EVEX_GENERAL_ERROR - bad data
|
||||
EVEX_GET_FIELD_FAILED - field count does not match. table corrupted?
|
||||
|
||||
DESCRIPTION
|
||||
Used both when an event is created and when it is altered.
|
||||
*/
|
||||
|
||||
static int
|
||||
evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update)
|
||||
{
|
||||
enum evex_table_field field_num;
|
||||
|
||||
DBUG_ENTER("evex_fill_row");
|
||||
|
||||
if (table->s->fields != EVEX_FIELD_COUNT)
|
||||
{
|
||||
my_error(ER_EVENT_COL_COUNT_DOESNT_MATCH, MYF(0), "mysql", "event");
|
||||
DBUG_RETURN(EVEX_GET_FIELD_FAILED);
|
||||
}
|
||||
|
||||
DBUG_PRINT("info", ("dbname.len=%d",et->dbname.length));
|
||||
DBUG_PRINT("info", ("name.len=%d",et->name.length));
|
||||
|
||||
if (table->field[field_num= EVEX_FIELD_DB]->
|
||||
store(et->dbname.str, et->dbname.length, system_charset_info))
|
||||
goto trunc_err;
|
||||
|
||||
if (table->field[field_num= EVEX_FIELD_NAME]->
|
||||
store(et->name.str, et->name.length, system_charset_info))
|
||||
goto trunc_err;
|
||||
|
||||
// both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()
|
||||
table->field[EVEX_FIELD_ON_COMPLETION]->store((longlong)et->on_completion);
|
||||
|
||||
table->field[EVEX_FIELD_STATUS]->store((longlong)et->status);
|
||||
|
||||
if (et->body.str)
|
||||
if (table->field[field_num= EVEX_FIELD_BODY]->
|
||||
store(et->body.str, et->body.length, system_charset_info))
|
||||
goto trunc_err;
|
||||
|
||||
if (et->starts.year)
|
||||
{
|
||||
table->field[EVEX_FIELD_STARTS]->set_notnull();// set NULL flag to OFF
|
||||
table->field[EVEX_FIELD_STARTS]->
|
||||
store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME);
|
||||
}
|
||||
|
||||
if (et->ends.year)
|
||||
{
|
||||
table->field[EVEX_FIELD_ENDS]->set_notnull();
|
||||
table->field[EVEX_FIELD_ENDS]->
|
||||
store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME);
|
||||
}
|
||||
|
||||
if (et->expression)
|
||||
{
|
||||
table->field[EVEX_FIELD_INTERVAL_EXPR]->set_notnull();
|
||||
table->field[EVEX_FIELD_INTERVAL_EXPR]->store((longlong)et->expression);
|
||||
|
||||
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!
|
||||
*/
|
||||
table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->interval+1);
|
||||
}
|
||||
else if (et->execute_at.year)
|
||||
{
|
||||
// fix_fields already called in init_execute_at
|
||||
table->field[EVEX_FIELD_EXECUTE_AT]->set_notnull();
|
||||
table->field[EVEX_FIELD_EXECUTE_AT]->store_time(&et->execute_at,
|
||||
MYSQL_TIMESTAMP_DATETIME);
|
||||
|
||||
table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->set_null();
|
||||
}
|
||||
else
|
||||
{
|
||||
DBUG_ASSERT(is_update);
|
||||
// it is normal to be here when the action is update
|
||||
// this is an error if the action is create. something is borked
|
||||
}
|
||||
|
||||
((Field_timestamp *)table->field[EVEX_FIELD_MODIFIED])->set_time();
|
||||
|
||||
if (et->comment.length)
|
||||
if (table->field[field_num= EVEX_FIELD_COMMENT]->
|
||||
store(et->comment.str, et->comment.length, system_charset_info))
|
||||
goto trunc_err;
|
||||
|
||||
DBUG_RETURN(0);
|
||||
trunc_err:
|
||||
my_error(ER_EVENT_DATA_TOO_LONG, MYF(0));
|
||||
DBUG_RETURN(EVEX_GENERAL_ERROR);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Creates an event in mysql.event
|
||||
|
||||
SYNOPSIS
|
||||
db_create_event()
|
||||
thd THD
|
||||
et event_timed object containing information for the event
|
||||
create_if_not - if an warning should be generated in case event exists
|
||||
rows_affected - how many rows were affected
|
||||
|
||||
Return value
|
||||
0 - OK
|
||||
EVEX_GENERAL_ERROR - Failure
|
||||
DESCRIPTION
|
||||
Creates an event. Relies on evex_fill_row which is shared with
|
||||
db_update_event. The name of the event is inside "et".
|
||||
*/
|
||||
|
||||
static int
|
||||
db_create_event(THD *thd, event_timed *et, my_bool create_if_not,
|
||||
uint *rows_affected)
|
||||
{
|
||||
int ret= 0;
|
||||
TABLE *table;
|
||||
char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
|
||||
char olddb[128];
|
||||
bool dbchanged= false;
|
||||
DBUG_ENTER("db_create_event");
|
||||
DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str));
|
||||
|
||||
*rows_affected= 0;
|
||||
DBUG_PRINT("info", ("open mysql.event for update"));
|
||||
if (evex_open_event_table(thd, TL_WRITE, &table))
|
||||
{
|
||||
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
|
||||
goto err;
|
||||
}
|
||||
|
||||
DBUG_PRINT("info", ("check existance of an event with the same name"));
|
||||
if (!evex_db_find_event_aux(thd, et->dbname, et->name, table))
|
||||
{
|
||||
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
|
||||
et->name.str);
|
||||
goto ok;
|
||||
}
|
||||
|
||||
DBUG_PRINT("info", ("non-existant, go forward"));
|
||||
if ((ret= sp_use_new_db(thd, et->dbname.str,olddb, sizeof(olddb),0, &dbchanged)))
|
||||
{
|
||||
my_error(ER_BAD_DB_ERROR, MYF(0));
|
||||
goto err;
|
||||
}
|
||||
|
||||
restore_record(table, s->default_values); // Get default values for fields
|
||||
|
||||
if (system_charset_info->cset->numchars(system_charset_info, et->dbname.str,
|
||||
et->dbname.str + et->dbname.length)
|
||||
> EVEX_DB_FIELD_LEN)
|
||||
{
|
||||
my_error(ER_TOO_LONG_IDENT, MYF(0), et->dbname.str);
|
||||
goto err;
|
||||
}
|
||||
if (system_charset_info->cset->numchars(system_charset_info, et->name.str,
|
||||
et->name.str + et->name.length)
|
||||
> EVEX_DB_FIELD_LEN)
|
||||
{
|
||||
my_error(ER_TOO_LONG_IDENT, MYF(0), et->name.str);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (et->body.length > table->field[EVEX_FIELD_BODY]->field_length)
|
||||
{
|
||||
my_error(ER_TOO_LONG_BODY, MYF(0), et->name.str);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!(et->expression) && !(et->execute_at.year))
|
||||
{
|
||||
DBUG_PRINT("error", ("neither expression nor execute_at are set!"));
|
||||
my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0));
|
||||
goto err;
|
||||
}
|
||||
|
||||
strxmov(definer, et->definer_user.str, "@", et->definer_host.str, NullS);
|
||||
if ((ret=table->field[EVEX_FIELD_DEFINER]->
|
||||
store(definer, et->definer_user.length + 1 + et->definer_host.length,
|
||||
system_charset_info)))
|
||||
{
|
||||
my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
((Field_timestamp *)table->field[EVEX_FIELD_CREATED])->set_time();
|
||||
|
||||
// evex_fill_row() calls my_error() in case of error so no need to handle it here
|
||||
if ((ret= evex_fill_row(thd, table, et, false)))
|
||||
goto err;
|
||||
|
||||
if (table->file->write_row(table->record[0]))
|
||||
{
|
||||
my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (mysql_bin_log.is_open())
|
||||
{
|
||||
thd->clear_error();
|
||||
/* Such a statement can always go directly to binlog, no trans cache */
|
||||
Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
|
||||
mysql_bin_log.write(&qinfo);
|
||||
}
|
||||
|
||||
*rows_affected= 1;
|
||||
ok:
|
||||
if (dbchanged)
|
||||
(void) mysql_change_db(thd, olddb, 1);
|
||||
if (table)
|
||||
close_thread_tables(thd);
|
||||
DBUG_RETURN(EVEX_OK);
|
||||
|
||||
err:
|
||||
if (dbchanged)
|
||||
(void) mysql_change_db(thd, olddb, 1);
|
||||
if (table)
|
||||
close_thread_tables(thd);
|
||||
DBUG_RETURN(EVEX_GENERAL_ERROR);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Used to execute ALTER EVENT. Pendant to evex_update_event().
|
||||
|
||||
SYNOPSIS
|
||||
db_update_event()
|
||||
thd THD
|
||||
sp_name the name of the event to alter
|
||||
et event's data
|
||||
|
||||
NOTES
|
||||
sp_name is passed since this is the name of the event to
|
||||
alter in case of RENAME TO.
|
||||
*/
|
||||
|
||||
static int
|
||||
db_update_event(THD *thd, event_timed *et, sp_name *new_name)
|
||||
{
|
||||
TABLE *table;
|
||||
int ret= EVEX_OPEN_TABLE_FAILED;
|
||||
DBUG_ENTER("db_update_event");
|
||||
DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str));
|
||||
if (new_name)
|
||||
DBUG_PRINT("enter", ("rename to: %.*s", new_name->m_name.length,
|
||||
new_name->m_name.str));
|
||||
|
||||
if (evex_open_event_table(thd, TL_WRITE, &table))
|
||||
{
|
||||
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
|
||||
goto err;
|
||||
}
|
||||
|
||||
// first look whether we overwrite
|
||||
if (new_name)
|
||||
{
|
||||
if (!sortcmp_lex_string(et->name, new_name->m_name, system_charset_info) &&
|
||||
!sortcmp_lex_string(et->dbname, new_name->m_db, system_charset_info))
|
||||
{
|
||||
my_error(ER_EVENT_SAME_NAME, MYF(0), et->name.str);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!evex_db_find_event_aux(thd, new_name->m_db, new_name->m_name, table))
|
||||
{
|
||||
my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
/*
|
||||
...and then whether there is such an event. don't exchange the blocks
|
||||
because you will get error 120 from table handler because new_name will
|
||||
overwrite the key and SE will tell us that it cannot find the already found
|
||||
row (copied into record[1] later
|
||||
*/
|
||||
if (EVEX_KEY_NOT_FOUND == evex_db_find_event_aux(thd, et->dbname, et->name,
|
||||
table))
|
||||
{
|
||||
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
store_record(table,record[1]);
|
||||
|
||||
// Don't update create on row update.
|
||||
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
|
||||
|
||||
// evex_fill_row() calls my_error() in case of error so no need to handle it here
|
||||
if ((ret= evex_fill_row(thd, table, et, true)))
|
||||
goto err;
|
||||
|
||||
if (new_name)
|
||||
{
|
||||
table->field[EVEX_FIELD_DB]->
|
||||
store(new_name->m_db.str, new_name->m_db.length, system_charset_info);
|
||||
table->field[EVEX_FIELD_NAME]->
|
||||
store(new_name->m_name.str, new_name->m_name.length, system_charset_info);
|
||||
}
|
||||
|
||||
if ((ret= table->file->update_row(table->record[1], table->record[0])))
|
||||
{
|
||||
my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
// close mysql.event or we crash later when loading the event from disk
|
||||
close_thread_tables(thd);
|
||||
DBUG_RETURN(0);
|
||||
|
||||
err:
|
||||
if (table)
|
||||
close_thread_tables(thd);
|
||||
DBUG_RETURN(EVEX_GENERAL_ERROR);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Looks for a named event in mysql.event and in case of success returns
|
||||
an object will data loaded from the table.
|
||||
|
||||
SYNOPSIS
|
||||
db_find_event()
|
||||
thd THD
|
||||
name the name of the event to find
|
||||
ett event's data if event is found
|
||||
tbl TABLE object to use when not NULL
|
||||
|
||||
NOTES
|
||||
1) Use sp_name for look up, return in **ett if found
|
||||
2) tbl is not closed at exit
|
||||
*/
|
||||
|
||||
static int
|
||||
db_find_event(THD *thd, sp_name *name, event_timed **ett, TABLE *tbl)
|
||||
{
|
||||
TABLE *table;
|
||||
int ret;
|
||||
const char *definer;
|
||||
char *ptr;
|
||||
event_timed *et;
|
||||
DBUG_ENTER("db_find_event");
|
||||
DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
|
||||
|
||||
if (tbl)
|
||||
table= tbl;
|
||||
else if (evex_open_event_table(thd, TL_READ, &table))
|
||||
{
|
||||
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
|
||||
ret= EVEX_GENERAL_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((ret= evex_db_find_event_aux(thd, name->m_db, name->m_name, table)))
|
||||
{
|
||||
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str);
|
||||
goto done;
|
||||
}
|
||||
et= new event_timed;
|
||||
|
||||
/*
|
||||
1)The table should not be closed beforehand. ::load_from_row() only loads
|
||||
and does not compile
|
||||
|
||||
2)::load_from_row() is silent on error therefore we emit error msg here
|
||||
*/
|
||||
if ((ret= et->load_from_row(&evex_mem_root, table)))
|
||||
{
|
||||
my_error(ER_EVENT_CANNOT_LOAD_FROM_TABLE, MYF(0));
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if (ret && et)
|
||||
{
|
||||
delete et;
|
||||
et= 0;
|
||||
}
|
||||
// don't close the table if we haven't opened it ourselves
|
||||
if (!tbl && table)
|
||||
close_thread_tables(thd);
|
||||
*ett= et;
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Looks for a named event in mysql.event and then loads it from
|
||||
the table, compiles it and insert it into the cache.
|
||||
|
||||
SYNOPSIS
|
||||
evex_load_and_compile_event()
|
||||
thd THD
|
||||
spn the name of the event to alter
|
||||
use_lock whether to obtain a lock on LOCK_event_arrays or not
|
||||
|
||||
RETURN VALUE
|
||||
0 - OK
|
||||
< 0 - error (in this case underlying functions call my_error()).
|
||||
|
||||
*/
|
||||
|
||||
static int
|
||||
evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock)
|
||||
{
|
||||
int ret= 0;
|
||||
MEM_ROOT *tmp_mem_root;
|
||||
event_timed *ett;
|
||||
Open_tables_state backup;
|
||||
|
||||
DBUG_ENTER("db_load_and_compile_event");
|
||||
DBUG_PRINT("enter", ("name: %*s", spn->m_name.length, spn->m_name.str));
|
||||
|
||||
tmp_mem_root= thd->mem_root;
|
||||
thd->mem_root= &evex_mem_root;
|
||||
|
||||
thd->reset_n_backup_open_tables_state(&backup);
|
||||
// no need to use my_error() here because db_find_event() has done it
|
||||
if ((ret= db_find_event(thd, spn, &ett, NULL)))
|
||||
goto done;
|
||||
|
||||
thd->restore_backup_open_tables_state(&backup);
|
||||
/*
|
||||
allocate on evex_mem_root. if you call without evex_mem_root
|
||||
then sphead will not be cleared!
|
||||
*/
|
||||
if ((ret= ett->compile(thd, &evex_mem_root)))
|
||||
goto done;
|
||||
|
||||
ett->compute_next_execution_time();
|
||||
if (use_lock)
|
||||
VOID(pthread_mutex_lock(&LOCK_event_arrays));
|
||||
|
||||
evex_queue_insert(&EVEX_EQ_NAME, (EVEX_PTOQEL) ett);
|
||||
|
||||
/*
|
||||
There is a copy in the array which we don't need. sphead won't be
|
||||
destroyed.
|
||||
*/
|
||||
|
||||
if (use_lock)
|
||||
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
||||
|
||||
done:
|
||||
if (thd->mem_root != tmp_mem_root)
|
||||
thd->mem_root= tmp_mem_root;
|
||||
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock)
|
||||
{
|
||||
uint i;
|
||||
|
||||
DBUG_ENTER("evex_remove_from_cache");
|
||||
/*
|
||||
It is possible that 2 (or 1) pass(es) won't find the event in memory.
|
||||
The reason is that DISABLED events are not cached.
|
||||
*/
|
||||
|
||||
if (use_lock)
|
||||
VOID(pthread_mutex_lock(&LOCK_event_arrays));
|
||||
|
||||
for (i= 0; i < evex_queue_num_elements(EVEX_EQ_NAME); ++i)
|
||||
{
|
||||
event_timed *et= evex_queue_element(&EVEX_EQ_NAME, i, event_timed*);
|
||||
DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?",db->str,name->str, et->dbname.str,
|
||||
et->name.str));
|
||||
if (!sortcmp_lex_string(*name, et->name, system_charset_info) &&
|
||||
!sortcmp_lex_string(*db, et->dbname, system_charset_info))
|
||||
{
|
||||
if (!et->is_running())
|
||||
{
|
||||
et->free_sp();
|
||||
delete et;
|
||||
}
|
||||
else
|
||||
{
|
||||
et->flags|= EVENT_EXEC_NO_MORE;
|
||||
et->dropped= true;
|
||||
}
|
||||
evex_queue_delete_element(&EVEX_EQ_NAME, i);
|
||||
// ok, we have cleaned
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (use_lock)
|
||||
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
The function exported to the world for creating of events.
|
||||
|
||||
SYNOPSIS
|
||||
evex_create_event()
|
||||
thd THD
|
||||
et event's data
|
||||
create_options Options specified when in the query. We are
|
||||
interested whether there is IF NOT EXISTS
|
||||
rows_affected How many rows were affected
|
||||
|
||||
NOTES
|
||||
- in case there is an event with the same name (db) and
|
||||
IF NOT EXISTS is specified, an warning is put into the W stack.
|
||||
*/
|
||||
|
||||
int
|
||||
evex_create_event(THD *thd, event_timed *et, uint create_options,
|
||||
uint *rows_affected)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
DBUG_ENTER("evex_create_event");
|
||||
DBUG_PRINT("enter", ("name: %*s options:%d", et->name.length,
|
||||
et->name.str, create_options));
|
||||
|
||||
if ((ret = db_create_event(thd, et,
|
||||
create_options & HA_LEX_CREATE_IF_NOT_EXISTS,
|
||||
rows_affected)))
|
||||
goto done;
|
||||
|
||||
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
||||
if (evex_is_running && et->status == MYSQL_EVENT_ENABLED)
|
||||
{
|
||||
sp_name spn(et->dbname, et->name);
|
||||
ret= evex_load_and_compile_event(thd, &spn, true);
|
||||
}
|
||||
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
||||
|
||||
done:
|
||||
// No need to close the table, it will be closed in sql_parse::do_command
|
||||
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
The function exported to the world for alteration of events.
|
||||
|
||||
SYNOPSIS
|
||||
evex_update_event()
|
||||
thd THD
|
||||
et event's data
|
||||
new_name set in case of RENAME TO.
|
||||
|
||||
NOTES
|
||||
et contains data about dbname and event name.
|
||||
name is the new name of the event, if not null this means
|
||||
that RENAME TO was specified in the query.
|
||||
*/
|
||||
|
||||
int
|
||||
evex_update_event(THD *thd, event_timed *et, sp_name *new_name,
|
||||
uint *rows_affected)
|
||||
{
|
||||
int ret, i;
|
||||
bool need_second_pass= true;
|
||||
|
||||
DBUG_ENTER("evex_update_event");
|
||||
DBUG_PRINT("enter", ("name: %*s", et->name.length, et->name.str));
|
||||
|
||||
/*
|
||||
db_update_event() opens & closes the table to prevent
|
||||
crash later in the code when loading and compiling the new definition.
|
||||
Also on error conditions my_error() is called so no need to handle here
|
||||
*/
|
||||
if ((ret= db_update_event(thd, et, new_name)))
|
||||
goto done;
|
||||
|
||||
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
||||
if (!evex_is_running)
|
||||
UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_evex_running, done);
|
||||
|
||||
VOID(pthread_mutex_lock(&LOCK_event_arrays));
|
||||
evex_remove_from_cache(&et->dbname, &et->name, false);
|
||||
if (et->status == MYSQL_EVENT_ENABLED)
|
||||
{
|
||||
if (new_name)
|
||||
ret= evex_load_and_compile_event(thd, new_name, false);
|
||||
else
|
||||
{
|
||||
sp_name spn(et->dbname, et->name);
|
||||
ret= evex_load_and_compile_event(thd, &spn, false);
|
||||
}
|
||||
if (ret == EVEX_COMPILE_ERROR)
|
||||
my_error(ER_EVENT_COMPILE_ERROR, MYF(0));
|
||||
}
|
||||
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
||||
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
||||
|
||||
done:
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Drops an event
|
||||
|
||||
SYNOPSIS
|
||||
evex_drop_event()
|
||||
thd THD
|
||||
et event's name
|
||||
drop_if_exists if set and the event not existing => warning onto the stack
|
||||
|
||||
*/
|
||||
|
||||
int
|
||||
evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists,
|
||||
uint *rows_affected)
|
||||
{
|
||||
TABLE *table;
|
||||
int ret= EVEX_OPEN_TABLE_FAILED;
|
||||
DBUG_ENTER("evex_drop_event");
|
||||
|
||||
if (evex_open_event_table(thd, TL_WRITE, &table))
|
||||
{
|
||||
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!(ret= evex_db_find_event_aux(thd, et->dbname, et->name, table)))
|
||||
{
|
||||
if ((ret= table->file->delete_row(table->record[0])))
|
||||
{
|
||||
my_error(ER_EVENT_CANNOT_DELETE, MYF(0));
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else if (ret == EVEX_KEY_NOT_FOUND)
|
||||
{
|
||||
if (drop_if_exists)
|
||||
{
|
||||
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
|
||||
"Event", et->name.str);
|
||||
ret= 0;
|
||||
} else
|
||||
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str);
|
||||
goto done;
|
||||
}
|
||||
|
||||
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
||||
if (evex_is_running)
|
||||
ret= evex_remove_from_cache(&et->dbname, &et->name, true);
|
||||
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
||||
|
||||
done:
|
||||
/*
|
||||
evex_drop_event() is used by event_timed::drop therefore
|
||||
we have to close our thread tables.
|
||||
*/
|
||||
close_thread_tables(thd);
|
||||
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
240
sql/event.h
Normal file
240
sql/event.h
Normal file
|
@ -0,0 +1,240 @@
|
|||
/* Copyright (C) 2004-2005 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.h"
|
||||
#include "sp_head.h"
|
||||
|
||||
#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_COMPILE_ERROR -19
|
||||
#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)
|
||||
|
||||
|
||||
extern ulong opt_event_executor;
|
||||
|
||||
enum enum_event_on_completion
|
||||
{
|
||||
MYSQL_EVENT_ON_COMPLETION_DROP = 1,
|
||||
MYSQL_EVENT_ON_COMPLETION_PRESERVE
|
||||
};
|
||||
|
||||
enum enum_event_status
|
||||
{
|
||||
MYSQL_EVENT_ENABLED = 1,
|
||||
MYSQL_EVENT_DISABLED
|
||||
};
|
||||
|
||||
|
||||
class event_timed
|
||||
{
|
||||
event_timed(const event_timed &); /* Prevent use of these */
|
||||
void operator=(event_timed &);
|
||||
my_bool running;
|
||||
pthread_mutex_t LOCK_running;
|
||||
|
||||
bool status_changed;
|
||||
bool last_executed_changed;
|
||||
TIME last_executed;
|
||||
|
||||
public:
|
||||
LEX_STRING dbname;
|
||||
LEX_STRING name;
|
||||
LEX_STRING body;
|
||||
|
||||
LEX_STRING definer_user;
|
||||
LEX_STRING definer_host;
|
||||
LEX_STRING definer;// combination of user and host
|
||||
|
||||
LEX_STRING comment;
|
||||
TIME starts;
|
||||
TIME ends;
|
||||
TIME execute_at;
|
||||
|
||||
longlong expression;
|
||||
interval_type interval;
|
||||
|
||||
longlong created;
|
||||
longlong modified;
|
||||
enum enum_event_on_completion on_completion;
|
||||
enum enum_event_status status;
|
||||
sp_head *sphead;
|
||||
|
||||
const uchar *body_begin;
|
||||
|
||||
bool dropped;
|
||||
bool free_sphead_on_delete;
|
||||
uint flags;//all kind of purposes
|
||||
|
||||
event_timed():running(0), status_changed(false), last_executed_changed(false),
|
||||
expression(0), created(0), modified(0),
|
||||
on_completion(MYSQL_EVENT_ON_COMPLETION_DROP),
|
||||
status(MYSQL_EVENT_ENABLED), sphead(0), dropped(false),
|
||||
free_sphead_on_delete(true), flags(0)
|
||||
|
||||
{
|
||||
pthread_mutex_init(&this->LOCK_running, MY_MUTEX_INIT_FAST);
|
||||
init();
|
||||
}
|
||||
|
||||
~event_timed()
|
||||
{
|
||||
pthread_mutex_destroy(&this->LOCK_running);
|
||||
if (free_sphead_on_delete)
|
||||
free_sp();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
init();
|
||||
|
||||
int
|
||||
init_definer(THD *thd);
|
||||
|
||||
int
|
||||
init_execute_at(THD *thd, Item *expr);
|
||||
|
||||
int
|
||||
init_interval(THD *thd, Item *expr, interval_type new_interval);
|
||||
|
||||
void
|
||||
init_name(THD *thd, sp_name *spn);
|
||||
|
||||
int
|
||||
init_starts(THD *thd, Item *starts);
|
||||
|
||||
int
|
||||
init_ends(THD *thd, Item *ends);
|
||||
|
||||
void
|
||||
event_timed::init_body(THD *thd);
|
||||
|
||||
void
|
||||
init_comment(THD *thd, LEX_STRING *set_comment);
|
||||
|
||||
int
|
||||
load_from_row(MEM_ROOT *mem_root, TABLE *table);
|
||||
|
||||
bool
|
||||
compute_next_execution_time();
|
||||
|
||||
void
|
||||
mark_last_executed();
|
||||
|
||||
int
|
||||
drop(THD *thd);
|
||||
|
||||
bool
|
||||
update_fields(THD *thd);
|
||||
|
||||
char *
|
||||
get_show_create_event(THD *thd, uint *length);
|
||||
|
||||
int
|
||||
execute(THD *thd, MEM_ROOT *mem_root= NULL);
|
||||
|
||||
int
|
||||
compile(THD *thd, MEM_ROOT *mem_root= NULL);
|
||||
|
||||
my_bool
|
||||
is_running()
|
||||
{
|
||||
my_bool ret;
|
||||
|
||||
VOID(pthread_mutex_lock(&this->LOCK_running));
|
||||
ret= running;
|
||||
VOID(pthread_mutex_unlock(&this->LOCK_running));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void free_sp()
|
||||
{
|
||||
delete sphead;
|
||||
sphead= 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int
|
||||
evex_create_event(THD *thd, event_timed *et, uint create_options,
|
||||
uint *rows_affected);
|
||||
|
||||
int
|
||||
evex_update_event(THD *thd, event_timed *et, sp_name *new_name,
|
||||
uint *rows_affected);
|
||||
|
||||
int
|
||||
evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists,
|
||||
uint *rows_affected);
|
||||
|
||||
|
||||
int
|
||||
init_events();
|
||||
|
||||
void
|
||||
shutdown_events();
|
||||
|
||||
|
||||
// auxiliary
|
||||
int
|
||||
event_timed_compare(event_timed **a, event_timed **b);
|
||||
|
||||
|
||||
/*
|
||||
CREATE TABLE event (
|
||||
db char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
name char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
body longblob NOT NULL,
|
||||
definer char(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
|
||||
execute_at DATETIME default NULL,
|
||||
interval_value int(11) default NULL,
|
||||
interval_field 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,
|
||||
modified TIMESTAMP NOT NULL,
|
||||
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';
|
||||
*/
|
||||
|
||||
#endif /* _EVENT_H_ */
|
618
sql/event_executor.cc
Normal file
618
sql/event_executor.cc
Normal file
|
@ -0,0 +1,618 @@
|
|||
/* Copyright (C) 2004-2005 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 "event_priv.h"
|
||||
#include "event.h"
|
||||
#include "sp.h"
|
||||
|
||||
|
||||
/*
|
||||
Make this define DBUG_FAULTY_THR to be able to put breakpoints inside
|
||||
code used by the scheduler's thread(s). In this case user connections
|
||||
are not possible because the scheduler thread code is ran inside the
|
||||
main thread (no spawning takes place. If you want to debug client
|
||||
connection then start with --one-thread and make the define
|
||||
DBUG_FAULTY_THR !
|
||||
*/
|
||||
#define DBUG_FAULTY_THR2
|
||||
|
||||
extern ulong thread_created;
|
||||
extern const char *my_localhost;
|
||||
|
||||
pthread_mutex_t LOCK_event_arrays,
|
||||
LOCK_workers_count,
|
||||
LOCK_evex_running;
|
||||
|
||||
|
||||
bool evex_is_running= false;
|
||||
|
||||
ulonglong evex_main_thread_id= 0;
|
||||
ulong opt_event_executor;
|
||||
volatile my_bool event_executor_running_global_var;
|
||||
static my_bool evex_mutexes_initted= false;
|
||||
static uint workers_count;
|
||||
|
||||
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);
|
||||
|
||||
static int
|
||||
evex_time_diff(TIME *a, TIME *b)
|
||||
{
|
||||
return sec_since_epoch_TIME(a) - sec_since_epoch_TIME(b);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
evex_init_mutexes()
|
||||
{
|
||||
if (evex_mutexes_initted)
|
||||
return;
|
||||
|
||||
evex_mutexes_initted= true;
|
||||
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);
|
||||
|
||||
event_executor_running_global_var= opt_event_executor;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
init_events()
|
||||
{
|
||||
pthread_t th;
|
||||
|
||||
DBUG_ENTER("init_events");
|
||||
|
||||
DBUG_PRINT("info",("Starting events main thread"));
|
||||
|
||||
evex_init_mutexes();
|
||||
|
||||
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
||||
evex_is_running= false;
|
||||
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
||||
|
||||
if (event_executor_running_global_var)
|
||||
{
|
||||
#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();
|
||||
thd->security_ctx->host= (char*)my_localhost;
|
||||
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;
|
||||
my_ulonglong cnt= 0;
|
||||
|
||||
DBUG_ENTER("event_executor_main");
|
||||
DBUG_PRINT("event_executor_main", ("EVEX thread started"));
|
||||
|
||||
|
||||
// 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= 1;
|
||||
|
||||
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_queuey"));
|
||||
|
||||
/*
|
||||
eventually manifest that we are running, not to crashe because of
|
||||
usage of non-initialized memory structures.
|
||||
*/
|
||||
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
||||
VOID(pthread_mutex_lock(&LOCK_event_arrays));
|
||||
evex_queue_init(&EVEX_EQ_NAME);
|
||||
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
||||
evex_is_running= true;
|
||||
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
||||
|
||||
thd->security_ctx->user= my_strdup("event_scheduler", MYF(0));
|
||||
|
||||
if (evex_load_events_from_db(thd))
|
||||
goto err;
|
||||
|
||||
evex_main_thread_id= thd->thread_id;
|
||||
|
||||
sql_print_information("Scheduler thread started");
|
||||
while (!thd->killed)
|
||||
{
|
||||
TIME time_now;
|
||||
my_time_t now;
|
||||
event_timed *et;
|
||||
|
||||
cnt++;
|
||||
DBUG_PRINT("info", ("EVEX External Loop %d", cnt));
|
||||
|
||||
thd->proc_info = "Sleeping";
|
||||
if (!evex_queue_num_elements(EVEX_EQ_NAME) ||
|
||||
!event_executor_running_global_var)
|
||||
{
|
||||
my_sleep(1000000);// sleep 1s
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
int t2sleep;
|
||||
/*
|
||||
now let's see how much time to sleep, we know there is at least 1
|
||||
element in the queue.
|
||||
*/
|
||||
VOID(pthread_mutex_lock(&LOCK_event_arrays));
|
||||
if (!evex_queue_num_elements(EVEX_EQ_NAME))
|
||||
{
|
||||
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
||||
continue;
|
||||
}
|
||||
et= evex_queue_first_element(&EVEX_EQ_NAME, event_timed*);
|
||||
if (et->status == MYSQL_EVENT_DISABLED)
|
||||
{
|
||||
DBUG_PRINT("evex_load_events_from_db",("Now it is disabled-exec no more"));
|
||||
if (et->dropped)
|
||||
et->drop(thd);
|
||||
delete et;
|
||||
evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top
|
||||
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
||||
sql_print_information("Event found disabled, dropping.");
|
||||
continue;
|
||||
}
|
||||
|
||||
time(&now);
|
||||
my_tz_UTC->gmt_sec_to_TIME(&time_now, now);
|
||||
t2sleep= evex_time_diff(&et->execute_at, &time_now);
|
||||
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
||||
if (t2sleep > 0)
|
||||
{
|
||||
/*
|
||||
We sleep t2sleep seconds but we check every second whether this thread
|
||||
has been killed, or there is a new candidate
|
||||
*/
|
||||
while (t2sleep-- && !thd->killed &&
|
||||
evex_queue_num_elements(EVEX_EQ_NAME) &&
|
||||
(evex_queue_first_element(&EVEX_EQ_NAME, event_timed*) == et))
|
||||
my_sleep(1000000);
|
||||
}
|
||||
if (!event_executor_running_global_var)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
VOID(pthread_mutex_lock(&LOCK_event_arrays));
|
||||
|
||||
if (!evex_queue_num_elements(EVEX_EQ_NAME))
|
||||
{
|
||||
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
||||
continue;
|
||||
}
|
||||
et= evex_queue_first_element(&EVEX_EQ_NAME, event_timed*);
|
||||
|
||||
/*
|
||||
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->execute_at.year > 1969 &&
|
||||
my_time_compare(&time_now, &et->execute_at) == -1)
|
||||
{
|
||||
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (et->status == MYSQL_EVENT_ENABLED)
|
||||
{
|
||||
pthread_t th;
|
||||
|
||||
et->mark_last_executed();
|
||||
et->compute_next_execution_time();
|
||||
et->update_fields(thd);
|
||||
DBUG_PRINT("info", (" 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");
|
||||
UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_event_arrays, err);
|
||||
}
|
||||
#else
|
||||
event_executor_worker((void *) et);
|
||||
#endif
|
||||
if ((et->execute_at.year && !et->expression) ||
|
||||
TIME_to_ulonglong_datetime(&et->execute_at) == 0)
|
||||
et->flags |= EVENT_EXEC_NO_MORE;
|
||||
|
||||
if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == MYSQL_EVENT_DISABLED)
|
||||
evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top
|
||||
else
|
||||
evex_queue_first_updated(&EVEX_EQ_NAME);
|
||||
}
|
||||
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
||||
}// while
|
||||
|
||||
err:
|
||||
// First manifest that this thread does not work and then destroy
|
||||
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
||||
evex_is_running= false;
|
||||
evex_main_thread_id= 0;
|
||||
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
||||
|
||||
sql_print_information("Event scheduler stopping");
|
||||
|
||||
/*
|
||||
TODO: A better will be with a conditional variable
|
||||
*/
|
||||
/*
|
||||
Read workers_count without lock, no need for locking.
|
||||
In the worst case we have to wait 1sec more.
|
||||
*/
|
||||
while (workers_count)
|
||||
my_sleep(1000000);// 1s
|
||||
|
||||
/*
|
||||
LEX_STRINGs reside in the memory root and will be destroyed with it.
|
||||
Hence no need of delete but only freeing of SP
|
||||
*/
|
||||
// First we free all objects ...
|
||||
for (i= 0; i < evex_queue_num_elements(EVEX_EQ_NAME); ++i)
|
||||
{
|
||||
event_timed *et= evex_queue_element(&EVEX_EQ_NAME, i, event_timed*);
|
||||
et->free_sp();
|
||||
delete et;
|
||||
}
|
||||
// ... then we can thras the whole queue at once
|
||||
evex_queue_destroy(&EVEX_EQ_NAME);
|
||||
|
||||
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--;
|
||||
#ifndef DBUG_FAULTY_THR
|
||||
THD_CHECK_SENTRY(thd);
|
||||
delete thd;
|
||||
#endif
|
||||
pthread_mutex_unlock(&LOCK_thread_count);
|
||||
|
||||
|
||||
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 scheduler stopped");
|
||||
|
||||
#ifndef DBUG_FAULTY_THR
|
||||
my_thread_end();
|
||||
pthread_exit(0);
|
||||
#endif
|
||||
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 */
|
||||
event_timed *event = (event_timed *) event_void;
|
||||
MEM_ROOT worker_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(&worker_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
|
||||
|
||||
#ifndef DBUG_FAULTY_THR
|
||||
my_thread_init();
|
||||
|
||||
if (!(thd = new THD)) // note that contructor of THD uses DBUG_ !
|
||||
{
|
||||
sql_print_error("Cannot create a THD structure in a scheduler worker thread");
|
||||
goto err_no_thd;
|
||||
}
|
||||
thd->thread_stack = (char*)&thd; // remember where our stack is
|
||||
thd->mem_root= &worker_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= 1;
|
||||
|
||||
VOID(pthread_mutex_lock(&LOCK_thread_count));
|
||||
threads.append(thd);
|
||||
thread_count++;
|
||||
thread_running++;
|
||||
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
||||
#else
|
||||
thd= current_thd;
|
||||
#endif
|
||||
|
||||
// thd->security_ctx->priv_host is char[MAX_HOSTNAME]
|
||||
|
||||
strxnmov(thd->security_ctx->priv_host, sizeof(thd->security_ctx->priv_host),
|
||||
event->definer_host.str, NullS);
|
||||
|
||||
thd->security_ctx->user= thd->security_ctx->priv_user=
|
||||
my_strdup(event->definer_user.str, MYF(0));
|
||||
|
||||
thd->db= event->dbname.str;
|
||||
if (!check_access(thd, EVENT_ACL, event->dbname.str, 0, 0, 0,
|
||||
is_schema_db(event->dbname.str)))
|
||||
{
|
||||
int ret;
|
||||
DBUG_PRINT("info", (" EVEX EXECUTING event %s.%s [EXPR:%d]",
|
||||
event->dbname.str, event->name.str,(int) event->expression));
|
||||
sql_print_information(" EVEX EXECUTING event %s.%s [EXPR:%d]",
|
||||
event->dbname.str, event->name.str,(int) event->expression);
|
||||
|
||||
ret= event->execute(thd, &worker_mem_root);
|
||||
|
||||
sql_print_information(" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d",
|
||||
event->dbname.str, event->name.str,
|
||||
(int) event->expression, ret);
|
||||
DBUG_PRINT("info", (" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d",
|
||||
event->dbname.str, event->name.str,
|
||||
(int) event->expression, ret));
|
||||
}
|
||||
if ((event->flags & EVENT_EXEC_NO_MORE) || event->status==MYSQL_EVENT_DISABLED)
|
||||
{
|
||||
if (event->dropped)
|
||||
event->drop(thd);
|
||||
delete event;
|
||||
}
|
||||
|
||||
thd->db= 0;
|
||||
|
||||
err:
|
||||
VOID(pthread_mutex_lock(&LOCK_thread_count));
|
||||
#ifndef DBUG_FAULTY_THR
|
||||
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;
|
||||
#endif
|
||||
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
||||
|
||||
err_no_thd:
|
||||
|
||||
free_root(&worker_mem_root, MYF(0));
|
||||
|
||||
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;
|
||||
int ret= -1;
|
||||
uint count= 0;
|
||||
|
||||
DBUG_ENTER("evex_load_events_from_db");
|
||||
|
||||
if ((ret= evex_open_event_table(thd, TL_READ, &table)))
|
||||
{
|
||||
sql_print_error("Table mysql.event is damaged.");
|
||||
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;
|
||||
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 ((ret= et->load_from_row(&evex_mem_root, table)))
|
||||
{
|
||||
sql_print_error("Error while loading from mysql.event. "
|
||||
"Table probably corrupted");
|
||||
goto end;
|
||||
}
|
||||
if (et->status != MYSQL_EVENT_ENABLED)
|
||||
{
|
||||
DBUG_PRINT("evex_load_events_from_db",("%s is disabled",et->name.str));
|
||||
delete et;
|
||||
continue;
|
||||
}
|
||||
|
||||
DBUG_PRINT("evex_load_events_from_db",
|
||||
("Event %s loaded from row. Time to compile", et->name.str));
|
||||
|
||||
if ((ret= et->compile(thd, &evex_mem_root)))
|
||||
{
|
||||
sql_print_error("Error while compiling %s.%s. Aborting load.",
|
||||
et->dbname.str, et->name.str);
|
||||
goto end;
|
||||
}
|
||||
|
||||
// let's find when to be executed
|
||||
et->compute_next_execution_time();
|
||||
|
||||
DBUG_PRINT("evex_load_events_from_db", ("Adding to the exec list."));
|
||||
|
||||
evex_queue_insert(&EVEX_EQ_NAME, (EVEX_PTOQEL) et);
|
||||
DBUG_PRINT("evex_load_events_from_db", ("%p %*s",
|
||||
et, et->name.length,et->name.str));
|
||||
count++;
|
||||
}
|
||||
|
||||
ret= 0;
|
||||
|
||||
end:
|
||||
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
|
||||
end_read_record(&read_record_info);
|
||||
|
||||
thd->version--; // Force close to free memory
|
||||
|
||||
close_thread_tables(thd);
|
||||
sql_print_information("Scheduler loaded %d event%s", count, (count == 1)?"":"s");
|
||||
DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count));
|
||||
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
|
||||
bool sys_var_event_executor::update(THD *thd, set_var *var)
|
||||
{
|
||||
// here start the thread if not running.
|
||||
VOID(pthread_mutex_lock(&LOCK_evex_running));
|
||||
*value= var->save_result.ulong_value;
|
||||
if ((my_bool) *value && !evex_is_running)
|
||||
{
|
||||
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
||||
init_events();
|
||||
} else
|
||||
VOID(pthread_mutex_unlock(&LOCK_evex_running));
|
||||
return 0;
|
||||
}
|
||||
|
95
sql/event_priv.h
Normal file
95
sql/event_priv.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
/* Copyright (C) 2004-2005 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_
|
||||
#include "mysql_priv.h"
|
||||
|
||||
|
||||
#define EVEX_USE_QUEUE
|
||||
|
||||
#define UNLOCK_MUTEX_AND_BAIL_OUT(__mutex, __label) \
|
||||
{ VOID(pthread_mutex_unlock(&__mutex)); goto __label; }
|
||||
|
||||
enum evex_table_field
|
||||
{
|
||||
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_DB_FIELD_LEN 64
|
||||
#define EVEX_NAME_FIELD_LEN 64
|
||||
|
||||
int
|
||||
my_time_compare(TIME *a, TIME *b);
|
||||
|
||||
int
|
||||
evex_db_find_event_aux(THD *thd, const LEX_STRING dbname,
|
||||
const LEX_STRING rname, TABLE *table);
|
||||
|
||||
int
|
||||
evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
|
||||
|
||||
int
|
||||
event_timed_compare_q(void *vptr, byte* a, byte *b);
|
||||
|
||||
|
||||
#define EXEC_QUEUE_QUEUE_NAME executing_queue
|
||||
#define EXEC_QUEUE_DARR_NAME evex_executing_queue
|
||||
|
||||
|
||||
#define EVEX_QUEUE_TYPE QUEUE
|
||||
#define EVEX_PTOQEL byte *
|
||||
|
||||
#define EVEX_EQ_NAME executing_queue
|
||||
#define evex_queue_first_element(queue, __cast) ((__cast)queue_top(queue))
|
||||
#define evex_queue_element(queue, idx, __cast) ((__cast)queue_element(queue, idx))
|
||||
#define evex_queue_delete_element(queue, idx) queue_remove(queue, idx)
|
||||
#define evex_queue_destroy(queue) delete_queue(queue)
|
||||
#define evex_queue_first_updated(queue) queue_replaced(queue)
|
||||
#define evex_queue_insert(queue, element) queue_insert_safe(queue, element);
|
||||
|
||||
|
||||
|
||||
void
|
||||
evex_queue_init(EVEX_QUEUE_TYPE *queue);
|
||||
|
||||
#define evex_queue_num_elements(queue) queue.elements
|
||||
|
||||
|
||||
extern bool evex_is_running;
|
||||
extern MEM_ROOT evex_mem_root;
|
||||
extern pthread_mutex_t LOCK_event_arrays,
|
||||
LOCK_workers_count,
|
||||
LOCK_evex_running;
|
||||
extern ulonglong evex_main_thread_id;
|
||||
extern QUEUE EVEX_EQ_NAME;
|
||||
|
||||
#endif /* _EVENT_PRIV_H_ */
|
994
sql/event_timed.cc
Normal file
994
sql/event_timed.cc
Normal file
|
@ -0,0 +1,994 @@
|
|||
/* Copyright (C) 2004-2005 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 "event_priv.h"
|
||||
#include "event.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");
|
||||
|
||||
dbname.str= name.str= body.str= comment.str= 0;
|
||||
dbname.length= name.length= body.length= comment.length= 0;
|
||||
|
||||
set_zero_time(&starts, MYSQL_TIMESTAMP_DATETIME);
|
||||
set_zero_time(&ends, MYSQL_TIMESTAMP_DATETIME);
|
||||
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
|
||||
set_zero_time(&last_executed, MYSQL_TIMESTAMP_DATETIME);
|
||||
|
||||
definer_user.str= definer_host.str= 0;
|
||||
definer_user.length= definer_host.length= 0;
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Set a name of the event
|
||||
|
||||
SYNOPSIS
|
||||
event_timed::init_name()
|
||||
thd THD
|
||||
spn the name extracted in the parser
|
||||
*/
|
||||
|
||||
void
|
||||
event_timed::init_name(THD *thd, sp_name *spn)
|
||||
{
|
||||
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 (spn)
|
||||
{
|
||||
dbname.length= spn->m_db.length;
|
||||
if (spn->m_db.length == 0)
|
||||
dbname.str= NULL;
|
||||
else
|
||||
dbname.str= strmake_root(root, spn->m_db.str, spn->m_db.length);
|
||||
name.length= spn->m_name.length;
|
||||
name.str= strmake_root(root, spn->m_name.str, spn->m_name.length);
|
||||
|
||||
if (spn->m_qname.length == 0)
|
||||
spn->init_qname(thd);
|
||||
}
|
||||
else if (thd->db)
|
||||
{
|
||||
dbname.length= thd->db_length;
|
||||
dbname.str= strmake_root(root, thd->db, dbname.length);
|
||||
}
|
||||
|
||||
DBUG_PRINT("dbname", ("len=%d db=%s",dbname.length, dbname.str));
|
||||
DBUG_PRINT("name", ("len=%d name=%s",name.length, 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;
|
||||
|
||||
body.length= thd->lex->ptr - body_begin;
|
||||
// Trim nuls at the end
|
||||
while (body.length && body_begin[body.length-1] == '\0')
|
||||
body.length--;
|
||||
|
||||
body.str= strmake_root(root, (char *)body_begin, 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));
|
||||
|
||||
|
||||
execute_at= ltime;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Set time for execution for transient events.
|
||||
|
||||
SYNOPSIS
|
||||
event_timed::init_interval()
|
||||
expr how much?
|
||||
new_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 new_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);
|
||||
|
||||
expression= tmp;
|
||||
interval= new_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 *new_starts)
|
||||
{
|
||||
my_bool not_used;
|
||||
TIME ltime;
|
||||
my_time_t my_time_tmp;
|
||||
|
||||
DBUG_ENTER("event_timed::init_starts");
|
||||
|
||||
if (new_starts->fix_fields(thd, &new_starts))
|
||||
DBUG_RETURN(EVEX_PARSE_ERROR);
|
||||
|
||||
if (new_starts->val_int() == MYSQL_TIMESTAMP_ERROR)
|
||||
DBUG_RETURN(EVEX_BAD_PARAMS);
|
||||
|
||||
if ((not_used= new_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));
|
||||
|
||||
starts= ltime;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Set deactivation time.
|
||||
|
||||
SYNOPSIS
|
||||
event_timed::init_ends()
|
||||
thd THD
|
||||
new_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 *new_ends)
|
||||
{
|
||||
TIME ltime;
|
||||
my_time_t my_time_tmp;
|
||||
my_bool not_used;
|
||||
|
||||
DBUG_ENTER("event_timed::init_ends");
|
||||
|
||||
if (new_ends->fix_fields(thd, &new_ends))
|
||||
DBUG_RETURN(EVEX_PARSE_ERROR);
|
||||
|
||||
// the field was already fixed in init_ends
|
||||
if ((not_used= new_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 (starts.year && my_time_compare(&starts, <ime) != -1)
|
||||
DBUG_RETURN(EVEX_BAD_PARAMS);
|
||||
|
||||
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 *set_comment)
|
||||
{
|
||||
DBUG_ENTER("event_timed::init_comment");
|
||||
|
||||
comment.str= strmake_root(thd->mem_root, set_comment->str,
|
||||
comment.length= set_comment->length);
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Inits definer (definer_user and definer_host) during
|
||||
parsing.
|
||||
|
||||
SYNOPSIS
|
||||
event_timed::init_definer()
|
||||
*/
|
||||
|
||||
int
|
||||
event_timed::init_definer(THD *thd)
|
||||
{
|
||||
DBUG_ENTER("event_timed::init_definer");
|
||||
|
||||
definer_user.str= strdup_root(thd->mem_root, thd->security_ctx->priv_user);
|
||||
definer_user.length= strlen(thd->security_ctx->priv_user);
|
||||
|
||||
definer_host.str= strdup_root(thd->mem_root, thd->security_ctx->priv_host);
|
||||
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()
|
||||
|
||||
REMARKS
|
||||
This method is silent on errors and should behave like that. Callers
|
||||
should handle throwing of error messages. The reason is that the class
|
||||
should not know about how to deal with communication.
|
||||
*/
|
||||
|
||||
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->dbname.str= get_field(mem_root,
|
||||
table->field[EVEX_FIELD_DB])) == NULL)
|
||||
goto error;
|
||||
|
||||
et->dbname.length= strlen(et->dbname.str);
|
||||
|
||||
if ((et->name.str= get_field(mem_root,
|
||||
table->field[EVEX_FIELD_NAME])) == NULL)
|
||||
goto error;
|
||||
|
||||
et->name.length= strlen(et->name.str);
|
||||
|
||||
if ((et->body.str= get_field(mem_root,
|
||||
table->field[EVEX_FIELD_BODY])) == NULL)
|
||||
goto error;
|
||||
|
||||
et->body.length= strlen(et->body.str);
|
||||
|
||||
if ((et->definer.str= get_field(mem_root,
|
||||
table->field[EVEX_FIELD_DEFINER])) == NullS)
|
||||
goto error;
|
||||
et->definer.length= strlen(et->definer.str);
|
||||
|
||||
ptr= strchr(et->definer.str, '@');
|
||||
|
||||
if (! ptr)
|
||||
ptr= et->definer.str;
|
||||
|
||||
len= ptr - et->definer.str;
|
||||
|
||||
et->definer_user.str= strmake_root(mem_root, et->definer.str, len);
|
||||
et->definer_user.length= len;
|
||||
len= et->definer.length - len - 1; //1 is because of @
|
||||
et->definer_host.str= strmake_root(mem_root, ptr + 1, len);//1: because of @
|
||||
et->definer_host.length= len;
|
||||
|
||||
|
||||
res1= table->field[EVEX_FIELD_STARTS]->
|
||||
get_date(&et->starts, TIME_NO_ZERO_DATE);
|
||||
|
||||
res2= table->field[EVEX_FIELD_ENDS]->
|
||||
get_date(&et->ends, TIME_NO_ZERO_DATE);
|
||||
|
||||
et->expression= 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->expression && table->field[EVEX_FIELD_EXECUTE_AT]->
|
||||
get_date(&et->execute_at, TIME_NO_ZERO_DATE))
|
||||
goto error;
|
||||
|
||||
/*
|
||||
In DB the values start from 1 but enum interval_type starts
|
||||
from 0
|
||||
*/
|
||||
et->interval= (interval_type)
|
||||
((ulonglong) table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->val_int() - 1);
|
||||
|
||||
et->modified= table->field[EVEX_FIELD_CREATED]->val_int();
|
||||
et->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() last_executed gets time_now
|
||||
and not 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(&last_executed, MYSQL_TIMESTAMP_DATETIME);
|
||||
#ifdef ANDREY_0
|
||||
table->field[EVEX_FIELD_LAST_EXECUTED]->
|
||||
get_date(&et->last_executed, TIME_NO_ZERO_DATE);
|
||||
#endif
|
||||
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->name.str, ptr));
|
||||
et->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->on_completion= (ptr[0]=='D'? MYSQL_EVENT_ON_COMPLETION_DROP:
|
||||
MYSQL_EVENT_ON_COMPLETION_PRESERVE);
|
||||
|
||||
et->comment.str= get_field(mem_root, table->field[EVEX_FIELD_COMMENT]);
|
||||
if (et->comment.str != NullS)
|
||||
et->comment.length= strlen(et->comment.str);
|
||||
else
|
||||
et->comment.length= 0;
|
||||
|
||||
DBUG_RETURN(0);
|
||||
error:
|
||||
DBUG_RETURN(EVEX_GET_FIELD_FAILED);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Note: In the comments this->ends is referenced as m_ends
|
||||
|
||||
*/
|
||||
|
||||
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 (status == MYSQL_EVENT_DISABLED)
|
||||
{
|
||||
DBUG_PRINT("compute_next_execution_time",
|
||||
("Event %s is DISABLED", name.str));
|
||||
goto ret;
|
||||
}
|
||||
//if one-time no need to do computation
|
||||
if (!expression)
|
||||
{
|
||||
//let's check whether it was executed
|
||||
if (last_executed.year)
|
||||
{
|
||||
DBUG_PRINT("compute_next_execution_time",
|
||||
("One-time event %s was already executed", name.str));
|
||||
if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
|
||||
{
|
||||
DBUG_PRINT("compute_next_execution_time",
|
||||
("One-time event will be dropped."));
|
||||
dropped= true;
|
||||
}
|
||||
status= MYSQL_EVENT_DISABLED;
|
||||
status_changed= true;
|
||||
}
|
||||
goto ret;
|
||||
}
|
||||
time(&now);
|
||||
my_tz_UTC->gmt_sec_to_TIME(&time_now, now);
|
||||
/*
|
||||
sql_print_information("[%s.%s]", dbname.str, 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("starts : [%d-%d-%d %d:%d:%d ]", starts.year,
|
||||
starts.month, starts.day, starts.hour,
|
||||
starts.minute, starts.second);
|
||||
sql_print_information("ends : [%d-%d-%d %d:%d:%d ]", ends.year,
|
||||
ends.month, ends.day, ends.hour,
|
||||
ends.minute, ends.second);
|
||||
sql_print_information("m_last_ex: [%d-%d-%d %d:%d:%d ]", last_executed.year,
|
||||
last_executed.month, last_executed.day,
|
||||
last_executed.hour, last_executed.minute,
|
||||
last_executed.second);
|
||||
*/
|
||||
//if time_now is after ends don't execute anymore
|
||||
if (ends.year && (tmp= my_time_compare(&ends, &time_now)) == -1)
|
||||
{
|
||||
// time_now is after ends. don't execute anymore
|
||||
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
|
||||
if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
|
||||
dropped= true;
|
||||
status= MYSQL_EVENT_DISABLED;
|
||||
status_changed= true;
|
||||
|
||||
goto ret;
|
||||
}
|
||||
|
||||
/*
|
||||
Here time_now is before or equals ends if the latter is set.
|
||||
Let's check whether time_now is before starts.
|
||||
If so schedule for starts
|
||||
*/
|
||||
if (starts.year && (tmp= my_time_compare(&time_now, &starts)) < 1)
|
||||
{
|
||||
if (tmp == 0 && my_time_compare(&starts, &last_executed) == 0)
|
||||
{
|
||||
/*
|
||||
time_now = starts = last_executed
|
||||
do nothing or we will schedule for second time execution at starts.
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
starts is in the future
|
||||
time_now before starts. Scheduling for starts
|
||||
*/
|
||||
execute_at= starts;
|
||||
goto ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (starts.year && ends.year)
|
||||
{
|
||||
/*
|
||||
Both starts and m_ends are set and time_now is between them (incl.)
|
||||
If last_executed is set then increase with m_expression. The new TIME is
|
||||
after m_ends set execute_at to 0. And check for on_completion
|
||||
If not set then schedule for now.
|
||||
*/
|
||||
if (!last_executed.year)
|
||||
execute_at= time_now;
|
||||
else
|
||||
{
|
||||
my_time_t last, ll_ends;
|
||||
|
||||
// There was previous execution
|
||||
last= sec_since_epoch_TIME(&last_executed) + expression;
|
||||
ll_ends= sec_since_epoch_TIME(&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(&execute_at, MYSQL_TIMESTAMP_DATETIME);
|
||||
if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
|
||||
dropped= true;
|
||||
}
|
||||
else
|
||||
my_tz_UTC->gmt_sec_to_TIME(&execute_at, last);
|
||||
}
|
||||
goto ret;
|
||||
}
|
||||
else if (!starts.year && !ends.year)
|
||||
{
|
||||
// both starts and m_ends are not set, se we schedule for the next
|
||||
// based on last_executed
|
||||
if (!last_executed.year)
|
||||
//last_executed not set. Schedule the event for now
|
||||
execute_at= time_now;
|
||||
else
|
||||
//ToDo Andrey: maybe check for error here?
|
||||
my_tz_UTC->gmt_sec_to_TIME(&execute_at,
|
||||
sec_since_epoch_TIME(&last_executed) + expression);
|
||||
goto ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
//either starts or m_ends is set
|
||||
if (starts.year)
|
||||
{
|
||||
/*
|
||||
- starts is set.
|
||||
- starts is not in the future according to check made before
|
||||
Hence schedule for starts + m_expression in case last_executed
|
||||
is not set, otherwise to last_executed + m_expression
|
||||
*/
|
||||
my_time_t last;
|
||||
|
||||
//convert either last_executed or starts to seconds
|
||||
if (last_executed.year)
|
||||
last= sec_since_epoch_TIME(&last_executed) + expression;
|
||||
else
|
||||
last= sec_since_epoch_TIME(&starts);
|
||||
|
||||
//now convert back to TIME
|
||||
//ToDo Andrey: maybe check for error here?
|
||||
my_tz_UTC->gmt_sec_to_TIME(&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_expression.
|
||||
If last_executed is not set then schedule for now
|
||||
*/
|
||||
my_time_t last, ll_ends;
|
||||
|
||||
if (!last_executed.year)
|
||||
execute_at= time_now;
|
||||
else
|
||||
{
|
||||
last= sec_since_epoch_TIME(&last_executed);
|
||||
ll_ends= sec_since_epoch_TIME(&ends);
|
||||
last+= expression;
|
||||
//now convert back to TIME
|
||||
//ToDo Andrey: maybe check for error here?
|
||||
if (ll_ends < last)
|
||||
{
|
||||
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
|
||||
if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
|
||||
dropped= true;
|
||||
}
|
||||
else
|
||||
my_tz_UTC->gmt_sec_to_TIME(&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);
|
||||
|
||||
last_executed= time_now; // was execute_at
|
||||
#ifdef ANDREY_0
|
||||
last_executed= execute_at;
|
||||
#endif
|
||||
last_executed_changed= true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Returns :
|
||||
0 - OK
|
||||
-1 - Cannot open mysql.event
|
||||
-2 - Cannot find the event in mysql.event (already deleted?)
|
||||
|
||||
others - return code from SE in case deletion of the event row
|
||||
failed.
|
||||
*/
|
||||
|
||||
int
|
||||
event_timed::drop(THD *thd)
|
||||
{
|
||||
TABLE *table;
|
||||
int ret= 0;
|
||||
DBUG_ENTER("event_timed::drop");
|
||||
|
||||
if (evex_open_event_table(thd, TL_WRITE, &table))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
if (evex_db_find_event_aux(thd, dbname, name, table))
|
||||
DBUG_RETURN(-2);
|
||||
|
||||
if ((ret= table->file->delete_row(table->record[0])))
|
||||
DBUG_RETURN(ret);
|
||||
|
||||
close_thread_tables(thd);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
event_timed::update_fields(THD *thd)
|
||||
{
|
||||
TABLE *table;
|
||||
Open_tables_state backup;
|
||||
int ret= 0;
|
||||
bool opened;
|
||||
|
||||
DBUG_ENTER("event_timed::update_time_fields");
|
||||
|
||||
DBUG_PRINT("enter", ("name: %*s", name.length, name.str));
|
||||
|
||||
//no need to update if nothing has changed
|
||||
if (!(status_changed || last_executed_changed))
|
||||
goto done;
|
||||
|
||||
thd->reset_n_backup_open_tables_state(&backup);
|
||||
|
||||
if (evex_open_event_table(thd, TL_WRITE, &table))
|
||||
{
|
||||
ret= SP_OPEN_TABLE_FAILED;
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
||||
if ((ret= evex_db_find_event_aux(thd, dbname, 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 (last_executed_changed)
|
||||
{
|
||||
table->field[EVEX_FIELD_LAST_EXECUTED]->set_notnull();
|
||||
table->field[EVEX_FIELD_LAST_EXECUTED]->store_time(&last_executed,
|
||||
MYSQL_TIMESTAMP_DATETIME);
|
||||
last_executed_changed= false;
|
||||
}
|
||||
if (status_changed)
|
||||
{
|
||||
table->field[EVEX_FIELD_STATUS]->set_notnull();
|
||||
table->field[EVEX_FIELD_STATUS]->store((longlong)status);
|
||||
status_changed= false;
|
||||
}
|
||||
|
||||
if ((table->file->update_row(table->record[1],table->record[0])))
|
||||
ret= EVEX_WRITE_ROW_FAILED;
|
||||
|
||||
done:
|
||||
close_thread_tables(thd);
|
||||
thd->restore_backup_open_tables_state(&backup);
|
||||
|
||||
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 `") + dbname.length + strlen(".") + name.length +
|
||||
strlen("` ON SCHEDULE EVERY 5 MINUTE DO ") + body.length + strlen(";");
|
||||
|
||||
ret= dst= (char*) alloc_root(thd->mem_root, len + 1);
|
||||
memcpy(dst, "CREATE EVENT `", tmp_len= strlen("CREATE EVENT `"));
|
||||
dst+= tmp_len;
|
||||
memcpy(dst, dbname.str, tmp_len=dbname.length);
|
||||
dst+= tmp_len;
|
||||
memcpy(dst, ".", tmp_len= strlen("."));
|
||||
dst+= tmp_len;
|
||||
memcpy(dst, name.str, tmp_len= name.length);
|
||||
dst+= tmp_len;
|
||||
memcpy(dst, "` ON SCHEDULE EVERY 5 MINUTE DO ",
|
||||
tmp_len= strlen("` ON SCHEDULE EVERY 5 MINUTE DO "));
|
||||
dst+= tmp_len;
|
||||
|
||||
memcpy(dst, body.str, tmp_len= 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(&this->LOCK_running));
|
||||
if (running)
|
||||
{
|
||||
VOID(pthread_mutex_unlock(&this->LOCK_running));
|
||||
DBUG_RETURN(-100);
|
||||
}
|
||||
running= true;
|
||||
VOID(pthread_mutex_unlock(&this->LOCK_running));
|
||||
|
||||
// TODO Andrey : make this as member variable and delete in destructor
|
||||
empty_item_list.empty();
|
||||
|
||||
if (!sphead && (ret= compile(thd, mem_root)))
|
||||
goto done;
|
||||
|
||||
ret= sphead->execute_procedure(thd, &empty_item_list);
|
||||
|
||||
VOID(pthread_mutex_lock(&this->LOCK_running));
|
||||
running= false;
|
||||
VOID(pthread_mutex_unlock(&this->LOCK_running));
|
||||
|
||||
done:
|
||||
// Don't cache sphead if allocated on another mem_root
|
||||
if (mem_root && sphead)
|
||||
{
|
||||
delete sphead;
|
||||
sphead= 0;
|
||||
}
|
||||
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Returns
|
||||
0 - Success
|
||||
EVEX_COMPILE_ERROR - Error during compilation
|
||||
|
||||
*/
|
||||
|
||||
|
||||
int
|
||||
event_timed::compile(THD *thd, MEM_ROOT *mem_root)
|
||||
{
|
||||
int ret= 0;
|
||||
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;
|
||||
CHARSET_INFO *old_character_set_client, *old_collation_connection,
|
||||
*old_character_set_results;
|
||||
|
||||
old_character_set_client= thd->variables.character_set_client;
|
||||
old_character_set_results= thd->variables.character_set_results;
|
||||
old_collation_connection= thd->variables.collation_connection;
|
||||
|
||||
thd->variables.character_set_client=
|
||||
thd->variables.character_set_results=
|
||||
thd->variables.collation_connection=
|
||||
get_charset_by_csname("utf8", MY_CS_PRIMARY, MYF(MY_WME));
|
||||
|
||||
thd->update_charset();
|
||||
|
||||
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= dbname.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;
|
||||
|
||||
ret= EVEX_COMPILE_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
sphead= lex.et->sphead;
|
||||
sphead->m_db= dbname;
|
||||
//copy also chistics since they will vanish otherwise we get 0x0 pointer
|
||||
// Todo : Handle sql_mode !!
|
||||
sphead->set_definer(definer.str, definer.length);
|
||||
sphead->set_info(0, 0, &lex.sp_chistics, 0/*sql_mode*/);
|
||||
sphead->optimize();
|
||||
ret= 0;
|
||||
done:
|
||||
lex.et->free_sphead_on_delete= false;
|
||||
delete lex.et;
|
||||
lex_end(&lex);
|
||||
thd->lex= old_lex;
|
||||
thd->query= old_query;
|
||||
thd->query_length= old_query_len;
|
||||
thd->db= old_db;
|
||||
|
||||
thd->variables.character_set_client= old_character_set_client;
|
||||
thd->variables.character_set_results= old_character_set_results;
|
||||
thd->variables.collation_connection= old_collation_connection;
|
||||
thd->update_charset();
|
||||
|
||||
/*
|
||||
Change the memory root for the execution time.
|
||||
*/
|
||||
if (mem_root)
|
||||
thd->mem_root= tmp_mem_root;
|
||||
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
10
sql/lex.h
10
sql/lex.h
|
@ -74,6 +74,7 @@ static SYMBOL symbols[] = {
|
|||
{ "ASC", SYM(ASC)},
|
||||
{ "ASCII", SYM(ASCII_SYM)},
|
||||
{ "ASENSITIVE", SYM(ASENSITIVE_SYM)},
|
||||
{ "AT", SYM(AT_SYM)},
|
||||
{ "AUTHORS", SYM(AUTHORS_SYM)},
|
||||
{ "AUTO_INCREMENT", SYM(AUTO_INC)},
|
||||
{ "AVG", SYM(AVG_SYM)},
|
||||
|
@ -121,6 +122,7 @@ static SYMBOL symbols[] = {
|
|||
{ "COMMIT", SYM(COMMIT_SYM)},
|
||||
{ "COMMITTED", SYM(COMMITTED_SYM)},
|
||||
{ "COMPACT", SYM(COMPACT_SYM)},
|
||||
{ "COMPLETION", SYM(COMPLETION_SYM)},
|
||||
{ "COMPRESSED", SYM(COMPRESSED_SYM)},
|
||||
{ "CONCURRENT", SYM(CONCURRENT)},
|
||||
{ "CONDITION", SYM(CONDITION_SYM)},
|
||||
|
@ -163,6 +165,7 @@ static SYMBOL symbols[] = {
|
|||
{ "DETERMINISTIC", SYM(DETERMINISTIC_SYM)},
|
||||
{ "DIRECTORY", SYM(DIRECTORY_SYM)},
|
||||
{ "DISABLE", SYM(DISABLE_SYM)},
|
||||
{ "DISABLED", SYM(DISABLED_SYM)},
|
||||
{ "DISCARD", SYM(DISCARD)},
|
||||
{ "DISTINCT", SYM(DISTINCT)},
|
||||
{ "DISTINCTROW", SYM(DISTINCT)}, /* Access likes this */
|
||||
|
@ -178,15 +181,19 @@ static SYMBOL symbols[] = {
|
|||
{ "ELSE", SYM(ELSE)},
|
||||
{ "ELSEIF", SYM(ELSEIF_SYM)},
|
||||
{ "ENABLE", SYM(ENABLE_SYM)},
|
||||
{ "ENABLED", SYM(ENABLED_SYM)},
|
||||
{ "ENCLOSED", SYM(ENCLOSED)},
|
||||
{ "END", SYM(END)},
|
||||
{ "ENDS", SYM(ENDS_SYM)},
|
||||
{ "ENGINE", SYM(ENGINE_SYM)},
|
||||
{ "ENGINES", SYM(ENGINES_SYM)},
|
||||
{ "ENUM", SYM(ENUM)},
|
||||
{ "ERRORS", SYM(ERRORS)},
|
||||
{ "ESCAPE", SYM(ESCAPE_SYM)},
|
||||
{ "ESCAPED", SYM(ESCAPED)},
|
||||
{ "EVENT", SYM(EVENT_SYM)},
|
||||
{ "EVENTS", SYM(EVENTS_SYM)},
|
||||
{ "EVERY", SYM(EVERY_SYM)},
|
||||
{ "EXECUTE", SYM(EXECUTE_SYM)},
|
||||
{ "EXISTS", SYM(EXISTS)},
|
||||
{ "EXIT", SYM(EXIT_SYM)},
|
||||
|
@ -384,6 +391,7 @@ static SYMBOL symbols[] = {
|
|||
{ "POLYGON", SYM(POLYGON)},
|
||||
{ "PRECISION", SYM(PRECISION)},
|
||||
{ "PREPARE", SYM(PREPARE_SYM)},
|
||||
{ "PRESERVE", SYM(PRESERVE_SYM)},
|
||||
{ "PREV", SYM(PREV_SYM)},
|
||||
{ "PRIMARY", SYM(PRIMARY_SYM)},
|
||||
{ "PRIVILEGES", SYM(PRIVILEGES)},
|
||||
|
@ -436,6 +444,7 @@ static SYMBOL symbols[] = {
|
|||
{ "ROW_FORMAT", SYM(ROW_FORMAT_SYM)},
|
||||
{ "RTREE", SYM(RTREE_SYM)},
|
||||
{ "SAVEPOINT", SYM(SAVEPOINT_SYM)},
|
||||
{ "SCHEDULE", SYM(SCHEDULE_SYM)},
|
||||
{ "SCHEMA", SYM(DATABASE)},
|
||||
{ "SCHEMAS", SYM(DATABASES)},
|
||||
{ "SECOND", SYM(SECOND_SYM)},
|
||||
|
@ -484,6 +493,7 @@ static SYMBOL symbols[] = {
|
|||
{ "SSL", SYM(SSL_SYM)},
|
||||
{ "START", SYM(START_SYM)},
|
||||
{ "STARTING", SYM(STARTING)},
|
||||
{ "STARTS", SYM(STARTS_SYM)},
|
||||
{ "STATUS", SYM(STATUS_SYM)},
|
||||
{ "STOP", SYM(STOP_SYM)},
|
||||
{ "STORAGE", SYM(STORAGE_SYM)},
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "stacktrace.h"
|
||||
#include "mysqld_suffix.h"
|
||||
#include "mysys_err.h"
|
||||
#include "event.h"
|
||||
|
||||
#include "ha_myisam.h"
|
||||
|
||||
|
@ -3616,6 +3617,8 @@ we force server id to 2, but this MySQL server will not act as a slave.");
|
|||
}
|
||||
}
|
||||
|
||||
init_events();
|
||||
|
||||
create_shutdown_thread();
|
||||
create_maintenance_thread();
|
||||
|
||||
|
@ -3679,6 +3682,7 @@ we force server id to 2, but this MySQL server will not act as a slave.");
|
|||
clean_up(1);
|
||||
wait_for_signal_thread_to_end();
|
||||
clean_up_mutexes();
|
||||
shutdown_events();
|
||||
my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
|
||||
|
||||
exit(0);
|
||||
|
@ -4648,7 +4652,7 @@ enum options_mysqld
|
|||
OPT_MAX_BINLOG_DUMP_EVENTS, OPT_SPORADIC_BINLOG_DUMP_FAIL,
|
||||
OPT_SAFE_USER_CREATE, OPT_SQL_MODE,
|
||||
OPT_HAVE_NAMED_PIPE,
|
||||
OPT_DO_PSTACK, OPT_REPORT_HOST,
|
||||
OPT_DO_PSTACK, OPT_EVENT_EXECUTOR, OPT_REPORT_HOST,
|
||||
OPT_REPORT_USER, OPT_REPORT_PASSWORD, OPT_REPORT_PORT,
|
||||
OPT_SHOW_SLAVE_AUTH_INFO,
|
||||
OPT_SLAVE_LOAD_TMPDIR, OPT_NO_MIX_TYPE,
|
||||
|
@ -4968,6 +4972,9 @@ 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-scheduler", OPT_EVENT_EXECUTOR, "Enable/disable the event scheduler.",
|
||||
(gptr*) &opt_event_executor, (gptr*) &opt_event_executor, 0, GET_BOOL, NO_ARG,
|
||||
0/*default*/, 0/*min-value*/, 1/*max-value*/, 0, 0, 0},
|
||||
{"exit-info", 'T', "Used for debugging; Use at your own risk!", 0, 0, 0,
|
||||
GET_LONG, OPT_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"external-locking", OPT_USE_LOCKING, "Use system (external) locking. With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running.",
|
||||
|
@ -6536,6 +6543,7 @@ SHOW_VAR status_vars[]= {
|
|||
{"Bytes_sent", (char*) offsetof(STATUS_VAR, bytes_sent), SHOW_LONG_STATUS},
|
||||
{"Com_admin_commands", (char*) offsetof(STATUS_VAR, com_other), SHOW_LONG_STATUS},
|
||||
{"Com_alter_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_DB]), SHOW_LONG_STATUS},
|
||||
{"Com_alter_event", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_EVENT]), SHOW_LONG_STATUS},
|
||||
{"Com_alter_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_TABLE]), SHOW_LONG_STATUS},
|
||||
{"Com_analyze", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ANALYZE]), SHOW_LONG_STATUS},
|
||||
{"Com_backup_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_BACKUP_TABLE]), SHOW_LONG_STATUS},
|
||||
|
@ -6546,6 +6554,7 @@ SHOW_VAR status_vars[]= {
|
|||
{"Com_checksum", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CHECKSUM]), SHOW_LONG_STATUS},
|
||||
{"Com_commit", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_COMMIT]), SHOW_LONG_STATUS},
|
||||
{"Com_create_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_DB]), SHOW_LONG_STATUS},
|
||||
{"Com_create_event", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_EVENT]), SHOW_LONG_STATUS},
|
||||
{"Com_create_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_FUNCTION]), SHOW_LONG_STATUS},
|
||||
{"Com_create_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_INDEX]), SHOW_LONG_STATUS},
|
||||
{"Com_create_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_TABLE]), SHOW_LONG_STATUS},
|
||||
|
@ -6554,6 +6563,7 @@ SHOW_VAR status_vars[]= {
|
|||
{"Com_delete_multi", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DELETE_MULTI]), SHOW_LONG_STATUS},
|
||||
{"Com_do", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DO]), SHOW_LONG_STATUS},
|
||||
{"Com_drop_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_DB]), SHOW_LONG_STATUS},
|
||||
{"Com_drop_event", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_EVENT]), SHOW_LONG_STATUS},
|
||||
{"Com_drop_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_FUNCTION]), SHOW_LONG_STATUS},
|
||||
{"Com_drop_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_INDEX]), SHOW_LONG_STATUS},
|
||||
{"Com_drop_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_TABLE]), SHOW_LONG_STATUS},
|
||||
|
@ -6595,6 +6605,7 @@ SHOW_VAR status_vars[]= {
|
|||
{"Com_show_collations", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLLATIONS]), SHOW_LONG_STATUS},
|
||||
{"Com_show_column_types", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLUMN_TYPES]), SHOW_LONG_STATUS},
|
||||
{"Com_show_create_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_DB]), SHOW_LONG_STATUS},
|
||||
{"Com_show_create_event", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_EVENT]), SHOW_LONG_STATUS},
|
||||
{"Com_show_create_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE]), SHOW_LONG_STATUS},
|
||||
{"Com_show_databases", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_DATABASES]), SHOW_LONG_STATUS},
|
||||
{"Com_show_engine_logs", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ENGINE_LOGS]), SHOW_LONG_STATUS},
|
||||
|
|
|
@ -104,6 +104,7 @@ extern ulong ndb_cache_check_time;
|
|||
|
||||
|
||||
|
||||
extern my_bool event_executor_running_global_var;
|
||||
|
||||
static HASH system_variable_hash;
|
||||
const char *bool_type_names[]= { "OFF", "ON", NullS };
|
||||
|
@ -208,6 +209,8 @@ 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_event_executor sys_event_executor("event_scheduler",
|
||||
&event_executor_running_global_var);
|
||||
sys_var_long_ptr sys_expire_logs_days("expire_logs_days",
|
||||
&expire_logs_days);
|
||||
sys_var_bool_ptr sys_flush("flush", &myisam_flush);
|
||||
|
@ -709,6 +712,7 @@ SHOW_VAR init_vars[]= {
|
|||
{sys_div_precincrement.name,(char*) &sys_div_precincrement,SHOW_SYS},
|
||||
{sys_engine_condition_pushdown.name,
|
||||
(char*) &sys_engine_condition_pushdown, SHOW_SYS},
|
||||
{sys_event_executor.name, (char*) &sys_event_executor, SHOW_SYS},
|
||||
{sys_expire_logs_days.name, (char*) &sys_expire_logs_days, SHOW_SYS},
|
||||
{sys_flush.name, (char*) &sys_flush, SHOW_SYS},
|
||||
{sys_flush_time.name, (char*) &sys_flush_time, SHOW_SYS},
|
||||
|
@ -3407,6 +3411,7 @@ bool sys_var_trust_routine_creators::update(THD *thd, set_var *var)
|
|||
return sys_var_bool_ptr::update(thd, var);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Used templates
|
||||
****************************************************************************/
|
||||
|
|
|
@ -782,6 +782,17 @@ public:
|
|||
bool update(THD *thd, set_var *var);
|
||||
};
|
||||
|
||||
|
||||
class sys_var_event_executor :public sys_var_bool_ptr
|
||||
{
|
||||
/* We need a derived class only to have a warn_deprecated() */
|
||||
public:
|
||||
sys_var_event_executor(const char *name_arg, my_bool *value_arg) :
|
||||
sys_var_bool_ptr(name_arg, value_arg) {};
|
||||
bool update(THD *thd, set_var *var);
|
||||
};
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Classes for parsing of the SET command
|
||||
****************************************************************************/
|
||||
|
|
5538
sql/share/errmsg.txt
5538
sql/share/errmsg.txt
File diff suppressed because it is too large
Load diff
|
@ -130,7 +130,6 @@ public:
|
|||
create_field m_return_field_def; /* This is used 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
|
||||
|
|
|
@ -353,6 +353,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);
|
||||
|
@ -4062,13 +4070,13 @@ static const char *command_array[]=
|
|||
"ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
|
||||
"LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT",
|
||||
"CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
|
||||
"CREATE USER"
|
||||
"CREATE USER", "EVENT"
|
||||
};
|
||||
|
||||
static uint command_lengths[]=
|
||||
{
|
||||
6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
|
||||
14, 13, 11
|
||||
14, 13, 11, 5
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#define CREATE_PROC_ACL (1L << 23)
|
||||
#define ALTER_PROC_ACL (1L << 24)
|
||||
#define CREATE_USER_ACL (1L << 25)
|
||||
#define EVENT_ACL (1L << 26)
|
||||
/*
|
||||
don't forget to update
|
||||
1. static struct show_privileges_st sys_privileges[]
|
||||
|
@ -56,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 | \
|
||||
|
@ -78,7 +79,7 @@
|
|||
REFERENCES_ACL | INDEX_ACL | ALTER_ACL | SHOW_DB_ACL | SUPER_ACL | \
|
||||
CREATE_TMP_ACL | LOCK_TABLES_ACL | REPL_SLAVE_ACL | REPL_CLIENT_ACL | \
|
||||
EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | CREATE_PROC_ACL | \
|
||||
ALTER_PROC_ACL | CREATE_USER_ACL)
|
||||
ALTER_PROC_ACL | CREATE_USER_ACL | EVENT_ACL)
|
||||
|
||||
#define DEFAULT_CREATE_PROC_ACLS \
|
||||
(ALTER_PROC_ACL | EXECUTE_ACL)
|
||||
|
@ -96,17 +97,20 @@
|
|||
#define DB_CHUNK3 (CREATE_VIEW_ACL | SHOW_VIEW_ACL | \
|
||||
CREATE_PROC_ACL | ALTER_PROC_ACL )
|
||||
#define DB_CHUNK4 (EXECUTE_ACL)
|
||||
#define DB_CHUNK5 (EVENT_ACL)
|
||||
|
||||
#define fix_rights_for_db(A) (((A) & DB_CHUNK0) | \
|
||||
(((A) << 4) & DB_CHUNK1) | \
|
||||
(((A) << 6) & DB_CHUNK2) | \
|
||||
(((A) << 9) & DB_CHUNK3) | \
|
||||
(((A) << 2) & DB_CHUNK4))
|
||||
(((A) << 2) & DB_CHUNK4))| \
|
||||
(((A) << 9) & DB_CHUNK5)
|
||||
#define get_rights_for_db(A) (((A) & DB_CHUNK0) | \
|
||||
(((A) & DB_CHUNK1) >> 4) | \
|
||||
(((A) & DB_CHUNK2) >> 6) | \
|
||||
(((A) & DB_CHUNK3) >> 9) | \
|
||||
(((A) & DB_CHUNK4) >> 2))
|
||||
(((A) & DB_CHUNK4) >> 2))| \
|
||||
(((A) & DB_CHUNK5) >> 9)
|
||||
#define TBL_CHUNK0 DB_CHUNK0
|
||||
#define TBL_CHUNK1 DB_CHUNK1
|
||||
#define TBL_CHUNK2 (CREATE_VIEW_ACL | SHOW_VIEW_ACL)
|
||||
|
|
|
@ -177,7 +177,9 @@ void lex_start(THD *thd, const uchar *buf, uint length)
|
|||
lex->spcont= NULL;
|
||||
lex->proc_list.first= 0;
|
||||
lex->query_tables_own_last= 0;
|
||||
lex->escape_used= FALSE;
|
||||
lex->escape_used= lex->et_compile_phase= FALSE;
|
||||
|
||||
lex->et= NULL;
|
||||
|
||||
if (lex->sroutines.records)
|
||||
my_hash_reset(&lex->sroutines);
|
||||
|
|
|
@ -26,6 +26,7 @@ class sp_name;
|
|||
class sp_instr;
|
||||
class sp_pcontext;
|
||||
class partition_info;
|
||||
class event_timed;
|
||||
|
||||
/*
|
||||
The following hack is needed because mysql_yacc.cc does not define
|
||||
|
@ -95,6 +96,9 @@ enum enum_sql_command {
|
|||
SQLCOM_INSTALL_PLUGIN, SQLCOM_UNINSTALL_PLUGIN,
|
||||
SQLCOM_SHOW_AUTHORS, SQLCOM_BINLOG_BASE64_EVENT,
|
||||
SQLCOM_SHOW_PLUGINS,
|
||||
SQLCOM_CREATE_EVENT, SQLCOM_ALTER_EVENT, SQLCOM_DROP_EVENT,
|
||||
SQLCOM_SHOW_CREATE_EVENT,
|
||||
|
||||
/* This should be the last !!! */
|
||||
|
||||
SQLCOM_END
|
||||
|
@ -910,6 +914,10 @@ typedef struct st_lex
|
|||
uint sroutines_list_own_elements;
|
||||
|
||||
st_sp_chistics sp_chistics;
|
||||
|
||||
event_timed *et;
|
||||
bool et_compile_phase;
|
||||
|
||||
bool only_view; /* used for SHOW CREATE TABLE/VIEW */
|
||||
/*
|
||||
field_list was created for view and should be removed before PS/SP
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "sp_head.h"
|
||||
#include "sp.h"
|
||||
#include "sp_cache.h"
|
||||
#include "event.h"
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
/*
|
||||
|
@ -642,6 +643,9 @@ void init_update_queries(void)
|
|||
uc_update_queries[SQLCOM_DROP_INDEX]=1;
|
||||
uc_update_queries[SQLCOM_CREATE_VIEW]=1;
|
||||
uc_update_queries[SQLCOM_DROP_VIEW]=1;
|
||||
uc_update_queries[SQLCOM_CREATE_EVENT]=1;
|
||||
uc_update_queries[SQLCOM_ALTER_EVENT]=1;
|
||||
uc_update_queries[SQLCOM_DROP_EVENT]=1;
|
||||
}
|
||||
|
||||
bool is_update_query(enum enum_sql_command command)
|
||||
|
@ -3682,6 +3686,61 @@ end_with_restore_list:
|
|||
res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
|
||||
break;
|
||||
}
|
||||
case SQLCOM_CREATE_EVENT:
|
||||
case SQLCOM_ALTER_EVENT:
|
||||
case SQLCOM_DROP_EVENT:
|
||||
{
|
||||
uint rows_affected= 1;
|
||||
DBUG_ASSERT(lex->et);
|
||||
do {
|
||||
if (! lex->et->dbname.str)
|
||||
{
|
||||
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
|
||||
res= true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (check_access(thd, EVENT_ACL, lex->et->dbname.str, 0, 0, 0,
|
||||
is_schema_db(lex->et->dbname.str)))
|
||||
break;
|
||||
|
||||
switch (lex->sql_command) {
|
||||
case SQLCOM_CREATE_EVENT:
|
||||
res= evex_create_event(thd, lex->et, (uint) lex->create_info.options,
|
||||
&rows_affected);
|
||||
break;
|
||||
case SQLCOM_ALTER_EVENT:
|
||||
res= evex_update_event(thd, lex->et, lex->spname, &rows_affected);
|
||||
break;
|
||||
case SQLCOM_DROP_EVENT:
|
||||
res= evex_drop_event(thd, lex->et, lex->drop_if_exists, &rows_affected);
|
||||
default:;
|
||||
}
|
||||
if (!res)
|
||||
send_ok(thd, rows_affected);
|
||||
|
||||
/* lex->unit.cleanup() is called outside, no need to call it here */
|
||||
} while (0);
|
||||
lex->et->free_sphead_on_delete= true;
|
||||
delete lex->et;
|
||||
lex->et= 0;
|
||||
break;
|
||||
}
|
||||
case SQLCOM_SHOW_CREATE_EVENT:
|
||||
{
|
||||
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)
|
||||
{
|
||||
my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
|
||||
goto error;
|
||||
}
|
||||
/* TODO : Implement it */
|
||||
send_ok(thd, 1);
|
||||
break;
|
||||
}
|
||||
case SQLCOM_CREATE_FUNCTION: // UDF function
|
||||
{
|
||||
if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0))
|
||||
|
@ -5611,6 +5670,12 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
|
|||
delete thd->lex->sphead;
|
||||
thd->lex->sphead= NULL;
|
||||
}
|
||||
if (thd->lex->et)
|
||||
{
|
||||
thd->lex->et->free_sphead_on_delete= true;
|
||||
delete thd->lex->et;
|
||||
thd->lex->et= NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -5646,6 +5711,12 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
|
|||
delete thd->lex->sphead;
|
||||
thd->lex->sphead= NULL;
|
||||
}
|
||||
if (thd->lex->et)
|
||||
{
|
||||
thd->lex->et->free_sphead_on_delete= true;
|
||||
delete thd->lex->et;
|
||||
thd->lex->et= NULL;
|
||||
}
|
||||
}
|
||||
thd->proc_info="freeing items";
|
||||
thd->end_statement();
|
||||
|
|
|
@ -249,6 +249,7 @@ static struct show_privileges_st sys_privileges[]=
|
|||
{"Create user", "Server Admin", "To create new users"},
|
||||
{"Delete", "Tables", "To delete existing rows"},
|
||||
{"Drop", "Databases,Tables", "To drop databases, tables, and views"},
|
||||
{"Event","Server Admin","Creation, alteration, deletion and execution of events."},
|
||||
{"Execute", "Functions,Procedures", "To execute stored routines"},
|
||||
{"File", "File access on server", "To read and write files on the server"},
|
||||
{"Grant option", "Databases,Tables,Functions,Procedures", "To give to other users those privileges you possess"},
|
||||
|
|
493
sql/sql_yacc.yy
493
sql/sql_yacc.yy
|
@ -38,6 +38,7 @@
|
|||
#include "sp_pcontext.h"
|
||||
#include "sp_rcontext.h"
|
||||
#include "sp.h"
|
||||
#include "event.h"
|
||||
#include <myisam.h>
|
||||
#include <myisammrg.h>
|
||||
|
||||
|
@ -108,6 +109,7 @@ inline Item *is_truth_value(Item *A, bool v1, bool v2)
|
|||
struct { int vars, conds, hndlrs, curs; } spblock;
|
||||
sp_name *spname;
|
||||
struct st_lex *lex;
|
||||
sp_head *sphead;
|
||||
}
|
||||
|
||||
%{
|
||||
|
@ -136,6 +138,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
%token ASC
|
||||
%token ASCII_SYM
|
||||
%token ASENSITIVE_SYM
|
||||
%token AT_SYM
|
||||
%token ATAN
|
||||
%token AUTHORS_SYM
|
||||
%token AUTO_INC
|
||||
|
@ -186,6 +189,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
%token COMMITTED_SYM
|
||||
%token COMMIT_SYM
|
||||
%token COMPACT_SYM
|
||||
%token COMPLETION_SYM
|
||||
%token COMPRESSED_SYM
|
||||
%token CONCAT
|
||||
%token CONCAT_WS
|
||||
|
@ -236,6 +240,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
%token DETERMINISTIC_SYM
|
||||
%token DIRECTORY_SYM
|
||||
%token DISABLE_SYM
|
||||
%token DISABLED_SYM
|
||||
%token DISCARD
|
||||
%token DISTINCT
|
||||
%token DIV_SYM
|
||||
|
@ -250,10 +255,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
%token ELSEIF_SYM
|
||||
%token ELT_FUNC
|
||||
%token ENABLE_SYM
|
||||
%token ENABLED_SYM
|
||||
%token ENCLOSED
|
||||
%token ENCODE_SYM
|
||||
%token ENCRYPT
|
||||
%token END
|
||||
%token ENDS_SYM
|
||||
%token ENGINES_SYM
|
||||
%token ENGINE_SYM
|
||||
%token ENUM
|
||||
|
@ -262,7 +269,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
%token ERRORS
|
||||
%token ESCAPED
|
||||
%token ESCAPE_SYM
|
||||
%token EVENT_SYM
|
||||
%token EVENTS_SYM
|
||||
%token EVERY_SYM
|
||||
%token EXECUTE_SYM
|
||||
%token EXISTS
|
||||
%token EXIT_SYM
|
||||
|
@ -488,6 +497,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
%token POSITION_SYM
|
||||
%token PRECISION
|
||||
%token PREPARE_SYM
|
||||
%token PRESERVE_SYM
|
||||
%token PREV_SYM
|
||||
%token PRIMARY_SYM
|
||||
%token PRIVILEGES
|
||||
|
@ -544,6 +554,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
%token ROW_SYM
|
||||
%token RTREE_SYM
|
||||
%token SAVEPOINT_SYM
|
||||
%token SCHEDULE_SYM
|
||||
%token SECOND_MICROSECOND_SYM
|
||||
%token SECOND_SYM
|
||||
%token SECURITY_SYM
|
||||
|
@ -583,6 +594,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
%token SSL_SYM
|
||||
%token STARTING
|
||||
%token START_SYM
|
||||
%token STARTS_SYM
|
||||
%token STATUS_SYM
|
||||
%token STD_SYM
|
||||
%token STDDEV_SAMP_SYM
|
||||
|
@ -676,6 +688,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
%token YEAR_SYM
|
||||
%token ZEROFILL
|
||||
|
||||
|
||||
%left JOIN_SYM INNER_SYM STRAIGHT_JOIN CROSS LEFT RIGHT
|
||||
/* A dummy token to force the priority of table_ref production in a join. */
|
||||
%left TABLE_REF_PRIORITY
|
||||
|
@ -857,6 +870,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
END_OF_INPUT
|
||||
|
||||
%type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt
|
||||
%type <NONE> sp_proc_stmt_statement sp_proc_stmt_return
|
||||
%type <NONE> sp_proc_stmt_if sp_proc_stmt_case_simple sp_proc_stmt_case
|
||||
%type <NONE> sp_labeled_control sp_proc_stmt_unlabeled sp_proc_stmt_leave
|
||||
%type <NONE> sp_proc_stmt_iterate sp_proc_stmt_label sp_proc_stmt_goto
|
||||
%type <NONE> sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close
|
||||
|
||||
%type <num> sp_decl_idents sp_opt_inout sp_handler_type sp_hcond_list
|
||||
%type <spcondtype> sp_cond sp_hcond
|
||||
%type <spblock> sp_decls sp_decl
|
||||
|
@ -1246,7 +1265,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);
|
||||
}
|
||||
'('
|
||||
|
@ -1279,9 +1298,13 @@ 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;
|
||||
|
||||
/*
|
||||
Restore flag if it was cleared above
|
||||
Be careful with counting. the block where we save the value
|
||||
is $4.
|
||||
*/
|
||||
YYTHD->client_capabilities |= $<ulong_num>4;
|
||||
sp->restore_thd_mem_root(YYTHD);
|
||||
}
|
||||
| CREATE
|
||||
|
@ -1296,7 +1319,244 @@ create:
|
|||
{
|
||||
Lex->sql_command = SQLCOM_CREATE_USER;
|
||||
}
|
||||
;
|
||||
| CREATE EVENT_SYM opt_if_not_exists sp_name
|
||||
/*
|
||||
BE CAREFUL when you add a new rule to update the block where
|
||||
YYTHD->client_capabilities is set back to original value
|
||||
*/
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
|
||||
if (lex->et)
|
||||
{
|
||||
/*
|
||||
Recursive events are not possible because recursive SPs
|
||||
are not also possible. lex->sp_head is not stacked.
|
||||
*/
|
||||
// ToDo Andrey : Change the error message
|
||||
my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT");
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
lex->create_info.options= $3;
|
||||
|
||||
if (!(lex->et= new event_timed())) // implicitly calls event_timed::init()
|
||||
YYABORT;
|
||||
|
||||
/*
|
||||
We have to turn of CLIENT_MULTI_QUERIES while parsing a
|
||||
stored procedure, otherwise yylex will chop it into pieces
|
||||
at each ';'.
|
||||
*/
|
||||
$<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES);
|
||||
|
||||
if (!lex->et_compile_phase)
|
||||
lex->et->init_name(YYTHD, $4);
|
||||
}
|
||||
ON SCHEDULE_SYM ev_schedule_time
|
||||
ev_on_completion
|
||||
ev_status
|
||||
ev_comment
|
||||
DO_SYM ev_sql_stmt
|
||||
{
|
||||
/*
|
||||
Restore flag if it was cleared above
|
||||
$1 - CREATE
|
||||
$2 - EVENT_SYM
|
||||
$3 - opt_if_not_exists
|
||||
$4 - sp_name
|
||||
$5 - the block above
|
||||
*/
|
||||
YYTHD->client_capabilities |= $<ulong_num>5;
|
||||
|
||||
/*
|
||||
sql_command is set here because some rules in ev_sql_stmt
|
||||
can overwrite it
|
||||
*/
|
||||
Lex->sql_command= SQLCOM_CREATE_EVENT;
|
||||
}
|
||||
;
|
||||
|
||||
ev_schedule_time: EVERY_SYM expr interval
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
if (!lex->et_compile_phase)
|
||||
{
|
||||
switch (lex->et->init_interval(YYTHD , $2, $3)) {
|
||||
case EVEX_PARSE_ERROR:
|
||||
yyerror(ER(ER_SYNTAX_ERROR));
|
||||
YYABORT;
|
||||
break;
|
||||
case EVEX_BAD_PARAMS:
|
||||
my_error(ER_EVENT_INTERVAL_NOT_POSITIVE, MYF(0));
|
||||
YYABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ev_starts
|
||||
ev_ends
|
||||
| AT_SYM expr
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
if (!lex->et_compile_phase)
|
||||
{
|
||||
switch (lex->et->init_execute_at(YYTHD, $2)) {
|
||||
case EVEX_PARSE_ERROR:
|
||||
yyerror(ER(ER_SYNTAX_ERROR));
|
||||
YYABORT;
|
||||
break;
|
||||
case EVEX_BAD_PARAMS:
|
||||
my_error(ER_EVENT_EXEC_TIME_IN_THE_PAST, MYF(0));
|
||||
YYABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
ev_status: /* empty */ {$<ulong_num>$= 0;}
|
||||
| ENABLED_SYM
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
if (!lex->et_compile_phase)
|
||||
lex->et->status= MYSQL_EVENT_ENABLED;
|
||||
$<ulong_num>$= 1;
|
||||
}
|
||||
| DISABLED_SYM
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
|
||||
if (!lex->et_compile_phase)
|
||||
lex->et->status= MYSQL_EVENT_DISABLED;
|
||||
$<ulong_num>$= 1;
|
||||
}
|
||||
;
|
||||
ev_starts: /* empty */
|
||||
| STARTS_SYM expr
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
if (!lex->et_compile_phase)
|
||||
lex->et->init_starts(YYTHD, $2);
|
||||
}
|
||||
;
|
||||
ev_ends: /* empty */
|
||||
| ENDS_SYM expr
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
if (!lex->et_compile_phase)
|
||||
{
|
||||
switch (lex->et->init_ends(YYTHD, $2)) {
|
||||
case EVEX_PARSE_ERROR:
|
||||
yyerror(ER(ER_SYNTAX_ERROR));
|
||||
YYABORT;
|
||||
break;
|
||||
case EVEX_BAD_PARAMS:
|
||||
my_error(ER_EVENT_ENDS_BEFORE_STARTS, MYF(0));
|
||||
YYABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
;
|
||||
ev_on_completion: /* empty */ {$<ulong_num>$= 0;}
|
||||
| ON COMPLETION_SYM PRESERVE_SYM
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
if (!lex->et_compile_phase)
|
||||
lex->et->on_completion= MYSQL_EVENT_ON_COMPLETION_PRESERVE;
|
||||
$<ulong_num>$= 1;
|
||||
}
|
||||
| ON COMPLETION_SYM NOT_SYM PRESERVE_SYM
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
if (!lex->et_compile_phase)
|
||||
lex->et->on_completion= MYSQL_EVENT_ON_COMPLETION_DROP;
|
||||
$<ulong_num>$= 1;
|
||||
}
|
||||
;
|
||||
ev_comment: /* empty */ {$<ulong_num>$= 0;}
|
||||
| COMMENT_SYM TEXT_STRING_sys
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
if (!lex->et_compile_phase)
|
||||
{
|
||||
lex->comment= $2;
|
||||
lex->et->init_comment(YYTHD, &$2);
|
||||
}
|
||||
$<ulong_num>$= 1;
|
||||
}
|
||||
;
|
||||
|
||||
ev_sql_stmt:
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
sp_head *sp;
|
||||
|
||||
$<sphead>$= lex->sphead;
|
||||
|
||||
if (!lex->sphead)
|
||||
{
|
||||
if (!(sp= new sp_head()))
|
||||
YYABORT;
|
||||
|
||||
sp->reset_thd_mem_root(YYTHD);
|
||||
sp->init(lex);
|
||||
|
||||
sp->m_type= TYPE_ENUM_PROCEDURE;
|
||||
|
||||
lex->sphead= sp;
|
||||
|
||||
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
|
||||
lex->sphead->m_chistics= &lex->sp_chistics;
|
||||
|
||||
lex->sphead->m_body_begin= lex->ptr;
|
||||
}
|
||||
|
||||
if (!lex->et_compile_phase)
|
||||
lex->et->body_begin= lex->ptr;
|
||||
}
|
||||
ev_sql_stmt_inner
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
|
||||
if (!$<sphead>1)
|
||||
{
|
||||
sp_head *sp= lex->sphead;
|
||||
// return back to the original memory root ASAP
|
||||
sp->init_strings(YYTHD, lex, NULL);
|
||||
sp->restore_thd_mem_root(YYTHD);
|
||||
|
||||
lex->sp_chistics.suid= SP_IS_SUID;//always the definer!
|
||||
|
||||
lex->et->sphead= lex->sphead;
|
||||
lex->sphead= NULL;
|
||||
}
|
||||
if (!lex->et_compile_phase)
|
||||
{
|
||||
lex->et->init_body(YYTHD);
|
||||
lex->et->init_definer(YYTHD);
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
ev_sql_stmt_inner:
|
||||
sp_proc_stmt_statement
|
||||
| sp_proc_stmt_return
|
||||
| sp_proc_stmt_if
|
||||
| sp_proc_stmt_case_simple
|
||||
| sp_proc_stmt_case
|
||||
| sp_labeled_control {}
|
||||
| sp_proc_stmt_unlabeled
|
||||
| sp_proc_stmt_leave
|
||||
| sp_proc_stmt_iterate
|
||||
| sp_proc_stmt_label
|
||||
| sp_proc_stmt_goto
|
||||
| sp_proc_stmt_open
|
||||
| sp_proc_stmt_fetch
|
||||
| sp_proc_stmt_close
|
||||
;
|
||||
|
||||
clear_privileges:
|
||||
/* Nothing */
|
||||
|
@ -1356,7 +1616,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;
|
||||
}
|
||||
|
@ -1411,8 +1671,7 @@ create_function_tail:
|
|||
YYABORT;
|
||||
}
|
||||
/* 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);
|
||||
}
|
||||
;
|
||||
|
@ -1945,6 +2204,28 @@ sp_opt_default:
|
|||
;
|
||||
|
||||
sp_proc_stmt:
|
||||
sp_proc_stmt_statement
|
||||
| sp_proc_stmt_return
|
||||
| sp_proc_stmt_if
|
||||
| sp_proc_stmt_case_simple
|
||||
| sp_proc_stmt_case
|
||||
| sp_labeled_control
|
||||
{}
|
||||
| sp_proc_stmt_unlabeled
|
||||
| sp_proc_stmt_leave
|
||||
| sp_proc_stmt_iterate
|
||||
| sp_proc_stmt_label
|
||||
| sp_proc_stmt_goto
|
||||
| sp_proc_stmt_open
|
||||
| sp_proc_stmt_fetch
|
||||
| sp_proc_stmt_close
|
||||
;
|
||||
|
||||
sp_proc_stmt_if:
|
||||
IF sp_if END IF {}
|
||||
;
|
||||
|
||||
sp_proc_stmt_statement:
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
|
||||
|
@ -1987,7 +2268,10 @@ sp_proc_stmt:
|
|||
}
|
||||
sp->restore_lex(YYTHD);
|
||||
}
|
||||
| RETURN_SYM
|
||||
;
|
||||
|
||||
sp_proc_stmt_return:
|
||||
RETURN_SYM
|
||||
{ Lex->sphead->reset_lex(YYTHD); }
|
||||
expr
|
||||
{
|
||||
|
@ -2010,13 +2294,18 @@ sp_proc_stmt:
|
|||
}
|
||||
sp->restore_lex(YYTHD);
|
||||
}
|
||||
| IF sp_if END IF {}
|
||||
| CASE_SYM WHEN_SYM
|
||||
;
|
||||
|
||||
sp_proc_stmt_case_simple:
|
||||
CASE_SYM WHEN_SYM
|
||||
{
|
||||
Lex->sphead->m_flags&= ~sp_head::IN_SIMPLE_CASE;
|
||||
}
|
||||
sp_case END CASE_SYM {}
|
||||
| CASE_SYM
|
||||
;
|
||||
|
||||
sp_proc_stmt_case:
|
||||
CASE_SYM
|
||||
{ Lex->sphead->reset_lex(YYTHD); }
|
||||
expr WHEN_SYM
|
||||
{
|
||||
|
@ -2042,9 +2331,10 @@ sp_proc_stmt:
|
|||
{
|
||||
Lex->spcont->pop_case_expr_id();
|
||||
}
|
||||
| sp_labeled_control
|
||||
{}
|
||||
| { /* Unlabeled controls get a secret label. */
|
||||
;
|
||||
|
||||
sp_proc_stmt_unlabeled:
|
||||
{ /* Unlabeled controls get a secret label. */
|
||||
LEX *lex= Lex;
|
||||
|
||||
lex->spcont->push_label((char *)"", lex->sphead->instructions());
|
||||
|
@ -2055,7 +2345,10 @@ sp_proc_stmt:
|
|||
|
||||
lex->sphead->backpatch(lex->spcont->pop_label());
|
||||
}
|
||||
| LEAVE_SYM label_ident
|
||||
;
|
||||
|
||||
sp_proc_stmt_leave:
|
||||
LEAVE_SYM label_ident
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
sp_head *sp = lex->sphead;
|
||||
|
@ -2085,7 +2378,10 @@ sp_proc_stmt:
|
|||
sp->add_instr(i);
|
||||
}
|
||||
}
|
||||
| ITERATE_SYM label_ident
|
||||
;
|
||||
|
||||
sp_proc_stmt_iterate:
|
||||
ITERATE_SYM label_ident
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
sp_head *sp= lex->sphead;
|
||||
|
@ -2113,7 +2409,10 @@ sp_proc_stmt:
|
|||
sp->add_instr(i);
|
||||
}
|
||||
}
|
||||
| LABEL_SYM IDENT
|
||||
;
|
||||
|
||||
sp_proc_stmt_label:
|
||||
LABEL_SYM IDENT
|
||||
{
|
||||
#ifdef SP_GOTO
|
||||
LEX *lex= Lex;
|
||||
|
@ -2138,7 +2437,10 @@ sp_proc_stmt:
|
|||
YYABORT;
|
||||
#endif
|
||||
}
|
||||
| GOTO_SYM IDENT
|
||||
;
|
||||
|
||||
sp_proc_stmt_goto:
|
||||
GOTO_SYM IDENT
|
||||
{
|
||||
#ifdef SP_GOTO
|
||||
LEX *lex= Lex;
|
||||
|
@ -2198,7 +2500,10 @@ sp_proc_stmt:
|
|||
YYABORT;
|
||||
#endif
|
||||
}
|
||||
| OPEN_SYM ident
|
||||
;
|
||||
|
||||
sp_proc_stmt_open:
|
||||
OPEN_SYM ident
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
sp_head *sp= lex->sphead;
|
||||
|
@ -2213,7 +2518,10 @@ sp_proc_stmt:
|
|||
i= new sp_instr_copen(sp->instructions(), lex->spcont, offset);
|
||||
sp->add_instr(i);
|
||||
}
|
||||
| FETCH_SYM sp_opt_fetch_noise ident INTO
|
||||
;
|
||||
|
||||
sp_proc_stmt_fetch:
|
||||
FETCH_SYM sp_opt_fetch_noise ident INTO
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
sp_head *sp= lex->sphead;
|
||||
|
@ -2230,7 +2538,10 @@ sp_proc_stmt:
|
|||
}
|
||||
sp_fetch_list
|
||||
{ }
|
||||
| CLOSE_SYM ident
|
||||
;
|
||||
|
||||
sp_proc_stmt_close:
|
||||
CLOSE_SYM ident
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
sp_head *sp= lex->sphead;
|
||||
|
@ -3954,7 +4265,93 @@ alter:
|
|||
}
|
||||
view_list_opt AS view_select view_check_option
|
||||
{}
|
||||
;
|
||||
| ALTER EVENT_SYM sp_name
|
||||
/*
|
||||
BE CAREFUL when you add a new rule to update the block where
|
||||
YYTHD->client_capabilities is set back to original value
|
||||
*/
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
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->spname= 0;//defensive programming
|
||||
|
||||
if (!(et= new event_timed()))// implicitly calls event_timed::init()
|
||||
YYABORT;
|
||||
lex->et = et;
|
||||
et->init_name(YYTHD, $3);
|
||||
|
||||
/*
|
||||
We have to turn of CLIENT_MULTI_QUERIES while parsing a
|
||||
stored procedure, otherwise yylex will chop it into pieces
|
||||
at each ';'.
|
||||
*/
|
||||
$<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
|
||||
YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
|
||||
|
||||
}
|
||||
ev_on_schedule
|
||||
ev_rename_to
|
||||
ev_on_completion
|
||||
ev_status
|
||||
ev_comment
|
||||
ev_opt_sql_stmt
|
||||
{
|
||||
/*
|
||||
$1 - ALTER
|
||||
$2 - EVENT_SYM
|
||||
$3 - sp_name
|
||||
$4 - the block above
|
||||
*/
|
||||
YYTHD->client_capabilities |= $<ulong_num>4;
|
||||
|
||||
/*
|
||||
sql_command is set here because some rules in ev_sql_stmt
|
||||
can overwrite it
|
||||
*/
|
||||
if (!($<ulong_num>5 || $<ulong_num>6 || $<ulong_num>7 ||
|
||||
$<ulong_num>8 || $<ulong_num>9 || $<ulong_num>10))
|
||||
{
|
||||
yyerror(ER(ER_SYNTAX_ERROR));
|
||||
YYABORT;
|
||||
}
|
||||
Lex->sql_command= SQLCOM_ALTER_EVENT;
|
||||
}
|
||||
;
|
||||
|
||||
ev_on_schedule: /* empty */ { $<ulong_num>$= 0;}
|
||||
| ON SCHEDULE_SYM ev_schedule_time
|
||||
{
|
||||
$<ulong_num>$= 1;
|
||||
}
|
||||
;
|
||||
|
||||
ev_rename_to: /* empty */ { $<ulong_num>$= 0;}
|
||||
| RENAME TO_SYM sp_name
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
lex->spname= $3; //use lex's spname to hold the new name
|
||||
//the original name is in the event_timed object
|
||||
$<ulong_num>$= 1;
|
||||
}
|
||||
;
|
||||
|
||||
ev_opt_sql_stmt: /* empty*/ { $<ulong_num>$= 0;}
|
||||
| DO_SYM ev_sql_stmt
|
||||
{
|
||||
$<ulong_num>$= 1;
|
||||
}
|
||||
;
|
||||
|
||||
ident_or_empty:
|
||||
/* empty */ { $$= 0; }
|
||||
|
@ -6653,7 +7050,29 @@ drop:
|
|||
lex->sql_command= SQLCOM_DROP_TRIGGER;
|
||||
lex->spname= $3;
|
||||
}
|
||||
;
|
||||
| DROP EVENT_SYM if_exists sp_name
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!(lex->et= new event_timed()))
|
||||
YYABORT;
|
||||
lex->et->init_name(YYTHD, $4);
|
||||
|
||||
lex->sql_command = SQLCOM_DROP_EVENT;
|
||||
lex->drop_if_exists= $3;
|
||||
}
|
||||
;
|
||||
|
||||
table_list:
|
||||
table_name
|
||||
|
@ -7336,7 +7755,12 @@ show_param:
|
|||
Lex->spname= $3;
|
||||
#endif
|
||||
}
|
||||
;
|
||||
| CREATE EVENT_SYM sp_name
|
||||
{
|
||||
Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT;
|
||||
Lex->spname= $3;
|
||||
};
|
||||
;
|
||||
|
||||
show_engine_param:
|
||||
STATUS_SYM
|
||||
|
@ -8226,6 +8650,7 @@ keyword_sp:
|
|||
| AGGREGATE_SYM {}
|
||||
| ALGORITHM_SYM {}
|
||||
| ANY_SYM {}
|
||||
| AT_SYM {}
|
||||
| AUTO_INC {}
|
||||
| AVG_ROW_LENGTH {}
|
||||
| AVG_SYM {}
|
||||
|
@ -8246,6 +8671,7 @@ keyword_sp:
|
|||
| COLUMNS {}
|
||||
| COMMITTED_SYM {}
|
||||
| COMPACT_SYM {}
|
||||
| COMPLETION_SYM {}
|
||||
| COMPRESSED_SYM {}
|
||||
| CONCURRENT {}
|
||||
| CONSISTENT_SYM {}
|
||||
|
@ -8258,22 +8684,27 @@ keyword_sp:
|
|||
| DELAY_KEY_WRITE_SYM {}
|
||||
| DES_KEY_FILE {}
|
||||
| DIRECTORY_SYM {}
|
||||
| DISABLED_SYM {}
|
||||
| DISCARD {}
|
||||
| DUMPFILE {}
|
||||
| DUPLICATE_SYM {}
|
||||
| DYNAMIC_SYM {}
|
||||
| ENDS_SYM {}
|
||||
| ENUM {}
|
||||
| ENGINE_SYM {}
|
||||
| ENGINES_SYM {}
|
||||
| ERRORS {}
|
||||
| ESCAPE_SYM {}
|
||||
| EVENT_SYM {}
|
||||
| EVENTS_SYM {}
|
||||
| EXPANSION_SYM {}
|
||||
| EVERY_SYM {}
|
||||
| EXPANSION_SYM {}
|
||||
| EXTENDED_SYM {}
|
||||
| FAST_SYM {}
|
||||
| FOUND_SYM {}
|
||||
| DISABLE_SYM {}
|
||||
| ENABLE_SYM {}
|
||||
| ENABLED_SYM {}
|
||||
| FULL {}
|
||||
| FILE_SYM {}
|
||||
| FIRST_SYM {}
|
||||
|
@ -8360,6 +8791,7 @@ keyword_sp:
|
|||
| PHASE_SYM {}
|
||||
| POINT_SYM {}
|
||||
| POLYGON {}
|
||||
| PRESERVE_SYM {}
|
||||
| PREV_SYM {}
|
||||
| PRIVILEGES {}
|
||||
| PROCESS {}
|
||||
|
@ -8389,6 +8821,7 @@ keyword_sp:
|
|||
| ROW_FORMAT_SYM {}
|
||||
| ROW_SYM {}
|
||||
| RTREE_SYM {}
|
||||
| SCHEDULE_SYM {}
|
||||
| SECOND_SYM {}
|
||||
| SERIAL_SYM {}
|
||||
| SERIALIZABLE_SYM {}
|
||||
|
@ -8402,6 +8835,7 @@ keyword_sp:
|
|||
| SQL_BUFFER_RESULT {}
|
||||
| SQL_NO_CACHE_SYM {}
|
||||
| SQL_THREAD {}
|
||||
| STARTS_SYM {}
|
||||
| STATUS_SYM {}
|
||||
| STORAGE_SYM {}
|
||||
| STRING_SYM {}
|
||||
|
@ -9128,6 +9562,7 @@ object_privilege:
|
|||
| CREATE ROUTINE_SYM { Lex->grant |= CREATE_PROC_ACL; }
|
||||
| ALTER ROUTINE_SYM { Lex->grant |= ALTER_PROC_ACL; }
|
||||
| CREATE USER { Lex->grant |= CREATE_USER_ACL; }
|
||||
| EVENT_SYM { Lex->grant |= EVENT_ACL;}
|
||||
;
|
||||
|
||||
|
||||
|
@ -9788,7 +10223,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));
|
||||
|
@ -9803,8 +10238,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"))
|
||||
|
|
|
@ -317,7 +317,8 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
|
|||
*/
|
||||
if (share->db.length == 5 &&
|
||||
!my_strcasecmp(system_charset_info, share->db.str, "mysql") &&
|
||||
!my_strcasecmp(system_charset_info, share->table_name.str, "proc"))
|
||||
(!my_strcasecmp(system_charset_info, share->table_name.str, "proc") ||
|
||||
!my_strcasecmp(system_charset_info, share->table_name.str, "event")))
|
||||
share->system_table= 1;
|
||||
error_given= 1;
|
||||
}
|
||||
|
|
|
@ -807,6 +807,18 @@ sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
Works like sec_since_epoch but expects TIME structure as parameter.
|
||||
*/
|
||||
|
||||
my_time_t
|
||||
sec_since_epoch_TIME(TIME *t)
|
||||
{
|
||||
return sec_since_epoch(t->year, t->month, t->day,
|
||||
t->hour, t->minute, t->second);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Converts local time in broken down TIME representation to my_time_t
|
||||
representation.
|
||||
|
|
|
@ -64,6 +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();
|
||||
extern my_time_t sec_since_epoch_TIME(TIME *t);
|
||||
|
||||
extern TABLE_LIST fake_time_zone_tables_list;
|
||||
|
||||
|
|
Loading…
Reference in a new issue