diff options
53 files changed, 5746 insertions, 4715 deletions
diff --git a/.bzrignore b/.bzrignore index 1cf0cf7b889..567bbe48423 100644 --- a/.bzrignore +++ b/.bzrignore @@ -287,7 +287,48 @@ build_tags.sh client/#mysql.cc# client/*.ds? client/*.vcproj +client/.deps/base64.Po +client/.deps/completion_hash.Po +client/.deps/dummy.Po +client/.deps/mf_tempdir.Po +client/.deps/my_bit.Po +client/.deps/my_bitmap.Po +client/.deps/my_getsystime.Po +client/.deps/my_new.Po +client/.deps/my_user.Po +client/.deps/my_vle.Po +client/.deps/mysql.Po +client/.deps/mysql_upgrade.Po +client/.deps/mysqladmin.Po +client/.deps/mysqlbinlog.Po +client/.deps/mysqlcheck.Po +client/.deps/mysqldump.Po +client/.deps/mysqlimport.Po +client/.deps/mysqlshow.Po +client/.deps/mysqlslap.Po +client/.deps/mysqltest.Po +client/.deps/readline.Po +client/.deps/sql_string.Po client/.libs -prune +client/.libs/lt-mysql +client/.libs/lt-mysqladmin +client/.libs/lt-mysqlbinlog +client/.libs/lt-mysqlcheck +client/.libs/lt-mysqldump +client/.libs/lt-mysqlimport +client/.libs/lt-mysqlshow +client/.libs/lt-mysqlslap +client/.libs/lt-mysqltest +client/.libs/mysql +client/.libs/mysql_upgrade +client/.libs/mysqladmin +client/.libs/mysqlbinlog +client/.libs/mysqlcheck +client/.libs/mysqldump +client/.libs/mysqlimport +client/.libs/mysqlshow +client/.libs/mysqlslap +client/.libs/mysqltest client/completion_hash.cpp client/decimal.c client/insert_test @@ -326,8 +367,66 @@ client/thread_test client_debug/* client_release/* client_test +cmd-line-utils/libedit/.deps/chared.Po +cmd-line-utils/libedit/.deps/common.Po +cmd-line-utils/libedit/.deps/el.Po +cmd-line-utils/libedit/.deps/emacs.Po +cmd-line-utils/libedit/.deps/fcns.Po +cmd-line-utils/libedit/.deps/fgetln.Po +cmd-line-utils/libedit/.deps/help.Po +cmd-line-utils/libedit/.deps/hist.Po +cmd-line-utils/libedit/.deps/history.Po +cmd-line-utils/libedit/.deps/key.Po +cmd-line-utils/libedit/.deps/map.Po +cmd-line-utils/libedit/.deps/parse.Po +cmd-line-utils/libedit/.deps/prompt.Po +cmd-line-utils/libedit/.deps/read.Po +cmd-line-utils/libedit/.deps/readline.Po +cmd-line-utils/libedit/.deps/refresh.Po +cmd-line-utils/libedit/.deps/search.Po +cmd-line-utils/libedit/.deps/sig.Po +cmd-line-utils/libedit/.deps/strlcat.Po +cmd-line-utils/libedit/.deps/strlcpy.Po +cmd-line-utils/libedit/.deps/term.Po +cmd-line-utils/libedit/.deps/tokenizer.Po +cmd-line-utils/libedit/.deps/tty.Po +cmd-line-utils/libedit/.deps/unvis.Po +cmd-line-utils/libedit/.deps/vi.Po +cmd-line-utils/libedit/.deps/vis.Po cmd-line-utils/libedit/common.h cmd-line-utils/libedit/makelist +cmd-line-utils/readline/.deps/bind.Po +cmd-line-utils/readline/.deps/callback.Po +cmd-line-utils/readline/.deps/compat.Po +cmd-line-utils/readline/.deps/complete.Po +cmd-line-utils/readline/.deps/display.Po +cmd-line-utils/readline/.deps/funmap.Po +cmd-line-utils/readline/.deps/histexpand.Po +cmd-line-utils/readline/.deps/histfile.Po +cmd-line-utils/readline/.deps/history.Po +cmd-line-utils/readline/.deps/histsearch.Po +cmd-line-utils/readline/.deps/input.Po +cmd-line-utils/readline/.deps/isearch.Po +cmd-line-utils/readline/.deps/keymaps.Po +cmd-line-utils/readline/.deps/kill.Po +cmd-line-utils/readline/.deps/macro.Po +cmd-line-utils/readline/.deps/mbutil.Po +cmd-line-utils/readline/.deps/misc.Po +cmd-line-utils/readline/.deps/nls.Po +cmd-line-utils/readline/.deps/parens.Po +cmd-line-utils/readline/.deps/readline.Po +cmd-line-utils/readline/.deps/rltty.Po +cmd-line-utils/readline/.deps/savestring.Po +cmd-line-utils/readline/.deps/search.Po +cmd-line-utils/readline/.deps/shell.Po +cmd-line-utils/readline/.deps/signals.Po +cmd-line-utils/readline/.deps/terminal.Po +cmd-line-utils/readline/.deps/text.Po +cmd-line-utils/readline/.deps/tilde.Po +cmd-line-utils/readline/.deps/undo.Po +cmd-line-utils/readline/.deps/util.Po +cmd-line-utils/readline/.deps/vi_mode.Po +cmd-line-utils/readline/.deps/xmalloc.Po comon.h comp_err/*.ds? comp_err/*.vcproj @@ -349,6 +448,11 @@ core.2430 db-*.*.* dbug/*.ds? dbug/*.vcproj +dbug/.deps/dbug.Po +dbug/.deps/dbug_analyze.Po +dbug/.deps/factorial.Po +dbug/.deps/my_main.Po +dbug/.deps/sanity.Po dbug/dbug_analyze dbug/example*.r dbug/factorial @@ -362,6 +466,15 @@ emacs.h examples/*.ds? examples/*.vcproj examples/udf_example/udf_example.def +extra/.deps/charset2html.Po +extra/.deps/comp_err.Po +extra/.deps/innochecksum.Po +extra/.deps/my_print_defaults.Po +extra/.deps/mysql_waitpid.Po +extra/.deps/perror.Po +extra/.deps/replace.Po +extra/.deps/resolve_stack_dump.Po +extra/.deps/resolveip.Po extra/charset2html extra/comp_err extra/created_include_files @@ -378,8 +491,54 @@ extra/resolve_stack_dump extra/resolveip extra/sql_state.h extra/tztime.cc +extra/yassl/src/.deps/buffer.Plo +extra/yassl/src/.deps/cert_wrapper.Plo +extra/yassl/src/.deps/crypto_wrapper.Plo +extra/yassl/src/.deps/handshake.Plo +extra/yassl/src/.deps/lock.Plo +extra/yassl/src/.deps/log.Plo +extra/yassl/src/.deps/socket_wrapper.Plo +extra/yassl/src/.deps/ssl.Plo +extra/yassl/src/.deps/template_instnt.Plo +extra/yassl/src/.deps/timer.Plo +extra/yassl/src/.deps/yassl_error.Plo +extra/yassl/src/.deps/yassl_imp.Plo +extra/yassl/src/.deps/yassl_int.Plo +extra/yassl/taocrypt/benchmark/.deps/benchmark-benchmark.Po extra/yassl/taocrypt/benchmark/benchmark +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-aes.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-aestables.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-algebra.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-arc4.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-asn.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-bftables.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-blowfish.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-coding.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-des.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-dh.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-dsa.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-file.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-hash.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-integer.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-md2.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-md4.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-md5.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-misc.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-random.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-ripemd.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-rsa.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-sha.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-template_instnt.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-tftables.Plo +extra/yassl/taocrypt/src/.deps/libtaocrypt_la-twofish.Plo +extra/yassl/taocrypt/test/.deps/test-test.Po extra/yassl/taocrypt/test/test +extra/yassl/testsuite/.deps/testsuite-client.Po +extra/yassl/testsuite/.deps/testsuite-echoclient.Po +extra/yassl/testsuite/.deps/testsuite-echoserver.Po +extra/yassl/testsuite/.deps/testsuite-server.Po +extra/yassl/testsuite/.deps/testsuite-test.Po +extra/yassl/testsuite/.deps/testsuite-testsuite.Po extra/yassl/testsuite/testsuite fcns.c fcns.h @@ -440,6 +599,130 @@ lib_release/* libmysql/*.c libmysql/*.ds? libmysql/*.vcproj +libmysql/.deps/array.Plo +libmysql/.deps/bchange.Plo +libmysql/.deps/bcmp.Plo +libmysql/.deps/bmove.Plo +libmysql/.deps/bmove_upp.Plo +libmysql/.deps/charset-def.Plo +libmysql/.deps/charset.Plo +libmysql/.deps/client.Plo +libmysql/.deps/conf_to_src.Po +libmysql/.deps/ctype-big5.Plo +libmysql/.deps/ctype-bin.Plo +libmysql/.deps/ctype-cp932.Plo +libmysql/.deps/ctype-czech.Plo +libmysql/.deps/ctype-euc_kr.Plo +libmysql/.deps/ctype-eucjpms.Plo +libmysql/.deps/ctype-extra.Plo +libmysql/.deps/ctype-gb2312.Plo +libmysql/.deps/ctype-gbk.Plo +libmysql/.deps/ctype-latin1.Plo +libmysql/.deps/ctype-mb.Plo +libmysql/.deps/ctype-simple.Plo +libmysql/.deps/ctype-sjis.Plo +libmysql/.deps/ctype-tis620.Plo +libmysql/.deps/ctype-uca.Plo +libmysql/.deps/ctype-ucs2.Plo +libmysql/.deps/ctype-ujis.Plo +libmysql/.deps/ctype-utf8.Plo +libmysql/.deps/ctype-win1250ch.Plo +libmysql/.deps/ctype.Plo +libmysql/.deps/dbug.Plo +libmysql/.deps/default.Plo +libmysql/.deps/default_modify.Plo +libmysql/.deps/errmsg.Plo +libmysql/.deps/errors.Plo +libmysql/.deps/get_password.Plo +libmysql/.deps/hash.Plo +libmysql/.deps/int2str.Plo +libmysql/.deps/is_prefix.Plo +libmysql/.deps/libmysql.Plo +libmysql/.deps/list.Plo +libmysql/.deps/llstr.Plo +libmysql/.deps/longlong2str.Plo +libmysql/.deps/manager.Plo +libmysql/.deps/md5.Plo +libmysql/.deps/mf_cache.Plo +libmysql/.deps/mf_dirname.Plo +libmysql/.deps/mf_fn_ext.Plo +libmysql/.deps/mf_format.Plo +libmysql/.deps/mf_iocache.Plo +libmysql/.deps/mf_iocache2.Plo +libmysql/.deps/mf_loadpath.Plo +libmysql/.deps/mf_pack.Plo +libmysql/.deps/mf_path.Plo +libmysql/.deps/mf_tempfile.Plo +libmysql/.deps/mf_unixpath.Plo +libmysql/.deps/mf_wcomp.Plo +libmysql/.deps/mulalloc.Plo +libmysql/.deps/my_alloc.Plo +libmysql/.deps/my_chsize.Plo +libmysql/.deps/my_compress.Plo +libmysql/.deps/my_create.Plo +libmysql/.deps/my_delete.Plo +libmysql/.deps/my_div.Plo +libmysql/.deps/my_error.Plo +libmysql/.deps/my_file.Plo +libmysql/.deps/my_fopen.Plo +libmysql/.deps/my_fstream.Plo +libmysql/.deps/my_gethostbyname.Plo +libmysql/.deps/my_getopt.Plo +libmysql/.deps/my_getwd.Plo +libmysql/.deps/my_init.Plo +libmysql/.deps/my_lib.Plo +libmysql/.deps/my_malloc.Plo +libmysql/.deps/my_messnc.Plo +libmysql/.deps/my_net.Plo +libmysql/.deps/my_once.Plo +libmysql/.deps/my_open.Plo +libmysql/.deps/my_port.Plo +libmysql/.deps/my_pread.Plo +libmysql/.deps/my_pthread.Plo +libmysql/.deps/my_read.Plo +libmysql/.deps/my_realloc.Plo +libmysql/.deps/my_rename.Plo +libmysql/.deps/my_seek.Plo +libmysql/.deps/my_sleep.Plo +libmysql/.deps/my_static.Plo +libmysql/.deps/my_strtoll10.Plo +libmysql/.deps/my_symlink.Plo +libmysql/.deps/my_thr_init.Plo +libmysql/.deps/my_time.Plo +libmysql/.deps/my_vsnprintf.Plo +libmysql/.deps/my_write.Plo +libmysql/.deps/net.Plo +libmysql/.deps/pack.Plo +libmysql/.deps/password.Plo +libmysql/.deps/safemalloc.Plo +libmysql/.deps/sha1.Plo +libmysql/.deps/str2int.Plo +libmysql/.deps/str_alloc.Plo +libmysql/.deps/strcend.Plo +libmysql/.deps/strcont.Plo +libmysql/.deps/strend.Plo +libmysql/.deps/strfill.Plo +libmysql/.deps/string.Plo +libmysql/.deps/strinstr.Plo +libmysql/.deps/strmake.Plo +libmysql/.deps/strmov.Plo +libmysql/.deps/strnlen.Plo +libmysql/.deps/strnmov.Plo +libmysql/.deps/strtod.Plo +libmysql/.deps/strtoll.Plo +libmysql/.deps/strtoull.Plo +libmysql/.deps/strxmov.Plo +libmysql/.deps/strxnmov.Plo +libmysql/.deps/thr_mutex.Plo +libmysql/.deps/typelib.Plo +libmysql/.deps/vio.Plo +libmysql/.deps/viosocket.Plo +libmysql/.deps/viossl.Plo +libmysql/.deps/viosslfactories.Plo +libmysql/.deps/xml.Plo +libmysql/.libs/libmysqlclient.lai +libmysql/.libs/libmysqlclient.so.15 +libmysql/.libs/libmysqlclient.so.15.0.0 libmysql/conf_to_src libmysql/debug/libmysql.exp libmysql/libmysql.ver @@ -449,7 +732,132 @@ libmysql/mysys_priv.h libmysql/net.c libmysql/release/libmysql.exp libmysql/vio_priv.h +libmysql/viosocket.o.6WmSJk libmysql_r/*.c +libmysql_r/.deps/array.Plo +libmysql_r/.deps/bchange.Plo +libmysql_r/.deps/bcmp.Plo +libmysql_r/.deps/bmove.Plo +libmysql_r/.deps/bmove_upp.Plo +libmysql_r/.deps/charset-def.Plo +libmysql_r/.deps/charset.Plo +libmysql_r/.deps/client.Plo +libmysql_r/.deps/conf_to_src.Po +libmysql_r/.deps/ctype-big5.Plo +libmysql_r/.deps/ctype-bin.Plo +libmysql_r/.deps/ctype-cp932.Plo +libmysql_r/.deps/ctype-czech.Plo +libmysql_r/.deps/ctype-euc_kr.Plo +libmysql_r/.deps/ctype-eucjpms.Plo +libmysql_r/.deps/ctype-extra.Plo +libmysql_r/.deps/ctype-gb2312.Plo +libmysql_r/.deps/ctype-gbk.Plo +libmysql_r/.deps/ctype-latin1.Plo +libmysql_r/.deps/ctype-mb.Plo +libmysql_r/.deps/ctype-simple.Plo +libmysql_r/.deps/ctype-sjis.Plo +libmysql_r/.deps/ctype-tis620.Plo +libmysql_r/.deps/ctype-uca.Plo +libmysql_r/.deps/ctype-ucs2.Plo +libmysql_r/.deps/ctype-ujis.Plo +libmysql_r/.deps/ctype-utf8.Plo +libmysql_r/.deps/ctype-win1250ch.Plo +libmysql_r/.deps/ctype.Plo +libmysql_r/.deps/dbug.Plo +libmysql_r/.deps/default.Plo +libmysql_r/.deps/default_modify.Plo +libmysql_r/.deps/errmsg.Plo +libmysql_r/.deps/errors.Plo +libmysql_r/.deps/get_password.Plo +libmysql_r/.deps/hash.Plo +libmysql_r/.deps/int2str.Plo +libmysql_r/.deps/is_prefix.Plo +libmysql_r/.deps/libmysql.Plo +libmysql_r/.deps/list.Plo +libmysql_r/.deps/llstr.Plo +libmysql_r/.deps/longlong2str.Plo +libmysql_r/.deps/manager.Plo +libmysql_r/.deps/md5.Plo +libmysql_r/.deps/mf_cache.Plo +libmysql_r/.deps/mf_dirname.Plo +libmysql_r/.deps/mf_fn_ext.Plo +libmysql_r/.deps/mf_format.Plo +libmysql_r/.deps/mf_iocache.Plo +libmysql_r/.deps/mf_iocache2.Plo +libmysql_r/.deps/mf_loadpath.Plo +libmysql_r/.deps/mf_pack.Plo +libmysql_r/.deps/mf_path.Plo +libmysql_r/.deps/mf_tempfile.Plo +libmysql_r/.deps/mf_unixpath.Plo +libmysql_r/.deps/mf_wcomp.Plo +libmysql_r/.deps/mulalloc.Plo +libmysql_r/.deps/my_alloc.Plo +libmysql_r/.deps/my_chsize.Plo +libmysql_r/.deps/my_compress.Plo +libmysql_r/.deps/my_create.Plo +libmysql_r/.deps/my_delete.Plo +libmysql_r/.deps/my_div.Plo +libmysql_r/.deps/my_error.Plo +libmysql_r/.deps/my_file.Plo +libmysql_r/.deps/my_fopen.Plo +libmysql_r/.deps/my_fstream.Plo +libmysql_r/.deps/my_gethostbyname.Plo +libmysql_r/.deps/my_getopt.Plo +libmysql_r/.deps/my_getwd.Plo +libmysql_r/.deps/my_init.Plo +libmysql_r/.deps/my_lib.Plo +libmysql_r/.deps/my_malloc.Plo +libmysql_r/.deps/my_messnc.Plo +libmysql_r/.deps/my_net.Plo +libmysql_r/.deps/my_once.Plo +libmysql_r/.deps/my_open.Plo +libmysql_r/.deps/my_port.Plo +libmysql_r/.deps/my_pread.Plo +libmysql_r/.deps/my_pthread.Plo +libmysql_r/.deps/my_read.Plo +libmysql_r/.deps/my_realloc.Plo +libmysql_r/.deps/my_rename.Plo +libmysql_r/.deps/my_seek.Plo +libmysql_r/.deps/my_sleep.Plo +libmysql_r/.deps/my_static.Plo +libmysql_r/.deps/my_strtoll10.Plo +libmysql_r/.deps/my_symlink.Plo +libmysql_r/.deps/my_thr_init.Plo +libmysql_r/.deps/my_time.Plo +libmysql_r/.deps/my_vsnprintf.Plo +libmysql_r/.deps/my_write.Plo +libmysql_r/.deps/net.Plo +libmysql_r/.deps/pack.Plo +libmysql_r/.deps/password.Plo +libmysql_r/.deps/safemalloc.Plo +libmysql_r/.deps/sha1.Plo +libmysql_r/.deps/str2int.Plo +libmysql_r/.deps/str_alloc.Plo +libmysql_r/.deps/strcend.Plo +libmysql_r/.deps/strcont.Plo +libmysql_r/.deps/strend.Plo +libmysql_r/.deps/strfill.Plo +libmysql_r/.deps/string.Plo +libmysql_r/.deps/strinstr.Plo +libmysql_r/.deps/strmake.Plo +libmysql_r/.deps/strmov.Plo +libmysql_r/.deps/strnlen.Plo +libmysql_r/.deps/strnmov.Plo +libmysql_r/.deps/strtod.Plo +libmysql_r/.deps/strtoll.Plo +libmysql_r/.deps/strtoull.Plo +libmysql_r/.deps/strxmov.Plo +libmysql_r/.deps/strxnmov.Plo +libmysql_r/.deps/thr_mutex.Plo +libmysql_r/.deps/typelib.Plo +libmysql_r/.deps/vio.Plo +libmysql_r/.deps/viosocket.Plo +libmysql_r/.deps/viossl.Plo +libmysql_r/.deps/viosslfactories.Plo +libmysql_r/.deps/xml.Plo +libmysql_r/.libs/libmysqlclient_r.lai +libmysql_r/.libs/libmysqlclient_r.so.15 +libmysql_r/.libs/libmysqlclient_r.so.15.0.0 libmysql_r/acconfig.h libmysql_r/conf_to_src libmysql_r/my_static.h @@ -457,6 +865,123 @@ libmysql_r/mysys_priv.h libmysql_r/vio_priv.h libmysqld/*.ds? libmysqld/*.vcproj +libmysqld/.deps/client.Po +libmysqld/.deps/derror.Po +libmysqld/.deps/discover.Po +libmysqld/.deps/emb_qcache.Po +libmysqld/.deps/errmsg.Po +libmysqld/.deps/event_data_objects.Po +libmysqld/.deps/event_db_repository.Po +libmysqld/.deps/event_queue.Po +libmysqld/.deps/event_scheduler.Po +libmysqld/.deps/events.Po +libmysqld/.deps/field.Po +libmysqld/.deps/field_conv.Po +libmysqld/.deps/filesort.Po +libmysqld/.deps/get_password.Po +libmysqld/.deps/gstream.Po +libmysqld/.deps/ha_berkeley.Po +libmysqld/.deps/ha_federated.Po +libmysqld/.deps/ha_heap.Po +libmysqld/.deps/ha_innodb.Po +libmysqld/.deps/ha_myisam.Po +libmysqld/.deps/ha_myisammrg.Po +libmysqld/.deps/ha_ndbcluster.Po +libmysqld/.deps/ha_ndbcluster_binlog.Po +libmysqld/.deps/ha_partition.Po +libmysqld/.deps/handler.Po +libmysqld/.deps/hash_filo.Po +libmysqld/.deps/hostname.Po +libmysqld/.deps/init.Po +libmysqld/.deps/item.Po +libmysqld/.deps/item_buff.Po +libmysqld/.deps/item_cmpfunc.Po +libmysqld/.deps/item_create.Po +libmysqld/.deps/item_func.Po +libmysqld/.deps/item_geofunc.Po +libmysqld/.deps/item_row.Po +libmysqld/.deps/item_strfunc.Po +libmysqld/.deps/item_subselect.Po +libmysqld/.deps/item_sum.Po +libmysqld/.deps/item_timefunc.Po +libmysqld/.deps/item_uniq.Po +libmysqld/.deps/item_xmlfunc.Po +libmysqld/.deps/key.Po +libmysqld/.deps/lib_sql.Po +libmysqld/.deps/libmysql.Po +libmysqld/.deps/libmysqld.Po +libmysqld/.deps/lock.Po +libmysqld/.deps/log.Po +libmysqld/.deps/log_event.Po +libmysqld/.deps/my_decimal.Po +libmysqld/.deps/my_time.Po +libmysqld/.deps/my_user.Po +libmysqld/.deps/net_serv.Po +libmysqld/.deps/opt_range.Po +libmysqld/.deps/opt_sum.Po +libmysqld/.deps/pack.Po +libmysqld/.deps/parse_file.Po +libmysqld/.deps/partition_info.Po +libmysqld/.deps/password.Po +libmysqld/.deps/procedure.Po +libmysqld/.deps/protocol.Po +libmysqld/.deps/records.Po +libmysqld/.deps/rpl_filter.Po +libmysqld/.deps/rpl_injector.Po +libmysqld/.deps/set_var.Po +libmysqld/.deps/sp.Po +libmysqld/.deps/sp_cache.Po +libmysqld/.deps/sp_head.Po +libmysqld/.deps/sp_pcontext.Po +libmysqld/.deps/sp_rcontext.Po +libmysqld/.deps/spatial.Po +libmysqld/.deps/sql_acl.Po +libmysqld/.deps/sql_analyse.Po +libmysqld/.deps/sql_base.Po +libmysqld/.deps/sql_builtin.Po +libmysqld/.deps/sql_cache.Po +libmysqld/.deps/sql_class.Po +libmysqld/.deps/sql_crypt.Po +libmysqld/.deps/sql_cursor.Po +libmysqld/.deps/sql_db.Po +libmysqld/.deps/sql_delete.Po +libmysqld/.deps/sql_derived.Po +libmysqld/.deps/sql_do.Po +libmysqld/.deps/sql_error.Po +libmysqld/.deps/sql_handler.Po +libmysqld/.deps/sql_help.Po +libmysqld/.deps/sql_insert.Po +libmysqld/.deps/sql_lex.Po +libmysqld/.deps/sql_list.Po +libmysqld/.deps/sql_load.Po +libmysqld/.deps/sql_manager.Po +libmysqld/.deps/sql_map.Po +libmysqld/.deps/sql_parse.Po +libmysqld/.deps/sql_partition.Po +libmysqld/.deps/sql_plugin.Po +libmysqld/.deps/sql_prepare.Po +libmysqld/.deps/sql_rename.Po +libmysqld/.deps/sql_select.Po +libmysqld/.deps/sql_show.Po +libmysqld/.deps/sql_state.Po +libmysqld/.deps/sql_string.Po +libmysqld/.deps/sql_table.Po +libmysqld/.deps/sql_tablespace.Po +libmysqld/.deps/sql_test.Po +libmysqld/.deps/sql_trigger.Po +libmysqld/.deps/sql_udf.Po +libmysqld/.deps/sql_union.Po +libmysqld/.deps/sql_update.Po +libmysqld/.deps/sql_view.Po +libmysqld/.deps/sql_yacc.Po +libmysqld/.deps/stacktrace.Po +libmysqld/.deps/strfunc.Po +libmysqld/.deps/table.Po +libmysqld/.deps/thr_malloc.Po +libmysqld/.deps/time.Po +libmysqld/.deps/tztime.Po +libmysqld/.deps/uniques.Po +libmysqld/.deps/unireg.Po libmysqld/backup_dir libmysqld/client.c libmysqld/client_settings.h @@ -466,10 +991,18 @@ libmysqld/discover.cc libmysqld/emb_qcache.cpp libmysqld/errmsg.c libmysqld/event.cc +libmysqld/event_data_objects.cc +libmysqld/event_db_repository.cc libmysqld/event_executor.cc +libmysqld/event_queue.cc libmysqld/event_scheduler.cc libmysqld/event_timed.cc libmysqld/events.cc +libmysqld/examples/.deps/completion_hash.Po +libmysqld/examples/.deps/mysql.Po +libmysqld/examples/.deps/mysql_client_test.Po +libmysqld/examples/.deps/mysqltest.Po +libmysqld/examples/.deps/readline.Po libmysqld/examples/client_test.c libmysqld/examples/client_test.cc libmysqld/examples/completion_hash.cc @@ -793,6 +1326,120 @@ mysqlserver/*.vcproj mysys/#mf_iocache.c# mysys/*.ds? mysys/*.vcproj +mysys/.deps/array.Po +mysys/.deps/base64.Po +mysys/.deps/charset-def.Po +mysys/.deps/charset.Po +mysys/.deps/checksum.Po +mysys/.deps/default.Po +mysys/.deps/default_modify.Po +mysys/.deps/errors.Po +mysys/.deps/hash.Po +mysys/.deps/list.Po +mysys/.deps/md5.Po +mysys/.deps/mf_brkhant.Po +mysys/.deps/mf_cache.Po +mysys/.deps/mf_dirname.Po +mysys/.deps/mf_fn_ext.Po +mysys/.deps/mf_format.Po +mysys/.deps/mf_getdate.Po +mysys/.deps/mf_iocache.Po +mysys/.deps/mf_iocache2.Po +mysys/.deps/mf_keycache.Po +mysys/.deps/mf_keycaches.Po +mysys/.deps/mf_loadpath.Po +mysys/.deps/mf_pack.Po +mysys/.deps/mf_path.Po +mysys/.deps/mf_qsort.Po +mysys/.deps/mf_qsort2.Po +mysys/.deps/mf_radix.Po +mysys/.deps/mf_same.Po +mysys/.deps/mf_sort.Po +mysys/.deps/mf_strip.Po +mysys/.deps/mf_tempdir.Po +mysys/.deps/mf_tempfile.Po +mysys/.deps/mf_unixpath.Po +mysys/.deps/mf_wcomp.Po +mysys/.deps/mf_wfile.Po +mysys/.deps/mulalloc.Po +mysys/.deps/my_access.Po +mysys/.deps/my_aes.Po +mysys/.deps/my_alarm.Po +mysys/.deps/my_alloc.Po +mysys/.deps/my_append.Po +mysys/.deps/my_atomic.Po +mysys/.deps/my_bit.Po +mysys/.deps/my_bitmap.Po +mysys/.deps/my_chsize.Po +mysys/.deps/my_clock.Po +mysys/.deps/my_compress.Po +mysys/.deps/my_copy.Po +mysys/.deps/my_crc32.Po +mysys/.deps/my_create.Po +mysys/.deps/my_delete.Po +mysys/.deps/my_div.Po +mysys/.deps/my_dup.Po +mysys/.deps/my_error.Po +mysys/.deps/my_file.Po +mysys/.deps/my_fopen.Po +mysys/.deps/my_fstream.Po +mysys/.deps/my_gethostbyname.Po +mysys/.deps/my_gethwaddr.Po +mysys/.deps/my_getncpus.Po +mysys/.deps/my_getopt.Po +mysys/.deps/my_getsystime.Po +mysys/.deps/my_getwd.Po +mysys/.deps/my_handler.Po +mysys/.deps/my_init.Po +mysys/.deps/my_largepage.Po +mysys/.deps/my_lib.Po +mysys/.deps/my_libwrap.Po +mysys/.deps/my_lock.Po +mysys/.deps/my_lockmem.Po +mysys/.deps/my_lread.Po +mysys/.deps/my_lwrite.Po +mysys/.deps/my_malloc.Po +mysys/.deps/my_memmem.Po +mysys/.deps/my_messnc.Po +mysys/.deps/my_mkdir.Po +mysys/.deps/my_mmap.Po +mysys/.deps/my_net.Po +mysys/.deps/my_netware.Po +mysys/.deps/my_new.Po +mysys/.deps/my_once.Po +mysys/.deps/my_open.Po +mysys/.deps/my_port.Po +mysys/.deps/my_pread.Po +mysys/.deps/my_pthread.Po +mysys/.deps/my_quick.Po +mysys/.deps/my_read.Po +mysys/.deps/my_realloc.Po +mysys/.deps/my_redel.Po +mysys/.deps/my_rename.Po +mysys/.deps/my_seek.Po +mysys/.deps/my_semaphore.Po +mysys/.deps/my_sleep.Po +mysys/.deps/my_static.Po +mysys/.deps/my_symlink.Po +mysys/.deps/my_symlink2.Po +mysys/.deps/my_sync.Po +mysys/.deps/my_thr_init.Po +mysys/.deps/my_vle.Po +mysys/.deps/my_windac.Po +mysys/.deps/my_write.Po +mysys/.deps/ptr_cmp.Po +mysys/.deps/queues.Po +mysys/.deps/rijndael.Po +mysys/.deps/safemalloc.Po +mysys/.deps/sha1.Po +mysys/.deps/string.Po +mysys/.deps/thr_alarm.Po +mysys/.deps/thr_lock.Po +mysys/.deps/thr_mutex.Po +mysys/.deps/thr_rwlock.Po +mysys/.deps/tree.Po +mysys/.deps/trie.Po +mysys/.deps/typelib.Po mysys/charset2html mysys/getopt.c mysys/getopt1.c @@ -1065,12 +1712,37 @@ ndbcluster-1186/ndb_3.pid ndbcluster-1186/ndb_3_cluster.log ndbcluster-1186/ndb_3_out.log ndbcluster-1186/ndbcluster.pid +netware/.deps/libmysqlmain.Po +netware/.deps/my_manage.Po +netware/.deps/mysql_install_db.Po +netware/.deps/mysql_test_run.Po +netware/.deps/mysqld_safe.Po pack_isam/*.ds? perror/*.ds? perror/*.vcproj +plugin/fulltext/.deps/mypluglib_la-plugin_example.Plo +plugin/fulltext/.libs/mypluglib.lai +plugin/fulltext/.libs/mypluglib.so.0 +plugin/fulltext/.libs/mypluglib.so.0.0.0 +pstack/.deps/bucomm.Po +pstack/.deps/debug.Po +pstack/.deps/filemode.Po +pstack/.deps/ieee.Po +pstack/.deps/linuxthreads.Po +pstack/.deps/pstack.Po +pstack/.deps/rddbg.Po +pstack/.deps/stabs.Po pull.log regex/*.ds? regex/*.vcproj +regex/.deps/debug.Po +regex/.deps/main.Po +regex/.deps/regcomp.Po +regex/.deps/regerror.Po +regex/.deps/regexec.Po +regex/.deps/regfree.Po +regex/.deps/reginit.Po +regex/.deps/split.Po regex/re repl-tests/test-repl-ts/repl-timestamp.master.reject repl-tests/test-repl/foo-dump-slave.master. @@ -1111,6 +1783,28 @@ scripts/mysqlhotcopy scripts/mysqlhotcopy.sh.rej scripts/safe_mysqld select_test +server-tools/instance-manager/.deps/buffer.Po +server-tools/instance-manager/.deps/command.Po +server-tools/instance-manager/.deps/commands.Po +server-tools/instance-manager/.deps/guardian.Po +server-tools/instance-manager/.deps/instance.Po +server-tools/instance-manager/.deps/instance_map.Po +server-tools/instance-manager/.deps/instance_options.Po +server-tools/instance-manager/.deps/liboptions_la-options.Plo +server-tools/instance-manager/.deps/liboptions_la-priv.Plo +server-tools/instance-manager/.deps/listener.Po +server-tools/instance-manager/.deps/log.Po +server-tools/instance-manager/.deps/manager.Po +server-tools/instance-manager/.deps/messages.Po +server-tools/instance-manager/.deps/mysql_connection.Po +server-tools/instance-manager/.deps/mysqlmanager.Po +server-tools/instance-manager/.deps/net_serv.Po +server-tools/instance-manager/.deps/parse.Po +server-tools/instance-manager/.deps/parse_output.Po +server-tools/instance-manager/.deps/protocol.Po +server-tools/instance-manager/.deps/thread_registry.Po +server-tools/instance-manager/.deps/user_management_commands.Po +server-tools/instance-manager/.deps/user_map.Po server-tools/instance-manager/buffer.cpp server-tools/instance-manager/client.c server-tools/instance-manager/client_settings.h @@ -1168,7 +1862,136 @@ sql-bench/test-wisconsin sql/*.cpp sql/*.ds? sql/*.vcproj +sql/.deps/client.Po +sql/.deps/derror.Po +sql/.deps/des_key_file.Po +sql/.deps/discover.Po +sql/.deps/event_data_objects.Po +sql/.deps/event_db_repository.Po +sql/.deps/event_queue.Po +sql/.deps/event_scheduler.Po +sql/.deps/events.Po +sql/.deps/field.Po +sql/.deps/field_conv.Po +sql/.deps/filesort.Po +sql/.deps/gen_lex_hash.Po +sql/.deps/gstream.Po +sql/.deps/ha_berkeley.Po +sql/.deps/ha_federated.Po +sql/.deps/ha_heap.Po +sql/.deps/ha_innodb.Po +sql/.deps/ha_myisam.Po +sql/.deps/ha_myisammrg.Po +sql/.deps/ha_ndbcluster.Po +sql/.deps/ha_ndbcluster_binlog.Po +sql/.deps/ha_partition.Po +sql/.deps/handler.Po +sql/.deps/hash_filo.Po +sql/.deps/hostname.Po +sql/.deps/init.Po +sql/.deps/item.Po +sql/.deps/item_buff.Po +sql/.deps/item_cmpfunc.Po +sql/.deps/item_create.Po +sql/.deps/item_func.Po +sql/.deps/item_geofunc.Po +sql/.deps/item_row.Po +sql/.deps/item_strfunc.Po +sql/.deps/item_subselect.Po +sql/.deps/item_sum.Po +sql/.deps/item_timefunc.Po +sql/.deps/item_uniq.Po +sql/.deps/item_xmlfunc.Po +sql/.deps/key.Po +sql/.deps/lock.Po +sql/.deps/log.Po +sql/.deps/log_event.Po +sql/.deps/mf_iocache.Po +sql/.deps/mini_client_errors.Po +sql/.deps/my_decimal.Po +sql/.deps/my_lock.Po +sql/.deps/my_time.Po +sql/.deps/my_user.Po +sql/.deps/mysql_tzinfo_to_sql.Po +sql/.deps/mysqld.Po +sql/.deps/net_serv.Po +sql/.deps/opt_range.Po +sql/.deps/opt_sum.Po +sql/.deps/pack.Po +sql/.deps/parse_file.Po +sql/.deps/partition_info.Po +sql/.deps/password.Po +sql/.deps/procedure.Po +sql/.deps/protocol.Po +sql/.deps/records.Po +sql/.deps/repl_failsafe.Po +sql/.deps/rpl_filter.Po +sql/.deps/rpl_injector.Po +sql/.deps/rpl_tblmap.Po +sql/.deps/set_var.Po +sql/.deps/slave.Po +sql/.deps/sp.Po +sql/.deps/sp_cache.Po +sql/.deps/sp_head.Po +sql/.deps/sp_pcontext.Po +sql/.deps/sp_rcontext.Po +sql/.deps/spatial.Po +sql/.deps/sql_acl.Po +sql/.deps/sql_analyse.Po +sql/.deps/sql_base.Po +sql/.deps/sql_binlog.Po +sql/.deps/sql_builtin.Po +sql/.deps/sql_cache.Po +sql/.deps/sql_class.Po +sql/.deps/sql_client.Po +sql/.deps/sql_crypt.Po +sql/.deps/sql_cursor.Po +sql/.deps/sql_db.Po +sql/.deps/sql_delete.Po +sql/.deps/sql_derived.Po +sql/.deps/sql_do.Po +sql/.deps/sql_error.Po +sql/.deps/sql_handler.Po +sql/.deps/sql_help.Po +sql/.deps/sql_insert.Po +sql/.deps/sql_lex.Po +sql/.deps/sql_list.Po +sql/.deps/sql_load.Po +sql/.deps/sql_manager.Po +sql/.deps/sql_map.Po +sql/.deps/sql_olap.Po +sql/.deps/sql_parse.Po +sql/.deps/sql_partition.Po +sql/.deps/sql_plugin.Po +sql/.deps/sql_prepare.Po +sql/.deps/sql_rename.Po +sql/.deps/sql_repl.Po +sql/.deps/sql_select.Po +sql/.deps/sql_show.Po +sql/.deps/sql_state.Po +sql/.deps/sql_string.Po +sql/.deps/sql_table.Po +sql/.deps/sql_tablespace.Po +sql/.deps/sql_test.Po +sql/.deps/sql_trigger.Po +sql/.deps/sql_udf.Po +sql/.deps/sql_union.Po +sql/.deps/sql_update.Po +sql/.deps/sql_view.Po +sql/.deps/sql_yacc.Po +sql/.deps/stacktrace.Po +sql/.deps/strfunc.Po +sql/.deps/table.Po +sql/.deps/thr_malloc.Po +sql/.deps/time.Po +sql/.deps/tztime.Po +sql/.deps/udf_example.Plo +sql/.deps/uniques.Po +sql/.deps/unireg.Po sql/.gdbinit +sql/.libs/udf_example.lai +sql/.libs/udf_example.so.0 +sql/.libs/udf_example.so.0.0.0 sql/client.c sql/gen_lex_hash sql/gmon.out @@ -1219,6 +2042,12 @@ stamp-h2.in stamp-h3 stamp-h4 start_mysqld.sh +storage/archive/.deps/archive_test-archive_test.Po +storage/archive/.deps/archive_test-azio.Po +storage/archive/.deps/ha_archive_la-azio.Plo +storage/archive/.deps/ha_archive_la-ha_archive.Plo +storage/archive/.deps/libarchive_a-azio.Po +storage/archive/.deps/libarchive_a-ha_archive.Po storage/archive/archive_test storage/bdb/*.ds? storage/bdb/*.vcproj @@ -1378,6 +2207,38 @@ storage/bdb/test/include.tcl storage/bdb/test/logtrack.list storage/bdb/txn/txn_auto.c storage/bdb/txn/txn_autop.c +storage/blackhole/.deps/ha_blackhole_la-ha_blackhole.Plo +storage/blackhole/.deps/libblackhole_a-ha_blackhole.Po +storage/csv/.deps/ha_csv_la-ha_tina.Plo +storage/csv/.deps/libcsv_a-ha_tina.Po +storage/example/.deps/ha_example_la-ha_example.Plo +storage/example/.deps/libexample_a-ha_example.Po +storage/heap/.deps/_check.Po +storage/heap/.deps/_rectest.Po +storage/heap/.deps/hp_block.Po +storage/heap/.deps/hp_clear.Po +storage/heap/.deps/hp_close.Po +storage/heap/.deps/hp_create.Po +storage/heap/.deps/hp_delete.Po +storage/heap/.deps/hp_extra.Po +storage/heap/.deps/hp_hash.Po +storage/heap/.deps/hp_info.Po +storage/heap/.deps/hp_open.Po +storage/heap/.deps/hp_panic.Po +storage/heap/.deps/hp_rename.Po +storage/heap/.deps/hp_rfirst.Po +storage/heap/.deps/hp_rkey.Po +storage/heap/.deps/hp_rlast.Po +storage/heap/.deps/hp_rnext.Po +storage/heap/.deps/hp_rprev.Po +storage/heap/.deps/hp_rrnd.Po +storage/heap/.deps/hp_rsame.Po +storage/heap/.deps/hp_scan.Po +storage/heap/.deps/hp_static.Po +storage/heap/.deps/hp_test1.Po +storage/heap/.deps/hp_test2.Po +storage/heap/.deps/hp_update.Po +storage/heap/.deps/hp_write.Po storage/heap/hp_test1 storage/heap/hp_test2 storage/innobase/autom4te-2.53.cache/* @@ -1388,13 +2249,155 @@ storage/innobase/autom4te.cache/* storage/innobase/autom4te.cache/output.0 storage/innobase/autom4te.cache/requests storage/innobase/autom4te.cache/traces.0 +storage/innobase/btr/.deps/btr0btr.Po +storage/innobase/btr/.deps/btr0cur.Po +storage/innobase/btr/.deps/btr0pcur.Po +storage/innobase/btr/.deps/btr0sea.Po +storage/innobase/buf/.deps/buf0buf.Po +storage/innobase/buf/.deps/buf0flu.Po +storage/innobase/buf/.deps/buf0lru.Po +storage/innobase/buf/.deps/buf0rea.Po storage/innobase/configure.lineno storage/innobase/conftest.s1 storage/innobase/conftest.subs +storage/innobase/data/.deps/data0data.Po +storage/innobase/data/.deps/data0type.Po +storage/innobase/dict/.deps/dict0boot.Po +storage/innobase/dict/.deps/dict0crea.Po +storage/innobase/dict/.deps/dict0dict.Po +storage/innobase/dict/.deps/dict0load.Po +storage/innobase/dict/.deps/dict0mem.Po +storage/innobase/dyn/.deps/dyn0dyn.Po +storage/innobase/eval/.deps/eval0eval.Po +storage/innobase/eval/.deps/eval0proc.Po +storage/innobase/fil/.deps/fil0fil.Po +storage/innobase/fsp/.deps/fsp0fsp.Po +storage/innobase/fut/.deps/fut0fut.Po +storage/innobase/fut/.deps/fut0lst.Po +storage/innobase/ha/.deps/ha0ha.Po +storage/innobase/ha/.deps/hash0hash.Po storage/innobase/ib_config.h storage/innobase/ib_config.h.in +storage/innobase/ibuf/.deps/ibuf0ibuf.Po +storage/innobase/lock/.deps/lock0lock.Po +storage/innobase/log/.deps/log0log.Po +storage/innobase/log/.deps/log0recv.Po +storage/innobase/mach/.deps/mach0data.Po +storage/innobase/mem/.deps/mem0mem.Po +storage/innobase/mem/.deps/mem0pool.Po storage/innobase/mkinstalldirs +storage/innobase/mtr/.deps/mtr0log.Po +storage/innobase/mtr/.deps/mtr0mtr.Po +storage/innobase/os/.deps/os0file.Po +storage/innobase/os/.deps/os0proc.Po +storage/innobase/os/.deps/os0sync.Po +storage/innobase/os/.deps/os0thread.Po +storage/innobase/page/.deps/page0cur.Po +storage/innobase/page/.deps/page0page.Po +storage/innobase/pars/.deps/lexyy.Po +storage/innobase/pars/.deps/pars0grm.Po +storage/innobase/pars/.deps/pars0opt.Po +storage/innobase/pars/.deps/pars0pars.Po +storage/innobase/pars/.deps/pars0sym.Po +storage/innobase/que/.deps/que0que.Po +storage/innobase/read/.deps/read0read.Po +storage/innobase/rem/.deps/rem0cmp.Po +storage/innobase/rem/.deps/rem0rec.Po +storage/innobase/row/.deps/row0ins.Po +storage/innobase/row/.deps/row0mysql.Po +storage/innobase/row/.deps/row0purge.Po +storage/innobase/row/.deps/row0row.Po +storage/innobase/row/.deps/row0sel.Po +storage/innobase/row/.deps/row0uins.Po +storage/innobase/row/.deps/row0umod.Po +storage/innobase/row/.deps/row0undo.Po +storage/innobase/row/.deps/row0upd.Po +storage/innobase/row/.deps/row0vers.Po +storage/innobase/srv/.deps/srv0que.Po +storage/innobase/srv/.deps/srv0srv.Po +storage/innobase/srv/.deps/srv0start.Po storage/innobase/stamp-h1 +storage/innobase/sync/.deps/sync0arr.Po +storage/innobase/sync/.deps/sync0rw.Po +storage/innobase/sync/.deps/sync0sync.Po +storage/innobase/thr/.deps/thr0loc.Po +storage/innobase/trx/.deps/trx0purge.Po +storage/innobase/trx/.deps/trx0rec.Po +storage/innobase/trx/.deps/trx0roll.Po +storage/innobase/trx/.deps/trx0rseg.Po +storage/innobase/trx/.deps/trx0sys.Po +storage/innobase/trx/.deps/trx0trx.Po +storage/innobase/trx/.deps/trx0undo.Po +storage/innobase/usr/.deps/usr0sess.Po +storage/innobase/ut/.deps/ut0byte.Po +storage/innobase/ut/.deps/ut0dbg.Po +storage/innobase/ut/.deps/ut0list.Po +storage/innobase/ut/.deps/ut0mem.Po +storage/innobase/ut/.deps/ut0rnd.Po +storage/innobase/ut/.deps/ut0ut.Po +storage/innobase/ut/.deps/ut0vec.Po +storage/innobase/ut/.deps/ut0wqueue.Po +storage/myisam/.deps/ft_boolean_search.Po +storage/myisam/.deps/ft_nlq_search.Po +storage/myisam/.deps/ft_parser.Po +storage/myisam/.deps/ft_static.Po +storage/myisam/.deps/ft_stopwords.Po +storage/myisam/.deps/ft_update.Po +storage/myisam/.deps/mi_cache.Po +storage/myisam/.deps/mi_changed.Po +storage/myisam/.deps/mi_check.Po +storage/myisam/.deps/mi_checksum.Po +storage/myisam/.deps/mi_close.Po +storage/myisam/.deps/mi_create.Po +storage/myisam/.deps/mi_dbug.Po +storage/myisam/.deps/mi_delete.Po +storage/myisam/.deps/mi_delete_all.Po +storage/myisam/.deps/mi_delete_table.Po +storage/myisam/.deps/mi_dynrec.Po +storage/myisam/.deps/mi_extra.Po +storage/myisam/.deps/mi_info.Po +storage/myisam/.deps/mi_key.Po +storage/myisam/.deps/mi_keycache.Po +storage/myisam/.deps/mi_locking.Po +storage/myisam/.deps/mi_log.Po +storage/myisam/.deps/mi_open.Po +storage/myisam/.deps/mi_packrec.Po +storage/myisam/.deps/mi_page.Po +storage/myisam/.deps/mi_panic.Po +storage/myisam/.deps/mi_preload.Po +storage/myisam/.deps/mi_range.Po +storage/myisam/.deps/mi_rename.Po +storage/myisam/.deps/mi_rfirst.Po +storage/myisam/.deps/mi_rkey.Po +storage/myisam/.deps/mi_rlast.Po +storage/myisam/.deps/mi_rnext.Po +storage/myisam/.deps/mi_rnext_same.Po +storage/myisam/.deps/mi_rprev.Po +storage/myisam/.deps/mi_rrnd.Po +storage/myisam/.deps/mi_rsame.Po +storage/myisam/.deps/mi_rsamepos.Po +storage/myisam/.deps/mi_scan.Po +storage/myisam/.deps/mi_search.Po +storage/myisam/.deps/mi_static.Po +storage/myisam/.deps/mi_statrec.Po +storage/myisam/.deps/mi_test1.Po +storage/myisam/.deps/mi_test2.Po +storage/myisam/.deps/mi_test3.Po +storage/myisam/.deps/mi_unique.Po +storage/myisam/.deps/mi_update.Po +storage/myisam/.deps/mi_write.Po +storage/myisam/.deps/myisam_ftdump.Po +storage/myisam/.deps/myisamchk.Po +storage/myisam/.deps/myisamlog.Po +storage/myisam/.deps/myisampack.Po +storage/myisam/.deps/rt_index.Po +storage/myisam/.deps/rt_key.Po +storage/myisam/.deps/rt_mbr.Po +storage/myisam/.deps/rt_split.Po +storage/myisam/.deps/rt_test.Po +storage/myisam/.deps/sort.Po +storage/myisam/.deps/sp_key.Po +storage/myisam/.deps/sp_test.Po storage/myisam/FT1.MYD storage/myisam/FT1.MYI storage/myisam/ft_dump @@ -1420,6 +2423,27 @@ storage/myisam/test1.MYD storage/myisam/test1.MYI storage/myisam/test2.MYD storage/myisam/test2.MYI +storage/myisammrg/.deps/myrg_close.Po +storage/myisammrg/.deps/myrg_create.Po +storage/myisammrg/.deps/myrg_delete.Po +storage/myisammrg/.deps/myrg_extra.Po +storage/myisammrg/.deps/myrg_info.Po +storage/myisammrg/.deps/myrg_locking.Po +storage/myisammrg/.deps/myrg_open.Po +storage/myisammrg/.deps/myrg_panic.Po +storage/myisammrg/.deps/myrg_queue.Po +storage/myisammrg/.deps/myrg_range.Po +storage/myisammrg/.deps/myrg_rfirst.Po +storage/myisammrg/.deps/myrg_rkey.Po +storage/myisammrg/.deps/myrg_rlast.Po +storage/myisammrg/.deps/myrg_rnext.Po +storage/myisammrg/.deps/myrg_rnext_same.Po +storage/myisammrg/.deps/myrg_rprev.Po +storage/myisammrg/.deps/myrg_rrnd.Po +storage/myisammrg/.deps/myrg_rsame.Po +storage/myisammrg/.deps/myrg_static.Po +storage/myisammrg/.deps/myrg_update.Po +storage/myisammrg/.deps/myrg_write.Po storage/ndb/bin/DbAsyncGenerator storage/ndb/bin/DbCreate storage/ndb/bin/acid @@ -1678,6 +2702,64 @@ storage/ndb/tools/ndb_waiter storage/ndb/tools/ndb_waiter.dsp strings/*.ds? strings/*.vcproj +strings/.deps/bchange.Po +strings/.deps/bcmp.Po +strings/.deps/bfill.Po +strings/.deps/bmove.Po +strings/.deps/bmove512.Po +strings/.deps/bmove_upp.Po +strings/.deps/conf_to_src.Po +strings/.deps/ctype-big5.Po +strings/.deps/ctype-bin.Po +strings/.deps/ctype-cp932.Po +strings/.deps/ctype-czech.Po +strings/.deps/ctype-euc_kr.Po +strings/.deps/ctype-eucjpms.Po +strings/.deps/ctype-extra.Po +strings/.deps/ctype-gb2312.Po +strings/.deps/ctype-gbk.Po +strings/.deps/ctype-latin1.Po +strings/.deps/ctype-mb.Po +strings/.deps/ctype-simple.Po +strings/.deps/ctype-sjis.Po +strings/.deps/ctype-tis620.Po +strings/.deps/ctype-uca.Po +strings/.deps/ctype-ucs2.Po +strings/.deps/ctype-ujis.Po +strings/.deps/ctype-utf8.Po +strings/.deps/ctype-win1250ch.Po +strings/.deps/ctype.Po +strings/.deps/decimal.Po +strings/.deps/int2str.Po +strings/.deps/is_prefix.Po +strings/.deps/llstr.Po +strings/.deps/longlong2str.Po +strings/.deps/longlong2str_asm.Po +strings/.deps/my_strchr.Po +strings/.deps/my_strtoll10.Po +strings/.deps/my_vsnprintf.Po +strings/.deps/r_strinstr.Po +strings/.deps/str2int.Po +strings/.deps/str_alloc.Po +strings/.deps/strappend.Po +strings/.deps/strcend.Po +strings/.deps/strcont.Po +strings/.deps/strend.Po +strings/.deps/strfill.Po +strings/.deps/strinstr.Po +strings/.deps/strmake.Po +strings/.deps/strmov.Po +strings/.deps/strnlen.Po +strings/.deps/strnmov.Po +strings/.deps/strstr.Po +strings/.deps/strtod.Po +strings/.deps/strtol.Po +strings/.deps/strtoll.Po +strings/.deps/strtoul.Po +strings/.deps/strtoull.Po +strings/.deps/strxmov.Po +strings/.deps/strxnmov.Po +strings/.deps/xml.Po strings/conf_to_src strings/ctype_autoconf.c strings/ctype_extra_sources.c @@ -1762,7 +2844,14 @@ test1/* test_xml tests/*.ds? tests/*.vcproj +tests/.deps/dummy.Po +tests/.deps/insert_test.Po +tests/.deps/mysql_client_test.Po +tests/.deps/select_test.Po +tests/.deps/thread_test.Po tests/.libs -prune +tests/.libs/lt-mysql_client_test +tests/.libs/mysql_client_test tests/client_test tests/connect_test tests/mysql_client_test @@ -1777,12 +2866,30 @@ tools/mysqlmngd tools/mysqltestmanager tools/mysys_priv.h unittest/examples/*.t +unittest/examples/.deps/no_plan-t.Po +unittest/examples/.deps/simple-t.Po +unittest/examples/.deps/skip-t.Po +unittest/examples/.deps/skip_all-t.Po +unittest/examples/.deps/todo-t.Po unittest/mysys/*.t +unittest/mysys/.deps/base64-t.Po +unittest/mysys/.deps/bitmap-t.Po +unittest/mysys/.deps/my_atomic-t.Po +unittest/mytap/.deps/tap.Po unittest/mytap/t/*.t +unittest/mytap/t/.deps/basic-t.Po unittest/unit vi.h vio/*.ds? vio/*.vcproj +vio/.deps/dummy.Po +vio/.deps/test-ssl.Po +vio/.deps/test-sslclient.Po +vio/.deps/test-sslserver.Po +vio/.deps/vio.Po +vio/.deps/viosocket.Po +vio/.deps/viossl.Po +vio/.deps/viosslfactories.Po vio/test-ssl vio/test-sslclient vio/test-sslserver diff --git a/client/mysqltest.c b/client/mysqltest.c index 06f6ab2f1ce..edcce59e136 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -155,6 +155,7 @@ static my_bool ps_protocol= 0, ps_protocol_enabled= 0; static my_bool sp_protocol= 0, sp_protocol_enabled= 0; static my_bool view_protocol= 0, view_protocol_enabled= 0; static my_bool cursor_protocol= 0, cursor_protocol_enabled= 0; +static my_bool opt_valgrind_test= 0; static int parsing_disabled= 0; static char **default_argv; @@ -3286,6 +3287,8 @@ static struct my_option my_long_options[] = 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"user", 'u', "User for login.", (gptr*) &user, (gptr*) &user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"valgrind", 'N', "Define VALGRIND_TEST to 1.", (gptr*) &opt_valgrind_test, + (gptr*) &opt_valgrind_test, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"verbose", 'v', "Write more.", (gptr*) &verbose, (gptr*) &verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"version", 'V', "Output version information and exit.", @@ -5060,6 +5063,9 @@ static void init_var_hash(MYSQL *mysql) die("Variable hash initialization failed"); my_hash_insert(&var_hash, (byte*) var_init(0,"BIG_TEST", 0, (opt_big_test) ? "1" : "0", 0)); + my_hash_insert(&var_hash, (byte*) var_init(0,"VALGRIND_TEST", 0, + (opt_valgrind_test) ? "1" : "0", + 0)); v= var_init(0,"MAX_TABLES", 0, (sizeof(ulong) == 4) ? "31" : "62",0); my_hash_insert(&var_hash, (byte*) v); v= var_init(0,"SERVER_VERSION", 0, mysql_get_server_info(mysql), 0); diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index befbd3fad3a..6100fba2212 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -68,7 +68,8 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.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 \ - event_scheduler.cc events.cc event_timed.cc \ + event_scheduler.cc events.cc event_data_objects.cc \ + event_queue.cc event_db_repository.cc \ rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \ sql_tablespace.cc \ rpl_injector.cc my_user.c partition_info.cc diff --git a/mysql-test/include/not_valgrind.inc b/mysql-test/include/not_valgrind.inc new file mode 100644 index 00000000000..bdddaf534ca --- /dev/null +++ b/mysql-test/include/not_valgrind.inc @@ -0,0 +1,4 @@ +--require r/not_valgrind.require +--disable_query_log +eval select $VALGRIND_TEST as using_valgrind; +--enable_query_log diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 45accee56fb..e5343a6eeab 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3873,6 +3873,11 @@ sub run_mysqltest ($) { mtr_add_arg($args, "--big-test"); } + if ( $opt_valgrind ) + { + mtr_add_arg($args, "--valgrind"); + } + if ( $opt_compress ) { mtr_add_arg($args, "--compress"); diff --git a/mysql-test/r/events.result b/mysql-test/r/events.result index e115e077535..05b6ff03786 100644 --- a/mysql-test/r/events.result +++ b/mysql-test/r/events.result @@ -85,13 +85,24 @@ SHOW EVENTS; Db Name Definer Type Execute at Interval value Interval field Starts Ends Status events_test event_starts_test root@localhost RECURRING NULL 20 SECOND # # ENABLED DROP EVENT event_starts_test; +create table test_nested(a int); create event e_43 on schedule every 1 second do set @a = 5; -set global event_scheduler = 1; alter event e_43 do alter event e_43 do set @a = 4; +ERROR HY000: Recursivity of EVENT DDL statements is forbidden when body is present +alter event e_43 do +begin +alter event e_43 on schedule every 5 minute; +insert into test_nested values(1); +end| +set global event_scheduler = 1; select db, name, body, status, interval_field, interval_value from mysql.event; db name body status interval_field interval_value -events_test e_43 set @a = 4 ENABLED SECOND 1 +events_test e_43 begin +alter event e_43 on schedule every 5 minute; +insert into test_nested values(1); +end ENABLED MINUTE 5 drop event e_43; +drop table test_nested; "Let's check whether we can use non-qualified names" create table non_qualif(a int); create event non_qualif_ev on schedule every 10 minute do insert into non_qualif values (800219); @@ -100,7 +111,15 @@ a 800219 drop event non_qualif_ev; drop table non_qualif; +alter event non_existant rename to non_existant_too; +ERROR HY000: Unknown event 'non_existant' set global event_scheduler = 2; +create event existant on schedule at now() + interval 1 year do select 12; +alter event non_existant rename to existant; +ERROR HY000: Event 'existant' already exists +alter event existant rename to events_test.existant; +ERROR HY000: Same old and new event name +drop event existant; create table t_event3 (a int, b float); drop event if exists event3; Warnings: @@ -195,6 +214,10 @@ ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND' SHOW EVENTS; ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND' drop event root22; +create event root23 on schedule every -100 year do select 1; +ERROR HY000: INTERVAL is either not positive or too big +create event root23 on schedule every 222222222222222222222 year do select 1; +ERROR HY000: INTERVAL is either not positive or too big drop event root6; drop event root7; drop event root8; @@ -285,9 +308,9 @@ select db, name, body, definer, convert_tz(execute_at, 'UTC', 'SYSTEM'), on_comp db name body definer convert_tz(execute_at, 'UTC', 'SYSTEM') on_completion events_test e_26 set @a = 5 root@localhost 2017-01-01 00:00:00 DROP drop event e_26; -create event e_26 on schedule at NULL disabled do set @a = 5; +create event e_26 on schedule at NULL disable do set @a = 5; ERROR HY000: Incorrect AT value: 'NULL' -create event e_26 on schedule at 'definitely not a datetime' disabled do set @a = 5; +create event e_26 on schedule at 'definitely not a datetime' disable do set @a = 5; ERROR HY000: Incorrect AT value: 'definitely not a datetime' set names utf8; create event задачка on schedule every 123 minute starts now() ends now() + interval 1 month do select 1; @@ -311,7 +334,6 @@ root@localhost закачка events_test "Should be only 1 process" select /*1*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; user host db command state info -event_scheduler localhost NULL Connect Suspended NULL select release_lock("test_lock1"); release_lock("test_lock1") 1 @@ -331,7 +353,7 @@ create event закачка on schedule every 10 hour do select get_lock("test_l "Should have only 2 processes: the scheduler and the locked event" select /*2*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; user host db command state info -event_scheduler localhost NULL Connect Sleeping NULL +event_scheduler localhost NULL Daemon Waiting for next activation NULL root localhost events_test Connect User lock select get_lock("test_lock2", 20) "Release the mutex, the event worker should finish." "Release the mutex, the event worker should finish." @@ -347,18 +369,17 @@ create event закачка21 on schedule every 10 hour do select get_lock("test "Should have only 3 processes: the scheduler, our conn and the locked event" select /*3*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; user host db command state info -event_scheduler localhost NULL Connect Sleeping NULL +event_scheduler localhost NULL Daemon Waiting for next activation NULL root localhost events_test Connect User lock select get_lock("test_lock2_1", 20) set global event_scheduler=2; "Should have only our process now:" select /*4*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; user host db command state info -event_scheduler localhost NULL Connect Suspended NULL root localhost events_test Connect User lock select get_lock("test_lock2_1", 20) drop event закачка21; create table t_16 (s1 int); create trigger t_16_bi before insert on t_16 for each row create event e_16 on schedule every 1 second do set @a=5; -ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger. +ERROR HY000: Recursivity of EVENT DDL statements is forbidden when body is present drop table t_16; create event white_space on schedule every 10 hour diff --git a/mysql-test/r/events_bugs.result b/mysql-test/r/events_bugs.result index 45733c91622..c06af6cc22b 100644 --- a/mysql-test/r/events_bugs.result +++ b/mysql-test/r/events_bugs.result @@ -5,10 +5,10 @@ CREATE EVENT Lower_case ON SCHEDULE EVERY 2 MINUTE DO SELECT 2; ERROR HY000: Event 'Lower_case' already exists DROP EVENT Lower_case; SET NAMES cp1251; -CREATE EVENT äîëåí_ðåãèñòúð_1251 ON SCHEDULE EVERY 1 YEAR DO SELECT 100; -CREATE EVENT ÄîËåÍ_ðåãèñòúð_1251 ON SCHEDULE EVERY 2 YEAR DO SELECT 200; +CREATE EVENT äîëåÃ_ðåãèñòúð_1251 ON SCHEDULE EVERY 1 YEAR DO SELECT 100; +CREATE EVENT ÄîËåÃ_ðåãèñòúð_1251 ON SCHEDULE EVERY 2 YEAR DO SELECT 200; ERROR HY000: Event 'ДоЛеÐ_региÑÑ‚ÑŠÑ€_1251' already exists -DROP EVENT ÄîËåÍ_ðåãèñòúð_1251; +DROP EVENT ÄîËåÃ_ðåãèñòúð_1251; SET NAMES utf8; CREATE EVENT долен_региÑÑ‚ÑŠÑ€_утф8 ON SCHEDULE EVERY 3 YEAR DO SELECT 300; CREATE EVENT ДОЛЕÐ_региÑÑ‚ÑŠÑ€_утф8 ON SCHEDULE EVERY 4 YEAR DO SELECT 400; @@ -17,26 +17,26 @@ DROP EVENT ДОЛЕÐ_региÑÑ‚ÑŠÑ€_утф8; SET NAMES latin1; set @a=3; CREATE PROCEDURE p_16 () CREATE EVENT e_16 ON SCHEDULE EVERY @a SECOND DO SET @a=5; -call p_16(); -"Here we used to crash!" -call p_16(); -ERROR HY000: Event 'e_16' already exists -call p_16(); -ERROR HY000: Event 'e_16' already exists -DROP EVENT e_16; -CALL p_16(); -CALL p_16(); -ERROR HY000: Event 'e_16' already exists -DROP PROCEDURE p_16; -DROP EVENT e_16; +ERROR HY000: Recursivity of EVENT DDL statements is forbidden when body is present create event e_55 on schedule at 99990101000000 do drop table t; ERROR HY000: Incorrect AT value: '99990101000000' create event e_55 on schedule every 10 hour starts 99990101000000 do drop table t; ERROR HY000: Incorrect STARTS value: '99990101000000' create event e_55 on schedule every 10 minute ends 99990101000000 do drop table t; ERROR HY000: ENDS is either invalid or before STARTS +create event e_55 on schedule at 10000101000000 do drop table t; +ERROR HY000: Activation (AT) time is in the past +create event e_55 on schedule at 20000101000000 do drop table t; +ERROR HY000: Activation (AT) time is in the past +create event e_55 on schedule at 20200101000000 starts 10000101000000 do drop table t; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'starts 10000101000000 do drop table t' at line 1 +create event e_55 on schedule at 20200101000000 ends 10000101000000 do drop table t; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ends 10000101000000 do drop table t' at line 1 +create event e_55 on schedule at 20200101000000 starts 10000101000000 ends 10000101000000 do drop table t; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'starts 10000101000000 ends 10000101000000 do drop table t' at line 1 +create event e_55 on schedule every 10 hour starts 10000101000000 do drop table t; +ERROR HY000: Incorrect STARTS value: '10000101000000' set global event_scheduler=2; -"Wait a bit to settle down" delete from mysql.event; set global event_scheduler= 1; set @old_sql_mode:=@@sql_mode; @@ -50,10 +50,10 @@ select get_lock('test_bug16407', 60); drop table "hashed_num"; end| "Now if everything is fine the event has compiled and is locked -select /*1*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; -user host db command state info -event_scheduler localhost NULL Connect Sleeping NULL -root localhost events_test Connect User lock select get_lock('test_bug16407', 60) +select /*1*/ user, host, db, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; +user host db info +event_scheduler localhost NULL NULL +root localhost events_test select get_lock('test_bug16407', 60) select release_lock('test_bug16407'); release_lock('test_bug16407') 1 @@ -68,6 +68,11 @@ select event_schema, event_name, sql_mode from information_schema.events order b event_schema event_name sql_mode events_test e_16407 STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER drop event e_16407; +set sql_mode="ansi"; +select get_lock('ee_16407_2', 60); +get_lock('ee_16407_2', 60) +1 +set global event_scheduler= 1; "Another sql_mode test" set sql_mode="traditional"; create table events_smode_test(ev_name char(10), a date) engine=myisam; @@ -75,6 +80,7 @@ create table events_smode_test(ev_name char(10), a date) engine=myisam; create event ee_16407_2 on schedule every 60 second do begin select get_lock('ee_16407_2', 60) /*ee_16407_2*/; +select release_lock('ee_16407_2'); insert into events_test.events_smode_test values('ee_16407_2','1980-19-02'); end| insert into events_smode_test values ('test','1980-19-02')| @@ -83,6 +89,7 @@ ERROR 22007: Incorrect date value: '1980-19-02' for column 'a' at row 1 create event ee_16407_3 on schedule every 60 second do begin select get_lock('ee_16407_2', 60) /*ee_16407_3*/; +select release_lock('ee_16407_2'); insert into events_test.events_smode_test values ('ee_16407_3','1980-02-19'); insert into events_test.events_smode_test values ('ee_16407_3','1980-02-29'); end| @@ -91,6 +98,7 @@ set sql_mode=""| create event ee_16407_4 on schedule every 60 second do begin select get_lock('ee_16407_2', 60) /*ee_16407_4*/; +select release_lock('ee_16407_2'); insert into events_test.events_smode_test values ('ee_16407_4','10-11-1956'); end| select event_schema, event_name, sql_mode from information_schema.events order by event_schema, event_name; @@ -98,23 +106,18 @@ event_schema event_name sql_mode events_test ee_16407_2 STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER events_test ee_16407_3 STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER events_test ee_16407_4 -set sql_mode="ansi"; -select get_lock('ee_16407_2', 60); -get_lock('ee_16407_2', 60) -1 -set global event_scheduler= 1; -select /*2*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; -user host db command state info -event_scheduler localhost NULL Connect Sleeping NULL -root localhost events_test Connect User lock select get_lock('ee_16407_2', 60) -root localhost events_test Connect User lock select get_lock('ee_16407_2', 60) -root localhost events_test Connect User lock select get_lock('ee_16407_2', 60) +select /*2*/ user, host, db, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; +user host db info +event_scheduler localhost NULL NULL +root localhost events_test select get_lock('ee_16407_2', 60) /*ee_16407_2*/ +root localhost events_test select get_lock('ee_16407_2', 60) /*ee_16407_3*/ +root localhost events_test select get_lock('ee_16407_2', 60) /*ee_16407_4*/ select release_lock('ee_16407_2'); release_lock('ee_16407_2') 1 -select /*3*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; -user host db command state info -event_scheduler localhost NULL Connect Sleeping NULL +select /*3*/ user, host, db, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; +user host db info +event_scheduler localhost NULL NULL set global event_scheduler= 2; select * from events_smode_test order by ev_name, a; ev_name a @@ -132,37 +135,39 @@ drop event ee_16407_3; drop event ee_16407_4; "And now one last test regarding sql_mode and call of SP from an event" delete from events_smode_test; +set sql_mode='ansi'; +select get_lock('ee_16407_5', 60); +get_lock('ee_16407_5', 60) +1 +set global event_scheduler= 1; set sql_mode='traditional'; create procedure ee_16407_5_pendant() begin insert into events_test.events_smode_test values('ee_16407_5','2001-02-29'); end| create procedure ee_16407_6_pendant() begin insert into events_test.events_smode_test values('ee_16407_6','2004-02-29'); end| create event ee_16407_5 on schedule every 60 second do begin select get_lock('ee_16407_5', 60) /*ee_16407_5*/; +select release_lock('ee_16407_5'); call events_test.ee_16407_5_pendant(); end| create event ee_16407_6 on schedule every 60 second do begin select get_lock('ee_16407_5', 60) /*ee_16407_6*/; +select release_lock('ee_16407_5'); call events_test.ee_16407_6_pendant(); end| -set sql_mode='ansi'; -select get_lock('ee_16407_5', 60); -get_lock('ee_16407_5', 60) -1 -set global event_scheduler= 1; "Should have 2 locked processes" -select /*4*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; -user host db command state info -event_scheduler localhost NULL Connect Sleeping NULL -root localhost events_test Connect User lock select get_lock('ee_16407_5', 60) -root localhost events_test Connect User lock select get_lock('ee_16407_5', 60) +select /*4*/ user, host, db, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; +user host db info +event_scheduler localhost NULL NULL +root localhost events_test select get_lock('ee_16407_5', 60) /*ee_16407_5*/ +root localhost events_test select get_lock('ee_16407_5', 60) /*ee_16407_6*/ select release_lock('ee_16407_5'); release_lock('ee_16407_5') 1 "Should have 0 processes locked" -select /*5*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; -user host db command state info -event_scheduler localhost NULL Connect Sleeping NULL +select /*5*/ user, host, db, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; +user host db info +event_scheduler localhost NULL NULL select * from events_smode_test order by ev_name, a; ev_name a ee_16407_6 2004-02-29 @@ -201,4 +206,12 @@ events_test mysqltest_user1 mysqltest_user1@localhost RECURRING ENABLED drop event events_test.mysqltest_user1; drop user mysqltest_user1@localhost; drop database mysqltest_db1; +create event e_53 on schedule at (select s1 from ttx) do drop table t; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select s1 from ttx) do drop table t' at line 1 +create event e_53 on schedule every (select s1 from ttx) second do drop table t; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select s1 from ttx) second do drop table t' at line 1 +create event e_53 on schedule every 5 second starts (select s1 from ttx) do drop table t; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select s1 from ttx) do drop table t' at line 1 +create event e_53 on schedule every 5 second ends (select s1 from ttx) do drop table t; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select s1 from ttx) do drop table t' at line 1 drop database events_test; diff --git a/mysql-test/r/events_logs_tests.result b/mysql-test/r/events_logs_tests.result index 950090399d5..5971dead3c8 100644 --- a/mysql-test/r/events_logs_tests.result +++ b/mysql-test/r/events_logs_tests.result @@ -1,5 +1,5 @@ -create database if not exists events_test; -use events_test; +CREATE DATABASE IF NOT EXISTS events_test; +USE events_test; "We use procedure here because its statements won't be logged into the general log" "If we had used normal select that are logged in different ways depending on whether" "the test suite is run in normal mode or with --ps-protocol" @@ -8,18 +8,21 @@ BEGIN SELECT user_host, argument FROM mysql.general_log WHERE argument LIKE '%alabala%'; END| "Check General Query Log" -SET GLOBAL event_scheduler=2; -create event log_general on schedule every 1 minute do SELect 'alabala', sleep(3) from dual; -TRUNCATE mysql.general_log; -"1 row, the current statement!" -call select_general_log(); +CALL select_general_log(); user_host argument +USER_HOST CREATE procedure select_general_log() +BEGIN +SELECT user_host, argument FROM mysql.general_log WHERE argument LIKE '%alabala%'; +END SET GLOBAL event_scheduler=1; +TRUNCATE mysql.general_log; +CREATE EVENT log_general ON SCHEDULE EVERY 1 MINUTE DO SELECT 'alabala', SLEEP(1) FROM DUAL; "Wait the scheduler to start" -"Should see 3 rows - the 'SELect' is in the middle. The other two are selects from general_log" -call select_general_log(); +"Should see 2 rows - the 'SELECT' is in the middle. The other two are selects from general_log" +CALL select_general_log(); user_host argument -USER_HOST SELect 'alabala', sleep(3) from dual +USER_HOST CREATE EVENT log_general ON SCHEDULE EVERY 1 MINUTE DO SELECT 'alabala', SLEEP(1) FROM DUAL +USER_HOST SELECT 'alabala', SLEEP(1) FROM DUAL DROP PROCEDURE select_general_log; DROP EVENT log_general; SET GLOBAL event_scheduler=2; @@ -52,10 +55,11 @@ TRUNCATE mysql.slow_log; CREATE TABLE slow_event_test (slo_val tinyint, val tinyint); SET SESSION long_query_time=1; "This won't go to the slow log" -CREATE EVENT long_event ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(3); SELECT * FROM slow_event_test; slo_val val +SET SESSION long_query_time=1; SET GLOBAL event_scheduler=1; +CREATE EVENT long_event ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(1.5); "Sleep some more time than the actual event run will take" SHOW VARIABLES LIKE 'event_scheduler'; Variable_name Value @@ -64,7 +68,7 @@ event_scheduler 1 SELECT * FROM slow_event_test; slo_val val 4 0 -"Check slow log. Should not see anything because 3 is under the threshold of 4 for GLOBAL, though over SESSION which is 2" +"Check slow log. Should not see anything because 1.5 is under the threshold of 300 for GLOBAL, though over SESSION which is 2" SELECT user_host, query_time, db, sql_text FROM mysql.slow_log; user_host query_time db sql_text "This should go to the slow log" @@ -89,4 +93,4 @@ TRUNCATE mysql.slow_log; DROP TABLE slow_event_test; SET GLOBAL long_query_time =@old_global_long_query_time; SET SESSION long_query_time =@old_session_long_query_time; -drop database events_test; +DROP DATABASE events_test; diff --git a/mysql-test/r/events_restart_phase1.result b/mysql-test/r/events_restart_phase1.result new file mode 100644 index 00000000000..cf0a24c267d --- /dev/null +++ b/mysql-test/r/events_restart_phase1.result @@ -0,0 +1,10 @@ +create database if not exists mysqltest_events_test; +use mysqltest_events_test; +set global event_scheduler=2; +create table execution_log(name char(10)); +create event abc1 on schedule every 1 second do insert into execution_log value('abc1'); +create event abc2 on schedule every 1 second do insert into execution_log value('abc2'); +create event abc3 on schedule every 1 second do insert into execution_log value('abc3'); +insert into mysql.event values ('db1','bad','select 42','root@localhost',NULL,1000,'MICROSECOND','2006-05-05 17:39:11','2006-05-05 17:39:20','2016-05-05 15:39:24','2016-05-05 15:39:11',NULL,'ENABLED','DROP','','comment1'); +insert into mysql.event values ('db1','bad2','sect','root@localhost',NULL,1000,'SECOND','2006-05-05 17:39:11','2006-05-05 17:39:20','2016-05-05 15:39:24','2016-05-05 15:39:11',NULL,'ENABLED','DROP','','comment2'); +"Now we restart the server" diff --git a/mysql-test/r/events_restart_phase2.result b/mysql-test/r/events_restart_phase2.result new file mode 100644 index 00000000000..703cb92324f --- /dev/null +++ b/mysql-test/r/events_restart_phase2.result @@ -0,0 +1,6 @@ +use mysqltest_events_test; +"Should get 0 rows because the queue aborted run +select distinct name from execution_log order by name; +name +delete from mysql.event where name like 'bad%'; +"Now restart the server again" diff --git a/mysql-test/r/events_restart_phase3.result b/mysql-test/r/events_restart_phase3.result new file mode 100644 index 00000000000..398a9d06546 --- /dev/null +++ b/mysql-test/r/events_restart_phase3.result @@ -0,0 +1,9 @@ +use mysqltest_events_test; +"Should get 3 rows : abc1, abc2, abc3 +select distinct name from execution_log order by name; +name +drop event abc1; +drop event abc2; +drop event abc3; +drop table execution_log; +drop database mysqltest_events_test; diff --git a/mysql-test/r/events_scheduling.result b/mysql-test/r/events_scheduling.result index eb44751c176..a068f7f19bc 100644 --- a/mysql-test/r/events_scheduling.result +++ b/mysql-test/r/events_scheduling.result @@ -6,7 +6,6 @@ CREATE TABLE table_3(a int); CREATE TABLE table_4(a int); CREATE TABLE T19170(s1 TIMESTAMP); SET GLOBAL event_scheduler=1; -CREATE EVENT E19170 ON SCHEDULE EVERY 2 SECOND DO INSERT INTO T19170 VALUES(CURRENT_TIMESTAMP); CREATE EVENT two_sec ON SCHEDULE EVERY 2 SECOND DO INSERT INTO table_1 VALUES(1); CREATE EVENT start_n_end ON SCHEDULE EVERY 1 SECOND @@ -39,17 +38,12 @@ DROP EVENT start_n_end; DROP EVENT only_one_time; ERROR HY000: Unknown event 'only_one_time' "Should be preserved" -SELECT EVENT_NAME, STATUS FROM INFORMATION_SCHEMA.EVENTS; +SELECT EVENT_NAME, STATUS FROM INFORMATION_SCHEMA.EVENTS ORDER BY EVENT_NAME; EVENT_NAME STATUS -E19170 ENABLED two_time DISABLED DROP EVENT two_time; DROP TABLE table_1; DROP TABLE table_2; DROP TABLE table_3; DROP TABLE table_4; -"Checking for multiple executions in one second, should not happen -> 0 as result" -SELECT COUNT(*) FROM (SELECT s1, COUNT(*) AS cnt FROM T19170 GROUP BY s1) AS tmp WHERE tmp.cnt > 1; -COUNT(*) -0 DROP DATABASE events_test; diff --git a/mysql-test/r/not_embedded_server.result b/mysql-test/r/not_embedded_server.result index 7cbe91b3753..e471b5a3afa 100644 --- a/mysql-test/r/not_embedded_server.result +++ b/mysql-test/r/not_embedded_server.result @@ -1,6 +1,5 @@ prepare stmt1 from ' show full processlist '; execute stmt1; Id User Host db Command Time State Info -number event_scheduler localhost NULL Connect time Suspended NULL number root localhost test Query time NULL show full processlist deallocate prepare stmt1; diff --git a/mysql-test/r/not_valgrind.require b/mysql-test/r/not_valgrind.require new file mode 100644 index 00000000000..5d779c6bfcb --- /dev/null +++ b/mysql-test/r/not_valgrind.require @@ -0,0 +1,2 @@ +using_valgrind +0 diff --git a/mysql-test/r/skip_name_resolve.result b/mysql-test/r/skip_name_resolve.result index 855876825ad..8ef52e75238 100644 --- a/mysql-test/r/skip_name_resolve.result +++ b/mysql-test/r/skip_name_resolve.result @@ -10,6 +10,5 @@ user() # show processlist; Id User Host db Command Time State Info -<id> event_scheduler <host> NULL <command> <time> <state> <info> <id> root <host> test <command> <time> <state> <info> <id> root <host> test <command> <time> <state> <info> diff --git a/mysql-test/r/sp-threads.result b/mysql-test/r/sp-threads.result index 3cba437e0a6..c516d7a643f 100644 --- a/mysql-test/r/sp-threads.result +++ b/mysql-test/r/sp-threads.result @@ -34,7 +34,6 @@ lock tables t2 write; call bug9486(); show processlist; Id User Host db Command Time State Info -# event_scheduler localhost NULL Connect # Suspended NULL # root localhost test Sleep # NULL # root localhost test Query # Locked update t1, t2 set val= 1 where id1=id2 # root localhost test Query # NULL show processlist diff --git a/mysql-test/r/sp_notembedded.result b/mysql-test/r/sp_notembedded.result index a15f5013ef6..564b8031c35 100644 --- a/mysql-test/r/sp_notembedded.result +++ b/mysql-test/r/sp_notembedded.result @@ -18,11 +18,9 @@ show processlist; end| call bug4902_2()| Id User Host db Command Time State Info -# event_scheduler localhost NULL Connect # Suspended NULL # root localhost test Query # NULL show processlist call bug4902_2()| Id User Host db Command Time State Info -# event_scheduler localhost NULL Connect # Suspended NULL # root localhost test Query # NULL show processlist drop procedure bug4902_2| drop table if exists t1| diff --git a/mysql-test/r/status.result b/mysql-test/r/status.result index 83c6a6f5288..45b84219f2a 100644 --- a/mysql-test/r/status.result +++ b/mysql-test/r/status.result @@ -52,22 +52,22 @@ drop table t1; FLUSH STATUS; SHOW STATUS LIKE 'max_used_connections'; Variable_name Value -Max_used_connections 2 +Max_used_connections 1 SET @save_thread_cache_size=@@thread_cache_size; SET GLOBAL thread_cache_size=3; SHOW STATUS LIKE 'max_used_connections'; Variable_name Value -Max_used_connections 4 +Max_used_connections 3 FLUSH STATUS; SHOW STATUS LIKE 'max_used_connections'; Variable_name Value -Max_used_connections 3 +Max_used_connections 2 SHOW STATUS LIKE 'max_used_connections'; Variable_name Value -Max_used_connections 4 +Max_used_connections 3 SHOW STATUS LIKE 'max_used_connections'; Variable_name Value -Max_used_connections 5 +Max_used_connections 4 SET GLOBAL thread_cache_size=@save_thread_cache_size; show status like 'com_show_status'; Variable_name Value diff --git a/mysql-test/t/events.test b/mysql-test/t/events.test index aac13a55dd3..2a27144bc27 100644 --- a/mysql-test/t/events.test +++ b/mysql-test/t/events.test @@ -18,7 +18,7 @@ CREATE EVENT e_x2 ON SCHEDULE EVERY 1 SECOND DO DROP TABLE x_table; connection default; SHOW DATABASES LIKE 'db_x'; SET GLOBAL event_scheduler=1; ---sleep 1.5 +--sleep 1.2 SHOW DATABASES LIKE 'db_x'; SHOW TABLES FROM db_x; SET GLOBAL event_scheduler=2; @@ -81,23 +81,42 @@ SHOW EVENTS; DROP EVENT event_starts_test; # # +create table test_nested(a int); create event e_43 on schedule every 1 second do set @a = 5; -set global event_scheduler = 1; ---sleep 2 +--error 1562 alter event e_43 do alter event e_43 do set @a = 4; ---sleep 2 +delimiter |; +alter event e_43 do +begin + alter event e_43 on schedule every 5 minute; + insert into test_nested values(1); +end| +delimiter ;| +set global event_scheduler = 1; +--sleep 3 select db, name, body, status, interval_field, interval_value from mysql.event; drop event e_43; ---sleep 1 +drop table test_nested; --echo "Let's check whether we can use non-qualified names" create table non_qualif(a int); create event non_qualif_ev on schedule every 10 minute do insert into non_qualif values (800219); ---sleep 1 +--sleep 1.1 select * from non_qualif; drop event non_qualif_ev; drop table non_qualif; + +--error ER_EVENT_DOES_NOT_EXIST +alter event non_existant rename to non_existant_too; + set global event_scheduler = 2; +create event existant on schedule at now() + interval 1 year do select 12; +--error ER_EVENT_ALREADY_EXISTS +alter event non_existant rename to existant; +--error ER_EVENT_SAME_NAME +alter event existant rename to events_test.existant; +drop event existant; + create table t_event3 (a int, b float); drop event if exists event3; @@ -156,6 +175,10 @@ show create event root22; --error ER_NOT_SUPPORTED_YET SHOW EVENTS; drop event root22; +--error ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG +create event root23 on schedule every -100 year do select 1; +--error ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG +create event root23 on schedule every 222222222222222222222 year do select 1; drop event root6; drop event root7; drop event root8; @@ -201,34 +224,34 @@ SHOW EVENTS; CREATE TABLE event_like LIKE mysql.event; INSERT INTO event_like SELECT * FROM mysql.event; #sleep a bit or we won't catch the change of time ---sleep 1 +--sleep 1.1 ALTER TABLE mysql.event MODIFY db char(64) character set cp1251 default ''; --error ER_CANNOT_LOAD_FROM_TABLE SELECT event_name FROM INFORMATION_SCHEMA.EVENTS; ALTER TABLE mysql.event MODIFY db char(20) character set utf8 collate utf8_bin default ''; #wait a bit or we won't see the difference because of seconds resolution ---sleep 1 +--sleep 1.1 SHOW CREATE TABLE mysql.event; --error ER_CANNOT_LOAD_FROM_TABLE SELECT event_name FROM INFORMATION_SCHEMA.EVENTS; ---sleep 1 +--sleep 1.1 ALTER TABLE mysql.event MODIFY db char(64) character set utf8 collate utf8_bin default ''; ---sleep 1 +--sleep 1.1 --echo "This should work" --replace_column 8 # 9 # SHOW EVENTS; ---sleep 1 +--sleep 1.1 ALTER TABLE mysql.event MODIFY db char(64) character set cp1251 default ''; --error ER_CANNOT_LOAD_FROM_TABLE SELECT event_name FROM INFORMATION_SCHEMA.EVENTS; ---sleep 1 +--sleep 1.1 ALTER TABLE mysql.event MODIFY db varchar(64) character set utf8 collate utf8_bin default ''; ---sleep 1 +--sleep 1.1 --error ER_CANNOT_LOAD_FROM_TABLE SELECT event_name FROM INFORMATION_SCHEMA.EVENTS; ---sleep 1 +--sleep 1.1 ALTER TABLE mysql.event DROP comment, DROP starts; ---sleep 1 +--sleep 1.1 --error ER_COL_COUNT_DOESNT_MATCH_CORRUPTED SELECT event_name FROM INFORMATION_SCHEMA.EVENTS; DROP TABLE mysql.event; @@ -246,9 +269,9 @@ create event e_26 on schedule at '2017-01-01 00:00:00' disable do set @a = 5; select db, name, body, definer, convert_tz(execute_at, 'UTC', 'SYSTEM'), on_completion from mysql.event; drop event e_26; --error ER_WRONG_VALUE -create event e_26 on schedule at NULL disabled do set @a = 5; +create event e_26 on schedule at NULL disable do set @a = 5; --error ER_WRONG_VALUE -create event e_26 on schedule at 'definitely not a datetime' disabled do set @a = 5; +create event e_26 on schedule at 'definitely not a datetime' disable do set @a = 5; set names utf8; create event задачка on schedule every 123 minute starts now() ends now() + interval 1 month do select 1; @@ -307,6 +330,7 @@ create event закачка21 on schedule every 10 hour do select get_lock("test --echo "Should have only 3 processes: the scheduler, our conn and the locked event" select /*3*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; set global event_scheduler=2; +--sleep 0.8 --echo "Should have only our process now:" select /*4*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; drop event закачка21; @@ -315,7 +339,7 @@ drop event закачка21; # Bug #16410 Events: CREATE EVENT is legal in a CREATE TRIGGER statement # create table t_16 (s1 int); ---error 1422 +--error 1562 create trigger t_16_bi before insert on t_16 for each row create event e_16 on schedule every 1 second do set @a=5; drop table t_16; # diff --git a/mysql-test/t/events_bugs.test b/mysql-test/t/events_bugs.test index 9434de7be7d..692e85255ef 100644 --- a/mysql-test/t/events_bugs.test +++ b/mysql-test/t/events_bugs.test @@ -30,19 +30,8 @@ SET NAMES latin1; # START - BUG#16408: Events: crash for an event in a procedure # set @a=3; +--error 1562 CREATE PROCEDURE p_16 () CREATE EVENT e_16 ON SCHEDULE EVERY @a SECOND DO SET @a=5; -call p_16(); ---echo "Here we used to crash!" ---error ER_EVENT_ALREADY_EXISTS -call p_16(); ---error ER_EVENT_ALREADY_EXISTS -call p_16(); -DROP EVENT e_16; -CALL p_16(); ---error ER_EVENT_ALREADY_EXISTS -CALL p_16(); -DROP PROCEDURE p_16; -DROP EVENT e_16; # # END - BUG#16408: Events: crash for an event in a procedure # @@ -56,6 +45,19 @@ create event e_55 on schedule at 99990101000000 do drop table t; create event e_55 on schedule every 10 hour starts 99990101000000 do drop table t; --error ER_EVENT_ENDS_BEFORE_STARTS create event e_55 on schedule every 10 minute ends 99990101000000 do drop table t; +--error ER_EVENT_EXEC_TIME_IN_THE_PAST +create event e_55 on schedule at 10000101000000 do drop table t; +--error ER_EVENT_EXEC_TIME_IN_THE_PAST +create event e_55 on schedule at 20000101000000 do drop table t; +--error ER_PARSE_ERROR +create event e_55 on schedule at 20200101000000 starts 10000101000000 do drop table t; +--error ER_PARSE_ERROR +create event e_55 on schedule at 20200101000000 ends 10000101000000 do drop table t; +--error ER_PARSE_ERROR +create event e_55 on schedule at 20200101000000 starts 10000101000000 ends 10000101000000 do drop table t; +--error ER_WRONG_VALUE +create event e_55 on schedule every 10 hour starts 10000101000000 do drop table t; + # # End - 16396: Events: Distant-future dates become past dates # @@ -64,8 +66,6 @@ create event e_55 on schedule every 10 minute ends 99990101000000 do drop table # Start - 16407: Events: Changes in sql_mode won't be taken into account # set global event_scheduler=2; ---echo "Wait a bit to settle down" ---sleep 1 delete from mysql.event; set global event_scheduler= 1; set @old_sql_mode:=@@sql_mode; @@ -78,11 +78,13 @@ begin drop table "hashed_num"; end| delimiter ;| ---sleep 1 +--sleep 0.8 --echo "Now if everything is fine the event has compiled and is locked -select /*1*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; +select /*1*/ user, host, db, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; select release_lock('test_bug16407'); + set global event_scheduler= 2; + select event_schema, event_name, sql_mode from information_schema.events order by event_schema, event_name; --echo "Let's check whether we change the sql_mode on ALTER EVENT" set sql_mode='traditional'; @@ -90,6 +92,10 @@ alter event e_16407 do select 1; select event_schema, event_name, sql_mode from information_schema.events order by event_schema, event_name; drop event e_16407; +set sql_mode="ansi"; +select get_lock('ee_16407_2', 60); + +set global event_scheduler= 1; --echo "Another sql_mode test" set sql_mode="traditional"; create table events_smode_test(ev_name char(10), a date) engine=myisam; @@ -98,6 +104,7 @@ delimiter |; create event ee_16407_2 on schedule every 60 second do begin select get_lock('ee_16407_2', 60) /*ee_16407_2*/; + select release_lock('ee_16407_2'); insert into events_test.events_smode_test values('ee_16407_2','1980-19-02'); end| --error ER_TRUNCATED_WRONG_VALUE @@ -106,6 +113,7 @@ insert into events_smode_test values ('test','1980-19-02')| create event ee_16407_3 on schedule every 60 second do begin select get_lock('ee_16407_2', 60) /*ee_16407_3*/; + select release_lock('ee_16407_2'); insert into events_test.events_smode_test values ('ee_16407_3','1980-02-19'); insert into events_test.events_smode_test values ('ee_16407_3','1980-02-29'); end| @@ -114,18 +122,16 @@ set sql_mode=""| create event ee_16407_4 on schedule every 60 second do begin select get_lock('ee_16407_2', 60) /*ee_16407_4*/; + select release_lock('ee_16407_2'); insert into events_test.events_smode_test values ('ee_16407_4','10-11-1956'); end| delimiter ;| select event_schema, event_name, sql_mode from information_schema.events order by event_schema, event_name; -set sql_mode="ansi"; -select get_lock('ee_16407_2', 60); -set global event_scheduler= 1; --sleep 1 -select /*2*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; +select /*2*/ user, host, db, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; select release_lock('ee_16407_2'); ---sleep 2 -select /*3*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; +--sleep 1.2 +select /*3*/ user, host, db, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; set global event_scheduler= 2; select * from events_smode_test order by ev_name, a; --echo "OK, last check before we drop them" @@ -137,6 +143,11 @@ drop event ee_16407_4; --echo "And now one last test regarding sql_mode and call of SP from an event" delete from events_smode_test; +set sql_mode='ansi'; +select get_lock('ee_16407_5', 60); + +set global event_scheduler= 1; + set sql_mode='traditional'; delimiter |; create procedure ee_16407_5_pendant() begin insert into events_test.events_smode_test values('ee_16407_5','2001-02-29'); end| @@ -144,24 +155,23 @@ create procedure ee_16407_6_pendant() begin insert into events_test.events_smode create event ee_16407_5 on schedule every 60 second do begin select get_lock('ee_16407_5', 60) /*ee_16407_5*/; + select release_lock('ee_16407_5'); call events_test.ee_16407_5_pendant(); end| create event ee_16407_6 on schedule every 60 second do begin select get_lock('ee_16407_5', 60) /*ee_16407_6*/; + select release_lock('ee_16407_5'); call events_test.ee_16407_6_pendant(); end| delimiter ;| -set sql_mode='ansi'; -select get_lock('ee_16407_5', 60); -set global event_scheduler= 1; --sleep 1 --echo "Should have 2 locked processes" -select /*4*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; +select /*4*/ user, host, db, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; select release_lock('ee_16407_5'); ---sleep 2 +--sleep 1.3 --echo "Should have 0 processes locked" -select /*5*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; +select /*5*/ user, host, db, info from information_schema.processlist where info is null or info not like '%processlist%' order by info; select * from events_smode_test order by ev_name, a; --echo "And here we check one more time before we drop the events" select event_schema, event_name, sql_mode from information_schema.events order by event_schema, event_name; @@ -209,4 +219,19 @@ drop database mysqltest_db1; # END - 18897: Events: unauthorized action possible with alter event rename # +# +# START - BUG#16394: Events: Crash if schedule contains SELECT +# +--error ER_PARSE_ERROR +create event e_53 on schedule at (select s1 from ttx) do drop table t; +--error ER_PARSE_ERROR +create event e_53 on schedule every (select s1 from ttx) second do drop table t; +--error ER_PARSE_ERROR +create event e_53 on schedule every 5 second starts (select s1 from ttx) do drop table t; +--error ER_PARSE_ERROR +create event e_53 on schedule every 5 second ends (select s1 from ttx) do drop table t; +# +# END - BUG#16394: Events: Crash if schedule contains SELECT +# + drop database events_test; diff --git a/mysql-test/t/events_logs_tests.test b/mysql-test/t/events_logs_tests.test index 5c252b1174b..62c2aedf6cc 100644 --- a/mysql-test/t/events_logs_tests.test +++ b/mysql-test/t/events_logs_tests.test @@ -1,8 +1,8 @@ # Can't test with embedded server that doesn't support grants -- source include/not_embedded.inc -create database if not exists events_test; -use events_test; +CREATE DATABASE IF NOT EXISTS events_test; +USE events_test; --echo "We use procedure here because its statements won't be logged into the general log" --echo "If we had used normal select that are logged in different ways depending on whether" --echo "the test suite is run in normal mode or with --ps-protocol" @@ -13,22 +13,19 @@ BEGIN END| delimiter ;| --echo "Check General Query Log" -SET GLOBAL event_scheduler=2; -create event log_general on schedule every 1 minute do SELect 'alabala', sleep(3) from dual; -TRUNCATE mysql.general_log; ---echo "1 row, the current statement!" --replace_column 1 USER_HOST -call select_general_log(); +CALL select_general_log(); SET GLOBAL event_scheduler=1; +TRUNCATE mysql.general_log; +CREATE EVENT log_general ON SCHEDULE EVERY 1 MINUTE DO SELECT 'alabala', SLEEP(1) FROM DUAL; --echo "Wait the scheduler to start" ---echo "Should see 3 rows - the 'SELect' is in the middle. The other two are selects from general_log" ---sleep 2 +--sleep 1.5 +--echo "Should see 2 rows - the 'SELECT' is in the middle. The other two are selects from general_log" --replace_column 1 USER_HOST -call select_general_log(); +CALL select_general_log(); DROP PROCEDURE select_general_log; DROP EVENT log_general; SET GLOBAL event_scheduler=2; ---sleep 1 --echo "Check slow query log" --disable_query_log @@ -72,15 +69,16 @@ TRUNCATE mysql.slow_log; CREATE TABLE slow_event_test (slo_val tinyint, val tinyint); SET SESSION long_query_time=1; --echo "This won't go to the slow log" -CREATE EVENT long_event ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(3); SELECT * FROM slow_event_test; +SET SESSION long_query_time=1; SET GLOBAL event_scheduler=1; +CREATE EVENT long_event ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(1.5); --echo "Sleep some more time than the actual event run will take" ---sleep 5 +--sleep 2 SHOW VARIABLES LIKE 'event_scheduler'; --echo "Check our table. Should see 1 row" SELECT * FROM slow_event_test; ---echo "Check slow log. Should not see anything because 3 is under the threshold of 4 for GLOBAL, though over SESSION which is 2" +--echo "Check slow log. Should not see anything because 1.5 is under the threshold of 300 for GLOBAL, though over SESSION which is 2" SELECT user_host, query_time, db, sql_text FROM mysql.slow_log; --echo "This should go to the slow log" DROP EVENT long_event; @@ -88,7 +86,7 @@ SET SESSION long_query_time=10; SET GLOBAL long_query_time=1; CREATE EVENT long_event2 ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(2); --echo "Sleep some more time than the actual event run will take" ---sleep 3 +--sleep 2.5 --echo "Check our table. Should see 2 rows" SELECT * FROM slow_event_test; --echo "Check slow log. Should see 1 row because 4 is over the threshold of 3 for GLOBAL, though under SESSION which is 10" @@ -102,4 +100,4 @@ DROP TABLE slow_event_test; SET GLOBAL long_query_time =@old_global_long_query_time; SET SESSION long_query_time =@old_session_long_query_time; -drop database events_test; +DROP DATABASE events_test; diff --git a/mysql-test/t/events_restart_phase1.test b/mysql-test/t/events_restart_phase1.test new file mode 100644 index 00000000000..879591e6141 --- /dev/null +++ b/mysql-test/t/events_restart_phase1.test @@ -0,0 +1,17 @@ +# Can't test with embedded server that doesn't support grants +-- source include/not_embedded.inc + +--disable_warnings +create database if not exists mysqltest_events_test; +--enable_warnings + +use mysqltest_events_test; +set global event_scheduler=2; +create table execution_log(name char(10)); +create event abc1 on schedule every 1 second do insert into execution_log value('abc1'); +create event abc2 on schedule every 1 second do insert into execution_log value('abc2'); +create event abc3 on schedule every 1 second do insert into execution_log value('abc3'); + +insert into mysql.event values ('db1','bad','select 42','root@localhost',NULL,1000,'MICROSECOND','2006-05-05 17:39:11','2006-05-05 17:39:20','2016-05-05 15:39:24','2016-05-05 15:39:11',NULL,'ENABLED','DROP','','comment1'); +insert into mysql.event values ('db1','bad2','sect','root@localhost',NULL,1000,'SECOND','2006-05-05 17:39:11','2006-05-05 17:39:20','2016-05-05 15:39:24','2016-05-05 15:39:11',NULL,'ENABLED','DROP','','comment2'); +--echo "Now we restart the server" diff --git a/mysql-test/t/events_restart_phase2-master.opt b/mysql-test/t/events_restart_phase2-master.opt new file mode 100644 index 00000000000..656b9e1b017 --- /dev/null +++ b/mysql-test/t/events_restart_phase2-master.opt @@ -0,0 +1 @@ +--event-scheduler=1 diff --git a/mysql-test/t/events_restart_phase2.test b/mysql-test/t/events_restart_phase2.test new file mode 100644 index 00000000000..845472377ba --- /dev/null +++ b/mysql-test/t/events_restart_phase2.test @@ -0,0 +1,9 @@ +# Can't test with embedded server that doesn't support grants +-- source include/not_embedded.inc + +use mysqltest_events_test; +--sleep 1.5 +--echo "Should get 0 rows because the queue aborted run +select distinct name from execution_log order by name; +delete from mysql.event where name like 'bad%'; +--echo "Now restart the server again" diff --git a/mysql-test/t/events_restart_phase3-master.opt b/mysql-test/t/events_restart_phase3-master.opt new file mode 100644 index 00000000000..656b9e1b017 --- /dev/null +++ b/mysql-test/t/events_restart_phase3-master.opt @@ -0,0 +1 @@ +--event-scheduler=1 diff --git a/mysql-test/t/events_restart_phase3.test b/mysql-test/t/events_restart_phase3.test new file mode 100644 index 00000000000..76cd9d22752 --- /dev/null +++ b/mysql-test/t/events_restart_phase3.test @@ -0,0 +1,14 @@ +# Can't test with embedded server that doesn't support grants +-- source include/not_embedded.inc + +use mysqltest_events_test; +--sleep 2 +--echo "Should get 3 rows : abc1, abc2, abc3 +select distinct name from execution_log order by name; + +drop event abc1; +drop event abc2; +drop event abc3; +drop table execution_log; + +drop database mysqltest_events_test; diff --git a/mysql-test/t/events_scheduling.test b/mysql-test/t/events_scheduling.test index 987939bc162..7a7502fab7e 100644 --- a/mysql-test/t/events_scheduling.test +++ b/mysql-test/t/events_scheduling.test @@ -1,5 +1,6 @@ # Can't test with embedded server that doesn't support grants -- source include/not_embedded.inc +-- source include/not_valgrind.inc CREATE DATABASE IF NOT EXISTS events_test; USE events_test; @@ -11,7 +12,6 @@ CREATE TABLE T19170(s1 TIMESTAMP); SET GLOBAL event_scheduler=1; # We need to have 2 to make it safe with valgrind. This is probably because # of when we calculate the timestamp value -CREATE EVENT E19170 ON SCHEDULE EVERY 2 SECOND DO INSERT INTO T19170 VALUES(CURRENT_TIMESTAMP); CREATE EVENT two_sec ON SCHEDULE EVERY 2 SECOND DO INSERT INTO table_1 VALUES(1); CREATE EVENT start_n_end ON SCHEDULE EVERY 1 SECOND @@ -34,12 +34,10 @@ DROP EVENT start_n_end; --error ER_EVENT_DOES_NOT_EXIST DROP EVENT only_one_time; --echo "Should be preserved" -SELECT EVENT_NAME, STATUS FROM INFORMATION_SCHEMA.EVENTS; +SELECT EVENT_NAME, STATUS FROM INFORMATION_SCHEMA.EVENTS ORDER BY EVENT_NAME; DROP EVENT two_time; DROP TABLE table_1; DROP TABLE table_2; DROP TABLE table_3; DROP TABLE table_4; ---echo "Checking for multiple executions in one second, should not happen -> 0 as result" -SELECT COUNT(*) FROM (SELECT s1, COUNT(*) AS cnt FROM T19170 GROUP BY s1) AS tmp WHERE tmp.cnt > 1; DROP DATABASE events_test; diff --git a/mysql-test/t/events_stress.test b/mysql-test/t/events_stress.test index 6546bce3a76..a0101861282 100644 --- a/mysql-test/t/events_stress.test +++ b/mysql-test/t/events_stress.test @@ -62,7 +62,7 @@ while ($1) --enable_query_log SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test2'; SET GLOBAL event_scheduler=1; ---sleep 6 +--sleep 2.5 DROP DATABASE events_conn1_test2; SET GLOBAL event_scheduler=2; @@ -101,7 +101,7 @@ while ($1) } --enable_query_log SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test2'; ---sleep 6 +--sleep 2.5 connection conn2; --send DROP DATABASE events_conn2_db; diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 95073b95ad6..c4bd39f2c63 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -51,7 +51,8 @@ ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.cc sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc time.cc tztime.cc uniques.cc unireg.cc item_xmlfunc.cc - rpl_tblmap.cc sql_binlog.cc event_scheduler.cc event_timed.cc + rpl_tblmap.cc sql_binlog.cc event_scheduler.cc event_data_objects.cc + event_queue.cc event_db_repository.cc sql_tablespace.cc events.cc ../sql-common/my_user.c partition_info.cc rpl_injector.cc sql_locale.cc ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc diff --git a/sql/Makefile.am b/sql/Makefile.am index 31d6a327c06..8bde6953358 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -64,8 +64,9 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ tztime.h my_decimal.h\ sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \ parse_file.h sql_view.h sql_trigger.h \ - sql_array.h sql_cursor.h events.h events_priv.h \ - sql_plugin.h authors.h sql_partition.h event_timed.h \ + sql_array.h sql_cursor.h events.h \ + event_db_repository.h event_queue.h \ + sql_plugin.h authors.h sql_partition.h event_data_objects.h \ partition_info.h partition_element.h event_scheduler.h \ contributors.h mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ @@ -104,7 +105,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ tztime.cc my_time.c my_user.c my_decimal.cc\ sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \ sp_cache.cc parse_file.cc sql_trigger.cc \ - event_scheduler.cc events.cc event_timed.cc \ + event_scheduler.cc events.cc event_data_objects.cc \ + event_queue.cc event_db_repository.cc \ sql_plugin.cc sql_binlog.cc \ sql_builtin.cc sql_tablespace.cc partition_info.cc diff --git a/sql/event_timed.cc b/sql/event_data_objects.cc index 98369e0e055..5cf8ec53a4d 100644 --- a/sql/event_timed.cc +++ b/sql/event_data_objects.cc @@ -16,111 +16,65 @@ #define MYSQL_LEX 1 #include "mysql_priv.h" -#include "events_priv.h" #include "events.h" -#include "event_timed.h" +#include "event_data_objects.h" +#include "event_db_repository.h" #include "sp_head.h" -/* - Constructor - - SYNOPSIS - Event_timed::Event_timed() -*/ +#define EVEX_MAX_INTERVAL_VALUE 1000000000L -Event_timed::Event_timed():in_spawned_thread(0),locked_by_thread_id(0), - running(0), thread_id(0), status_changed(false), - last_executed_changed(false), expression(0), - created(0), modified(0), - on_completion(Event_timed::ON_COMPLETION_DROP), - status(Event_timed::ENABLED), sphead(0), - sql_mode(0), body_begin(0), dropped(false), - free_sphead_on_delete(true), flags(0) - -{ - pthread_mutex_init(&this->LOCK_running, MY_MUTEX_INIT_FAST); - pthread_cond_init(&this->COND_finished, NULL); - init(); -} +static bool +event_change_security_context(THD *thd, LEX_STRING user, LEX_STRING host, + LEX_STRING db, Security_context *backup); +static void +event_restore_security_context(THD *thd, Security_context *backup); /* - Destructor + Returns a new instance SYNOPSIS - Event_timed::~Event_timed() -*/ - -Event_timed::~Event_timed() -{ - deinit_mutexes(); - - if (free_sphead_on_delete) - free_sp(); -} - - -/* - Destructor - - SYNOPSIS - Event_timed::~deinit_mutexes() -*/ - -void -Event_timed::deinit_mutexes() -{ - pthread_mutex_destroy(&this->LOCK_running); - pthread_cond_destroy(&this->COND_finished); -} - - -/* - Checks whether the event is running + Event_parse_data::new_instance() - SYNOPSIS - Event_timed::is_running() + RETURN VALUE + Address or NULL in case of error + + NOTE + Created on THD's mem_root */ -bool -Event_timed::is_running() +Event_parse_data * +Event_parse_data::new_instance(THD *thd) { - bool ret; - - VOID(pthread_mutex_lock(&this->LOCK_running)); - ret= running; - VOID(pthread_mutex_unlock(&this->LOCK_running)); - - return ret; + return new (thd->mem_root) Event_parse_data; } /* - Init all member variables + Constructor SYNOPSIS - Event_timed::init() + Event_parse_data::Event_parse_data() */ -void -Event_timed::init() +Event_parse_data::Event_parse_data() { - DBUG_ENTER("Event_timed::init"); + DBUG_ENTER("Event_parse_data::Event_parse_data"); - dbname.str= name.str= body.str= comment.str= 0; - dbname.length= name.length= body.length= comment.length= 0; + item_execute_at= item_expression= item_starts= item_ends= NULL; + status= ENABLED; + on_completion= ON_COMPLETION_DROP; + expression= 0; + /* Actually in the parser STARTS is always set */ 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); starts_null= ends_null= execute_at_null= TRUE; - definer_user.str= definer_host.str= 0; - definer_user.length= definer_host.length= 0; - - sql_mode= 0; + body.str= comment.str= NULL; + body.length= comment.length= 0; DBUG_VOID_RETURN; } @@ -130,30 +84,25 @@ Event_timed::init() Set a name of the event SYNOPSIS - Event_timed::init_name() + Event_parse_data::init_name() thd THD spn the name extracted in the parser */ void -Event_timed::init_name(THD *thd, sp_name *spn) +Event_parse_data::init_name(THD *thd, sp_name *spn) { - DBUG_ENTER("Event_timed::init_name"); - /* During parsing, we must use thd->mem_root */ - MEM_ROOT *root= thd->mem_root; + DBUG_ENTER("Event_parse_data::init_name"); /* We have to copy strings to get them into the right memroot */ dbname.length= spn->m_db.length; - dbname.str= strmake_root(root, spn->m_db.str, spn->m_db.length); + dbname.str= thd->strmake(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); + name.str= thd->strmake(spn->m_name.str, spn->m_name.length); if (spn->m_qname.length == 0) spn->init_qname(thd); - 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; } @@ -162,22 +111,22 @@ Event_timed::init_name(THD *thd, sp_name *spn) Set body of the event - what should be executed. SYNOPSIS - Event_timed::init_body() + Event_parse_data::init_body() thd THD NOTE The body is extracted by copying all data between the start of the body set by another method and the current pointer in Lex. - + Some questionable removal of characters is done in here, and that part should be refactored when the parser is smarter. */ void -Event_timed::init_body(THD *thd) +Event_parse_data::init_body(THD *thd) { - DBUG_ENTER("Event_timed::init_body"); - DBUG_PRINT("info", ("body=[%s] body_begin=0x%ld end=0x%ld", body_begin, + DBUG_ENTER("Event_parse_data::init_body"); + DBUG_PRINT("info", ("body=[%s] body_begin=0x%lx end=0x%lx", body_begin, body_begin, thd->lex->ptr)); body.length= thd->lex->ptr - body_begin; @@ -195,7 +144,7 @@ Event_timed::init_body(THD *thd) continue; } - /* + /* consume closing comments This is arguably wrong, but it's the best we have until the parser is @@ -228,54 +177,58 @@ Event_timed::init_body(THD *thd) ++body_begin; --body.length; } - body.str= strmake_root(thd->mem_root, (char *)body_begin, body.length); + body.str= thd->strmake((char *)body_begin, body.length); DBUG_VOID_RETURN; } /* - Set time for execution for one time events. + Sets time for execution for one-time event. SYNOPSIS - Event_timed::init_execute_at() - expr when (datetime) + Event_parse_data::init_execute_at() + thd Thread RETURN VALUE - 0 OK - EVEX_PARSE_ERROR fix_fields failed - EVEX_BAD_PARAMS datetime is in the past - ER_WRONG_VALUE wrong value for execute at + 0 OK + ER_WRONG_VALUE Wrong value for execute at (reported) */ int -Event_timed::init_execute_at(THD *thd, Item *expr) +Event_parse_data::init_execute_at(THD *thd) { my_bool not_used; TIME ltime; my_time_t t; - TIME time_tmp; - DBUG_ENTER("Event_timed::init_execute_at"); - if (expr->fix_fields(thd, &expr)) - DBUG_RETURN(EVEX_PARSE_ERROR); + DBUG_ENTER("Event_parse_data::init_execute_at"); + + if (!item_execute_at) + DBUG_RETURN(0); + + if (item_execute_at->fix_fields(thd, &item_execute_at)) + goto wrong_value; /* no starts and/or ends in case of execute_at */ DBUG_PRINT("info", ("starts_null && ends_null should be 1 is %d", (starts_null && ends_null))); DBUG_ASSERT(starts_null && ends_null); - + /* 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 ((not_used= expr->get_date(<ime, TIME_NO_ZERO_DATE))) - DBUG_RETURN(ER_WRONG_VALUE); + if ((not_used= item_execute_at->get_date(<ime, TIME_NO_ZERO_DATE))) + goto wrong_value; if (TIME_to_ulonglong_datetime(<ime) < TIME_to_ulonglong_datetime(&time_tmp)) - DBUG_RETURN(EVEX_BAD_PARAMS); + { + my_error(ER_EVENT_EXEC_TIME_IN_THE_PAST, MYF(0)); + DBUG_RETURN(ER_WRONG_VALUE); + } /* This may result in a 1970-01-01 date if ltime is > 2037-xx-xx. @@ -287,48 +240,64 @@ Event_timed::init_execute_at(THD *thd, Item *expr) if (!t) { DBUG_PRINT("error", ("Execute AT after year 2037")); - DBUG_RETURN(ER_WRONG_VALUE); + goto wrong_value; } execute_at_null= FALSE; execute_at= ltime; DBUG_RETURN(0); + +wrong_value: + report_bad_value("AT", item_execute_at); + DBUG_RETURN(ER_WRONG_VALUE); } /* - Set time for execution for transient events. + Sets time for execution of multi-time event.s SYNOPSIS - Event_timed::init_interval() - expr how much? - new_interval what is the interval + Event_parse_data::init_interval() + thd Thread RETURN VALUE - 0 OK - EVEX_PARSE_ERROR fix_fields failed - EVEX_BAD_PARAMS Interval is not positive - EVEX_MICROSECOND_UNSUP Microseconds are not supported. + 0 OK + EVEX_BAD_PARAMS Interval is not positive or MICROSECOND (reported) + ER_WRONG_VALUE Wrong value for interval (reported) */ int -Event_timed::init_interval(THD *thd, Item *expr, interval_type new_interval) +Event_parse_data::init_interval(THD *thd) { String value; INTERVAL interval_tmp; - DBUG_ENTER("Event_timed::init_interval"); + DBUG_ENTER("Event_parse_data::init_interval"); + if (!item_expression) + DBUG_RETURN(0); - if (expr->fix_fields(thd, &expr)) - DBUG_RETURN(EVEX_PARSE_ERROR); + switch (interval) { + case INTERVAL_MINUTE_MICROSECOND: + case INTERVAL_HOUR_MICROSECOND: + case INTERVAL_DAY_MICROSECOND: + case INTERVAL_SECOND_MICROSECOND: + case INTERVAL_MICROSECOND: + my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND"); + DBUG_RETURN(EVEX_BAD_PARAMS); + default: + break; + } + + if (item_expression->fix_fields(thd, &item_expression)) + goto wrong_value; value.alloc(MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN); - if (get_interval_value(expr, new_interval, &value, &interval_tmp)) - DBUG_RETURN(EVEX_PARSE_ERROR); + if (get_interval_value(item_expression, interval, &value, &interval_tmp)) + goto wrong_value; expression= 0; - switch (new_interval) { + switch (interval) { case INTERVAL_YEAR: expression= interval_tmp.year; break; @@ -366,44 +335,37 @@ Event_timed::init_interval(THD *thd, Item *expr, interval_type new_interval) interval_tmp.minute)*60 + interval_tmp.second; break; - case INTERVAL_MINUTE_MICROSECOND: /* day and hour are 0 */ - case INTERVAL_HOUR_MICROSECOND: /* day is anyway 0 */ - case INTERVAL_DAY_MICROSECOND: - DBUG_RETURN(EVEX_MICROSECOND_UNSUP); - expression= ((((interval_tmp.day*24) + interval_tmp.hour)*60+ - interval_tmp.minute)*60 + - interval_tmp.second) * 1000000L + interval_tmp.second_part; - break; case INTERVAL_HOUR_MINUTE: expression= interval_tmp.hour * 60 + interval_tmp.minute; break; case INTERVAL_MINUTE_SECOND: expression= interval_tmp.minute * 60 + interval_tmp.second; break; - case INTERVAL_SECOND_MICROSECOND: - DBUG_RETURN(EVEX_MICROSECOND_UNSUP); - expression= interval_tmp.second * 1000000L + interval_tmp.second_part; - break; - case INTERVAL_MICROSECOND: - DBUG_RETURN(EVEX_MICROSECOND_UNSUP); case INTERVAL_LAST: DBUG_ASSERT(0); + default: + ;/* these are the microsec stuff */ } if (interval_tmp.neg || expression > EVEX_MAX_INTERVAL_VALUE) + { + my_error(ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG, MYF(0)); DBUG_RETURN(EVEX_BAD_PARAMS); + } - interval= new_interval; DBUG_RETURN(0); + +wrong_value: + report_bad_value("INTERVAL", item_execute_at); + DBUG_RETURN(ER_WRONG_VALUE); } /* - Set activation time. + Sets STARTS. SYNOPSIS - Event_timed::init_starts() - expr how much? - interval what is the interval + Event_parse_data::init_starts() + expr how much? NOTES Note that activation time is not execution time. @@ -414,25 +376,26 @@ Event_timed::init_interval(THD *thd, Item *expr, interval_type new_interval) same time. RETURN VALUE - 0 OK - EVEX_PARSE_ERROR fix_fields failed - EVEX_BAD_PARAMS starts before now + 0 OK + ER_WRONG_VALUE Starts before now */ int -Event_timed::init_starts(THD *thd, Item *new_starts) +Event_parse_data::init_starts(THD *thd) { my_bool not_used; TIME ltime, time_tmp; my_time_t t; - DBUG_ENTER("Event_timed::init_starts"); + DBUG_ENTER("Event_parse_data::init_starts"); + if (!item_starts) + DBUG_RETURN(0); - if (new_starts->fix_fields(thd, &new_starts)) - DBUG_RETURN(EVEX_PARSE_ERROR); + if (item_starts->fix_fields(thd, &item_starts)) + goto wrong_value; - if ((not_used= new_starts->get_date(<ime, TIME_NO_ZERO_DATE))) - DBUG_RETURN(EVEX_BAD_PARAMS); + if ((not_used= item_starts->get_date(<ime, TIME_NO_ZERO_DATE))) + goto wrong_value; /* Let's check whether time is in the past */ thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, @@ -442,7 +405,7 @@ Event_timed::init_starts(THD *thd, Item *new_starts) DBUG_PRINT("info",("starts=%lld", TIME_to_ulonglong_datetime(<ime))); if (TIME_to_ulonglong_datetime(<ime) < TIME_to_ulonglong_datetime(&time_tmp)) - DBUG_RETURN(EVEX_BAD_PARAMS); + goto wrong_value; /* This may result in a 1970-01-01 date if ltime is > 2037-xx-xx. @@ -452,24 +415,24 @@ Event_timed::init_starts(THD *thd, Item *new_starts) */ my_tz_UTC->gmt_sec_to_TIME(<ime,t=TIME_to_timestamp(thd, <ime, ¬_used)); if (!t) - { - DBUG_PRINT("error", ("STARTS after year 2037")); - DBUG_RETURN(EVEX_BAD_PARAMS); - } + goto wrong_value; starts= ltime; starts_null= FALSE; DBUG_RETURN(0); + +wrong_value: + report_bad_value("STARTS", item_starts); + DBUG_RETURN(ER_WRONG_VALUE); } /* - Set deactivation time. + Sets ENDS (deactivation time). SYNOPSIS - Event_timed::init_ends() + Event_parse_data::init_ends() thd THD - new_ends when? NOTES Note that activation time is not execution time. @@ -481,26 +444,26 @@ Event_timed::init_starts(THD *thd, Item *new_starts) RETURN VALUE 0 OK - EVEX_PARSE_ERROR fix_fields failed - ER_WRONG_VALUE starts distant date (after year 2037) - EVEX_BAD_PARAMS ENDS before STARTS + EVEX_BAD_PARAMS Error (reported) */ int -Event_timed::init_ends(THD *thd, Item *new_ends) +Event_parse_data::init_ends(THD *thd) { TIME ltime, ltime_now; my_bool not_used; my_time_t t; - DBUG_ENTER("Event_timed::init_ends"); + DBUG_ENTER("Event_parse_data::init_ends"); + if (!item_ends) + DBUG_RETURN(0); - if (new_ends->fix_fields(thd, &new_ends)) - DBUG_RETURN(EVEX_PARSE_ERROR); + if (item_ends->fix_fields(thd, &item_ends)) + goto error_bad_params; DBUG_PRINT("info", ("convert to TIME")); - if ((not_used= new_ends->get_date(<ime, TIME_NO_ZERO_DATE))) - DBUG_RETURN(EVEX_BAD_PARAMS); + if ((not_used= item_ends->get_date(<ime, TIME_NO_ZERO_DATE))) + goto error_bad_params; /* This may result in a 1970-01-01 date if ltime is > 2037-xx-xx. @@ -511,15 +474,12 @@ Event_timed::init_ends(THD *thd, Item *new_ends) DBUG_PRINT("info", ("get the UTC time")); my_tz_UTC->gmt_sec_to_TIME(<ime,t=TIME_to_timestamp(thd, <ime, ¬_used)); if (!t) - { - DBUG_PRINT("error", ("ENDS after year 2037")); - DBUG_RETURN(EVEX_BAD_PARAMS); - } + goto error_bad_params; /* Check whether ends is after starts */ DBUG_PRINT("info", ("ENDS after STARTS?")); if (!starts_null && my_time_compare(&starts, <ime) != -1) - DBUG_RETURN(EVEX_BAD_PARAMS); + goto error_bad_params; /* The parser forces starts to be provided but one day STARTS could be @@ -529,32 +489,65 @@ Event_timed::init_ends(THD *thd, Item *new_ends) DBUG_PRINT("info", ("ENDS after NOW?")); my_tz_UTC->gmt_sec_to_TIME(<ime_now, thd->query_start()); if (my_time_compare(<ime_now, <ime) == 1) - DBUG_RETURN(EVEX_BAD_PARAMS); + goto error_bad_params; ends= ltime; ends_null= FALSE; DBUG_RETURN(0); + +error_bad_params: + my_error(ER_EVENT_ENDS_BEFORE_STARTS, MYF(0)); + DBUG_RETURN(EVEX_BAD_PARAMS); } /* - Sets comment. + Prints an error message about invalid value. Internally used + during input data verification SYNOPSIS - Event_timed::init_comment() - thd THD - used for memory allocation - comment the string. + Event_parse_data::report_bad_value() + item_name The name of the parameter + bad_item The parameter */ void -Event_timed::init_comment(THD *thd, LEX_STRING *set_comment) +Event_parse_data::report_bad_value(const char *item_name, Item *bad_item) +{ + char buff[120]; + String str(buff,(uint32) sizeof(buff), system_charset_info); + String *str2= bad_item->fixed? bad_item->val_str(&str):NULL; + my_error(ER_WRONG_VALUE, MYF(0), item_name, str2? str2->c_ptr_safe():"NULL"); +} + + +/* + Checks for validity the data gathered during the parsing phase. + + SYNOPSIS + Event_parse_data::check_parse_data() + thd Thread + + RETURN VALUE + FALSE OK + TRUE Error (reported) +*/ + +bool +Event_parse_data::check_parse_data(THD *thd) { - DBUG_ENTER("Event_timed::init_comment"); + bool ret; + DBUG_ENTER("Event_parse_data::check_parse_data"); + DBUG_PRINT("info", ("execute_at=0x%lx expr=0x%lx starts=0x%lx ends=0x%lx", + item_execute_at, item_expression, item_starts, item_ends)); - comment.str= strmake_root(thd->mem_root, set_comment->str, - comment.length= set_comment->length); + init_name(thd, identifier); - DBUG_VOID_RETURN; + init_definer(thd); + + ret= init_execute_at(thd) || init_interval(thd) || init_starts(thd) || + init_ends(thd); + DBUG_RETURN(ret); } @@ -562,51 +555,235 @@ Event_timed::init_comment(THD *thd, LEX_STRING *set_comment) Inits definer (definer_user and definer_host) during parsing. SYNOPSIS - Event_timed::init_definer() - - RETURN VALUE - 0 OK + Event_parse_data::init_definer() + thd Thread */ -int -Event_timed::init_definer(THD *thd) +void +Event_parse_data::init_definer(THD *thd) { - DBUG_ENTER("Event_timed::init_definer"); + int definer_user_len; + int definer_host_len; + DBUG_ENTER("Event_parse_data::init_definer"); DBUG_PRINT("info",("init definer_user thd->mem_root=0x%lx " "thd->sec_ctx->priv_user=0x%lx", thd->mem_root, thd->security_ctx->priv_user)); - definer_user.str= strdup_root(thd->mem_root, thd->security_ctx->priv_user); - definer_user.length= strlen(thd->security_ctx->priv_user); - DBUG_PRINT("info",("init definer_host thd->s_c->priv_host=0x%lx", - thd->security_ctx->priv_host)); - definer_host.str= strdup_root(thd->mem_root, thd->security_ctx->priv_host); - definer_host.length= strlen(thd->security_ctx->priv_host); + definer_user_len= strlen(thd->security_ctx->priv_user); + definer_host_len= strlen(thd->security_ctx->priv_host); + /* + 1 for @ */ DBUG_PRINT("info",("init definer as whole")); - definer.length= definer_user.length + definer_host.length + 1; - definer.str= alloc_root(thd->mem_root, definer.length + 1); + definer.length= definer_user_len + definer_host_len + 1; + definer.str= thd->alloc(definer.length + 1); DBUG_PRINT("info",("copy the user")); - memcpy(definer.str, definer_user.str, definer_user.length); - definer.str[definer_user.length]= '@'; + memcpy(definer.str, thd->security_ctx->priv_user, definer_user_len); + definer.str[definer_user_len]= '@'; DBUG_PRINT("info",("copy the host")); - memcpy(definer.str + definer_user.length + 1, definer_host.str, - definer_host.length); + memcpy(definer.str + definer_user_len + 1, thd->security_ctx->priv_host, + definer_host_len); definer.str[definer.length]= '\0'; - DBUG_PRINT("info",("definer initted")); + DBUG_PRINT("info",("definer [%s] initted", definer.str)); - DBUG_RETURN(0); + DBUG_VOID_RETURN; } /* - Loads an event from a row from mysql.event + Constructor SYNOPSIS - Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) + Event_basic::Event_basic() +*/ + +Event_basic::Event_basic() +{ + DBUG_ENTER("Event_basic::Event_basic"); + /* init memory root */ + init_alloc_root(&mem_root, 256, 512); + dbname.str= name.str= NULL; + dbname.length= name.length= 0; + DBUG_VOID_RETURN; +} + + +/* + Destructor + + SYNOPSIS + Event_basic::Event_basic() +*/ + +Event_basic::~Event_basic() +{ + DBUG_ENTER("Event_basic::~Event_basic"); + free_root(&mem_root, MYF(0)); + DBUG_VOID_RETURN; +} + + +/* + Short function to load a char column into a LEX_STRING + + SYNOPSIS + Event_basic::load_string_field() + field_name The field( enum_events_table_field is not actually used + because it's unknown in event_data_objects.h) + fields The Field array + field_value The value +*/ + +bool +Event_basic::load_string_fields(Field **fields, ...) +{ + bool ret= FALSE; + va_list args; + enum enum_events_table_field field_name; + LEX_STRING *field_value; + + DBUG_ENTER("Event_basic::load_string_fields"); + + va_start(args, fields); + field_name= (enum enum_events_table_field) va_arg(args, int); + while (field_name != ET_FIELD_COUNT) + { + field_value= va_arg(args, LEX_STRING *); + if ((field_value->str= get_field(&mem_root, fields[field_name])) == NullS) + { + ret= TRUE; + break; + } + field_value->length= strlen(field_value->str); + + field_name= (enum enum_events_table_field) va_arg(args, int); + } + va_end(args); + + DBUG_RETURN(ret); +} + + +/* + Constructor + + SYNOPSIS + Event_queue_element::Event_queue_element() +*/ + +Event_queue_element::Event_queue_element(): + status_changed(FALSE), last_executed_changed(FALSE), + on_completion(ON_COMPLETION_DROP), status(ENABLED), + expression(0), dropped(FALSE), execution_count(0) +{ + DBUG_ENTER("Event_queue_element::Event_queue_element"); + + 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); + starts_null= ends_null= execute_at_null= TRUE; + + DBUG_VOID_RETURN; +} + + +/* + Destructor + + SYNOPSIS + Event_queue_element::Event_queue_element() +*/ +Event_queue_element::~Event_queue_element() +{ +} + + +/* + Constructor + + SYNOPSIS + Event_timed::Event_timed() +*/ + +Event_timed::Event_timed(): + created(0), modified(0), sql_mode(0) +{ + DBUG_ENTER("Event_timed::Event_timed"); + init(); + DBUG_VOID_RETURN; +} + + +/* + Destructor + + SYNOPSIS + Event_timed::~Event_timed() +*/ + +Event_timed::~Event_timed() +{ +} + + +/* + Constructor + + SYNOPSIS + Event_job_data::Event_job_data() +*/ + +Event_job_data::Event_job_data(): + thd(NULL), sphead(NULL), sql_mode(0) +{ +} + + +/* + Destructor + + SYNOPSIS + Event_timed::~Event_timed() +*/ + +Event_job_data::~Event_job_data() +{ + DBUG_ENTER("Event_job_data::~Event_job_data"); + delete sphead; + sphead= NULL; + DBUG_VOID_RETURN; +} + + +/* + Init all member variables + + SYNOPSIS + Event_timed::init() +*/ + +void +Event_timed::init() +{ + DBUG_ENTER("Event_timed::init"); + + definer_user.str= definer_host.str= body.str= comment.str= NULL; + definer_user.length= definer_host.length= body.length= comment.length= 0; + + sql_mode= 0; + + DBUG_VOID_RETURN; +} + + +/* + Loads an event's body from a row from mysql.event + + SYNOPSIS + Event_job_data::load_from_row(MEM_ROOT *mem_root, TABLE *table) RETURN VALUE 0 OK @@ -619,124 +796,185 @@ Event_timed::init_definer(THD *thd) */ int -Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) +Event_job_data::load_from_row(TABLE *table) { char *ptr; - Event_timed *et; uint len; - bool res1, res2; - - DBUG_ENTER("Event_timed::load_from_row"); + DBUG_ENTER("Event_job_data::load_from_row"); if (!table) goto error; - et= this; - - if (table->s->fields != Events::FIELD_COUNT) + if (table->s->fields != ET_FIELD_COUNT) goto error; - if ((et->dbname.str= get_field(mem_root, - table->field[Events::FIELD_DB])) == NULL) - goto error; + load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name, + ET_FIELD_BODY, &body, ET_FIELD_DEFINER, &definer, + ET_FIELD_COUNT); - et->dbname.length= strlen(et->dbname.str); + ptr= strchr(definer.str, '@'); - if ((et->name.str= get_field(mem_root, - table->field[Events::FIELD_NAME])) == NULL) - goto error; + if (! ptr) + ptr= definer.str; - et->name.length= strlen(et->name.str); + len= ptr - definer.str; + definer_user.str= strmake_root(&mem_root, definer.str, len); + definer_user.length= len; + len= definer.length - len - 1; + /* 1:because of @ */ + definer_host.str= strmake_root(&mem_root, ptr + 1, len); + definer_host.length= len; - if ((et->body.str= get_field(mem_root, - table->field[Events::FIELD_BODY])) == NULL) - goto error; + sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int(); + + DBUG_RETURN(0); +error: + DBUG_RETURN(EVEX_GET_FIELD_FAILED); +} + + +/* + Loads an event from a row from mysql.event - et->body.length= strlen(et->body.str); + SYNOPSIS + Event_queue_element::load_from_row(MEM_ROOT *mem_root, TABLE *table) + + RETURN VALUE + 0 OK + EVEX_GET_FIELD_FAILED Error - if ((et->definer.str= get_field(mem_root, - table->field[Events::FIELD_DEFINER])) == NullS) + NOTES + 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_queue_element::load_from_row(TABLE *table) +{ + char *ptr; + bool res1, res2; + + DBUG_ENTER("Event_queue_element::load_from_row"); + + if (!table) goto error; - et->definer.length= strlen(et->definer.str); - ptr= strchr(et->definer.str, '@'); + if (table->s->fields != ET_FIELD_COUNT) + goto error; - if (! ptr) - ptr= et->definer.str; + load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name, + ET_FIELD_DEFINER, &definer, ET_FIELD_COUNT); - len= ptr - et->definer.str; + starts_null= table->field[ET_FIELD_STARTS]->is_null(); + res1= table->field[ET_FIELD_STARTS]->get_date(&starts, TIME_NO_ZERO_DATE); - 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; - - et->starts_null= table->field[Events::FIELD_STARTS]->is_null(); - res1= table->field[Events::FIELD_STARTS]-> - get_date(&et->starts,TIME_NO_ZERO_DATE); + ends_null= table->field[ET_FIELD_ENDS]->is_null(); + res2= table->field[ET_FIELD_ENDS]->get_date(&ends, TIME_NO_ZERO_DATE); - et->ends_null= table->field[Events::FIELD_ENDS]->is_null(); - res2= table->field[Events::FIELD_ENDS]->get_date(&et->ends, TIME_NO_ZERO_DATE); - - if (!table->field[Events::FIELD_INTERVAL_EXPR]->is_null()) - et->expression= table->field[Events::FIELD_INTERVAL_EXPR]->val_int(); + if (!table->field[ET_FIELD_INTERVAL_EXPR]->is_null()) + expression= table->field[ET_FIELD_INTERVAL_EXPR]->val_int(); else - et->expression= 0; + expression= 0; /* - If res1 and res2 are true then both fields are empty. - Hence if Events::FIELD_EXECUTE_AT is empty there is an error. + If res1 and res2 are TRUE then both fields are empty. + Hence, if ET_FIELD_EXECUTE_AT is empty there is an error. */ - et->execute_at_null= - table->field[Events::FIELD_EXECUTE_AT]->is_null(); - DBUG_ASSERT(!(et->starts_null && et->ends_null && !et->expression && - et->execute_at_null)); - if (!et->expression && - table->field[Events::FIELD_EXECUTE_AT]-> get_date(&et->execute_at, - TIME_NO_ZERO_DATE)) + execute_at_null= table->field[ET_FIELD_EXECUTE_AT]->is_null(); + DBUG_ASSERT(!(starts_null && ends_null && !expression && execute_at_null)); + if (!expression && + table->field[ET_FIELD_EXECUTE_AT]->get_date(&execute_at, + TIME_NO_ZERO_DATE)) goto error; /* In DB the values start from 1 but enum interval_type starts from 0 */ - if (!table->field[Events::FIELD_TRANSIENT_INTERVAL]->is_null()) - et->interval= (interval_type) ((ulonglong) - table->field[Events::FIELD_TRANSIENT_INTERVAL]->val_int() - 1); + if (!table->field[ET_FIELD_TRANSIENT_INTERVAL]->is_null()) + interval= (interval_type) ((ulonglong) + table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_int() - 1); else - et->interval= (interval_type) 0; + interval= (interval_type) 0; - et->created= table->field[Events::FIELD_CREATED]->val_int(); - et->modified= table->field[Events::FIELD_MODIFIED]->val_int(); + table->field[ET_FIELD_LAST_EXECUTED]->get_date(&last_executed, + TIME_NO_ZERO_DATE); + last_executed_changed= FALSE; - table->field[Events::FIELD_LAST_EXECUTED]-> - get_date(&et->last_executed, TIME_NO_ZERO_DATE); - last_executed_changed= false; + if ((ptr= get_field(&mem_root, table->field[ET_FIELD_STATUS])) == NullS) + goto error; + + DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", name.str, ptr)); + status= (ptr[0]=='E'? Event_queue_element::ENABLED: + Event_queue_element::DISABLED); /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */ - if ((ptr= get_field(mem_root, table->field[Events::FIELD_STATUS])) == NullS) + if ((ptr= get_field(&mem_root, + table->field[ET_FIELD_ON_COMPLETION])) == NullS) goto error; - DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->name.str, ptr)); - et->status= (ptr[0]=='E'? Event_timed::ENABLED:Event_timed::DISABLED); + on_completion= (ptr[0]=='D'? Event_queue_element::ON_COMPLETION_DROP: + Event_queue_element::ON_COMPLETION_PRESERVE); - /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */ - if ((ptr= get_field(mem_root, - table->field[Events::FIELD_ON_COMPLETION])) == NullS) + DBUG_RETURN(0); +error: + DBUG_RETURN(EVEX_GET_FIELD_FAILED); +} + + +/* + Loads an event from a row from mysql.event + + SYNOPSIS + Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) + + RETURN VALUE + 0 OK + EVEX_GET_FIELD_FAILED Error + + NOTES + 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(TABLE *table) +{ + char *ptr; + uint len; + + DBUG_ENTER("Event_timed::load_from_row"); + + if (Event_queue_element::load_from_row(table)) goto error; - et->on_completion= (ptr[0]=='D'? Event_timed::ON_COMPLETION_DROP: - Event_timed::ON_COMPLETION_PRESERVE); + load_string_fields(table->field, ET_FIELD_BODY, &body, ET_FIELD_COUNT); - et->comment.str= get_field(mem_root, table->field[Events::FIELD_COMMENT]); - if (et->comment.str != NullS) - et->comment.length= strlen(et->comment.str); + ptr= strchr(definer.str, '@'); + + if (! ptr) + ptr= definer.str; + + len= ptr - definer.str; + definer_user.str= strmake_root(&mem_root, definer.str, len); + definer_user.length= len; + len= definer.length - len - 1; + /* 1:because of @ */ + definer_host.str= strmake_root(&mem_root, ptr + 1, len); + definer_host.length= len; + + created= table->field[ET_FIELD_CREATED]->val_int(); + modified= table->field[ET_FIELD_MODIFIED]->val_int(); + + comment.str= get_field(&mem_root, table->field[ET_FIELD_COMMENT]); + if (comment.str != NullS) + comment.length= strlen(comment.str); else - et->comment.length= 0; - + comment.length= 0; - et->sql_mode= (ulong) table->field[Events::FIELD_SQL_MODE]->val_int(); + sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int(); DBUG_RETURN(0); error: @@ -755,7 +993,7 @@ error: time_now current time i_value quantity of time type interval to add i_type type of interval to add (SECOND, MINUTE, HOUR, WEEK ...) - + RETURN VALUE 0 OK 1 Error @@ -890,7 +1128,7 @@ bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec, tmp= *start; if ((ret= date_add_interval(&tmp, INTERVAL_MONTH, interval))) goto done; - + /* If `tmp` is still before time_now just add one more time the interval */ if (my_time_compare(&tmp, time_now) == -1) { @@ -914,7 +1152,7 @@ done: Computes next execution time. SYNOPSIS - Event_timed::compute_next_execution_time() + Event_queue_element::compute_next_execution_time() RETURN VALUE FALSE OK @@ -926,18 +1164,18 @@ done: */ bool -Event_timed::compute_next_execution_time() +Event_queue_element::compute_next_execution_time() { TIME time_now; int tmp; - DBUG_ENTER("Event_timed::compute_next_execution_time"); - DBUG_PRINT("enter", ("starts=%llu ends=%llu last_executed=%llu", + DBUG_ENTER("Event_queue_element::compute_next_execution_time"); + DBUG_PRINT("enter", ("starts=%llu ends=%llu last_executed=%llu this=0x%lx", TIME_to_ulonglong_datetime(&starts), TIME_to_ulonglong_datetime(&ends), - TIME_to_ulonglong_datetime(&last_executed))); + TIME_to_ulonglong_datetime(&last_executed), this)); - if (status == Event_timed::DISABLED) + if (status == Event_queue_element::DISABLED) { DBUG_PRINT("compute_next_execution_time", ("Event %s is DISABLED", name.str)); @@ -951,11 +1189,11 @@ Event_timed::compute_next_execution_time() { DBUG_PRINT("info",("One-time event %s.%s of was already executed", dbname.str, name.str, definer.str)); - dropped= (on_completion == Event_timed::ON_COMPLETION_DROP); + dropped= (on_completion == Event_queue_element::ON_COMPLETION_DROP); DBUG_PRINT("info",("One-time event will be dropped=%d.", dropped)); - status= Event_timed::DISABLED; - status_changed= true; + status= Event_queue_element::DISABLED; + status_changed= TRUE; } goto ret; } @@ -971,11 +1209,11 @@ Event_timed::compute_next_execution_time() /* time_now is after ends. don't execute anymore */ set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME); execute_at_null= TRUE; - if (on_completion == Event_timed::ON_COMPLETION_DROP) - dropped= true; + if (on_completion == Event_queue_element::ON_COMPLETION_DROP) + dropped= TRUE; DBUG_PRINT("info", ("Dropped=%d", dropped)); - status= Event_timed::DISABLED; - status_changed= true; + status= Event_queue_element::DISABLED; + status_changed= TRUE; goto ret; } @@ -1037,10 +1275,10 @@ Event_timed::compute_next_execution_time() /* Next execution after ends. No more executions */ set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME); execute_at_null= TRUE; - if (on_completion == Event_timed::ON_COMPLETION_DROP) - dropped= true; - status= Event_timed::DISABLED; - status_changed= true; + if (on_completion == Event_queue_element::ON_COMPLETION_DROP) + dropped= TRUE; + status= Event_queue_element::DISABLED; + status_changed= TRUE; } else { @@ -1130,10 +1368,10 @@ Event_timed::compute_next_execution_time() DBUG_PRINT("info", ("Next execution after ENDS. Stop executing.")); set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME); execute_at_null= TRUE; - status= Event_timed::DISABLED; - status_changed= true; - if (on_completion == Event_timed::ON_COMPLETION_DROP) - dropped= true; + status= Event_queue_element::DISABLED; + status_changed= TRUE; + if (on_completion == Event_queue_element::ON_COMPLETION_DROP) + dropped= TRUE; } else { @@ -1147,11 +1385,12 @@ Event_timed::compute_next_execution_time() goto ret; } ret: - DBUG_PRINT("info", ("ret=0")); - DBUG_RETURN(false); + DBUG_PRINT("info", ("ret=0 execute_at=%llu", + TIME_to_ulonglong_datetime(&execute_at))); + DBUG_RETURN(FALSE); err: DBUG_PRINT("info", ("ret=1")); - DBUG_RETURN(true); + DBUG_RETURN(TRUE); } @@ -1160,12 +1399,12 @@ err: time according to thd->query_start(), so the THD's clock. SYNOPSIS - Event_timed::drop() + Event_queue_element::mark_last_executed() thd thread context */ void -Event_timed::mark_last_executed(THD *thd) +Event_queue_element::mark_last_executed(THD *thd) { TIME time_now; @@ -1173,7 +1412,9 @@ Event_timed::mark_last_executed(THD *thd) my_tz_UTC->gmt_sec_to_TIME(&time_now, (my_time_t) thd->query_start()); last_executed= time_now; /* was execute_at */ - last_executed_changed= true; + last_executed_changed= TRUE; + + execution_count++; } @@ -1181,7 +1422,7 @@ Event_timed::mark_last_executed(THD *thd) Drops the event SYNOPSIS - Event_timed::drop() + Event_queue_element::drop() thd thread context RETURN VALUE @@ -1194,12 +1435,13 @@ Event_timed::mark_last_executed(THD *thd) */ int -Event_timed::drop(THD *thd) +Event_queue_element::drop(THD *thd) { uint tmp= 0; - DBUG_ENTER("Event_timed::drop"); + DBUG_ENTER("Event_queue_element::drop"); - DBUG_RETURN(db_drop_event(thd, this, false, &tmp)); + DBUG_RETURN(Events::get_instance()-> + drop_event(thd, dbname, name, FALSE, &tmp, TRUE)); } @@ -1207,26 +1449,23 @@ Event_timed::drop(THD *thd) Saves status and last_executed_at to the disk if changed. SYNOPSIS - Event_timed::update_fields() + Event_queue_element::update_timing_fields() thd - thread context RETURN VALUE - 0 OK - EVEX_OPEN_TABLE_FAILED Error while opening mysql.event for writing - EVEX_WRITE_ROW_FAILED On error to write to disk - - others return code from SE in case deletion of the event - row failed. + FALSE OK + TRUE Error while opening mysql.event for writing or during write on disk */ bool -Event_timed::update_fields(THD *thd) +Event_queue_element::update_timing_fields(THD *thd) { TABLE *table; + Field **fields; Open_tables_state backup; - int ret; + int ret= FALSE; - DBUG_ENTER("Event_timed::update_time_fields"); + DBUG_ENTER("Event_queue_element::update_timing_fields"); DBUG_PRINT("enter", ("name: %*s", name.length, name.str)); @@ -1236,14 +1475,14 @@ Event_timed::update_fields(THD *thd) thd->reset_n_backup_open_tables_state(&backup); - if (Events::open_event_table(thd, TL_WRITE, &table)) + if (Events::get_instance()->open_event_table(thd, TL_WRITE, &table)) { - ret= EVEX_OPEN_TABLE_FAILED; + ret= TRUE; goto done; } - - - if ((ret= evex_db_find_event_by_name(thd, dbname, name, table))) + fields= table->field; + if ((ret= Events::get_instance()->db_repository-> + find_named_event(thd, dbname, name, table))) goto done; store_record(table,record[1]); @@ -1252,20 +1491,20 @@ Event_timed::update_fields(THD *thd) if (last_executed_changed) { - table->field[Events::FIELD_LAST_EXECUTED]->set_notnull(); - table->field[Events::FIELD_LAST_EXECUTED]->store_time(&last_executed, + fields[ET_FIELD_LAST_EXECUTED]->set_notnull(); + fields[ET_FIELD_LAST_EXECUTED]->store_time(&last_executed, MYSQL_TIMESTAMP_DATETIME); - last_executed_changed= false; + last_executed_changed= FALSE; } if (status_changed) { - table->field[Events::FIELD_STATUS]->set_notnull(); - table->field[Events::FIELD_STATUS]->store((longlong)status, true); - status_changed= false; + fields[ET_FIELD_STATUS]->set_notnull(); + fields[ET_FIELD_STATUS]->store((longlong)status, TRUE); + status_changed= FALSE; } - if ((table->file->ha_update_row(table->record[1],table->record[0]))) - ret= EVEX_WRITE_ROW_FAILED; + if ((table->file->ha_update_row(table->record[1], table->record[0]))) + ret= TRUE; done: close_thread_tables(thd); @@ -1294,8 +1533,8 @@ int Event_timed::get_create_event(THD *thd, String *buf) { int multipl= 0; - char tmp_buff[128]; - String expr_buf(tmp_buff, sizeof(tmp_buff), system_charset_info); + char tmp_buf[2 * STRING_BUFFER_USUAL_SIZE]; + String expr_buf(tmp_buf, sizeof(tmp_buf), system_charset_info); expr_buf.length(0); DBUG_ENTER("get_create_event"); @@ -1308,10 +1547,9 @@ Event_timed::get_create_event(THD *thd, String *buf) buf->append(STRING_WITH_LEN("CREATE EVENT ")); append_identifier(thd, buf, name.str, name.length); - buf->append(STRING_WITH_LEN(" ON SCHEDULE ")); if (expression) { - buf->append(STRING_WITH_LEN("EVERY ")); + buf->append(STRING_WITH_LEN(" ON SCHEDULE EVERY ")); buf->append(expr_buf); buf->append(' '); LEX_STRING *ival= &interval_type_to_name[interval]; @@ -1320,7 +1558,7 @@ Event_timed::get_create_event(THD *thd, String *buf) else { char dtime_buff[20*2+32];/* +32 to make my_snprintf_{8bit|ucs2} happy */ - buf->append(STRING_WITH_LEN("AT '")); + buf->append(STRING_WITH_LEN(" ON SCHEDULE AT '")); /* Pass the buffer and the second param tells fills the buffer and returns the number of chars to copy. @@ -1352,42 +1590,60 @@ Event_timed::get_create_event(THD *thd, String *buf) /* + Get SHOW CREATE EVENT as string + + SYNOPSIS + Event_job_data::get_create_event(THD *thd, String *buf) + thd Thread + buf String*, should be already allocated. CREATE EVENT goes inside. + + RETURN VALUE + 0 OK + EVEX_MICROSECOND_UNSUP Error (for now if mysql.event has been + tampered and MICROSECONDS interval or + derivative has been put there. +*/ + +int +Event_job_data::get_fake_create_event(THD *thd, String *buf) +{ + DBUG_ENTER("Event_job_data::get_create_event"); + buf->append(STRING_WITH_LEN("CREATE EVENT anonymous ON SCHEDULE " + "EVERY 3337 HOUR DO ")); + buf->append(body.str, body.length); + + DBUG_RETURN(0); +} + + +/* Executes the event (the underlying sp_head object); SYNOPSIS - evex_fill_row() + Event_job_data::execute() thd THD - mem_root If != NULL use it to compile the event on it RETURN VALUE 0 success -99 No rights on this.dbname.str - -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) +Event_job_data::execute(THD *thd) { + Security_context save_ctx; /* this one is local and not needed after exec */ - Security_context security_ctx; int ret= 0; - DBUG_ENTER("Event_timed::execute"); - DBUG_PRINT("info", (" EVEX EXECUTING event %s.%s [EXPR:%d]", - dbname.str, name.str, (int) expression)); - - 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)); + DBUG_ENTER("Event_job_data::execute"); + DBUG_PRINT("info", ("EXECUTING %s.%s", dbname.str, name.str)); - if (!sphead && (ret= compile(thd, mem_root))) + if ((ret= compile(thd, NULL))) goto done; + + event_change_security_context(thd, definer_user, definer_host, dbname, + &save_ctx); /* THD::~THD will clean this or if there is DROP DATABASE in the SP then it will be free there. It should not point to our buffer which is allocated @@ -1412,41 +1668,14 @@ Event_timed::execute(THD *thd, MEM_ROOT *mem_root) ret= -99; } - VOID(pthread_mutex_lock(&this->LOCK_running)); - running= false; - /* Will compile every time a new sp_head on different root */ - free_sp(); - VOID(pthread_mutex_unlock(&this->LOCK_running)); - + event_restore_security_context(thd, &save_ctx); done: - /* - 1. Don't cache sphead if allocated on another mem_root - 2. Don't call security_ctx.destroy() because this will free our dbname.str - name.str and definer.str - */ - if (mem_root && sphead) - { - delete sphead; - sphead= 0; - } - DBUG_PRINT("info", (" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d", - dbname.str, name.str, (int) expression, ret)); + thd->end_statement(); + thd->cleanup_after_query(); - DBUG_RETURN(ret); -} + DBUG_PRINT("info", ("EXECUTED %s.%s ret=%d", dbname.str, name.str, ret)); - -/* - Frees the memory of the sp_head object we hold - SYNOPSIS - Event_timed::free_sp() -*/ - -void -Event_timed::free_sp() -{ - delete sphead; - sphead= 0; + DBUG_RETURN(ret); } @@ -1455,7 +1684,7 @@ Event_timed::free_sp() sp_head object held by the event SYNOPSIS - Event_timed::compile() + Event_job_data::compile() thd thread context, used for memory allocation mostly mem_root if != NULL then this memory root is used for allocs instead of thd->mem_root @@ -1467,7 +1696,7 @@ Event_timed::free_sp() */ int -Event_timed::compile(THD *thd, MEM_ROOT *mem_root) +Event_job_data::compile(THD *thd, MEM_ROOT *mem_root) { int ret= 0; MEM_ROOT *tmp_mem_root= 0; @@ -1477,20 +1706,18 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root) char *old_query; uint old_query_len; ulong old_sql_mode= thd->variables.sql_mode; - char create_buf[2048]; + char create_buf[15 * STRING_BUFFER_USUAL_SIZE]; String show_create(create_buf, sizeof(create_buf), system_charset_info); CHARSET_INFO *old_character_set_client, *old_collation_connection, *old_character_set_results; - Security_context *save_ctx; - /* this one is local and not needed after exec */ - Security_context security_ctx; + Security_context save_ctx; - DBUG_ENTER("Event_timed::compile"); + DBUG_ENTER("Event_job_data::compile"); show_create.length(0); - switch (get_create_event(thd, &show_create)) { + switch (get_fake_create_event(thd, &show_create)) { case EVEX_MICROSECOND_UNSUP: sql_print_error("Scheduler"); DBUG_RETURN(EVEX_MICROSECOND_UNSUP); @@ -1526,15 +1753,14 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root) thd->db= dbname.str; thd->db_length= dbname.length; - thd->query= show_create.c_ptr(); + thd->query= show_create.c_ptr_safe(); thd->query_length= show_create.length(); DBUG_PRINT("info", ("query:%s",thd->query)); - change_security_context(thd, definer_user, definer_host, dbname, - &security_ctx, &save_ctx); + event_change_security_context(thd, definer_user, definer_host, dbname, + &save_ctx); thd->lex= &lex; - lex_start(thd, (uchar*)thd->query, thd->query_length); - lex.et_compile_phase= TRUE; + mysql_init_query(thd, (uchar*) thd->query, thd->query_length); if (MYSQLparse((void *)thd) || thd->is_fatal_error) { DBUG_PRINT("error", ("error during compile or thd->is_fatal_error=%d", @@ -1545,31 +1771,25 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root) */ sql_print_error("error during compile of %s.%s or thd->is_fatal_error=%d", dbname.str, name.str, thd->is_fatal_error); - if (lex.sphead) - { - if (&lex != thd->lex) - thd->lex->sphead->restore_lex(thd); - delete lex.sphead; - lex.sphead= 0; - } + + lex.unit.cleanup(); + delete lex.sphead; + sphead= lex.sphead= NULL; ret= EVEX_COMPILE_ERROR; goto done; } DBUG_PRINT("note", ("success compiling %s.%s", dbname.str, name.str)); - sphead= lex.et->sphead; - sphead->m_db= dbname; + sphead= lex.sphead; sphead->set_definer(definer.str, definer.length); sphead->set_info(0, 0, &lex.sp_chistics, sql_mode); sphead->optimize(); ret= 0; done: - lex.et->free_sphead_on_delete= false; - lex.et->deinit_mutexes(); lex_end(&lex); - restore_security_context(thd, save_ctx); + event_restore_security_context(thd, &save_ctx); DBUG_PRINT("note", ("return old data on its place. set back NAMES")); thd->lex= old_lex; @@ -1591,200 +1811,23 @@ done: } -extern pthread_attr_t connection_attrib; - -/* - Checks whether is possible and forks a thread. Passes self as argument. - - RETURN VALUE - EVENT_EXEC_STARTED OK - EVENT_EXEC_ALREADY_EXEC Thread not forked, already working - EVENT_EXEC_CANT_FORK Unable to spawn thread (error) -*/ - -int -Event_timed::spawn_now(void * (*thread_func)(void*), void *arg) -{ - THD *thd= current_thd; - int ret= EVENT_EXEC_STARTED; - DBUG_ENTER("Event_timed::spawn_now"); - DBUG_PRINT("info", ("[%s.%s]", dbname.str, name.str)); - - VOID(pthread_mutex_lock(&this->LOCK_running)); - - DBUG_PRINT("info", ("SCHEDULER: execute_at of %s is %lld", name.str, - TIME_to_ulonglong_datetime(&execute_at))); - mark_last_executed(thd); - if (compute_next_execution_time()) - { - sql_print_error("SCHEDULER: Error while computing time of %s.%s . " - "Disabling after execution.", dbname.str, name.str); - status= DISABLED; - } - DBUG_PRINT("evex manager", ("[%10s] next exec at [%llu]", name.str, - TIME_to_ulonglong_datetime(&execute_at))); - /* - 1. For one-time event : year is > 0 and expression is 0 - 2. For recurring, expression is != -=> check execute_at_null in this case - */ - if ((execute_at.year && !expression) || execute_at_null) - { - sql_print_information("SCHEDULER: [%s.%s of %s] no more executions " - "after this one", dbname.str, name.str, - definer.str); - flags |= EVENT_EXEC_NO_MORE | EVENT_FREE_WHEN_FINISHED; - } - - update_fields(thd); - - if (!in_spawned_thread) - { - pthread_t th; - in_spawned_thread= true; - - if (pthread_create(&th, &connection_attrib, thread_func, arg)) - { - DBUG_PRINT("info", ("problem while spawning thread")); - ret= EVENT_EXEC_CANT_FORK; - in_spawned_thread= false; - } - } - else - { - DBUG_PRINT("info", ("already in spawned thread. skipping")); - ret= EVENT_EXEC_ALREADY_EXEC; - } - VOID(pthread_mutex_unlock(&this->LOCK_running)); - - DBUG_RETURN(ret); -} - - -bool -Event_timed::spawn_thread_finish(THD *thd) -{ - bool should_free; - DBUG_ENTER("Event_timed::spawn_thread_finish"); - VOID(pthread_mutex_lock(&LOCK_running)); - in_spawned_thread= false; - DBUG_PRINT("info", ("Sending COND_finished for thread %d", thread_id)); - thread_id= 0; - if (dropped) - drop(thd); - pthread_cond_broadcast(&COND_finished); - should_free= flags & EVENT_FREE_WHEN_FINISHED; - VOID(pthread_mutex_unlock(&LOCK_running)); - DBUG_RETURN(should_free); -} - - -/* - Kills a running event - SYNOPSIS - Event_timed::kill_thread() - - RETURN VALUE - 0 OK - -1 EVEX_CANT_KILL - !0 Error -*/ - -int -Event_timed::kill_thread(THD *thd) -{ - int ret= 0; - DBUG_ENTER("Event_timed::kill_thread"); - pthread_mutex_lock(&LOCK_running); - DBUG_PRINT("info", ("thread_id=%lu", thread_id)); - - if (thread_id == thd->thread_id) - { - /* - We don't kill ourselves in cases like : - alter event e_43 do alter event e_43 do set @a = 4 because - we will never receive COND_finished. - */ - DBUG_PRINT("info", ("It's not safe to kill ourselves in self altering queries")); - ret= EVEX_CANT_KILL; - } - else if (thread_id && !(ret= kill_one_thread(thd, thread_id, false))) - { - thd->enter_cond(&COND_finished, &LOCK_running, "Waiting for finished"); - DBUG_PRINT("info", ("Waiting for COND_finished from thread %d", thread_id)); - while (thread_id) - pthread_cond_wait(&COND_finished, &LOCK_running); - - DBUG_PRINT("info", ("Got COND_finished")); - /* This will implicitly unlock LOCK_running. Hence we return before that */ - thd->exit_cond(""); - - DBUG_RETURN(0); - } - else if (!thread_id && in_spawned_thread) - { - /* - Because the manager thread waits for the forked thread to update thread_id - this situation is impossible. - */ - DBUG_ASSERT(0); - } - pthread_mutex_unlock(&LOCK_running); - DBUG_PRINT("exit", ("%d", ret)); - DBUG_RETURN(ret); -} - - -/* - Checks whether two events have the same name - - SYNOPSIS - event_timed_name_equal() - - RETURN VALUE - TRUE names are equal - FALSE names are not equal -*/ - -bool -event_timed_name_equal(Event_timed *et, LEX_STRING *name) -{ - return !sortcmp_lex_string(et->name, *name, system_charset_info); -} - - /* Checks whether two events are in the same schema SYNOPSIS - event_timed_db_equal() + event_basic_db_equal() + db Schema + et Compare et->dbname to `db` RETURN VALUE - TRUE schemas are equal - FALSE schemas are not equal -*/ - -bool -event_timed_db_equal(Event_timed *et, LEX_STRING *db) -{ - return !sortcmp_lex_string(et->dbname, *db, system_charset_info); -} - - -/* - Checks whether two events have the same definer - - SYNOPSIS - event_timed_definer_equal() - - Returns - TRUE definers are equal - FALSE definers are not equal + TRUE Equal + FALSE Not equal */ bool -event_timed_definer_equal(Event_timed *et, LEX_STRING *definer) +event_basic_db_equal(LEX_STRING db, Event_basic *et) { - return !sortcmp_lex_string(et->definer, *definer, system_charset_info); + return !sortcmp_lex_string(et->dbname, db, system_charset_info); } @@ -1792,75 +1835,75 @@ event_timed_definer_equal(Event_timed *et, LEX_STRING *definer) Checks whether two events are equal by identifiers SYNOPSIS - event_timed_identifier_equal() + event_basic_identifier_equal() RETURN VALUE - TRUE equal - FALSE not equal + TRUE Equal + FALSE Not equal */ bool -event_timed_identifier_equal(Event_timed *a, Event_timed *b) +event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b) { - return event_timed_name_equal(a, &b->name) && - event_timed_db_equal(a, &b->dbname) && - event_timed_definer_equal(a, &b->definer); + return !sortcmp_lex_string(name, b->name, system_charset_info) && + !sortcmp_lex_string(db, b->dbname, system_charset_info); } /* Switches the security context SYNOPSIS - change_security_context() + event_change_security_context() thd Thread user The user host The host of the user db The schema for which the security_ctx will be loaded - s_ctx Security context to load state into backup Where to store the old context RETURN VALUE - 0 - OK - 1 - Error (generates error too) + FALSE OK + TRUE Error (generates error too) */ -bool -change_security_context(THD *thd, LEX_STRING user, LEX_STRING host, - LEX_STRING db, Security_context *s_ctx, - Security_context **backup) +static bool +event_change_security_context(THD *thd, LEX_STRING user, LEX_STRING host, + LEX_STRING db, Security_context *backup) { - DBUG_ENTER("change_security_context"); + DBUG_ENTER("event_change_security_context"); DBUG_PRINT("info",("%s@%s@%s", user.str, host.str, db.str)); #ifndef NO_EMBEDDED_ACCESS_CHECKS - s_ctx->init(); - *backup= 0; - if (acl_getroot_no_password(s_ctx, user.str, host.str, host.str, db.str)) + + *backup= thd->main_security_ctx; + if (acl_getroot_no_password(&thd->main_security_ctx, user.str, host.str, + host.str, db.str)) { my_error(ER_NO_SUCH_USER, MYF(0), user.str, host.str); DBUG_RETURN(TRUE); } - *backup= thd->security_ctx; - thd->security_ctx= s_ctx; + thd->security_ctx= &thd->main_security_ctx; #endif DBUG_RETURN(FALSE); -} +} /* Restores the security context SYNOPSIS - restore_security_context() - thd - thread - backup - switch to this context + event_restore_security_context() + thd Thread + backup Context to switch to */ -void -restore_security_context(THD *thd, Security_context *backup) +static void +event_restore_security_context(THD *thd, Security_context *backup) { - DBUG_ENTER("restore_security_context"); + DBUG_ENTER("event_restore_security_context"); #ifndef NO_EMBEDDED_ACCESS_CHECKS if (backup) - thd->security_ctx= backup; + { + thd->main_security_ctx= *backup; + thd->security_ctx= &thd->main_security_ctx; + } #endif DBUG_VOID_RETURN; } diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h new file mode 100644 index 00000000000..a912953c927 --- /dev/null +++ b/sql/event_data_objects.h @@ -0,0 +1,285 @@ +#ifndef _EVENT_DATA_OBJECTS_H_ +#define _EVENT_DATA_OBJECTS_H_ +/* Copyright (C) 2004-2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#define EVEX_GET_FIELD_FAILED -2 +#define EVEX_COMPILE_ERROR -3 +#define EVEX_GENERAL_ERROR -4 +#define EVEX_BAD_PARAMS -5 +#define EVEX_MICROSECOND_UNSUP -6 + + +class sp_head; +class Sql_alloc; + + +class Event_basic +{ +protected: + MEM_ROOT mem_root; + +public: + LEX_STRING dbname; + LEX_STRING name; + LEX_STRING definer;// combination of user and host + + Event_basic(); + virtual ~Event_basic(); + + virtual int + load_from_row(TABLE *table) = 0; + +protected: + bool + load_string_fields(Field **fields, ...); +}; + + + +class Event_queue_element : public Event_basic +{ +protected: + bool status_changed; + bool last_executed_changed; + +public: + enum enum_status + { + ENABLED = 1, + DISABLED + }; + + enum enum_on_completion + { + ON_COMPLETION_DROP = 1, + ON_COMPLETION_PRESERVE + }; + + enum enum_on_completion on_completion; + enum enum_status status; + TIME last_executed; + + TIME execute_at; + TIME starts; + TIME ends; + my_bool starts_null; + my_bool ends_null; + my_bool execute_at_null; + + longlong expression; + interval_type interval; + + bool dropped; + + uint execution_count; + + Event_queue_element(); + virtual ~Event_queue_element(); + + virtual int + load_from_row(TABLE *table); + + bool + compute_next_execution_time(); + + int + drop(THD *thd); + + void + mark_last_executed(THD *thd); + + bool + update_timing_fields(THD *thd); + + static void *operator new(size_t size) + { + void *p; + DBUG_ENTER("Event_queue_element::new(size)"); + p= my_malloc(size, MYF(0)); + DBUG_PRINT("info", ("alloc_ptr=0x%lx", p)); + DBUG_RETURN(p); + } + + static void operator delete(void *ptr, size_t size) + { + DBUG_ENTER("Event_queue_element::delete(ptr,size)"); + DBUG_PRINT("enter", ("free_ptr=0x%lx", ptr)); + TRASH(ptr, size); + my_free((gptr) ptr, MYF(0)); + DBUG_VOID_RETURN; + } +}; + + +class Event_timed : public Event_queue_element +{ + Event_timed(const Event_timed &); /* Prevent use of these */ + void operator=(Event_timed &); + +public: + LEX_STRING body; + + LEX_STRING definer_user; + LEX_STRING definer_host; + + LEX_STRING comment; + + ulonglong created; + ulonglong modified; + + ulong sql_mode; + + Event_timed(); + virtual ~Event_timed(); + + void + init(); + + virtual int + load_from_row(TABLE *table); + + int + get_create_event(THD *thd, String *buf); +}; + + +class Event_job_data : public Event_basic +{ +public: + THD *thd; + sp_head *sphead; + + LEX_STRING body; + LEX_STRING definer_user; + LEX_STRING definer_host; + + ulong sql_mode; + + uint execution_count; + + Event_job_data(); + virtual ~Event_job_data(); + + virtual int + load_from_row(TABLE *table); + + int + execute(THD *thd); + + int + compile(THD *thd, MEM_ROOT *mem_root); +private: + int + get_fake_create_event(THD *thd, String *buf); + + Event_job_data(const Event_job_data &); /* Prevent use of these */ + void operator=(Event_job_data &); +}; + + +class Event_parse_data : public Sql_alloc +{ +public: + enum enum_status + { + ENABLED = 1, + DISABLED + }; + + enum enum_on_completion + { + ON_COMPLETION_DROP = 1, + ON_COMPLETION_PRESERVE + }; + enum enum_on_completion on_completion; + enum enum_status status; + + const uchar *body_begin; + + LEX_STRING dbname; + LEX_STRING name; + LEX_STRING definer;// combination of user and host + LEX_STRING body; + LEX_STRING comment; + + Item* item_starts; + Item* item_ends; + Item* item_execute_at; + + TIME starts; + TIME ends; + TIME execute_at; + my_bool starts_null; + my_bool ends_null; + my_bool execute_at_null; + + sp_name *identifier; + Item* item_expression; + longlong expression; + interval_type interval; + + static Event_parse_data * + new_instance(THD *thd); + + bool + check_parse_data(THD *); + + void + init_body(THD *thd); + +private: + + void + init_definer(THD *thd); + + void + init_name(THD *thd, sp_name *spn); + + int + init_execute_at(THD *thd); + + int + init_interval(THD *thd); + + int + init_starts(THD *thd); + + int + init_ends(THD *thd); + + Event_parse_data(); + ~Event_parse_data(); + + void + report_bad_value(const char *item_name, Item *bad_item); + + Event_parse_data(const Event_parse_data &); /* Prevent use of these */ + void operator=(Event_parse_data &); +}; + + +/* Compares only the schema part of the identifier */ +bool +event_basic_db_equal(LEX_STRING db, Event_basic *et); + +/* Compares the whole identifier*/ +bool +event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b); + + +#endif /* _EVENT_DATA_OBJECTS_H_ */ diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc new file mode 100644 index 00000000000..69bbaeeae03 --- /dev/null +++ b/sql/event_db_repository.cc @@ -0,0 +1,1004 @@ +/* Copyright (C) 2004-2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysql_priv.h" +#include "event_db_repository.h" +#include "event_data_objects.h" +#include "events.h" +#include "sql_show.h" +#include "sp.h" +#include "sp_head.h" + +#define EVEX_DB_FIELD_LEN 64 +#define EVEX_NAME_FIELD_LEN 64 + + +time_t mysql_event_last_create_time= 0L; + +static +TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] = +{ + { + {(char *) STRING_WITH_LEN("db")}, + {(char *) STRING_WITH_LEN("char(64)")}, + {(char *) STRING_WITH_LEN("utf8")} + }, + { + {(char *) STRING_WITH_LEN("name")}, + {(char *) STRING_WITH_LEN("char(64)")}, + {(char *) STRING_WITH_LEN("utf8")} + }, + { + {(char *) STRING_WITH_LEN("body")}, + {(char *) STRING_WITH_LEN("longblob")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("definer")}, + {(char *) STRING_WITH_LEN("char(77)")}, + {(char *) STRING_WITH_LEN("utf8")} + }, + { + {(char *) STRING_WITH_LEN("execute_at")}, + {(char *) STRING_WITH_LEN("datetime")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("interval_value")}, + {(char *) STRING_WITH_LEN("int(11)")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("interval_field")}, + {(char *) STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY'," + "'HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR'," + "'DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND'," + "'DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND'," + "'SECOND_MICROSECOND')")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("created")}, + {(char *) STRING_WITH_LEN("timestamp")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("modified")}, + {(char *) STRING_WITH_LEN("timestamp")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("last_executed")}, + {(char *) STRING_WITH_LEN("datetime")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("starts")}, + {(char *) STRING_WITH_LEN("datetime")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("ends")}, + {(char *) STRING_WITH_LEN("datetime")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("status")}, + {(char *) STRING_WITH_LEN("enum('ENABLED','DISABLED')")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("on_completion")}, + {(char *) STRING_WITH_LEN("enum('DROP','PRESERVE')")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("sql_mode")}, + {(char *) STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES'," + "'IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION'," + "'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB'," + "'NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40'," + "'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES'," + "'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES'," + "'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER'," + "'HIGH_NOT_PRECEDENCE')")}, + {NULL, 0} + }, + { + {(char *) STRING_WITH_LEN("comment")}, + {(char *) STRING_WITH_LEN("char(64)")}, + {(char *) STRING_WITH_LEN("utf8")} + } +}; + + +/* + Puts some data common to CREATE and ALTER EVENT into a row. + + SYNOPSIS + mysql_event_fill_row() + thd THD + table The row to fill out + et Event's data + is_update CREATE EVENT or ALTER EVENT + + RETURN VALUE + 0 OK + EVEX_GENERAL_ERROR Bad data + EVEX_GET_FIELD_FAILED Field count does not match. table corrupted? + + DESCRIPTION + Used both when an event is created and when it is altered. +*/ + +static int +mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et, + my_bool is_update) +{ + CHARSET_INFO *scs= system_charset_info; + enum enum_events_table_field f_num; + Field **fields= table->field; + + DBUG_ENTER("mysql_event_fill_row"); + + DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str)); + DBUG_PRINT("info", ("name =[%s]", et->name.str)); + DBUG_PRINT("info", ("body =[%s]", et->body.str)); + + if (fields[f_num= ET_FIELD_DEFINER]-> + store(et->definer.str, et->definer.length, scs)) + goto err_truncate; + + if (fields[f_num= ET_FIELD_DB]->store(et->dbname.str, et->dbname.length, scs)) + goto err_truncate; + + if (fields[f_num= ET_FIELD_NAME]->store(et->name.str, et->name.length, scs)) + goto err_truncate; + + /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/ + fields[ET_FIELD_ON_COMPLETION]->store((longlong)et->on_completion, TRUE); + + fields[ET_FIELD_STATUS]->store((longlong)et->status, TRUE); + + /* + Change the SQL_MODE only if body was present in an ALTER EVENT and of course + always during CREATE EVENT. + */ + if (et->body.str) + { + fields[ET_FIELD_SQL_MODE]->store((longlong)thd->variables.sql_mode, TRUE); + if (fields[f_num= ET_FIELD_BODY]->store(et->body.str, et->body.length, scs)) + goto err_truncate; + } + + if (et->expression) + { + fields[ET_FIELD_INTERVAL_EXPR]->set_notnull(); + fields[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, TRUE); + + fields[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull(); + /* + In the enum (C) intervals start from 0 but in mysql enum valid values + start from 1. Thus +1 offset is needed! + */ + fields[ET_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->interval+1, TRUE); + + fields[ET_FIELD_EXECUTE_AT]->set_null(); + + if (!et->starts_null) + { + fields[ET_FIELD_STARTS]->set_notnull(); + fields[ET_FIELD_STARTS]->store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME); + } + + if (!et->ends_null) + { + fields[ET_FIELD_ENDS]->set_notnull(); + fields[ET_FIELD_ENDS]->store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME); + } + } + else if (et->execute_at.year) + { + fields[ET_FIELD_INTERVAL_EXPR]->set_null(); + fields[ET_FIELD_TRANSIENT_INTERVAL]->set_null(); + fields[ET_FIELD_STARTS]->set_null(); + fields[ET_FIELD_ENDS]->set_null(); + + fields[ET_FIELD_EXECUTE_AT]->set_notnull(); + fields[ET_FIELD_EXECUTE_AT]-> + store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME); + } + else + { + DBUG_ASSERT(is_update); + /* + it is normal to be here when the action is update + this is an error if the action is create. something is borked + */ + } + + ((Field_timestamp *)fields[ET_FIELD_MODIFIED])->set_time(); + + if (et->comment.str) + { + if (fields[f_num= ET_FIELD_COMMENT]-> + store(et->comment.str, et->comment.length, scs)) + goto err_truncate; + } + + DBUG_RETURN(0); + +err_truncate: + my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), fields[f_num]->field_name); + DBUG_RETURN(EVEX_GENERAL_ERROR); +} + + +/* + Performs an index scan of event_table (mysql.event) and fills schema_table. + + Synopsis + Event_db_repository::index_read_for_db_for_i_s() + thd Thread + schema_table The I_S.EVENTS table + event_table The event table to use for loading (mysql.event) + + Returns + 0 OK + 1 Error +*/ + +int +Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table, + TABLE *event_table, char *db) +{ + int ret=0; + CHARSET_INFO *scs= system_charset_info; + KEY *key_info; + uint key_len; + byte *key_buf= NULL; + LINT_INIT(key_buf); + + DBUG_ENTER("Event_db_repository::index_read_for_db_for_i_s"); + + DBUG_PRINT("info", ("Using prefix scanning on PK")); + event_table->file->ha_index_init(0, 1); + event_table->field[ET_FIELD_DB]->store(db, strlen(db), scs); + key_info= event_table->key_info; + key_len= key_info->key_part[0].store_length; + + if (!(key_buf= (byte *)alloc_root(thd->mem_root, key_len))) + { + ret= 1; + /* Don't send error, it would be done by sql_alloc_error_handler() */ + } + else + { + key_copy(key_buf, event_table->record[0], key_info, key_len); + if (!(ret= event_table->file->index_read(event_table->record[0], key_buf, + key_len, HA_READ_PREFIX))) + { + DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret)); + do + { + ret= copy_event_to_schema_table(thd, schema_table, event_table); + if (ret == 0) + ret= event_table->file->index_next_same(event_table->record[0], + key_buf, key_len); + } while (ret == 0); + } + DBUG_PRINT("info", ("Scan finished. ret=%d", ret)); + } + event_table->file->ha_index_end(); + /* ret is guaranteed to be != 0 */ + if (ret == HA_ERR_END_OF_FILE || ret == HA_ERR_KEY_NOT_FOUND) + DBUG_RETURN(0); + DBUG_RETURN(1); +} + + +/* + Performs a table scan of event_table (mysql.event) and fills schema_table. + + Synopsis + Events_db_repository::table_scan_all_for_i_s() + thd Thread + schema_table The I_S.EVENTS in memory table + event_table The event table to use for loading. + + Returns + 0 OK + 1 Error +*/ + +int +Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table, + TABLE *event_table) +{ + int ret; + READ_RECORD read_record_info; + + DBUG_ENTER("Event_db_repository::table_scan_all_for_i_s"); + init_read_record(&read_record_info, thd, event_table, NULL, 1, 0); + + /* + rr_sequential, in read_record(), returns 137==HA_ERR_END_OF_FILE, + but rr_handle_error returns -1 for that reason. Thus, read_record() + returns -1 eventually. + */ + do + { + ret= read_record_info.read_record(&read_record_info); + if (ret == 0) + ret= copy_event_to_schema_table(thd, schema_table, event_table); + } while (ret == 0); + + DBUG_PRINT("info", ("Scan finished. ret=%d", ret)); + end_read_record(&read_record_info); + + /* ret is guaranteed to be != 0 */ + DBUG_RETURN(ret == -1? 0:1); +} + + +/* + Fills I_S.EVENTS with data loaded from mysql.event. Also used by + SHOW EVENTS + + Synopsis + Event_db_repository::fill_schema_events() + thd Thread + tables The schema table + db If not NULL then get events only from this schema + + Returns + 0 OK + 1 Error +*/ + +int +Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables, char *db) +{ + TABLE *schema_table= tables->table; + TABLE *event_table= NULL; + Open_tables_state backup; + int ret= 0; + + DBUG_ENTER("Event_db_repository::fill_schema_events"); + DBUG_PRINT("info",("db=%s", db? db:"(null)")); + + thd->reset_n_backup_open_tables_state(&backup); + if (open_event_table(thd, TL_READ, &event_table)) + { + sql_print_error("Table mysql.event is damaged."); + thd->restore_backup_open_tables_state(&backup); + DBUG_RETURN(1); + } + + /* + 1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order + thus we won't order it. OTOH, SHOW EVENTS will be + ordered. + 2. SHOW EVENTS => PRIMARY KEY with prefix scanning on (db) + Reasoning: Events are per schema, therefore a scan over an index + will save use from doing a table scan and comparing + every single row's `db` with the schema which we show. + */ + if (db) + ret= index_read_for_db_for_i_s(thd, schema_table, event_table, db); + else + ret= table_scan_all_for_i_s(thd, schema_table, event_table); + + close_thread_tables(thd); + thd->restore_backup_open_tables_state(&backup); + + DBUG_PRINT("info", ("Return code=%d", ret)); + DBUG_RETURN(ret); +} + + +/* + Open mysql.event table for read + + SYNOPSIS + Events::open_event_table() + thd [in] Thread context + lock_type [in] How to lock the table + table [out] We will store the open table here + + RETURN VALUE + 1 Cannot lock table + 2 The table is corrupted - different number of fields + 0 OK +*/ + +int +Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type, + TABLE **table) +{ + TABLE_LIST tables; + DBUG_ENTER("Event_db_repository::open_event_table"); + + bzero((char*) &tables, sizeof(tables)); + tables.db= (char*) "mysql"; + tables.table_name= tables.alias= (char*) "event"; + tables.lock_type= lock_type; + + if (simple_open_n_lock_tables(thd, &tables)) + DBUG_RETURN(1); + + if (table_check_intact(tables.table, ET_FIELD_COUNT, + event_table_fields, + &mysql_event_last_create_time, + ER_CANNOT_LOAD_FROM_TABLE)) + { + close_thread_tables(thd); + DBUG_RETURN(2); + } + *table= tables.table; + tables.table->use_all_columns(); + DBUG_RETURN(0); +} + + +/* + Checks parameters which we got from the parsing phase. + + SYNOPSIS + check_parse_params() + thd THD + et event's data + + RETURNS + 0 OK + EVEX_BAD_PARAMS Error (reported) +*/ + +static int +check_parse_params(THD *thd, Event_parse_data *parse_data) +{ + const char *pos= NULL; + Item *bad_item; + int res; + + DBUG_ENTER("check_parse_params"); + + if (parse_data->check_parse_data(thd)) + DBUG_RETURN(EVEX_BAD_PARAMS); + + if (!parse_data->dbname.str || + (thd->lex->sql_command == SQLCOM_ALTER_EVENT && thd->lex->spname && + !thd->lex->spname->m_db.str)) + { + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); + DBUG_RETURN(EVEX_BAD_PARAMS); + } + + if (check_access(thd, EVENT_ACL, parse_data->dbname.str, 0, 0, 0, + is_schema_db(parse_data->dbname.str)) || + (thd->lex->sql_command == SQLCOM_ALTER_EVENT && thd->lex->spname && + (check_access(thd, EVENT_ACL, thd->lex->spname->m_db.str, 0, 0, 0, + is_schema_db(thd->lex->spname->m_db.str))))) + DBUG_RETURN(EVEX_BAD_PARAMS); + + DBUG_RETURN(0); +} + + +/* + Creates an event in mysql.event + + SYNOPSIS + Event_db_repository::create_event() + thd [in] THD + et [in] Object containing info about the event + create_if_not [in] Whether to generate anwarning in case event exists + rows_affected [out] How many rows were affected + + RETURN VALUE + 0 OK + EVEX_GENERAL_ERROR Failure + + DESCRIPTION + Creates an event. Relies on mysql_event_fill_row which is shared with + ::update_event. The name of the event is inside "et". +*/ + +int +Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, + my_bool create_if_not, uint *rows_affected) +{ + int ret= 0; + CHARSET_INFO *scs= system_charset_info; + TABLE *table= NULL; + char old_db_buf[NAME_LEN+1]; + LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; + bool dbchanged= FALSE; + + DBUG_ENTER("Event_db_repository::create_event"); + + *rows_affected= 0; + DBUG_PRINT("info", ("open mysql.event for update")); + if (open_event_table(thd, TL_WRITE, &table)) + { + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + goto err; + } + + if (check_parse_params(thd, parse_data)) + goto err; + + DBUG_PRINT("info", ("name: %.*s", parse_data->name.length, + parse_data->name.str)); + + DBUG_PRINT("info", ("check existance of an event with the same name")); + if (!find_named_event(thd, parse_data->dbname, parse_data->name, table)) + { + if (create_if_not) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS), + parse_data->name.str); + goto ok; + } + my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str); + goto err; + } + + DBUG_PRINT("info", ("non-existant, go forward")); + + if ((ret= sp_use_new_db(thd, parse_data->dbname, &old_db, 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, parse_data->dbname.str, + parse_data->dbname.str + + parse_data->dbname.length) > EVEX_DB_FIELD_LEN) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->dbname.str); + goto err; + } + if (system_charset_info->cset-> + numchars(system_charset_info, parse_data->name.str, + parse_data->name.str + + parse_data->name.length) > EVEX_DB_FIELD_LEN) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->name.str); + goto err; + } + + if (parse_data->body.length > table->field[ET_FIELD_BODY]->field_length) + { + my_error(ER_TOO_LONG_BODY, MYF(0), parse_data->name.str); + goto err; + } + + if (!(parse_data->expression) && !(parse_data->execute_at.year)) + { + DBUG_PRINT("error", ("neither expression nor execute_at are set!")); + my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0)); + goto err; + } + + ((Field_timestamp *)table->field[ET_FIELD_CREATED])->set_time(); + + /* + mysql_event_fill_row() calls my_error() in case of error so no need to + handle it here + */ + if ((ret= mysql_event_fill_row(thd, table, parse_data, FALSE))) + goto err; + + /* Close active transaction only if We are going to modify disk */ + if (end_active_trans(thd)) + goto err; + + if (table->file->ha_write_row(table->record[0])) + { + my_error(ER_EVENT_STORE_FAILED, MYF(0), parse_data->name.str, ret); + goto err; + } + + *rows_affected= 1; +ok: + if (dbchanged) + (void) mysql_change_db(thd, old_db.str, 1); + /* + When valgrinded, the following call may lead to the following error: + + Syscall param pwrite64(buf) points to uninitialised byte(s) + at 0x406003B: do_pwrite64 (in /lib/tls/libpthread.so.0) + by 0x40600EF: pwrite64 (in /lib/tls/libpthread.so.0) + by 0x856FF74: my_pwrite (my_pread.c:146) + by 0x85734E1: flush_cached_blocks (mf_keycache.c:2280) + .... + Address 0x6618110 is 56 bytes inside a block of size 927,772 alloc'd + at 0x401C451: malloc (vg_replace_malloc.c:149) + by 0x8578CDC: _mymalloc (safemalloc.c:138) + by 0x858E5E2: my_large_malloc (my_largepage.c:65) + by 0x8570634: init_key_cache (mf_keycache.c:343) + by 0x82EDA51: ha_init_key_cache(char const*, st_key_cache*) (handler.cc:2509) + by 0x8212071: process_key_caches(int (*)(char const*, st_key_cache*)) + (set_var.cc:3824) + by 0x8206D75: init_server_components() (mysqld.cc:3304) + by 0x8207163: main (mysqld.cc:3578) + + I think it is safe not to think about it. + */ + close_thread_tables(thd); + DBUG_RETURN(0); + +err: + if (dbchanged) + (void) mysql_change_db(thd, old_db.str, 1); + if (table) + close_thread_tables(thd); + DBUG_RETURN(EVEX_GENERAL_ERROR); +} + + +/* + Used to execute ALTER EVENT. Pendant to Events::update_event(). + + SYNOPSIS + Event_db_repository::update_event() + thd THD + sp_name the name of the event to alter + et event's data + + RETURN VALUE + 0 OK + EVEX_GENERAL_ERROR Error occured and reported + + NOTES + sp_name is passed since this is the name of the event to + alter in case of RENAME TO. +*/ + +bool +Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data, + LEX_STRING *new_dbname, LEX_STRING *new_name) +{ + CHARSET_INFO *scs= system_charset_info; + TABLE *table= NULL; + int ret; + DBUG_ENTER("Event_db_repository::update_event"); + + if (open_event_table(thd, TL_WRITE, &table)) + { + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + goto err; + } + + if (check_parse_params(thd, parse_data)) + goto err; + + DBUG_PRINT("info", ("dbname: %s", parse_data->dbname.str)); + DBUG_PRINT("info", ("name: %s", parse_data->name.str)); + DBUG_PRINT("info", ("user: %s", parse_data->definer.str)); + if (new_dbname) + DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str)); + + /* first look whether we overwrite */ + if (new_dbname) + { + if (!sortcmp_lex_string(parse_data->name, *new_name, scs) && + !sortcmp_lex_string(parse_data->dbname, *new_dbname, scs)) + { + my_error(ER_EVENT_SAME_NAME, MYF(0), parse_data->name.str); + goto err; + } + + if (!find_named_event(thd, *new_dbname, *new_name, table)) + { + my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str); + goto err; + } + } + /* + ...and then if there is such an event. Don't exchange the blocks + because you will get error 120 from table handler because new_name will + overwrite the key and SE will tell us that it cannot find the already found + row (copied into record[1] later + */ + if (find_named_event(thd, parse_data->dbname, parse_data->name, table)) + { + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), parse_data->name.str); + goto err; + } + + store_record(table,record[1]); + + /* Don't update create on row update. */ + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; + + /* + mysql_event_fill_row() calls my_error() in case of error so no need to + handle it here + */ + if (mysql_event_fill_row(thd, table, parse_data, TRUE)) + goto err; + + if (new_dbname) + { + table->field[ET_FIELD_DB]->store(new_dbname->str, new_dbname->length, scs); + table->field[ET_FIELD_NAME]->store(new_name->str, new_name->length, scs); + } + + /* Close active transaction only if We are going to modify disk */ + if (end_active_trans(thd)) + goto err; + + if (table->file->ha_update_row(table->record[1], table->record[0])) + { + my_error(ER_EVENT_STORE_FAILED, MYF(0), parse_data->name.str, ret); + goto err; + } + + /* close mysql.event or we crash later when loading the event from disk */ + close_thread_tables(thd); + DBUG_RETURN(FALSE); + +err: + if (table) + close_thread_tables(thd); + DBUG_RETURN(TRUE); +} + + +/* + Drops an event + + SYNOPSIS + Event_db_repository::drop_event() + thd [in] THD + db [in] Database name + name [in] Event's name + drop_if_exists [in] If set and the event not existing => warning + onto the stack + rows_affected [out] Affected number of rows is returned heres + + RETURN VALUE + FALSE OK + TRUE Error (reported) +*/ + +bool +Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name, + bool drop_if_exists, uint *rows_affected) +{ + TABLE *table= NULL; + Open_tables_state backup; + int ret; + + DBUG_ENTER("Event_db_repository::drop_event"); + DBUG_PRINT("enter", ("%s@%s", db.str, name.str)); + + thd->reset_n_backup_open_tables_state(&backup); + if ((ret= open_event_table(thd, TL_WRITE, &table))) + { + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + goto done; + } + + if (!(ret= find_named_event(thd, db, name, table))) + { + /* Close active transaction only if we are actually going to modify disk */ + if (!(ret= end_active_trans(thd)) && + (ret= table->file->ha_delete_row(table->record[0]))) + my_error(ER_EVENT_CANNOT_DELETE, MYF(0)); + } + else + { + if (drop_if_exists) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), + "Event", name.str); + ret= 0; + } else + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str); + } + +done: + if (table) + close_thread_tables(thd); + thd->restore_backup_open_tables_state(&backup); + + DBUG_RETURN(ret); +} + + +/* + Positions the internal pointer of `table` to the place where (db, name) + is stored. + + SYNOPSIS + Event_db_repository::find_named_event() + thd Thread + db Schema + name Event name + table Opened mysql.event + + RETURN VALUE + FALSE OK + TRUE No such event +*/ + +bool +Event_db_repository::find_named_event(THD *thd, LEX_STRING db, LEX_STRING name, + TABLE *table) +{ + byte key[MAX_KEY_LENGTH]; + DBUG_ENTER("Event_db_repository::find_named_event"); + DBUG_PRINT("enter", ("name: %.*s", name.length, name.str)); + + /* + Create key to find row. We have to use field->store() to be able to + handle VARCHAR and CHAR fields. + Assumption here is that the two first fields in the table are + 'db' and 'name' and the first key is the primary key over the + same fields. + */ + if (db.length > table->field[ET_FIELD_DB]->field_length || + name.length > table->field[ET_FIELD_NAME]->field_length) + DBUG_RETURN(TRUE); + + table->field[ET_FIELD_DB]->store(db.str, db.length, &my_charset_bin); + table->field[ET_FIELD_NAME]->store(name.str, name.length, &my_charset_bin); + + key_copy(key, table->record[0], table->key_info, table->key_info->key_length); + + if (table->file->index_read_idx(table->record[0], 0, key, + table->key_info->key_length, + HA_READ_KEY_EXACT)) + { + DBUG_PRINT("info", ("Row not found")); + DBUG_RETURN(TRUE); + } + + DBUG_PRINT("info", ("Row found!")); + DBUG_RETURN(FALSE); +} + + +/* + Drops all events in the selected database, from mysql.event. + + SYNOPSIS + Event_db_repository::drop_schema_events() + thd Thread + schema The database to clean from events + + RETURN VALUE + 0 OK + !0 Error (Reported) +*/ + +int +Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema) +{ + return drop_events_by_field(thd, ET_FIELD_DB, schema); +} + + +/* + Drops all events by field which has specific value of the field + + SYNOPSIS + Event_db_repository::drop_events_by_field() + thd Thread + table mysql.event TABLE + field Which field of the row to use for matching + field_value The value that should match + + RETURN VALUE + 0 OK + !0 Error from ha_delete_row +*/ + +int +Event_db_repository::drop_events_by_field(THD *thd, + enum enum_events_table_field field, + LEX_STRING field_value) +{ + int ret= 0; + TABLE *table= NULL; + Open_tables_state backup; + READ_RECORD read_record_info; + DBUG_ENTER("Event_db_repository::drop_events_by_field"); + DBUG_PRINT("enter", ("field=%d field_value=%s", field, field_value.str)); + + if (open_event_table(thd, TL_WRITE, &table)) + { + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + DBUG_RETURN(1); + } + + /* only enabled events are in memory, so we go now and delete the rest */ + init_read_record(&read_record_info, thd, table, NULL, 1, 0); + while (!ret && !(read_record_info.read_record(&read_record_info)) ) + { + char *et_field= get_field(thd->mem_root, table->field[field]); + + LEX_STRING et_field_lex= {et_field, strlen(et_field)}; + DBUG_PRINT("info", ("Current event %s name=%s", et_field, + get_field(thd->mem_root, table->field[ET_FIELD_NAME]))); + + if (!sortcmp_lex_string(et_field_lex, field_value, system_charset_info)) + { + DBUG_PRINT("info", ("Dropping")); + if ((ret= table->file->ha_delete_row(table->record[0]))) + my_error(ER_EVENT_DROP_FAILED, MYF(0), + get_field(thd->mem_root, table->field[ET_FIELD_NAME])); + } + } + end_read_record(&read_record_info); + thd->version--; /* Force close to free memory */ + + DBUG_RETURN(ret); +} + + +/* + Looks for a named event in mysql.event and then loads it from + the table, compiles and inserts it into the cache. + + SYNOPSIS + Event_db_repository::load_named_event() + thd [in] THD + dbname [in] Event's db name + name [in] Event's name + etn_new [out] The loaded event + + RETURN VALUE + FALSE OK + TRUE Error (reported) +*/ + +bool +Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname, + LEX_STRING name, Event_basic *etn) +{ + TABLE *table= NULL; + int ret= 0; + Open_tables_state backup; + + DBUG_ENTER("Event_db_repository::load_named_event"); + DBUG_PRINT("enter",("thd=0x%lx name:%*s",thd, name.length, name.str)); + + thd->reset_n_backup_open_tables_state(&backup); + + if ((ret= open_event_table(thd, TL_READ, &table))) + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + else if ((ret= find_named_event(thd, dbname, name, table))) + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str); + else if ((ret= etn->load_from_row(table))) + my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event"); + + if (table) + close_thread_tables(thd); + + thd->restore_backup_open_tables_state(&backup); + /* In this case no memory was allocated so we don't need to clean */ + + DBUG_RETURN(ret); +} diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h new file mode 100644 index 00000000000..dce447a71e8 --- /dev/null +++ b/sql/event_db_repository.h @@ -0,0 +1,108 @@ +#ifndef _EVENT_DB_REPOSITORY_H_ +#define _EVENT_DB_REPOSITORY_H_ +/* Copyright (C) 2004-2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#define EVEX_OPEN_TABLE_FAILED -1 + +enum enum_events_table_field +{ + ET_FIELD_DB = 0, + ET_FIELD_NAME, + ET_FIELD_BODY, + ET_FIELD_DEFINER, + ET_FIELD_EXECUTE_AT, + ET_FIELD_INTERVAL_EXPR, + ET_FIELD_TRANSIENT_INTERVAL, + ET_FIELD_CREATED, + ET_FIELD_MODIFIED, + ET_FIELD_LAST_EXECUTED, + ET_FIELD_STARTS, + ET_FIELD_ENDS, + ET_FIELD_STATUS, + ET_FIELD_ON_COMPLETION, + ET_FIELD_SQL_MODE, + ET_FIELD_COMMENT, + ET_FIELD_COUNT /* a cool trick to count the number of fields :) */ +}; + + +int +events_table_index_read_for_db(THD *thd, TABLE *schema_table, + TABLE *event_table); + +int +events_table_scan_all(THD *thd, TABLE *schema_table, TABLE *event_table); + +int +fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */); + +class Event_basic; +class Event_parse_data; + +class Event_db_repository +{ +public: + Event_db_repository(){} + + int + create_event(THD *thd, Event_parse_data *parse_data, my_bool create_if_not, + uint *rows_affected); + + bool + update_event(THD *thd, Event_parse_data *parse_data, LEX_STRING *new_dbname, + LEX_STRING *new_name); + + bool + drop_event(THD *thd, LEX_STRING db, LEX_STRING name, bool drop_if_exists, + uint *rows_affected); + + int + drop_schema_events(THD *thd, LEX_STRING schema); + + bool + find_named_event(THD *thd, LEX_STRING db, LEX_STRING name, TABLE *table); + + bool + load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_basic *et); + + + int + open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); + + int + fill_schema_events(THD *thd, TABLE_LIST *tables, char *db); + +private: + int + drop_events_by_field(THD *thd, enum enum_events_table_field field, + LEX_STRING field_value); + int + index_read_for_db_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table, + char *db); + + int + table_scan_all_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table); + + static bool + check_system_tables(THD *thd); + + /* Prevent use of these */ + Event_db_repository(const Event_db_repository &); + void operator=(Event_db_repository &); +}; + +#endif /* _EVENT_DB_REPOSITORY_H_ */ diff --git a/sql/event_queue.cc b/sql/event_queue.cc new file mode 100644 index 00000000000..39b237987e9 --- /dev/null +++ b/sql/event_queue.cc @@ -0,0 +1,1084 @@ +/* Copyright (C) 2004-2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysql_priv.h" +#include "event_queue.h" +#include "event_data_objects.h" +#include "event_db_repository.h" +#include "event_scheduler.h" + + +#define EVENT_QUEUE_INITIAL_SIZE 30 +#define EVENT_QUEUE_EXTENT 30 + +#ifdef __GNUC__ +#if __GNUC__ >= 2 +#define SCHED_FUNC __FUNCTION__ +#endif +#else +#define SCHED_FUNC "<unknown>" +#endif + +#define LOCK_QUEUE_DATA() lock_data(SCHED_FUNC, __LINE__) +#define UNLOCK_QUEUE_DATA() unlock_data(SCHED_FUNC, __LINE__) + +struct event_queue_param +{ + THD *thd; + Event_queue *queue; + pthread_mutex_t LOCK_loaded; + pthread_cond_t COND_loaded; + bool loading_finished; +}; + + +/* + Compares the execute_at members of two Event_queue_element instances. + Used as callback for the prioritized queue when shifting + elements inside. + + SYNOPSIS + event_queue_element_data_compare_q() + vptr Not used (set it to NULL) + a First Event_queue_element object + b Second Event_queue_element object + + RETURN VALUE + -1 a->execute_at < b->execute_at + 0 a->execute_at == b->execute_at + 1 a->execute_at > b->execute_at + + NOTES + execute_at.second_part is not considered during comparison +*/ + +static int +event_queue_element_compare_q(void *vptr, byte* a, byte *b) +{ + return my_time_compare(&((Event_queue_element *)a)->execute_at, + &((Event_queue_element *)b)->execute_at); +} + + +pthread_handler_t +event_queue_loader_thread(void *arg) +{ + /* needs to be first for thread_stack */ + THD *thd= (THD *)((struct event_queue_param *) arg)->thd; + struct event_queue_param *param= (struct event_queue_param *) arg; + thd->thread_stack= (char *) &thd; + + if (post_init_event_thread(thd)) + goto end; + + DBUG_ENTER("event_queue_loader_thread"); + + + pthread_mutex_lock(¶m->LOCK_loaded); + param->queue->check_system_tables(thd); + param->queue->load_events_from_db(thd); + + param->loading_finished= TRUE; + pthread_cond_signal(¶m->COND_loaded); + + pthread_mutex_unlock(¶m->LOCK_loaded); + +end: + deinit_event_thread(thd); + DBUG_RETURN(0); // Against gcc warnings +} + + +/* + Constructor of class Event_queue. + + SYNOPSIS + Event_queue::Event_queue() +*/ + +Event_queue::Event_queue() +{ + mutex_last_unlocked_at_line= mutex_last_locked_at_line= + mutex_last_attempted_lock_at_line= 0; + + mutex_last_unlocked_in_func= mutex_last_locked_in_func= + mutex_last_attempted_lock_in_func= ""; + + mutex_queue_data_locked= mutex_queue_data_attempting_lock= FALSE; +} + + +/* + Inits mutexes. + + SYNOPSIS + Event_queue::init_mutexes() +*/ + +void +Event_queue::init_mutexes() +{ + pthread_mutex_init(&LOCK_event_queue, MY_MUTEX_INIT_FAST); +} + + +/* + Destroys mutexes. + + SYNOPSIS + Event_queue::deinit_mutexes() +*/ + +void +Event_queue::deinit_mutexes() +{ + pthread_mutex_destroy(&LOCK_event_queue); +} + + +/* + Inits the queue + + SYNOPSIS + Event_queue::init() + + RETURN VALUE + FALSE OK + TRUE Error +*/ + +bool +Event_queue::init_queue(Event_db_repository *db_repo, Event_scheduler *sched) +{ + THD *new_thd; + pthread_t th; + bool res; + struct event_queue_param *event_queue_param_value= NULL; + + DBUG_ENTER("Event_queue::init_queue"); + DBUG_PRINT("enter", ("this=0x%lx", this)); + + LOCK_QUEUE_DATA(); + db_repository= db_repo; + scheduler= sched; + + if (init_queue_ex(&queue, EVENT_QUEUE_INITIAL_SIZE , 0 /*offset*/, + 0 /*max_on_top*/, event_queue_element_compare_q, + NULL, EVENT_QUEUE_EXTENT)) + { + sql_print_error("SCHEDULER: Can't initialize the execution queue"); + goto err; + } + + if (sizeof(my_time_t) != sizeof(time_t)) + { + sql_print_error("SCHEDULER: sizeof(my_time_t) != sizeof(time_t) ." + "The scheduler may not work correctly. Stopping."); + DBUG_ASSERT(0); + goto err; + } + + if (!(new_thd= new THD)) + goto err; + + pre_init_event_thread(new_thd); + new_thd->security_ctx->set_user((char*)"event_scheduler_loader"); + + event_queue_param_value= (struct event_queue_param *) + my_malloc(sizeof(struct event_queue_param), MYF(0)); + + event_queue_param_value->thd= new_thd; + event_queue_param_value->queue= this; + event_queue_param_value->loading_finished= FALSE; + pthread_mutex_init(&event_queue_param_value->LOCK_loaded, MY_MUTEX_INIT_FAST); + pthread_cond_init(&event_queue_param_value->COND_loaded, NULL); + + pthread_mutex_lock(&event_queue_param_value->LOCK_loaded); + DBUG_PRINT("info", ("Forking new thread for scheduduler. THD=0x%lx", new_thd)); + if (!(res= pthread_create(&th, &connection_attrib, event_queue_loader_thread, + (void*)event_queue_param_value))) + { + do { + pthread_cond_wait(&event_queue_param_value->COND_loaded, + &event_queue_param_value->LOCK_loaded); + } while (event_queue_param_value->loading_finished == FALSE); + } + + pthread_mutex_unlock(&event_queue_param_value->LOCK_loaded); + pthread_mutex_destroy(&event_queue_param_value->LOCK_loaded); + pthread_cond_destroy(&event_queue_param_value->COND_loaded); + my_free((char *)event_queue_param_value, MYF(0)); + + UNLOCK_QUEUE_DATA(); + DBUG_RETURN(res); + +err: + UNLOCK_QUEUE_DATA(); + DBUG_RETURN(TRUE); +} + + +/* + Deinits the queue. Remove all elements from it and destroys them + too. + + SYNOPSIS + Event_queue::deinit_queue() +*/ + +void +Event_queue::deinit_queue() +{ + DBUG_ENTER("Event_queue::deinit_queue"); + + LOCK_QUEUE_DATA(); + empty_queue(); + delete_queue(&queue); + UNLOCK_QUEUE_DATA(); + + DBUG_VOID_RETURN; +} + + +/* + Adds an event to the queue. + + SYNOPSIS + Event_queue::create_event() + dbname The schema of the new event + name The name of the new event + + RETURN VALUE + OP_OK OK or scheduler not working + OP_LOAD_ERROR Error during loading from disk +*/ + +int +Event_queue::create_event(THD *thd, LEX_STRING dbname, LEX_STRING name) +{ + int res; + Event_queue_element *new_element; + DBUG_ENTER("Event_queue::create_event"); + DBUG_PRINT("enter", ("thd=0x%lx et=%s.%s",thd, dbname.str, name.str)); + + new_element= new Event_queue_element(); + res= db_repository->load_named_event(thd, dbname, name, new_element); + if (res || new_element->status == Event_queue_element::DISABLED) + delete new_element; + else + { + new_element->compute_next_execution_time(); + + LOCK_QUEUE_DATA(); + DBUG_PRINT("info", ("new event in the queue 0x%lx", new_element)); + queue_insert_safe(&queue, (byte *) new_element); + dbug_dump_queue(thd->query_start()); + UNLOCK_QUEUE_DATA(); + + notify_observers(); + } + + DBUG_RETURN(res); +} + + +/* + Updates an event from the scheduler queue + + SYNOPSIS + Event_queue::update_event() + thd Thread + dbname Schema of the event + name Name of the event + new_schema New schema, in case of RENAME TO, otherwise NULL + new_name New name, in case of RENAME TO, otherwise NULL + + RETURN VALUE + OP_OK OK or scheduler not working + OP_LOAD_ERROR Error during loading from disk +*/ + +int +Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name, + LEX_STRING *new_schema, LEX_STRING *new_name) +{ + int res; + Event_queue_element *old_element= NULL, + *new_element; + + DBUG_ENTER("Event_queue::update_event"); + DBUG_PRINT("enter", ("thd=0x%lx et=[%s.%s]", thd, dbname.str, name.str)); + + new_element= new Event_queue_element(); + + res= db_repository->load_named_event(thd, new_schema? *new_schema:dbname, + new_name? *new_name:name, new_element); + if (res) + { + delete new_element; + goto end; + } + else if (new_element->status == Event_queue_element::DISABLED) + { + DBUG_PRINT("info", ("The event is disabled.")); + /* + Destroy the object but don't skip to end: because we may have to remove + object from the cache. + */ + delete new_element; + new_element= NULL; + } + else + new_element->compute_next_execution_time(); + + LOCK_QUEUE_DATA(); + if (!(old_element= find_n_remove_event(dbname, name))) + { + DBUG_PRINT("info", ("%s.%s not cached, probably was DISABLED", + dbname.str, name.str)); + } + /* If not disabled event */ + if (new_element) + { + DBUG_PRINT("info", ("new event in the Q 0x%lx old 0x%lx", + new_element, old_element)); + queue_insert_safe(&queue, (byte *) new_element); + } + dbug_dump_queue(thd->query_start()); + UNLOCK_QUEUE_DATA(); + + if (new_element) + notify_observers(); + + if (old_element) + delete old_element; +end: + DBUG_PRINT("info", ("res=%d", res)); + DBUG_RETURN(res); +} + + +/* + Drops an event from the queue + + SYNOPSIS + Event_queue::drop_event() + thd Thread + dbname Schema of the event to drop + name Name of the event to drop +*/ + +void +Event_queue::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name) +{ + int res; + Event_queue_element *element; + DBUG_ENTER("Event_queue::drop_event"); + DBUG_PRINT("enter", ("thd=0x%lx name=0x%lx", thd, name)); + + LOCK_QUEUE_DATA(); + element= find_n_remove_event(dbname, name); + dbug_dump_queue(thd->query_start()); + UNLOCK_QUEUE_DATA(); + + if (element) + delete element; + else + DBUG_PRINT("info", ("No such event found, probably DISABLED")); + + /* + We don't signal here because the scheduler will catch the change + next time it wakes up. + */ + + DBUG_VOID_RETURN; +} + + +/* + Drops all events from the in-memory queue and disk that match + certain pattern evaluated by a comparator function + + SYNOPSIS + Event_queue::drop_matching_events() + thd THD + pattern A pattern string + comparator The function to use for comparing + + RETURN VALUE + >=0 Number of dropped events + + NOTE + Expected is the caller to acquire lock on LOCK_event_queue +*/ + +void +Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern, + bool (*comparator)(LEX_STRING, Event_basic *)) +{ + DBUG_ENTER("Event_queue::drop_matching_events"); + DBUG_PRINT("enter", ("pattern=%s", pattern.str)); + + uint i= 0; + while (i < queue.elements) + { + Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i); + DBUG_PRINT("info", ("[%s.%s]?", et->dbname.str, et->name.str)); + if (comparator(pattern, et)) + { + /* + The queue is ordered. If we remove an element, then all elements after + it will shift one position to the left, if we imagine it as an array + from left to the right. In this case we should not increment the + counter and the (i < queue.elements) condition is ok. + */ + queue_remove(&queue, i); + delete et; + } + else + i++; + } + /* + We don't call notify_observers() . If we remove the top event: + 1. The queue is empty. The scheduler will wake up at some time and realize + that the queue is empty. If create_event() comes inbetween it will + signal the scheduler + 2. The queue is not empty, but the next event after the previous top, won't + be executed any time sooner than the element we removed. Hence, we may + not notify the scheduler and it will realize the change when it + wakes up from timedwait. + */ + + DBUG_VOID_RETURN; +} + + +/* + Drops all events from the in-memory queue and disk that are from + certain schema. + + SYNOPSIS + Event_queue::drop_schema_events() + thd THD + db The schema name + + RETURN VALUE + >=0 Number of dropped events +*/ + +void +Event_queue::drop_schema_events(THD *thd, LEX_STRING schema) +{ + DBUG_ENTER("Event_queue::drop_schema_events"); + LOCK_QUEUE_DATA(); + drop_matching_events(thd, schema, event_basic_db_equal); + UNLOCK_QUEUE_DATA(); + DBUG_VOID_RETURN; +} + + +/* + Signals the observers (the main scheduler thread) that the + state of the queue has been changed. + + SYNOPSIS + Event_queue::notify_observers() +*/ + +void +Event_queue::notify_observers() +{ + DBUG_ENTER("Event_queue::notify_observers"); + DBUG_PRINT("info", ("Signalling change of the queue")); + scheduler->queue_changed(); + DBUG_VOID_RETURN; +} + + +/* + Searches for an event in the queue + + SYNOPSIS + Event_queue::find_n_remove_event() + db The schema of the event to find + name The event to find + + RETURN VALUE + NULL Not found + otherwise Address + + NOTE + The caller should do the locking also the caller is responsible for + actual signalling in case an event is removed from the queue. +*/ + +Event_queue_element * +Event_queue::find_n_remove_event(LEX_STRING db, LEX_STRING name) +{ + uint i; + DBUG_ENTER("Event_queue::find_n_remove_event"); + + for (i= 0; i < queue.elements; ++i) + { + Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i); + DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?", db.str, name.str, + et->dbname.str, et->name.str)); + if (event_basic_identifier_equal(db, name, et)) + { + queue_remove(&queue, i); + DBUG_RETURN(et); + } + } + + DBUG_RETURN(NULL); +} + + +/* + Loads all ENABLED events from mysql.event into the prioritized + queue. Called during scheduler main thread initialization. Compiles + the events. Creates Event_queue_element instances for every ENABLED event + from mysql.event. + + SYNOPSIS + Event_queue::load_events_from_db() + thd - Thread context. Used for memory allocation in some cases. + + RETURN VALUE + 0 OK + !0 Error (EVEX_OPEN_TABLE_FAILED, EVEX_MICROSECOND_UNSUP, + EVEX_COMPILE_ERROR) - in all these cases mysql.event was + tampered. + + NOTES + Reports the error to the console +*/ + +int +Event_queue::load_events_from_db(THD *thd) +{ + TABLE *table; + READ_RECORD read_record_info; + int ret= -1; + uint count= 0; + bool clean_the_queue= TRUE; + /* Compile the events on this root but only for syntax check, then discard */ + MEM_ROOT boot_root; + + DBUG_ENTER("Event_queue::load_events_from_db"); + DBUG_PRINT("enter", ("thd=0x%lx", thd)); + + if ((ret= db_repository->open_event_table(thd, TL_READ, &table))) + { + sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open."); + DBUG_RETURN(EVEX_OPEN_TABLE_FAILED); + } + + init_read_record(&read_record_info, thd, table ,NULL,1,0); + while (!(read_record_info.read_record(&read_record_info))) + { + Event_queue_element *et; + if (!(et= new Event_queue_element)) + { + DBUG_PRINT("info", ("Out of memory")); + break; + } + DBUG_PRINT("info", ("Loading event from row.")); + + if ((ret= et->load_from_row(table))) + { + sql_print_error("SCHEDULER: Error while loading from mysql.event. " + "Table probably corrupted"); + break; + } + if (et->status != Event_queue_element::ENABLED) + { + DBUG_PRINT("info",("%s is disabled",et->name.str)); + delete et; + continue; + } + + /* let's find when to be executed */ + if (et->compute_next_execution_time()) + { + sql_print_error("SCHEDULER: Error while computing execution time of %s.%s." + " Skipping", et->dbname.str, et->name.str); + continue; + } + + { + Event_job_data temp_job_data; + DBUG_PRINT("info", ("Event %s loaded from row. ", et->name.str)); + + temp_job_data.load_from_row(table); + + /* We load only on scheduler root just to check whether the body compiles */ + switch (ret= temp_job_data.compile(thd, thd->mem_root)) { + case EVEX_MICROSECOND_UNSUP: + sql_print_error("SCHEDULER: mysql.event is tampered. MICROSECOND is not " + "supported but found in mysql.event"); + break; + case EVEX_COMPILE_ERROR: + sql_print_error("SCHEDULER: Error while compiling %s.%s. Aborting load.", + et->dbname.str, et->name.str); + break; + default: + break; + } + thd->end_statement(); + thd->cleanup_after_query(); + } + if (ret) + { + delete et; + goto end; + } + + DBUG_PRINT("load_events_from_db", ("Adding 0x%lx to the exec list.")); + queue_insert_safe(&queue, (byte *) et); + count++; + } + clean_the_queue= FALSE; +end: + end_read_record(&read_record_info); + + if (clean_the_queue) + { + empty_queue(); + ret= -1; + } + else + { + ret= 0; + sql_print_information("SCHEDULER: Loaded %d event%s", count, (count == 1)?"":"s"); + } + + /* Force close to free memory */ + thd->version--; + + close_thread_tables(thd); + + DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count)); + DBUG_RETURN(ret); +} + + +/* + Opens mysql.db and mysql.user and checks whether: + 1. mysql.db has column Event_priv at column 20 (0 based); + 2. mysql.user has column Event_priv at column 29 (0 based); + + SYNOPSIS + Event_queue::check_system_tables() + thd Thread + + RETURN VALUE + FALSE OK + TRUE Error +*/ + +void +Event_queue::check_system_tables(THD *thd) +{ + TABLE_LIST tables; + bool not_used; + Open_tables_state backup; + bool ret; + + DBUG_ENTER("Event_queue::check_system_tables"); + DBUG_PRINT("enter", ("thd=0x%lx", thd)); + + thd->reset_n_backup_open_tables_state(&backup); + + bzero((char*) &tables, sizeof(tables)); + tables.db= (char*) "mysql"; + tables.table_name= tables.alias= (char*) "db"; + tables.lock_type= TL_READ; + + if ((ret= simple_open_n_lock_tables(thd, &tables))) + { + sql_print_error("Cannot open mysql.db"); + goto end; + } + ret= table_check_intact(tables.table, MYSQL_DB_FIELD_COUNT, + mysql_db_table_fields, &mysql_db_table_last_check, + ER_CANNOT_LOAD_FROM_TABLE); + close_thread_tables(thd); + + bzero((char*) &tables, sizeof(tables)); + tables.db= (char*) "mysql"; + tables.table_name= tables.alias= (char*) "user"; + tables.lock_type= TL_READ; + + if (simple_open_n_lock_tables(thd, &tables)) + sql_print_error("Cannot open mysql.db"); + else + { + if (tables.table->s->fields < 29 || + strncmp(tables.table->field[29]->field_name, + STRING_WITH_LEN("Event_priv"))) + sql_print_error("mysql.user has no `Event_priv` column at position 29"); + close_thread_tables(thd); + } + +end: + thd->restore_backup_open_tables_state(&backup); + + DBUG_VOID_RETURN; +} + + +/* + Recalculates activation times in the queue. There is one reason for + that. Because the values (execute_at) by which the queue is ordered are + changed by calls to compute_next_execution_time() on a request from the + scheduler thread, if it is not running then the values won't be updated. + Once the scheduler is started again the values has to be recalculated + so they are right for the current time. + + SYNOPSIS + Event_queue::recalculate_activation_times() + thd Thread +*/ + +void +Event_queue::recalculate_activation_times(THD *thd) +{ + uint i; + DBUG_ENTER("Event_queue::recalculate_activation_times"); + + LOCK_QUEUE_DATA(); + DBUG_PRINT("info", ("%u loaded events to be recalculated", queue.elements)); + for (i= 0; i < queue.elements; i++) + { + ((Event_queue_element*)queue_element(&queue, i))->compute_next_execution_time(); + ((Event_queue_element*)queue_element(&queue, i))->update_timing_fields(thd); + } + queue_fix(&queue); + UNLOCK_QUEUE_DATA(); + + DBUG_VOID_RETURN; +} + + +/* + Empties the queue and destroys the Event_queue_element objects in the + queue. + + SYNOPSIS + Event_queue::empty_queue() + + NOTE + Should be called with LOCK_event_queue locked +*/ + +void +Event_queue::empty_queue() +{ + uint i; + DBUG_ENTER("Event_queue::empty_queue"); + DBUG_PRINT("enter", ("Purging the queue. %d element(s)", queue.elements)); + sql_print_information("SCHEDULER: Purging queue. %u events", queue.elements); + /* empty the queue */ + for (i= 0; i < queue.elements; ++i) + { + Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i); + delete et; + } + resize_queue(&queue, 0); + DBUG_VOID_RETURN; +} + + +/* + Dumps the queue to the trace log. + + SYNOPSIS + Event_queue::dbug_dump_queue() + now Current timestamp +*/ + +void +Event_queue::dbug_dump_queue(time_t now) +{ +#ifndef DBUG_OFF + Event_queue_element *et; + uint i; + DBUG_ENTER("Event_queue::dbug_dump_queue"); + DBUG_PRINT("info", ("Dumping queue . Elements=%u", queue.elements)); + for (i = 0; i < queue.elements; i++) + { + et= ((Event_queue_element*)queue_element(&queue, i)); + DBUG_PRINT("info",("et=0x%lx db=%s name=%s",et, et->dbname.str, et->name.str)); + DBUG_PRINT("info", ("exec_at=%llu starts=%llu ends=%llu execs_so_far=%u" + " expr=%lld et.exec_at=%d now=%d (et.exec_at - now)=%d if=%d", + TIME_to_ulonglong_datetime(&et->execute_at), + TIME_to_ulonglong_datetime(&et->starts), + TIME_to_ulonglong_datetime(&et->ends), + et->execution_count, + et->expression, sec_since_epoch_TIME(&et->execute_at), now, + (int)(sec_since_epoch_TIME(&et->execute_at) - now), + sec_since_epoch_TIME(&et->execute_at) <= now)); + } + DBUG_VOID_RETURN; +#endif +} + + +/* + Checks whether the top of the queue is elligible for execution and + returns an Event_job_data instance in case it should be executed. + `now` is compared against `execute_at` of the top element in the queue. + + SYNOPSIS + Event_queue::get_top_for_execution_if_time() + thd [in] Thread + now [in] Current timestamp + job_data [out] The object to execute + abstime [out] Time to sleep + + RETURN VALUE + FALSE No error. If *job_data==NULL then top not elligible for execution. + Could be that there is no top. If abstime->tv_sec is set to value + greater than zero then use abstime with pthread_cond_timedwait(). + If abstime->tv_sec is zero then sleep with pthread_cond_wait(). + abstime->tv_nsec is always zero. + TRUE Error + +*/ + +bool +Event_queue::get_top_for_execution_if_time(THD *thd, time_t now, + Event_job_data **job_data, + struct timespec *abstime) +{ + bool ret= FALSE; + struct timespec top_time; + *job_data= NULL; + DBUG_ENTER("Event_queue::get_top_for_execution_if_time"); + DBUG_PRINT("enter", ("thd=0x%lx now=%d", thd, now)); + abstime->tv_nsec= 0; + LOCK_QUEUE_DATA(); + do { + int res; + if (!queue.elements) + { + abstime->tv_sec= 0; + break; + } + + Event_queue_element *top= ((Event_queue_element*) queue_element(&queue, 0)); + + top_time.tv_sec= sec_since_epoch_TIME(&top->execute_at); + + if (top_time.tv_sec > now) + { + abstime->tv_sec= top_time.tv_sec; + DBUG_PRINT("info", ("Have to wait %d till %d", abstime->tv_sec - now, + abstime->tv_sec)); + break; + } + + DBUG_PRINT("info", ("Ready for execution")); + abstime->tv_sec= 0; + if (!(*job_data= new Event_job_data())) + { + ret= TRUE; + break; + } + if ((res= db_repository->load_named_event(thd, top->dbname, top->name, + *job_data))) + { + delete *job_data; + *job_data= NULL; + ret= TRUE; + break; + } + + top->mark_last_executed(thd); + if (top->compute_next_execution_time()) + top->status= Event_queue_element::DISABLED; + DBUG_PRINT("info", ("event %s status is %d", top->name.str, top->status)); + + (*job_data)->execution_count= top->execution_count; + + top->update_timing_fields(thd); + if (((top->execute_at.year && !top->expression) || top->execute_at_null) || + (top->status == Event_queue_element::DISABLED)) + { + DBUG_PRINT("info", ("removing from the queue")); + sql_print_information("SCHEDULER: Last execution of %s.%s. %s", + top->dbname.str, top->name.str, + top->dropped? "Dropping.":""); + if (top->dropped) + top->drop(thd); + delete top; + queue_remove(&queue, 0); + } + else + queue_replaced(&queue); + + dbug_dump_queue(now); + } while (0); + UNLOCK_QUEUE_DATA(); + + DBUG_PRINT("info", ("returning %d. et_new=0x%lx abstime.tv_sec=%d ", + ret, *job_data, abstime->tv_sec)); + + if (*job_data) + DBUG_PRINT("info", ("db=%s name=%s definer=%s", (*job_data)->dbname.str, + (*job_data)->name.str, (*job_data)->definer.str)); + + DBUG_RETURN(ret); +} + + +/* + Auxiliary function for locking LOCK_event_queue. Used by the + LOCK_QUEUE_DATA macro + + SYNOPSIS + Event_queue::lock_data() + func Which function is requesting mutex lock + line On which line mutex lock is requested +*/ + +void +Event_queue::lock_data(const char *func, uint line) +{ + DBUG_ENTER("Event_queue::lock_data"); + DBUG_PRINT("enter", ("func=%s line=%u", func, line)); + mutex_last_attempted_lock_in_func= func; + mutex_last_attempted_lock_at_line= line; + mutex_queue_data_attempting_lock= TRUE; + pthread_mutex_lock(&LOCK_event_queue); + mutex_last_attempted_lock_in_func= ""; + mutex_last_attempted_lock_at_line= 0; + mutex_queue_data_attempting_lock= FALSE; + + mutex_last_locked_in_func= func; + mutex_last_locked_at_line= line; + mutex_queue_data_locked= TRUE; + + DBUG_VOID_RETURN; +} + + +/* + Auxiliary function for unlocking LOCK_event_queue. Used by the + UNLOCK_QUEUE_DATA macro + + SYNOPSIS + Event_queue::unlock_data() + func Which function is requesting mutex unlock + line On which line mutex unlock is requested +*/ + +void +Event_queue::unlock_data(const char *func, uint line) +{ + DBUG_ENTER("Event_queue::unlock_data"); + DBUG_PRINT("enter", ("func=%s line=%u", func, line)); + mutex_last_unlocked_at_line= line; + mutex_queue_data_locked= FALSE; + mutex_last_unlocked_in_func= func; + pthread_mutex_unlock(&LOCK_event_queue); + DBUG_VOID_RETURN; +} + + +/* + Dumps the internal status of the queue + + SYNOPSIS + Event_queue::dump_internal_status() + thd Thread + + RETURN VALUE + FALSE OK + TRUE Error +*/ + +bool +Event_queue::dump_internal_status(THD *thd) +{ + DBUG_ENTER("Event_queue::dump_internal_status"); +#ifndef DBUG_OFF + CHARSET_INFO *scs= system_charset_info; + Protocol *protocol= thd->protocol; + List<Item> field_list; + int ret; + char tmp_buff[5*STRING_BUFFER_USUAL_SIZE]; + char int_buff[STRING_BUFFER_USUAL_SIZE]; + String tmp_string(tmp_buff, sizeof(tmp_buff), scs); + String int_string(int_buff, sizeof(int_buff), scs); + tmp_string.length(0); + int_string.length(0); + + /* workers_count */ + protocol->prepare_for_resend(); + protocol->store(STRING_WITH_LEN("queue element count"), scs); + int_string.set((longlong) queue.elements, scs); + protocol->store(&int_string); + ret= protocol->write(); + + /* queue_data_locked */ + protocol->prepare_for_resend(); + protocol->store(STRING_WITH_LEN("queue data locked"), scs); + int_string.set((longlong) mutex_queue_data_locked, scs); + protocol->store(&int_string); + ret= protocol->write(); + + /* queue_data_attempting_lock */ + protocol->prepare_for_resend(); + protocol->store(STRING_WITH_LEN("queue data attempting lock"), scs); + int_string.set((longlong) mutex_queue_data_attempting_lock, scs); + protocol->store(&int_string); + ret= protocol->write(); + + /* last locked at*/ + protocol->prepare_for_resend(); + protocol->store(STRING_WITH_LEN("queue last locked at"), scs); + tmp_string.length(scs->cset->snprintf(scs, (char*) tmp_string.ptr(), + tmp_string.alloced_length(), "%s::%d", + mutex_last_locked_in_func, + mutex_last_locked_at_line)); + protocol->store(&tmp_string); + ret= protocol->write(); + + /* last unlocked at*/ + protocol->prepare_for_resend(); + protocol->store(STRING_WITH_LEN("queue last unlocked at"), scs); + tmp_string.length(scs->cset->snprintf(scs, (char*) tmp_string.ptr(), + tmp_string.alloced_length(), "%s::%d", + mutex_last_unlocked_in_func, + mutex_last_unlocked_at_line)); + protocol->store(&tmp_string); + ret= protocol->write(); + + /* last attempted lock at*/ + protocol->prepare_for_resend(); + protocol->store(STRING_WITH_LEN("queue last attempted lock at"), scs); + tmp_string.length(scs->cset->snprintf(scs, (char*) tmp_string.ptr(), + tmp_string.alloced_length(), "%s::%d", + mutex_last_attempted_lock_in_func, + mutex_last_attempted_lock_at_line)); + protocol->store(&tmp_string); + ret= protocol->write(); + +#endif + DBUG_RETURN(FALSE); +} diff --git a/sql/event_queue.h b/sql/event_queue.h new file mode 100644 index 00000000000..e8b46abde92 --- /dev/null +++ b/sql/event_queue.h @@ -0,0 +1,119 @@ +#ifndef _EVENT_QUEUE_H_ +#define _EVENT_QUEUE_H_ +/* Copyright (C) 2004-2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +class Event_basic; +class Event_db_repository; +class Event_job_data; +class Event_queue_element; + +class THD; +class Event_scheduler; + +class Event_queue +{ +public: + Event_queue(); + + void + init_mutexes(); + + void + deinit_mutexes(); + + bool + init_queue(Event_db_repository *db_repo, Event_scheduler *sched); + + void + deinit_queue(); + + /* Methods for queue management follow */ + + int + create_event(THD *thd, LEX_STRING dbname, LEX_STRING name); + + int + update_event(THD *thd, LEX_STRING dbname, LEX_STRING name, + LEX_STRING *new_schema, LEX_STRING *new_name); + + void + drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name); + + void + drop_schema_events(THD *thd, LEX_STRING schema); + + void + check_system_tables(THD *thd); + + void + recalculate_activation_times(THD *thd); + + bool + get_top_for_execution_if_time(THD *thd, time_t now, Event_job_data **job_data, + struct timespec *abstime); + bool + dump_internal_status(THD *thd); + + int + load_events_from_db(THD *thd); + +protected: + Event_queue_element * + find_n_remove_event(LEX_STRING db, LEX_STRING name); + + + void + drop_matching_events(THD *thd, LEX_STRING pattern, + bool (*)(LEX_STRING, Event_basic *)); + + void + empty_queue(); + + void + notify_observers(); + + void + dbug_dump_queue(time_t now); + + /* LOCK_event_queue is the mutex which protects the access to the queue. */ + pthread_mutex_t LOCK_event_queue; + + Event_db_repository *db_repository; + + Event_scheduler *scheduler; + + /* The sorted queue with the Event_job_data objects */ + QUEUE queue; + + uint mutex_last_locked_at_line; + uint mutex_last_unlocked_at_line; + uint mutex_last_attempted_lock_at_line; + const char* mutex_last_locked_in_func; + const char* mutex_last_unlocked_in_func; + const char* mutex_last_attempted_lock_in_func; + bool mutex_queue_data_locked; + bool mutex_queue_data_attempting_lock; + + /* helper functions for working with mutexes & conditionals */ + void + lock_data(const char *func, uint line); + + void + unlock_data(const char *func, uint line); +}; + +#endif /* _EVENT_QUEUE_H_ */ diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 1b4a0d290e6..8385d89b0c9 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -15,228 +15,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mysql_priv.h" -#include "events_priv.h" #include "events.h" -#include "event_timed.h" +#include "event_data_objects.h" #include "event_scheduler.h" -#include "sp_head.h" - -/* - ToDo: - 1. Talk to Alik to get a check for configure.in for my_time_t and time_t - 2. Look at guardian.h|cc to see its life cycle, has similarities. -*/ - - -/* - The scheduler is implemented as class Event_scheduler. Only one instance is - kept during the runtime of the server, by implementing the Singleton DP. - Object instance is always there because the memory is allocated statically - and initialized when the OS loader loads mysqld. This initialization is - bare. Extended initialization is done during the call to - Event_scheduler::init() in Events::init(). The reason for that late initialization - is that some subsystems needed to boot the Scheduler are not available at - earlier stages of the mysqld boot procedure. Events::init() is called in - mysqld.cc . If the mysqld is started with --event-scheduler=0 then - no initialization takes place and the scheduler is unavailable during this - server run. The server should be started with --event-scheduler=1 to have - the scheduler initialized and able to execute jobs. This starting alwa - s implies that the jobs execution will start immediately. If the server - is started with --event-scheduler=2 then the scheduler is started in suspended - state. Default state, if --event-scheduler is not specified is 2. - - The scheduler only manages execution of the events. Their creation, - alteration and deletion is delegated to other routines found in event.cc . - These routines interact with the scheduler : - - CREATE EVENT -> Event_scheduler::create_event() - - ALTER EVENT -> Event_scheduler::update_event() - - DROP EVENT -> Event_scheduler::drop_event() - - There is one mutex in the single Event_scheduler object which controls - the simultaneous access to the objects invariants. Using one lock makes - it easy to follow the workflow. This mutex is LOCK_scheduler_data. It is - initialized in Event_scheduler::init(). Which in turn is called by the - Facade class Events in event.cc, coming from init_thread_environment() from - mysqld.cc -> no concurrency at this point. It's destroyed in - Events::destroy_mutexes() called from clean_up_mutexes() in mysqld.cc . - - The full initialization is done in Event_scheduler::init() called from - Events::init(). It's done before any requests coming in, so this is a - guarantee for not having concurrency. - - The scheduler is started with Event_scheduler::start() and stopped with - Event_scheduler::stop(). When the scheduler starts it loads all events - from mysql.event table. Unfortunately, there is a race condition between - the event disk management functions and the scheduler ones - (add/replace/drop_event & load_events_from_db()), because the operations - do not happen under one global lock but the disk operations are guarded - by the MYISAM lock on mysql.event. In the same time, the queue operations - are guarded by LOCK_scheduler_data. If the scheduler is start()-ed during - server startup and stopped()-ed during server shutdown (in Events::shutdown() - called by kill_server() in mysqld.cc) these races does not exist. - - Since the user may want to temporarily inhibit execution of events the - scheduler can be suspended and then it can be forced to resume its - operations. The API call to perform these is - Event_scheduler::suspend_or_resume(enum enum_suspend_or_resume) . - When the scheduler is suspended the main scheduler thread, which ATM - happens to have thread_id 1, locks on a condition COND_suspend_or_resume. - When this is signal is sent for the reverse operation the main scheduler - loops continues to roll and execute events. - - When the scheduler is suspended all add/replace/drop_event() operations - work as expected and the modify the queue but no events execution takes - place. - - In contrast to the previous scheduler implementation, found in - event_executor.cc, the start, shutdown, suspend and resume are synchronous - operations. As a whole all operations are synchronized and no busy waits - are used except in stop_all_running_events(), which waits until all - running event worker threads have finished. It would have been nice to - use a conditional on which this method will wait and the last thread to - finish would signal it but this implies subclassing THD. - - The scheduler does not keep a counter of how many event worker threads are - running, at any specific moment, because this will copy functionality - already existing in the server. Namely, all THDs are registered in the - global `threads` array. THD has member variable system_thread which - identifies the type of thread. Connection threads being NON_SYSTEM_THREAD, - all other have their enum value. Important for the scheduler are - SYSTEM_THREAD_EVENT_SCHEDULER and SYSTEM_THREAD_EVENT_WORKER. - - Class THD subclasses class ilink, which is the linked list of all threads. - When a THD instance is destroyed it's being removed from threads, thus - no manual intervention is needed. On the contrary registering is manual - with threads.append() . Traversing the threads array every time a subclass - of THD, for instance if we would have had THD_scheduler_worker to see - how many events we have and whether the scheduler is shutting down will - take much time and lead to a deadlock. stop_all_running_events() is called - under LOCK_scheduler_data. If the THD_scheduler_worker was aware of - the single Event_scheduler instance it will try to check - Event_scheduler::state but for this it would need to acquire - LOCK_scheduler_data => deadlock. Thus stop_all_running_events() uses a - busy wait. - - DROP DATABASE DDL should drop all events defined in a specific schema. - DROP USER also should drop all events who has as definer the user being - dropped (this one is not addressed at the moment but a hook exists). For - this specific needs Event_scheduler::drop_matching_events() is - implemented. Which expects a callback to be applied on every object in - the queue. Thus events that match specific schema or user, will be - removed from the queue. The exposed interface is : - - Event_scheduler::drop_schema_events() - - Event_scheduler::drop_user_events() - - This bulk dropping happens under LOCK_scheduler_data, thus no two or - more threads can execute it in parallel. However, DROP DATABASE is also - synchronized, currently, in the server thus this does not impact the - overall performance. In addition, DROP DATABASE is not that often - executed DDL. - - Though the interface to the scheduler is only through the public methods - of class Event_scheduler, there are currently few functions which are - used during its operations. Namely : - - static evex_print_warnings() - After every event execution all errors/warnings are dumped, so the user - can see in case of a problem what the problem was. - - - static init_event_thread() - This function is both used by event_scheduler_thread() and - event_worker_thread(). It initializes the THD structure. The - initialization looks pretty similar to the one in slave.cc done for the - replication threads. However, though the similarities it cannot be - factored out to have one routine. - - - static event_scheduler_thread() - Because our way to register functions to be used by the threading library - does not allow usage of static methods this function is used to start the - scheduler in it. It does THD initialization and then calls - Event_scheduler::run(). - - - static event_worker_thread() - With already stated the reason for not being able to use methods, this - function executes the worker threads. - - The execution of events is, to some extent, synchronized to inhibit race - conditions when Event_timed::thread_id is being updated with the thread_id of - the THD in which the event is being executed. The thread_id is in the - Event_timed object because we need to be able to kill quickly a specific - event during ALTER/DROP EVENT without traversing the global `threads` array. - However, this makes the scheduler's code more complicated. The event worker - thread is started by Event_timed::spawn_now(), which in turn calls - pthread_create(). The thread_id which will be associated in init_event_thread - is not known in advance thus the registering takes place in - event_worker_thread(). This registering has to be synchronized under - LOCK_scheduler_data, so no kill_event() on a object in - replace_event/drop_event/drop_matching_events() could take place. - - This synchronization is done through class Worker_thread_param that is - local to this file. Event_scheduler::execute_top() is called under - LOCK_scheduler_data. This method : - 1. Creates an instance of Worker_thread_param on the stack - 2. Locks Worker_thread_param::LOCK_started - 3. Calls Event_timed::spawn_now() which in turn creates a new thread. - 4. Locks on Worker_thread_param::COND_started_or_stopped and waits till the - worker thread send signal. The code is spurious wake-up safe because - Worker_thread_param::started is checked. - 5. The worker thread initializes its THD, then sets Event_timed::thread_id, - sets Worker_thread_param::started to TRUE and sends back - Worker_thread_param::COND_started. From this moment on, the event - is being executed and could be killed by using Event_timed::thread_id. - When Event_timed::spawn_thread_finish() is called in the worker thread, - it sets thread_id to 0. From this moment on, the worker thread should not - touch the Event_timed instance. - - - The life-cycle of the server is a FSA. - enum enum_state Event_scheduler::state keeps the state of the scheduler. - - The states are: - - |---UNINITIALIZED - | - | |------------------> IN_SHUTDOWN - --> INITIALIZED -> COMMENCING ---> RUNNING ----------| - ^ ^ | | ^ | - | |- CANTSTART <--| | |- SUSPENDED <-| - |______________________________| - - - UNINITIALIZED :The object is created and only the mutex is initialized - - INITIALIZED :All member variables are initialized - - COMMENCING :The scheduler is starting, no other attempt to start - should succeed before the state is back to INITIALIZED. - - CANTSTART :Set by the ::run() method in case it can't start for some - reason. In this case the connection thread that tries to - start the scheduler sees that some error has occurred and - returns an error to the user. Finally, the connection - thread sets the state to INITIALIZED, so further attempts - to start the scheduler could be made. - - RUNNING :The scheduler is running. New events could be added, - dropped, altered. The scheduler could be stopped. - - SUSPENDED :Like RUNNING but execution of events does not take place. - Operations on the memory queue are possible. - - IN_SHUTDOWN :The scheduler is shutting down, due to request by setting - the global event_scheduler to 0/FALSE, or because of a - KILL command sent by a user to the master thread. - - In every method the macros LOCK_SCHEDULER_DATA() and UNLOCK_SCHEDULER_DATA() - are used for (un)locking purposes. They are used to save the programmer - from typing everytime - lock_data(__FUNCTION__, __LINE__); - All locking goes through Event_scheduler::lock_data() and ::unlock_data(). - These two functions then record in variables where for last time - LOCK_scheduler_data was locked and unlocked (two different variables). In - multithreaded environment, in some cases they make no sense but are useful for - inspecting deadlocks without having the server debug log turned on and the - server is still running. - - The same strategy is used for conditional variables. - Event_scheduler::cond_wait() is invoked from all places with parameter - an enum enum_cond_vars. In this manner, it's possible to inspect the last - on which condition the last call to cond_wait() was waiting. If the server - was started with debug trace switched on, the trace file also holds information - about conditional variables used. -*/ +#include "event_queue.h" #ifdef __GNUC__ #if __GNUC__ >= 2 @@ -246,102 +28,40 @@ #define SCHED_FUNC "<unknown>" #endif -#define LOCK_SCHEDULER_DATA() lock_data(SCHED_FUNC, __LINE__) -#define UNLOCK_SCHEDULER_DATA() unlock_data(SCHED_FUNC, __LINE__) +#define LOCK_DATA() lock_data(SCHED_FUNC, __LINE__) +#define UNLOCK_DATA() unlock_data(SCHED_FUNC, __LINE__) +#define COND_STATE_WAIT(mythd, abstime, msg) \ + cond_wait(mythd, abstime, msg, SCHED_FUNC, __LINE__) +extern pthread_attr_t connection_attrib; -#ifndef DBUG_OFF static -LEX_STRING states_names[] = -{ - {(char*) STRING_WITH_LEN("UNINITIALIZED")}, - {(char*) STRING_WITH_LEN("INITIALIZED")}, - {(char*) STRING_WITH_LEN("COMMENCING")}, - {(char*) STRING_WITH_LEN("CANTSTART")}, - {(char*) STRING_WITH_LEN("RUNNING")}, - {(char*) STRING_WITH_LEN("SUSPENDED")}, - {(char*) STRING_WITH_LEN("IN_SHUTDOWN")} -}; -#endif - - -Event_scheduler -Event_scheduler::singleton; - - -const char * const -Event_scheduler::cond_vars_names[Event_scheduler::COND_LAST] = +LEX_STRING scheduler_states_names[] = { - "new work", - "started or stopped", - "suspend or resume" + { C_STRING_WITH_LEN("INITIALIZED")}, + { C_STRING_WITH_LEN("RUNNING")}, + { C_STRING_WITH_LEN("STOPPING")} }; - -class Worker_thread_param -{ -public: - Event_timed *et; - pthread_mutex_t LOCK_started; - pthread_cond_t COND_started; - bool started; - - Worker_thread_param(Event_timed *etn):et(etn), started(FALSE) - { - pthread_mutex_init(&LOCK_started, MY_MUTEX_INIT_FAST); - pthread_cond_init(&COND_started, NULL); - } - - ~Worker_thread_param() - { - pthread_mutex_destroy(&LOCK_started); - pthread_cond_destroy(&COND_started); - } +struct scheduler_param { + THD *thd; + Event_scheduler *scheduler; }; /* - Compares the execute_at members of 2 Event_timed instances. - Used as callback for the prioritized queue when shifting - elements inside. - - SYNOPSIS - event_timed_compare_q() - - vptr - not used (set it to NULL) - a - first Event_timed object - b - second Event_timed object - - RETURN VALUE - -1 - a->execute_at < b->execute_at - 0 - a->execute_at == b->execute_at - 1 - a->execute_at > b->execute_at - - NOTES - execute_at.second_part is not considered during comparison -*/ - -static int -event_timed_compare_q(void *vptr, byte* a, byte *b) -{ - return my_time_compare(&((Event_timed *)a)->execute_at, - &((Event_timed *)b)->execute_at); -} - - -/* Prints the stack of infos, warnings, errors from thd to the console so it can be fetched by the logs-into-tables and checked later. SYNOPSIS evex_print_warnings - thd - thread used during the execution of the event - et - the event itself + thd Thread used during the execution of the event + et The event itself */ static void -evex_print_warnings(THD *thd, Event_timed *et) +evex_print_warnings(THD *thd, Event_job_data *et) { MYSQL_ERROR *err; DBUG_ENTER("evex_print_warnings"); @@ -354,9 +74,7 @@ evex_print_warnings(THD *thd, Event_timed *et) prefix.length(0); prefix.append("SCHEDULER: ["); - append_identifier(thd, &prefix, et->definer_user.str, et->definer_user.length); - prefix.append('@'); - append_identifier(thd, &prefix, et->definer_host.str, et->definer_host.length); + append_identifier(thd, &prefix, et->definer.str, et->definer.length); prefix.append("][", 2); append_identifier(thd,&prefix, et->dbname.str, et->dbname.length); prefix.append('.'); @@ -381,86 +99,116 @@ evex_print_warnings(THD *thd, Event_timed *et) /* - Inits an scheduler thread handler, both the main and a worker + Performs post initialization of structures in a new thread. + + SYNOPSIS + post_init_event_thread() + thd Thread +*/ + +bool +post_init_event_thread(THD *thd) +{ + my_thread_init(); + pthread_detach_this_thread(); + thd->real_id= pthread_self(); + if (init_thr_lock() || thd->store_globals()) + { + thd->cleanup(); + return TRUE; + } + +#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 + pthread_mutex_lock(&LOCK_thread_count); + threads.append(thd); + thread_count++; + thread_running++; + pthread_mutex_unlock(&LOCK_thread_count); + + return FALSE; +} + + +/* + Cleans up the THD and the threaded environment of the thread. SYNOPSIS - init_event_thread() - thd - the THD of the thread. Has to be allocated by the caller. + deinit_event_thread() + thd Thread +*/ + +void +deinit_event_thread(THD *thd) +{ + thd->proc_info= "Clearing"; + DBUG_ASSERT(thd->net.buff != 0); + net_end(&thd->net); + DBUG_PRINT("exit", ("Event thread finishing")); + pthread_mutex_lock(&LOCK_thread_count); + thread_count--; + thread_running--; + delete thd; + pthread_mutex_unlock(&LOCK_thread_count); + + my_thread_end(); +} + + +/* + Performs pre- pthread_create() initialisation of THD. Do this + in the thread that will pass THD to the child thread. In the + child thread call post_init_event_thread(). + + SYNOPSIS + pre_init_event_thread() + thd The THD of the thread. Has to be allocated by the caller. NOTES 1. The host of the thead is my_localhost 2. thd->net is initted with NULL - no communication. - - RETURN VALUE - 0 OK - -1 Error */ -static int -init_event_thread(THD** t, enum enum_thread_type thread_type) +void +pre_init_event_thread(THD* thd) { - THD *thd= *t; - thd->thread_stack= (char*)t; // remember where our stack is - DBUG_ENTER("init_event_thread"); + DBUG_ENTER("pre_init_event_thread"); thd->client_capabilities= 0; thd->security_ctx->master_access= 0; thd->security_ctx->db_access= 0; thd->security_ctx->host_or_ip= (char*)my_localhost; - my_net_init(&thd->net, 0); + my_net_init(&thd->net, NULL); + thd->security_ctx->set_user((char*)"event_scheduler"); thd->net.read_timeout= slave_net_timeout; thd->slave_thread= 0; thd->options|= OPTION_AUTO_IS_NULL; thd->client_capabilities|= CLIENT_MULTI_RESULTS; - thd->real_id=pthread_self(); - VOID(pthread_mutex_lock(&LOCK_thread_count)); + pthread_mutex_lock(&LOCK_thread_count); thd->thread_id= thread_id++; - threads.append(thd); - thread_count++; - thread_running++; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - - if (init_thr_lock() || thd->store_globals()) - { - thd->cleanup(); - 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 + pthread_mutex_unlock(&LOCK_thread_count); /* Guarantees that we will see the thread in SHOW PROCESSLIST though its vio is NULL. */ - thd->system_thread= thread_type; thd->proc_info= "Initialized"; thd->version= refresh_version; thd->set_time(); - DBUG_RETURN(0); + DBUG_VOID_RETURN; } /* - Inits the main scheduler thread and then calls Event_scheduler::run() - of arg. + Function that executes the scheduler, SYNOPSIS event_scheduler_thread() - arg void* ptr to Event_scheduler - - NOTES - 1. The host of the thead is my_localhost - 2. thd->net is initted with NULL - no communication. - 3. The reason to have a proxy function is that it's not possible to - use a method as function to be executed in a spawned thread: - - our pthread_hander_t macro uses extern "C" - - separating thread setup from the real execution loop is also to be - considered good. + arg Pointer to `struct scheduler_param` RETURN VALUE 0 OK @@ -470,58 +218,21 @@ pthread_handler_t event_scheduler_thread(void *arg) { /* needs to be first for thread_stack */ - THD *thd= NULL; - Event_scheduler *scheduler= (Event_scheduler *) arg; + THD *thd= (THD *)((struct scheduler_param *) arg)->thd; + Event_scheduler *scheduler= ((struct scheduler_param *) arg)->scheduler; - DBUG_ENTER("event_scheduler_thread"); + my_free((char*)arg, MYF(0)); - my_thread_init(); - pthread_detach_this_thread(); + thd->thread_stack= (char *)&thd; // remember where our stack is - /* note that constructor of THD uses DBUG_ ! */ - if (!(thd= new THD) || init_event_thread(&thd, SYSTEM_THREAD_EVENT_SCHEDULER)) - { - sql_print_error("SCHEDULER: Cannot init manager event thread."); - scheduler->report_error_during_start(); - } - else - { - thd->security_ctx->set_user((char*)"event_scheduler"); + DBUG_ENTER("event_scheduler_thread"); - sql_print_information("SCHEDULER: Manager thread booting"); - if (Event_scheduler::check_system_tables(thd)) - scheduler->report_error_during_start(); - else - scheduler->run(thd); + if (!post_init_event_thread(thd)) + scheduler->run(thd); - /* - NOTE: Don't touch `scheduler` after this point because we have notified - the - thread which shuts us down that we have finished cleaning. In this - very moment a new scheduler thread could be started and a crash is - not welcome. - */ - } + deinit_event_thread(thd); - /* - If we cannot create THD then don't decrease because we haven't touched - thread_count and thread_running in init_event_thread() which was never - called. In init_event_thread() thread_count and thread_running are - always increased even in the case the method returns an error. - */ - if (thd) - { - thd->proc_info= "Clearing"; - DBUG_ASSERT(thd->net.buff != 0); - net_end(&thd->net); - pthread_mutex_lock(&LOCK_thread_count); - thread_count--; - thread_running--; - delete thd; - pthread_mutex_unlock(&LOCK_thread_count); - } - my_thread_end(); - DBUG_RETURN(0); // Can't return anything here + DBUG_RETURN(0); // Against gcc warnings } @@ -531,7 +242,7 @@ event_scheduler_thread(void *arg) SYNOPSIS event_worker_thread() - arg The Event_timed object to be processed + arg The Event_job_data object to be processed RETURN VALUE 0 OK @@ -540,1241 +251,374 @@ event_scheduler_thread(void *arg) pthread_handler_t event_worker_thread(void *arg) { - THD *thd; /* needs to be first for thread_stack */ - Worker_thread_param *param= (Worker_thread_param *) arg; - Event_timed *event= param->et; + /* needs to be first for thread_stack */ + THD *thd; + Event_job_data *event= (Event_job_data *)arg; int ret; - bool startup_error= FALSE; - Security_context *save_ctx; - /* this one is local and not needed after exec */ - Security_context security_ctx; - DBUG_ENTER("event_worker_thread"); - DBUG_PRINT("enter", ("event=[%s.%s]", event->dbname.str, event->name.str)); + thd= event->thd; - my_thread_init(); - pthread_detach_this_thread(); + thd->thread_stack= (char *) &thd; // remember where our stack is + DBUG_ENTER("event_worker_thread"); - if (!(thd= new THD) || init_event_thread(&thd, SYSTEM_THREAD_EVENT_WORKER)) + if (!post_init_event_thread(thd)) { - sql_print_error("SCHEDULER: Startup failure."); - startup_error= TRUE; - event->spawn_thread_finish(thd); - } - else - event->set_thread_id(thd->thread_id); + DBUG_PRINT("info", ("Baikonur, time is %d, BURAN reporting and operational." + "THD=0x%lx", time(NULL), thd)); + + sql_print_information("SCHEDULER: [%s.%s of %s] executing in thread %lu. " + "Execution %u", + event->dbname.str, event->name.str, + event->definer.str, thd->thread_id, + event->execution_count); - DBUG_PRINT("info", ("master_access=%d db_access=%d", - thd->security_ctx->master_access, thd->security_ctx->db_access)); - /* - If we don't change it before we send the signal back, then an intermittent - DROP EVENT will take LOCK_scheduler_data and try to kill this thread, because - event->thread_id is already real. However, because thd->security_ctx->user - is not initialized then a crash occurs in kill_one_thread(). Thus, we have - to change the context before sending the signal. We are under - LOCK_scheduler_data being held by Event_scheduler::run() -> ::execute_top(). - */ - change_security_context(thd, event->definer_user, event->definer_host, - event->dbname, &security_ctx, &save_ctx); - DBUG_PRINT("info", ("master_access=%d db_access=%d", - thd->security_ctx->master_access, thd->security_ctx->db_access)); - - /* Signal the scheduler thread that we have started successfully */ - pthread_mutex_lock(¶m->LOCK_started); - param->started= TRUE; - pthread_cond_signal(¶m->COND_started); - pthread_mutex_unlock(¶m->LOCK_started); - - if (!startup_error) - { - thd->init_for_queries(); thd->enable_slow_log= TRUE; - event->set_thread_id(thd->thread_id); - sql_print_information("SCHEDULER: [%s.%s of %s] executing in thread %lu", - event->dbname.str, event->name.str, - event->definer.str, thd->thread_id); + ret= event->execute(thd); - ret= event->execute(thd, thd->mem_root); evex_print_warnings(thd, event); - sql_print_information("SCHEDULER: [%s.%s of %s] executed. RetCode=%d", - event->dbname.str, event->name.str, - event->definer.str, ret); + + sql_print_information("SCHEDULER: [%s.%s of %s] executed in thread %lu. " + "RetCode=%d", event->dbname.str, event->name.str, + event->definer.str, thd->thread_id, ret); if (ret == EVEX_COMPILE_ERROR) sql_print_information("SCHEDULER: COMPILE ERROR for event %s.%s of %s", event->dbname.str, event->name.str, event->definer.str); else if (ret == EVEX_MICROSECOND_UNSUP) sql_print_information("SCHEDULER: MICROSECOND is not supported"); - - DBUG_PRINT("info", ("master_access=%d db_access=%d", - thd->security_ctx->master_access, thd->security_ctx->db_access)); - - /* If true is returned, we are expected to free it */ - if (event->spawn_thread_finish(thd)) - { - DBUG_PRINT("info", ("Freeing object pointer")); - delete event; - } } +end: + DBUG_PRINT("info", ("BURAN %s.%s is landing!", event->dbname.str, + event->name.str)); + delete event; - if (thd) - { - thd->proc_info= "Clearing"; - DBUG_ASSERT(thd->net.buff != 0); - /* - Free it here because net.vio is NULL for us => THD::~THD will check it - and won't call net_end(&net); See also replication code. - */ - net_end(&thd->net); - DBUG_PRINT("info", ("Worker thread %lu exiting", thd->thread_id)); - VOID(pthread_mutex_lock(&LOCK_thread_count)); - thread_count--; - thread_running--; - delete thd; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - } + deinit_event_thread(thd); - my_thread_end(); DBUG_RETURN(0); // Can't return anything here } /* - Constructor of class Event_scheduler. + Performs initialization of the scheduler data, outside of the + threading primitives. SYNOPSIS - Event_scheduler::Event_scheduler() + Event_scheduler::init_scheduler() */ -Event_scheduler::Event_scheduler() - :state(UNINITIALIZED), start_scheduler_suspended(FALSE), - thread_id(0), mutex_last_locked_at_line(0), - mutex_last_unlocked_at_line(0), mutex_last_locked_in_func(""), - mutex_last_unlocked_in_func(""), cond_waiting_on(COND_NONE), - mutex_scheduler_data_locked(FALSE) -{ -} - - -/* - Returns the singleton instance of the class. - - SYNOPSIS - Event_scheduler::get_instance() - - RETURN VALUE - address -*/ - -Event_scheduler* -Event_scheduler::get_instance() -{ - DBUG_ENTER("Event_scheduler::get_instance"); - DBUG_RETURN(&singleton); -} - - -/* - The implementation of full-fledged initialization. - - SYNOPSIS - Event_scheduler::init() - - RETURN VALUE - FALSE OK - TRUE Error -*/ - -bool -Event_scheduler::init() +void +Event_scheduler::init_scheduler(Event_queue *q) { - int i= 0; - bool ret= FALSE; - DBUG_ENTER("Event_scheduler::init"); - DBUG_PRINT("enter", ("this=%p", this)); - - LOCK_SCHEDULER_DATA(); - for (;i < COND_LAST; i++) - if (pthread_cond_init(&cond_vars[i], NULL)) - { - sql_print_error("SCHEDULER: Unable to initalize conditions"); - ret= TRUE; - goto end; - } - - /* init memory root */ - init_alloc_root(&scheduler_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); - - 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("SCHEDULER: Can't initialize the execution queue"); - ret= TRUE; - goto end; - } - - if (sizeof(my_time_t) != sizeof(time_t)) - { - sql_print_error("SCHEDULER: sizeof(my_time_t) != sizeof(time_t) ." - "The scheduler may not work correctly. Stopping."); - DBUG_ASSERT(0); - ret= TRUE; - goto end; - } - + LOCK_DATA(); + queue= q; + started_events= 0; + thread_id= 0; state= INITIALIZED; -end: - UNLOCK_SCHEDULER_DATA(); - DBUG_RETURN(ret); + UNLOCK_DATA(); } -/* - Frees all memory allocated by the scheduler object. - - SYNOPSIS - Event_scheduler::destroy() - - RETURN VALUE - FALSE OK - TRUE Error -*/ - void -Event_scheduler::destroy() -{ - DBUG_ENTER("Event_scheduler"); - - LOCK_SCHEDULER_DATA(); - switch (state) { - case UNINITIALIZED: - break; - case INITIALIZED: - delete_queue(&queue); - free_root(&scheduler_root, MYF(0)); - int i; - for (i= 0; i < COND_LAST; i++) - pthread_cond_destroy(&cond_vars[i]); - state= UNINITIALIZED; - break; - default: - sql_print_error("SCHEDULER: Destroying while state is %d", state); - /* I trust my code but ::safe() > ::sorry() */ - DBUG_ASSERT(0); - break; - } - UNLOCK_SCHEDULER_DATA(); - - DBUG_VOID_RETURN; -} - - -/* - Creates an event in the scheduler queue - - SYNOPSIS - Event_scheduler::create_event() - et The event to add - check_existence Whether to check if already loaded. - - RETURN VALUE - OP_OK OK or scheduler not working - OP_LOAD_ERROR Error during loading from disk -*/ - -enum Event_scheduler::enum_error_code -Event_scheduler::create_event(THD *thd, Event_timed *et, bool check_existence) -{ - enum enum_error_code res; - Event_timed *et_new; - DBUG_ENTER("Event_scheduler::create_event"); - DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et,&LOCK_scheduler_data)); - - LOCK_SCHEDULER_DATA(); - if (!is_running_or_suspended()) - { - DBUG_PRINT("info", ("scheduler not running but %d. doing nothing", state)); - UNLOCK_SCHEDULER_DATA(); - DBUG_RETURN(OP_OK); - } - if (check_existence && find_event(et, FALSE)) - { - res= OP_ALREADY_EXISTS; - goto end; - } - - /* We need to load the event on scheduler_root */ - if (!(res= load_named_event(thd, et, &et_new))) - { - queue_insert_safe(&queue, (byte *) et_new); - DBUG_PRINT("info", ("Sending COND_new_work")); - pthread_cond_signal(&cond_vars[COND_new_work]); - } - else if (res == OP_DISABLED_EVENT) - res= OP_OK; -end: - UNLOCK_SCHEDULER_DATA(); - DBUG_RETURN(res); -} - - -/* - Drops an event from the scheduler queue - - SYNOPSIS - Event_scheduler::drop_event() - etn The event to drop - state Wait the event or kill&drop - - RETURN VALUE - FALSE OK (replaced or scheduler not working) - TRUE Failure -*/ - -bool -Event_scheduler::drop_event(THD *thd, Event_timed *et) -{ - int res; - Event_timed *et_old; - DBUG_ENTER("Event_scheduler::drop_event"); - DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et,&LOCK_scheduler_data)); - - LOCK_SCHEDULER_DATA(); - if (!is_running_or_suspended()) - { - DBUG_PRINT("info", ("scheduler not running but %d. doing nothing", state)); - UNLOCK_SCHEDULER_DATA(); - DBUG_RETURN(OP_OK); - } - - if (!(et_old= find_event(et, TRUE))) - DBUG_PRINT("info", ("No such event found, probably DISABLED")); - - UNLOCK_SCHEDULER_DATA(); - - /* See comments in ::replace_event() why this is split in two parts. */ - if (et_old) - { - switch ((res= et_old->kill_thread(thd))) { - case EVEX_CANT_KILL: - /* Don't delete but continue */ - et_old->flags |= EVENT_FREE_WHEN_FINISHED; - break; - case 0: - /* - kill_thread() waits till the spawned thread finishes after it's - killed. Hence, we delete here memory which is no more referenced from - a running thread. - */ - delete et_old; - /* - We don't signal COND_new_work here because: - 1. Even if the dropped event is on top of the queue this will not - move another one to be executed before the time the one on the - top (but could be at the same second as the dropped one) - 2. If this was the last event on the queue, then pthread_cond_timedwait - in ::run() will finish and then see that the queue is empty and - call cond_wait(). Hence, no need to interrupt the blocked - ::run() thread. - */ - break; - default: - sql_print_error("SCHEDULER: Got unexpected error %d", res); - DBUG_ASSERT(0); - } - } - - DBUG_RETURN(FALSE); -} - - -/* - Updates an event from the scheduler queue - - SYNOPSIS - Event_scheduler::replace_event() - et The event to replace(add) into the queue - state Async or sync stopping - - RETURN VALUE - OP_OK OK or scheduler not working - OP_LOAD_ERROR Error during loading from disk - OP_ALREADY_EXISTS Event already in the queue -*/ - -enum Event_scheduler::enum_error_code -Event_scheduler::update_event(THD *thd, Event_timed *et, - LEX_STRING *new_schema, - LEX_STRING *new_name) -{ - enum enum_error_code res; - Event_timed *et_old, *et_new= NULL; - LEX_STRING old_schema, old_name; - - LINT_INIT(old_schema.str); - LINT_INIT(old_schema.length); - LINT_INIT(old_name.str); - LINT_INIT(old_name.length); - - DBUG_ENTER("Event_scheduler::update_event"); - DBUG_PRINT("enter", ("thd=%p et=%p et=[%s.%s] lock=%p", - thd, et, et->dbname.str, et->name.str, &LOCK_scheduler_data)); - - LOCK_SCHEDULER_DATA(); - if (!is_running_or_suspended()) - { - DBUG_PRINT("info", ("scheduler not running but %d. doing nothing", state)); - UNLOCK_SCHEDULER_DATA(); - DBUG_RETURN(OP_OK); - } - - if (!(et_old= find_event(et, TRUE))) - DBUG_PRINT("info", ("%s.%s not found cached, probably was DISABLED", - et->dbname.str, et->name.str)); - - if (new_schema && new_name) - { - old_schema= et->dbname; - old_name= et->name; - et->dbname= *new_schema; - et->name= *new_name; - } - /* - We need to load the event (it's strings but on the object itself) - on scheduler_root. et_new could be NULL : - 1. Error occured - 2. If the replace is DISABLED, we don't load it into the queue. - */ - if (!(res= load_named_event(thd, et, &et_new))) - { - queue_insert_safe(&queue, (byte *) et_new); - DBUG_PRINT("info", ("Sending COND_new_work")); - pthread_cond_signal(&cond_vars[COND_new_work]); - } - else if (res == OP_DISABLED_EVENT) - res= OP_OK; - - if (new_schema && new_name) - { - et->dbname= old_schema; - et->name= old_name; - } - - UNLOCK_SCHEDULER_DATA(); - /* - Andrey: Is this comment still truthful ??? - - We don't move this code above because a potential kill_thread will call - THD::awake(). Which in turn will try to acqure mysys_var->current_mutex, - which is LOCK_scheduler_data on which the COND_new_work in ::run() locks. - Hence, we try to acquire a lock which we have already acquired and we run - into an assert. Holding LOCK_scheduler_data however is not needed because - we don't touch any invariant of the scheduler anymore. ::drop_event() does - the same. - */ - if (et_old) - { - switch (et_old->kill_thread(thd)) { - case EVEX_CANT_KILL: - /* Don't delete but continue */ - et_old->flags |= EVENT_FREE_WHEN_FINISHED; - break; - case 0: - /* - kill_thread() waits till the spawned thread finishes after it's - killed. Hence, we delete here memory which is no more referenced from - a running thread. - */ - delete et_old; - /* - We don't signal COND_new_work here because: - 1. Even if the dropped event is on top of the queue this will not - move another one to be executed before the time the one on the - top (but could be at the same second as the dropped one) - 2. If this was the last event on the queue, then pthread_cond_timedwait - in ::run() will finish and then see that the queue is empty and - call cond_wait(). Hence, no need to interrupt the blocked - ::run() thread. - */ - break; - default: - DBUG_ASSERT(0); - } - } - - DBUG_RETURN(res); -} - - -/* - Searches for an event in the scheduler queue - - SYNOPSIS - Event_scheduler::find_event() - etn The event to find - comparator The function to use for comparing - remove_from_q If found whether to remove from the Q - - RETURN VALUE - NULL Not found - otherwise Address - - NOTE - The caller should do the locking also the caller is responsible for - actual signalling in case an event is removed from the queue - (signalling COND_new_work for instance). -*/ - -Event_timed * -Event_scheduler::find_event(Event_timed *etn, bool remove_from_q) -{ - uint i; - DBUG_ENTER("Event_scheduler::find_event"); - - for (i= 0; i < queue.elements; ++i) - { - Event_timed *et= (Event_timed *) queue_element(&queue, i); - DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?", etn->dbname.str, etn->name.str, - et->dbname.str, et->name.str)); - if (event_timed_identifier_equal(etn, et)) - { - if (remove_from_q) - queue_remove(&queue, i); - DBUG_RETURN(et); - } - } - - DBUG_RETURN(NULL); -} +Event_scheduler::deinit_scheduler() {} /* - Drops all events from the in-memory queue and disk that match - certain pattern evaluated by a comparator function + Inits scheduler's threading primitives. SYNOPSIS - Event_scheduler::drop_matching_events() - thd THD - pattern A pattern string - comparator The function to use for comparing - - RETURN VALUE - -1 Scheduler not working - >=0 Number of dropped events - - NOTE - Expected is the caller to acquire lock on LOCK_scheduler_data + Event_scheduler::init_mutexes() */ void -Event_scheduler::drop_matching_events(THD *thd, LEX_STRING *pattern, - bool (*comparator)(Event_timed *,LEX_STRING *)) +Event_scheduler::init_mutexes() { - DBUG_ENTER("Event_scheduler::drop_matching_events"); - DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern->length, pattern->str, - state)); - if (is_running_or_suspended()) - { - uint i= 0, dropped= 0; - while (i < queue.elements) - { - Event_timed *et= (Event_timed *) queue_element(&queue, i); - DBUG_PRINT("info", ("[%s.%s]?", et->dbname.str, et->name.str)); - if (comparator(et, pattern)) - { - /* - The queue is ordered. If we remove an element, then all elements after - it will shift one position to the left, if we imagine it as an array - from left to the right. In this case we should not increment the - counter and the (i < queue.elements) condition is ok. - */ - queue_remove(&queue, i); - - /* See replace_event() */ - switch (et->kill_thread(thd)) { - case EVEX_CANT_KILL: - /* Don't delete but continue */ - et->flags |= EVENT_FREE_WHEN_FINISHED; - ++dropped; - break; - case 0: - delete et; - ++dropped; - break; - default: - DBUG_ASSERT(0); - } - } - else - i++; - } - DBUG_PRINT("info", ("Dropped %lu", dropped)); - } - /* - Don't send COND_new_work because no need to wake up the scheduler thread. - When it wakes next time up it will recalculate how much more it should - sleep if the top of the queue has been changed by this method. - */ - - DBUG_VOID_RETURN; + pthread_mutex_init(&LOCK_scheduler_state, MY_MUTEX_INIT_FAST); + pthread_cond_init(&COND_state, NULL); } /* - Drops all events from the in-memory queue and disk that are from - certain schema. + Deinits scheduler's threading primitives. SYNOPSIS - Event_scheduler::drop_schema_events() - thd THD - db The schema name - - RETURN VALUE - -1 Scheduler not working - >=0 Number of dropped events + Event_scheduler::deinit_mutexes() */ -int -Event_scheduler::drop_schema_events(THD *thd, LEX_STRING *schema) +void +Event_scheduler::deinit_mutexes() { - int ret; - DBUG_ENTER("Event_scheduler::drop_schema_events"); - LOCK_SCHEDULER_DATA(); - if (is_running_or_suspended()) - drop_matching_events(thd, schema, event_timed_db_equal); - - ret= db_drop_events_from_table(thd, schema); - UNLOCK_SCHEDULER_DATA(); - - DBUG_RETURN(ret); + pthread_mutex_destroy(&LOCK_scheduler_state); + pthread_cond_destroy(&COND_state); } -extern pthread_attr_t connection_attrib; - - /* - Starts the event scheduler + Starts the scheduler (again). Creates a new THD and passes it to + a forked thread. Does not wait for acknowledgement from the new + thread that it has started. Asynchronous starting. Most of the + needed initializations are done in the current thread to minimize + the chance of failure in the spawned thread. SYNOPSIS Event_scheduler::start() RETURN VALUE FALSE OK - TRUE Error + TRUE Error (not reported) */ bool Event_scheduler::start() { + THD *new_thd= NULL; bool ret= FALSE; pthread_t th; + struct scheduler_param *scheduler_param_value; DBUG_ENTER("Event_scheduler::start"); - LOCK_SCHEDULER_DATA(); - /* If already working or starting don't make another attempt */ - DBUG_ASSERT(state == INITIALIZED); + LOCK_DATA(); + DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state])); if (state > INITIALIZED) + goto end; + + if (!(new_thd= new THD)) { - DBUG_PRINT("info", ("scheduler is already running or starting")); + sql_print_error("SCHEDULER: Cannot init manager event thread."); ret= TRUE; goto end; } + pre_init_event_thread(new_thd); + new_thd->system_thread= SYSTEM_THREAD_EVENT_SCHEDULER; + new_thd->command= COM_DAEMON; - /* - Now if another thread calls start it will bail-out because the branch - above will be executed. Thus no two or more child threads will be forked. - If the child thread cannot start for some reason then `state` is set - to CANTSTART and COND_started is also signaled. In this case we - set `state` back to INITIALIZED so another attempt to start the scheduler - can be made. - */ - state= COMMENCING; - /* Fork */ + scheduler_param_value= + (struct scheduler_param *)my_malloc(sizeof(struct scheduler_param), MYF(0)); + scheduler_param_value->thd= new_thd; + scheduler_param_value->scheduler= this; + + DBUG_PRINT("info", ("Forking new thread for scheduduler. THD=0x%lx", new_thd)); if (pthread_create(&th, &connection_attrib, event_scheduler_thread, - (void*)this)) + (void*)scheduler_param_value)) { DBUG_PRINT("error", ("cannot create a new thread")); state= INITIALIZED; ret= TRUE; - goto end; } + DBUG_PRINT("info", ("Setting state go RUNNING")); + state= RUNNING; +end: + UNLOCK_DATA(); - /* Wait till the child thread has booted (w/ or wo success) */ - while (!is_running_or_suspended() && state != CANTSTART) - cond_wait(COND_started_or_stopped, &LOCK_scheduler_data); - - /* - If we cannot start for some reason then don't prohibit further attempts. - Set back to INITIALIZED. - */ - if (state == CANTSTART) + if (ret && new_thd) { - state= INITIALIZED; - ret= TRUE; - goto end; + DBUG_PRINT("info", ("There was an error during THD creation. Clean up")); + new_thd->proc_info= "Clearing"; + DBUG_ASSERT(new_thd->net.buff != 0); + net_end(&new_thd->net); + pthread_mutex_lock(&LOCK_thread_count); + thread_count--; + thread_running--; + delete new_thd; + pthread_mutex_unlock(&LOCK_thread_count); } - -end: - UNLOCK_SCHEDULER_DATA(); DBUG_RETURN(ret); } /* - Starts the event scheduler in suspended mode. - - SYNOPSIS - Event_scheduler::start_suspended() - - RETURN VALUE - TRUE OK - FALSE Error -*/ - -bool -Event_scheduler::start_suspended() -{ - DBUG_ENTER("Event_scheduler::start_suspended"); - start_scheduler_suspended= TRUE; - DBUG_RETURN(start()); -} - - - -/* - Report back that we cannot start. Used for ocasions where - we can't go into ::run() and have to report externally. - - SYNOPSIS - Event_scheduler::report_error_during_start() -*/ - -inline void -Event_scheduler::report_error_during_start() -{ - DBUG_ENTER("Event_scheduler::report_error_during_start"); - - LOCK_SCHEDULER_DATA(); - state= CANTSTART; - DBUG_PRINT("info", ("Sending back COND_started_or_stopped")); - pthread_cond_signal(&cond_vars[COND_started_or_stopped]); - UNLOCK_SCHEDULER_DATA(); - - DBUG_VOID_RETURN; -} - - -/* - The internal loop of the event scheduler + The main loop of the scheduler. SYNOPSIS Event_scheduler::run() thd Thread RETURN VALUE - FALSE OK - TRUE Failure + FALSE OK + TRUE Error (Serious error) */ bool Event_scheduler::run(THD *thd) { - int ret; + int res; struct timespec abstime; + Event_job_data *job_data; DBUG_ENTER("Event_scheduler::run"); - DBUG_PRINT("enter", ("thd=%p", thd)); - - LOCK_SCHEDULER_DATA(); - ret= load_events_from_db(thd); - if (!ret) - { - thread_id= thd->thread_id; - state= start_scheduler_suspended? SUSPENDED:RUNNING; - start_scheduler_suspended= FALSE; - } - else - state= CANTSTART; - - DBUG_PRINT("info", ("Sending back COND_started_or_stopped")); - pthread_cond_signal(&cond_vars[COND_started_or_stopped]); - if (ret) - { - UNLOCK_SCHEDULER_DATA(); - DBUG_RETURN(TRUE); - } - if (!check_n_suspend_if_needed(thd)) - UNLOCK_SCHEDULER_DATA(); + LOCK_DATA(); + thread_id= thd->thread_id; sql_print_information("SCHEDULER: Manager thread started with id %lu", - thd->thread_id); - abstime.tv_nsec= 0; - while (is_running_or_suspended()) + thread_id); + /* + Recalculate the values in the queue because there could have been stops + in executions of the scheduler and some times could have passed by. + */ + queue->recalculate_activation_times(thd); + while (state == RUNNING) { - Event_timed *et; - - LOCK_SCHEDULER_DATA(); - if (check_n_wait_for_non_empty_queue(thd)) - continue; - - /* On TRUE data is unlocked, go back to the beginning */ - if (check_n_suspend_if_needed(thd)) - continue; - - /* Guaranteed locked here */ - if (state == IN_SHUTDOWN || shutdown_in_progress) + thd->end_time(); + /* Gets a minimized version */ + if (queue->get_top_for_execution_if_time(thd, thd->query_start(), + &job_data, &abstime)) { - UNLOCK_SCHEDULER_DATA(); + sql_print_information("SCHEDULER: Serious error during getting next" + " event to execute. Stopping."); break; } - DBUG_ASSERT(state == RUNNING); - et= (Event_timed *)queue_top(&queue); - - /* Skip disabled events */ - if (et->status != Event_timed::ENABLED) + DBUG_PRINT("info", ("get_top returned job_data=0x%lx now=%d " + "abs_time.tv_sec=%d", + job_data, thd->query_start(), abstime.tv_sec)); + if (!job_data && !abstime.tv_sec) { - /* - It could be a one-timer scheduled for a time, already in the past when the - scheduler was suspended. - */ - sql_print_information("SCHEDULER: Found a disabled event %*s.%*s in the queue", - et->dbname.length, et->dbname.str, et->name.length, - et->name.str); - queue_remove(&queue, 0); - /* ToDo: check this again */ - if (et->dropped) - et->drop(thd); - delete et; - UNLOCK_SCHEDULER_DATA(); - continue; + DBUG_PRINT("info", ("The queue is empty. Going to sleep")); + COND_STATE_WAIT(thd, NULL, "Waiting on empty queue"); + DBUG_PRINT("info", ("Woke up. Got COND_state")); } - thd->proc_info= (char *)"Computing"; - DBUG_PRINT("evex manager",("computing time to sleep till next exec")); - /* Timestamp is in UTC */ - abstime.tv_sec= sec_since_epoch_TIME(&et->execute_at); - - thd->end_time(); - if (abstime.tv_sec > thd->query_start()) + else if (abstime.tv_sec) { - /* Event trigger time is in the future */ - thd->proc_info= (char *)"Sleep"; - DBUG_PRINT("info", ("Going to sleep. Should wakeup after approx %d secs", - abstime.tv_sec - thd->query_start())); - DBUG_PRINT("info", ("Entering condition because waiting for activation")); - /* - Use THD::enter_cond()/exit_cond() or we won't be able to kill a - sleeping thread. Though ::stop() can do it by sending COND_new_work - an user can't by just issuing 'KILL x'; . In the latter case - pthread_cond_timedwait() will wait till `abstime`. - "Sleeping until next time" - */ - thd->enter_cond(&cond_vars[COND_new_work],&LOCK_scheduler_data,"Sleeping"); + DBUG_PRINT("info", ("Have to sleep some time %u s. till %u", + abstime.tv_sec - thd->query_start(), abstime.tv_sec)); - pthread_cond_timedwait(&cond_vars[COND_new_work], &LOCK_scheduler_data, - &abstime); - - DBUG_PRINT("info", ("Manager woke up. state is %d", state)); + COND_STATE_WAIT(thd, &abstime, "Waiting for next activation"); /* If we get signal we should recalculate the whether it's the right time because there could be : 1. Spurious wake-up - 2. The top of the queue was changed (new one becase of add/drop/replace) + 2. The top of the queue was changed (new one becase of create/update) */ - /* This will do implicit UNLOCK_SCHEDULER_DATA() */ - thd->exit_cond(""); + DBUG_PRINT("info", ("Woke up. Got COND_stat or time for execution.")); } else { - thd->proc_info= (char *)"Executing"; - /* - Execute the event. An error may occur if a thread cannot be forked. - In this case stop the manager. - We should enter ::execute_top() with locked LOCK_scheduler_data. - */ - int ret= execute_top(thd); - UNLOCK_SCHEDULER_DATA(); - if (ret) + UNLOCK_DATA(); + res= execute_top(thd, job_data); + LOCK_DATA(); + if (res) break; + ++started_events; } + DBUG_PRINT("info", ("state=%s", scheduler_states_names[state].str)); } - - thd->proc_info= (char *)"Cleaning"; - - LOCK_SCHEDULER_DATA(); - /* - It's possible that a user has used (SQL)COM_KILL. Hence set the appropriate - state because it is only set by ::stop(). - */ - if (state != IN_SHUTDOWN) - { - DBUG_PRINT("info", ("We got KILL but the but not from ::stop()")); - state= IN_SHUTDOWN; - } - UNLOCK_SCHEDULER_DATA(); - - sql_print_information("SCHEDULER: Shutting down"); - - thd->proc_info= (char *)"Cleaning queue"; - clean_queue(thd); - THD_CHECK_SENTRY(thd); - - /* free mamager_root memory but don't destroy the root */ - thd->proc_info= (char *)"Cleaning memory root"; - free_root(&scheduler_root, MYF(0)); - THD_CHECK_SENTRY(thd); - - /* - We notify the waiting thread which shutdowns us that we have cleaned. - There are few more instructions to be executed in this pthread but - they don't affect manager structures thus it's safe to signal already - at this point. - */ - LOCK_SCHEDULER_DATA(); - thd->proc_info= (char *)"Sending shutdown signal"; - DBUG_PRINT("info", ("Sending COND_started_or_stopped")); - if (state == IN_SHUTDOWN) - pthread_cond_signal(&cond_vars[COND_started_or_stopped]); - + DBUG_PRINT("info", ("Signalling back to the stopper COND_state")); + pthread_cond_signal(&COND_state); +error: state= INITIALIZED; - /* - We set it here because ::run() can stop not only because of ::stop() - call but also because of `KILL x` - */ - thread_id= 0; + UNLOCK_DATA(); sql_print_information("SCHEDULER: Stopped"); - UNLOCK_SCHEDULER_DATA(); - /* We have modified, we set back */ - thd->query= NULL; - thd->query_length= 0; - - DBUG_RETURN(FALSE); + DBUG_RETURN(res); } /* - Executes the top element of the queue. Auxiliary method for ::run(). + Creates a new THD instance and then forks a new thread, while passing + the THD pointer and job_data to it. SYNOPSIS Event_scheduler::execute_top() RETURN VALUE - FALSE OK - TRUE Failure - - NOTE - NO locking is done. EXPECTED is that the caller should have locked - the queue (w/ LOCK_scheduler_data). + FALSE OK + TRUE Error (Serious error) */ bool -Event_scheduler::execute_top(THD *thd) +Event_scheduler::execute_top(THD *thd, Event_job_data *job_data) { - int spawn_ret_code; - bool ret= FALSE; + THD *new_thd; + pthread_t th; + int res= 0; DBUG_ENTER("Event_scheduler::execute_top"); - DBUG_PRINT("enter", ("thd=%p", thd)); - - Event_timed *et= (Event_timed *)queue_top(&queue); - - /* Is it good idea to pass a stack address ?*/ - Worker_thread_param param(et); - - pthread_mutex_lock(¶m.LOCK_started); - /* - We don't lock LOCK_scheduler_data fpr workers_increment() because it's a - pre-requisite for calling the current_method. - */ - switch ((spawn_ret_code= et->spawn_now(event_worker_thread, ¶m))) { - case EVENT_EXEC_CANT_FORK: - /* - We don't lock LOCK_scheduler_data here because it's a pre-requisite - for calling the current_method. - */ - sql_print_error("SCHEDULER: Problem while trying to create a thread"); - ret= TRUE; - break; - case EVENT_EXEC_ALREADY_EXEC: - /* - We don't lock LOCK_scheduler_data here because it's a pre-requisite - for calling the current_method. - */ - sql_print_information("SCHEDULER: %s.%s in execution. Skip this time.", - et->dbname.str, et->name.str); - if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == Event_timed::DISABLED) - queue_remove(&queue, 0);// 0 is top, internally 1 - else - queue_replaced(&queue); - break; - default: - DBUG_ASSERT(!spawn_ret_code); - if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == Event_timed::DISABLED) - queue_remove(&queue, 0);// 0 is top, internally 1 - else - queue_replaced(&queue); - /* - We don't lock LOCK_scheduler_data here because it's a pre-requisite - for calling the current_method. - */ - if (likely(!spawn_ret_code)) - { - /* Wait the forked thread to start */ - do { - pthread_cond_wait(¶m.COND_started, ¶m.LOCK_started); - } while (!param.started); - } - /* - param was allocated on the stack so no explicit delete as well as - in this moment it's no more used in the spawned thread so it's safe - to be deleted. - */ - break; - } - pthread_mutex_unlock(¶m.LOCK_started); - /* `param` is on the stack and will be destructed by the compiler */ - - DBUG_RETURN(ret); -} - - -/* - Cleans the scheduler's queue. Auxiliary method for ::run(). + if (!(new_thd= new THD)) + goto error; - SYNOPSIS - Event_scheduler::clean_queue() - thd Thread -*/ - -void -Event_scheduler::clean_queue(THD *thd) -{ - CHARSET_INFO *scs= system_charset_info; - uint i; - DBUG_ENTER("Event_scheduler::clean_queue"); - DBUG_PRINT("enter", ("thd=%p", thd)); - - LOCK_SCHEDULER_DATA(); - stop_all_running_events(thd); - UNLOCK_SCHEDULER_DATA(); - - sql_print_information("SCHEDULER: Emptying the queue"); - - /* empty the queue */ - for (i= 0; i < queue.elements; ++i) - { - Event_timed *et= (Event_timed *) queue_element(&queue, i); - et->free_sp(); - delete et; - } - resize_queue(&queue, 0); - - DBUG_VOID_RETURN; -} + pre_init_event_thread(new_thd); + new_thd->system_thread= SYSTEM_THREAD_EVENT_WORKER; + job_data->thd= new_thd; + DBUG_PRINT("info", ("BURAN %s@%s ready for start t-3..2..1..0..ignition", + job_data->dbname.str, job_data->name.str)); + /* Major failure */ + if ((res= pthread_create(&th, &connection_attrib, event_worker_thread, + job_data))) + goto error; -/* - Stops all running events - - SYNOPSIS - Event_scheduler::stop_all_running_events() - thd Thread - - NOTE - LOCK_scheduler data must be acquired prior to call to this method -*/ - -void -Event_scheduler::stop_all_running_events(THD *thd) -{ - CHARSET_INFO *scs= system_charset_info; - uint i; - DYNAMIC_ARRAY running_threads; - THD *tmp; - DBUG_ENTER("Event_scheduler::stop_all_running_events"); - DBUG_PRINT("enter", ("workers_count=%d", workers_count())); - - my_init_dynamic_array(&running_threads, sizeof(ulong), 10, 10); - - bool had_super= FALSE; - VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list - I_List_iterator<THD> it(threads); - while ((tmp=it++)) - { - if (tmp->command == COM_DAEMON) - continue; - if (tmp->system_thread == SYSTEM_THREAD_EVENT_WORKER) - push_dynamic(&running_threads, (gptr) &tmp->thread_id); - } - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - - /* We need temporarily SUPER_ACL to be able to kill our offsprings */ - if (!(thd->security_ctx->master_access & SUPER_ACL)) - thd->security_ctx->master_access|= SUPER_ACL; - else - had_super= TRUE; - - char tmp_buff[10*STRING_BUFFER_USUAL_SIZE]; - char int_buff[STRING_BUFFER_USUAL_SIZE]; - String tmp_string(tmp_buff, sizeof(tmp_buff), scs); - String int_string(int_buff, sizeof(int_buff), scs); - tmp_string.length(0); + DBUG_PRINT("info", ("Launch succeeded. BURAN is in THD=0x%lx", new_thd)); + DBUG_RETURN(FALSE); - for (i= 0; i < running_threads.elements; ++i) +error: + DBUG_PRINT("error", ("Baikonur, we have a problem! res=%d", res)); + if (new_thd) { - int ret; - ulong thd_id= *dynamic_element(&running_threads, i, ulong*); - - int_string.set((longlong) thd_id,scs); - tmp_string.append(int_string); - if (i < running_threads.elements - 1) - tmp_string.append(' '); - - if ((ret= kill_one_thread(thd, thd_id, FALSE))) - { - sql_print_error("SCHEDULER: Error killing %lu code=%d", thd_id, ret); - break; - } + new_thd->proc_info= "Clearing"; + DBUG_ASSERT(new_thd->net.buff != 0); + net_end(&new_thd->net); + pthread_mutex_lock(&LOCK_thread_count); + thread_count--; + thread_running--; + delete new_thd; + pthread_mutex_unlock(&LOCK_thread_count); } - if (running_threads.elements) - sql_print_information("SCHEDULER: Killing workers :%s", tmp_string.c_ptr()); - - if (!had_super) - thd->security_ctx->master_access &= ~SUPER_ACL; - - delete_dynamic(&running_threads); - - sql_print_information("SCHEDULER: Waiting for worker threads to finish"); - - while (workers_count()) - my_sleep(100000); - - DBUG_VOID_RETURN; + delete job_data; + DBUG_RETURN(TRUE); } /* - Stops the event scheduler + Stops the scheduler (again). Waits for acknowledgement from the + scheduler that it has stopped - synchronous stopping. SYNOPSIS Event_scheduler::stop() RETURN VALUE - OP_OK OK - OP_CANT_KILL Error during stopping of manager thread - OP_NOT_RUNNING Manager not working - - NOTE - The caller must have acquited LOCK_scheduler_data. + FALSE OK + TRUE Error (not reported) */ -enum Event_scheduler::enum_error_code +bool Event_scheduler::stop() { THD *thd= current_thd; DBUG_ENTER("Event_scheduler::stop"); - DBUG_PRINT("enter", ("thd=%p", current_thd)); + DBUG_PRINT("enter", ("thd=0x%lx", current_thd)); - LOCK_SCHEDULER_DATA(); - if (!is_running_or_suspended()) - { - /* - One situation to be here is if there was a start that forked a new - thread but the new thread did not acquire yet LOCK_scheduler_data. - Hence, in this case return an error. - */ - DBUG_PRINT("info", ("manager not running but %d. doing nothing", state)); - UNLOCK_SCHEDULER_DATA(); - DBUG_RETURN(OP_NOT_RUNNING); - } - state= IN_SHUTDOWN; + LOCK_DATA(); + DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state])); + if (state != RUNNING) + goto end; + + state= STOPPING; DBUG_PRINT("info", ("Manager thread has id %d", thread_id)); sql_print_information("SCHEDULER: Killing manager thread %lu", thread_id); - /* - Sending the COND_new_work to ::run() is a way to get this working without - race conditions. If we use kill_one_thread() it will call THD::awake() and - because in ::run() both THD::enter_cond()/::exit_cond() are used, - THD::awake() will try to lock LOCK_scheduler_data. If we UNLOCK it before, - then the pthread_cond_signal(COND_started_or_stopped) could be signaled in - ::run() and we can miss the signal before we relock. A way is to use - another mutex for this shutdown procedure but better not. - */ - pthread_cond_signal(&cond_vars[COND_new_work]); - /* Or we are suspended - then we should wake up */ - pthread_cond_signal(&cond_vars[COND_suspend_or_resume]); + pthread_cond_signal(&COND_state); /* Guarantee we don't catch spurious signals */ sql_print_information("SCHEDULER: Waiting the manager thread to reply"); - while (state != INITIALIZED) - { + do { DBUG_PRINT("info", ("Waiting for COND_started_or_stopped from the manager " - "thread. Current value of state is %d . " - "workers count=%d", state, workers_count())); - cond_wait(COND_started_or_stopped, &LOCK_scheduler_data); - } + "thread. Current value of state is %s . " + "workers count=%d", scheduler_states_names[state].str, + workers_count())); + /* thd could be 0x0, when shutting down */ + COND_STATE_WAIT(thd, NULL, "Waiting scheduler to stop"); + } while (state == STOPPING); DBUG_PRINT("info", ("Manager thread has cleaned up. Set state to INIT")); - UNLOCK_SCHEDULER_DATA(); - - DBUG_RETURN(OP_OK); -} - - -/* - Suspends or resumes the scheduler. - SUSPEND - it won't execute any event till resumed. - RESUME - it will resume if suspended. - - SYNOPSIS - Event_scheduler::suspend_or_resume() - RETURN VALUE - OP_OK OK -*/ - -enum Event_scheduler::enum_error_code -Event_scheduler::suspend_or_resume( - enum Event_scheduler::enum_suspend_or_resume action) -{ - DBUG_ENTER("Event_scheduler::suspend_or_resume"); - DBUG_PRINT("enter", ("action=%d", action)); - - LOCK_SCHEDULER_DATA(); - - if ((action == SUSPEND && state == SUSPENDED) || - (action == RESUME && state == RUNNING)) - { - DBUG_PRINT("info", ("Either trying to suspend suspended or resume " - "running scheduler. Doing nothing.")); - } - else - { - /* Wake the main thread up if he is asleep */ - DBUG_PRINT("info", ("Sending signal")); - if (action==SUSPEND) - { - state= SUSPENDED; - pthread_cond_signal(&cond_vars[COND_new_work]); - } - else - { - state= RUNNING; - pthread_cond_signal(&cond_vars[COND_suspend_or_resume]); - } - DBUG_PRINT("info", ("Waiting on COND_suspend_or_resume")); - cond_wait(COND_suspend_or_resume, &LOCK_scheduler_data); - DBUG_PRINT("info", ("Got response")); - } - UNLOCK_SCHEDULER_DATA(); - DBUG_RETURN(OP_OK); + thread_id= 0; +end: + UNLOCK_DATA(); + DBUG_RETURN(FALSE); } /* - Returns the number of executing events. + Returns the number of living event worker threads. SYNOPSIS Event_scheduler::workers_count() @@ -1787,7 +631,7 @@ Event_scheduler::workers_count() uint count= 0; DBUG_ENTER("Event_scheduler::workers_count"); - VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list + pthread_mutex_lock(&LOCK_thread_count); // For unlink from list I_List_iterator<THD> it(threads); while ((tmp=it++)) { @@ -1796,148 +640,47 @@ Event_scheduler::workers_count() if (tmp->system_thread == SYSTEM_THREAD_EVENT_WORKER) ++count; } - VOID(pthread_mutex_unlock(&LOCK_thread_count)); + pthread_mutex_unlock(&LOCK_thread_count); DBUG_PRINT("exit", ("%d", count)); - DBUG_RETURN(count); + DBUG_RETURN(count); } /* - Checks and suspends if needed + Signals the main scheduler thread that the queue has changed + its state. SYNOPSIS - Event_scheduler::check_n_suspend_if_needed() - thd Thread - - RETURN VALUE - FALSE Not suspended, we haven't slept - TRUE We were suspended. LOCK_scheduler_data is unlocked. - - NOTE - The caller should have locked LOCK_scheduler_data! - The mutex will be unlocked in case this function returns TRUE + Event_scheduler::queue_changed() */ -bool -Event_scheduler::check_n_suspend_if_needed(THD *thd) -{ - bool was_suspended= FALSE; - DBUG_ENTER("Event_scheduler::check_n_suspend_if_needed"); - if (thd->killed && !shutdown_in_progress) - { - state= SUSPENDED; - thd->killed= THD::NOT_KILLED; - } - if (state == SUSPENDED) - { - thd->enter_cond(&cond_vars[COND_suspend_or_resume], &LOCK_scheduler_data, - "Suspended"); - /* Send back signal to the thread that asked us to suspend operations */ - pthread_cond_signal(&cond_vars[COND_suspend_or_resume]); - sql_print_information("SCHEDULER: Suspending operations"); - was_suspended= TRUE; - } - while (state == SUSPENDED) - { - cond_wait(COND_suspend_or_resume, &LOCK_scheduler_data); - DBUG_PRINT("info", ("Woke up after waiting on COND_suspend_or_resume")); - if (state != SUSPENDED) - { - pthread_cond_signal(&cond_vars[COND_suspend_or_resume]); - sql_print_information("SCHEDULER: Resuming operations"); - } - } - if (was_suspended) - { - if (queue.elements) - { - uint i; - DBUG_PRINT("info", ("We have to recompute the execution times")); - - for (i= 0; i < queue.elements; i++) - { - ((Event_timed*)queue_element(&queue, i))->compute_next_execution_time(); - ((Event_timed*)queue_element(&queue, i))->update_fields(thd); - } - queue_fix(&queue); - } - /* This will implicitly unlock LOCK_scheduler_data */ - thd->exit_cond(""); - } - DBUG_RETURN(was_suspended); -} - - -/* - Checks for empty queue and waits till new element gets in - - SYNOPSIS - Event_scheduler::check_n_wait_for_non_empty_queue() - thd Thread - - RETURN VALUE - FALSE Did not wait - LOCK_scheduler_data still locked. - TRUE Waited - LOCK_scheduler_data unlocked. - - NOTE - The caller should have locked LOCK_scheduler_data! -*/ - -bool -Event_scheduler::check_n_wait_for_non_empty_queue(THD *thd) +void +Event_scheduler::queue_changed() { - bool slept= FALSE; - DBUG_ENTER("Event_scheduler::check_n_wait_for_non_empty_queue"); - DBUG_PRINT("enter", ("q.elements=%lu state=%s", - queue.elements, states_names[state])); - - if (!queue.elements) - thd->enter_cond(&cond_vars[COND_new_work], &LOCK_scheduler_data, - "Empty queue, sleeping"); - - /* Wait in a loop protecting against catching spurious signals */ - while (!queue.elements && state == RUNNING) - { - slept= TRUE; - DBUG_PRINT("info", ("Entering condition because of empty queue")); - cond_wait(COND_new_work, &LOCK_scheduler_data); - DBUG_PRINT("info", ("Manager woke up. Hope we have events now. state=%d", - state)); - /* - exit_cond does implicit mutex_UNLOCK, we needed it locked if - 1. we loop again - 2. end the current loop and start doing calculations - */ - } - if (slept) - thd->exit_cond(""); - - DBUG_PRINT("exit", ("q.elements=%lu state=%s thd->killed=%d", - queue.elements, states_names[state], thd->killed)); - - DBUG_RETURN(slept); + DBUG_ENTER("Event_scheduler::queue_changed"); + DBUG_PRINT("info", ("Sending COND_state. state (read wo lock)=%s ", + scheduler_states_names[state].str)); + pthread_cond_signal(&COND_state); + DBUG_VOID_RETURN; } /* - Wrapper for pthread_mutex_lock + Auxiliary function for locking LOCK_scheduler_state. Used + by the LOCK_DATA macro. SYNOPSIS Event_scheduler::lock_data() - mutex Mutex to lock - line The line number on which the lock is done - - RETURN VALUE - Error code of pthread_mutex_lock() + func Which function is requesting mutex lock + line On which line mutex lock is requested */ -inline void +void Event_scheduler::lock_data(const char *func, uint line) { - DBUG_ENTER("Event_scheduler::lock_mutex"); - DBUG_PRINT("enter", ("mutex_lock=%p func=%s line=%u", - &LOCK_scheduler_data, func, line)); - pthread_mutex_lock(&LOCK_scheduler_data); + DBUG_ENTER("Event_scheduler::lock_data"); + DBUG_PRINT("enter", ("func=%s line=%u", func, line)); + pthread_mutex_lock(&LOCK_scheduler_state); mutex_last_locked_in_func= func; mutex_last_locked_at_line= line; mutex_scheduler_data_locked= TRUE; @@ -1946,68 +689,70 @@ Event_scheduler::lock_data(const char *func, uint line) /* - Wrapper for pthread_mutex_unlock + Auxiliary function for unlocking LOCK_scheduler_state. Used + by the UNLOCK_DATA macro. SYNOPSIS Event_scheduler::unlock_data() - mutex Mutex to unlock - line The line number on which the unlock is done + func Which function is requesting mutex unlock + line On which line mutex unlock is requested */ -inline void +void Event_scheduler::unlock_data(const char *func, uint line) { - DBUG_ENTER("Event_scheduler::UNLOCK_mutex"); - DBUG_PRINT("enter", ("mutex_unlock=%p func=%s line=%u", - &LOCK_scheduler_data, func, line)); + DBUG_ENTER("Event_scheduler::unlock_data"); + DBUG_PRINT("enter", ("func=%s line=%u", func, line)); mutex_last_unlocked_at_line= line; mutex_scheduler_data_locked= FALSE; mutex_last_unlocked_in_func= func; - pthread_mutex_unlock(&LOCK_scheduler_data); + pthread_mutex_unlock(&LOCK_scheduler_state); DBUG_VOID_RETURN; } /* - Wrapper for pthread_cond_wait + Wrapper for pthread_cond_wait/timedwait SYNOPSIS Event_scheduler::cond_wait() - cond Conditional to wait for - mutex Mutex of the conditional - - RETURN VALUE - Error code of pthread_cond_wait() + thd Thread (Could be NULL during shutdown procedure) + abstime If not null then call pthread_cond_timedwait() + func Which function is requesting cond_wait + line On which line cond_wait is requested */ -inline int -Event_scheduler::cond_wait(enum Event_scheduler::enum_cond_vars cond, - pthread_mutex_t *mutex) +void +Event_scheduler::cond_wait(THD *thd, struct timespec *abstime, const char* msg, + const char *func, uint line) { - int ret; DBUG_ENTER("Event_scheduler::cond_wait"); - DBUG_PRINT("enter", ("cond=%s mutex=%p", cond_vars_names[cond], mutex)); - ret= pthread_cond_wait(&cond_vars[cond_waiting_on=cond], mutex); - cond_waiting_on= COND_NONE; - DBUG_RETURN(ret); -} - - -/* - Checks whether the scheduler is in a running or suspended state. - - SYNOPSIS - Event_scheduler::is_running_or_suspended() - - RETURN VALUE - TRUE Either running or suspended - FALSE IN_SHUTDOWN, not started, etc. -*/ + waiting_on_cond= TRUE; + mutex_last_unlocked_at_line= line; + mutex_scheduler_data_locked= FALSE; + mutex_last_unlocked_in_func= func; + if (thd) + thd->enter_cond(&COND_state, &LOCK_scheduler_state, msg); -inline bool -Event_scheduler::is_running_or_suspended() -{ - return (state == SUSPENDED || state == RUNNING); + DBUG_PRINT("info", ("pthread_cond_%swait", abstime? "timed":"")); + if (!abstime) + pthread_cond_wait(&COND_state, &LOCK_scheduler_state); + else + pthread_cond_timedwait(&COND_state, &LOCK_scheduler_state, abstime); + if (thd) + { + /* + This will free the lock so we need to relock. Not the best thing to + do but we need to obey cond_wait() + */ + thd->exit_cond(""); + LOCK_DATA(); + } + mutex_last_locked_in_func= func; + mutex_last_locked_at_line= line; + mutex_scheduler_data_locked= TRUE; + waiting_on_cond= FALSE; + DBUG_VOID_RETURN; } @@ -2016,6 +761,9 @@ Event_scheduler::is_running_or_suspended() SYNOPSIS Event_scheduler::get_state() + + RETURN VALUE + The state of the scheduler (INITIALIZED | RUNNING | STOPPING) */ enum Event_scheduler::enum_state @@ -2023,355 +771,39 @@ Event_scheduler::get_state() { enum Event_scheduler::enum_state ret; DBUG_ENTER("Event_scheduler::get_state"); - /* lock_data & unlock_data are not static */ - pthread_mutex_lock(&singleton.LOCK_scheduler_data); - ret= singleton.state; - pthread_mutex_unlock(&singleton.LOCK_scheduler_data); + LOCK_DATA(); + ret= state; + UNLOCK_DATA(); DBUG_RETURN(ret); } /* + REMOVE THIS COMMENT AFTER PATCH REVIEW. USED TO HELP DIFF Returns whether the scheduler was initialized. - - SYNOPSIS - Event_scheduler::initialized() - - RETURN VALUE - FALSE Was not initialized so far - TRUE Was initialized -*/ - -bool -Event_scheduler::initialized() -{ - DBUG_ENTER("Event_scheduler::initialized"); - DBUG_RETURN(Event_scheduler::get_state() != UNINITIALIZED); -} - - -/* - Returns the number of elements in the queue - - SYNOPSIS - Event_scheduler::events_count() - - RETURN VALUE - 0 Number of Event_timed objects in the queue -*/ - -uint -Event_scheduler::events_count() -{ - uint n; - DBUG_ENTER("Event_scheduler::events_count"); - LOCK_SCHEDULER_DATA(); - n= queue.elements; - UNLOCK_SCHEDULER_DATA(); - - DBUG_RETURN(n); -} - - -/* - Looks for a named event in mysql.event and then loads it from - the table, compiles and inserts it into the cache. - - SYNOPSIS - Event_scheduler::load_named_event() - thd THD - etn The name of the event to load and compile on scheduler's root - etn_new The loaded event - - RETURN VALUE - NULL Error during compile or the event is non-enabled. - otherwise Address -*/ - -enum Event_scheduler::enum_error_code -Event_scheduler::load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new) -{ - int ret= 0; - MEM_ROOT *tmp_mem_root; - Event_timed *et_loaded= NULL; - Open_tables_state backup; - - DBUG_ENTER("Event_scheduler::load_and_compile_event"); - DBUG_PRINT("enter",("thd=%p name:%*s",thd, etn->name.length, etn->name.str)); - - thd->reset_n_backup_open_tables_state(&backup); - /* No need to use my_error() here because db_find_event() has done it */ - { - sp_name spn(etn->dbname, etn->name); - ret= db_find_event(thd, &spn, &et_loaded, NULL, &scheduler_root); - } - thd->restore_backup_open_tables_state(&backup); - /* In this case no memory was allocated so we don't need to clean */ - if (ret) - DBUG_RETURN(OP_LOAD_ERROR); - - if (et_loaded->status != Event_timed::ENABLED) - { - /* - We don't load non-enabled events. - In db_find_event() `et_new` was allocated on the heap and not on - scheduler_root therefore we delete it here. - */ - delete et_loaded; - DBUG_RETURN(OP_DISABLED_EVENT); - } - - et_loaded->compute_next_execution_time(); - *etn_new= et_loaded; - - DBUG_RETURN(OP_OK); -} - - -/* - Loads all ENABLED events from mysql.event into the prioritized - queue. Called during scheduler main thread initialization. Compiles - the events. Creates Event_timed instances for every ENABLED event - from mysql.event. - - SYNOPSIS - Event_scheduler::load_events_from_db() - thd - Thread context. Used for memory allocation in some cases. - - RETURN VALUE - 0 OK - !0 Error (EVEX_OPEN_TABLE_FAILED, EVEX_MICROSECOND_UNSUP, - EVEX_COMPILE_ERROR) - in all these cases mysql.event was - tampered. - - NOTES - Reports the error to the console -*/ - -int -Event_scheduler::load_events_from_db(THD *thd) -{ - TABLE *table; - READ_RECORD read_record_info; - int ret= -1; - uint count= 0; - bool clean_the_queue= FALSE; - /* Compile the events on this root but only for syntax check, then discard */ - MEM_ROOT boot_root; - - DBUG_ENTER("Event_scheduler::load_events_from_db"); - DBUG_PRINT("enter", ("thd=%p", thd)); - - if (state > COMMENCING) - { - DBUG_ASSERT(0); - sql_print_error("SCHEDULER: Trying to load events while already running."); - DBUG_RETURN(EVEX_GENERAL_ERROR); - } - - if ((ret= Events::open_event_table(thd, TL_READ, &table))) - { - sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open."); - DBUG_RETURN(EVEX_OPEN_TABLE_FAILED); - } - - init_alloc_root(&boot_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); - 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("info", ("Out of memory")); - clean_the_queue= TRUE; - break; - } - DBUG_PRINT("info", ("Loading event from row.")); - - if ((ret= et->load_from_row(&scheduler_root, table))) - { - clean_the_queue= TRUE; - sql_print_error("SCHEDULER: Error while loading from mysql.event. " - "Table probably corrupted"); - break; - } - if (et->status != Event_timed::ENABLED) - { - DBUG_PRINT("info",("%s is disabled",et->name.str)); - delete et; - continue; - } - - DBUG_PRINT("info", ("Event %s loaded from row. ", et->name.str)); - - /* We load only on scheduler root just to check whether the body compiles */ - switch (ret= et->compile(thd, &boot_root)) { - case EVEX_MICROSECOND_UNSUP: - et->free_sp(); - sql_print_error("SCHEDULER: mysql.event is tampered. MICROSECOND is not " - "supported but found in mysql.event"); - goto end; - case EVEX_COMPILE_ERROR: - sql_print_error("SCHEDULER: Error while compiling %s.%s. Aborting load.", - et->dbname.str, et->name.str); - goto end; - default: - /* Free it, it will be compiled again on the worker thread */ - et->free_sp(); - break; - } - - /* let's find when to be executed */ - if (et->compute_next_execution_time()) - { - sql_print_error("SCHEDULER: Error while computing execution time of %s.%s." - " Skipping", et->dbname.str, et->name.str); - continue; - } - - DBUG_PRINT("load_events_from_db", ("Adding %p to the exec list.")); - queue_insert_safe(&queue, (byte *) et); - count++; - } -end: - end_read_record(&read_record_info); - free_root(&boot_root, MYF(0)); - - if (clean_the_queue) - { - for (count= 0; count < queue.elements; ++count) - queue_remove(&queue, 0); - ret= -1; - } - else - { - ret= 0; - sql_print_information("SCHEDULER: Loaded %d event%s", count, (count == 1)?"":"s"); - } - - /* Force close to free memory */ - thd->version--; - - close_thread_tables(thd); - - DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count)); - DBUG_RETURN(ret); -} - - -/* - Opens mysql.db and mysql.user and checks whether: - 1. mysql.db has column Event_priv at column 20 (0 based); - 2. mysql.user has column Event_priv at column 29 (0 based); - - SYNOPSIS - Event_scheduler::check_system_tables() */ -bool -Event_scheduler::check_system_tables(THD *thd) -{ - TABLE_LIST tables; - bool not_used; - Open_tables_state backup; - bool ret; - - DBUG_ENTER("Event_scheduler::check_system_tables"); - DBUG_PRINT("enter", ("thd=%p", thd)); - - thd->reset_n_backup_open_tables_state(&backup); - - bzero((char*) &tables, sizeof(tables)); - tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*) "db"; - tables.lock_type= TL_READ; - - if ((ret= simple_open_n_lock_tables(thd, &tables))) - sql_print_error("Cannot open mysql.db"); - else - { - ret= table_check_intact(tables.table, MYSQL_DB_FIELD_COUNT, - mysql_db_table_fields, &mysql_db_table_last_check, - ER_CANNOT_LOAD_FROM_TABLE); - close_thread_tables(thd); - } - if (ret) - DBUG_RETURN(TRUE); - - bzero((char*) &tables, sizeof(tables)); - tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*) "user"; - tables.lock_type= TL_READ; - - if ((ret= simple_open_n_lock_tables(thd, &tables))) - sql_print_error("Cannot open mysql.db"); - else - { - if (tables.table->s->fields < 29 || - strncmp(tables.table->field[29]->field_name, - STRING_WITH_LEN("Event_priv"))) - { - sql_print_error("mysql.user has no `Event_priv` column at position 29"); - ret= TRUE; - } - close_thread_tables(thd); - } - - thd->restore_backup_open_tables_state(&backup); - - DBUG_RETURN(ret); -} - - /* - Inits mutexes. - - SYNOPSIS - Event_scheduler::init_mutexes() -*/ - -void -Event_scheduler::init_mutexes() -{ - pthread_mutex_init(&singleton.LOCK_scheduler_data, MY_MUTEX_INIT_FAST); -} - - -/* - Destroys mutexes. - - SYNOPSIS - Event_scheduler::destroy_mutexes() -*/ - -void -Event_scheduler::destroy_mutexes() -{ - pthread_mutex_destroy(&singleton.LOCK_scheduler_data); -} - - -/* - Dumps some data about the internal status of the scheduler. + Dumps the internal status of the scheduler SYNOPSIS Event_scheduler::dump_internal_status() - thd THD + thd Thread RETURN VALUE - 0 OK - 1 Error + FALSE OK + TRUE Error */ -int +bool Event_scheduler::dump_internal_status(THD *thd) { - DBUG_ENTER("dump_internal_status"); + int ret= 0; + DBUG_ENTER("Event_scheduler::dump_internal_status"); + #ifndef DBUG_OFF CHARSET_INFO *scs= system_charset_info; Protocol *protocol= thd->protocol; - List<Item> field_list; - int ret; char tmp_buff[5*STRING_BUFFER_USUAL_SIZE]; char int_buff[STRING_BUFFER_USUAL_SIZE]; String tmp_string(tmp_buff, sizeof(tmp_buff), scs); @@ -2379,85 +811,83 @@ Event_scheduler::dump_internal_status(THD *thd) tmp_string.length(0); int_string.length(0); - field_list.push_back(new Item_empty_string("Name", 20)); - field_list.push_back(new Item_empty_string("Value",20)); - if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | - Protocol::SEND_EOF)) - DBUG_RETURN(1); + do + { + protocol->prepare_for_resend(); + protocol->store(STRING_WITH_LEN("scheduler state"), scs); + protocol->store(scheduler_states_names[state].str, + scheduler_states_names[state].length, scs); - protocol->prepare_for_resend(); - protocol->store(STRING_WITH_LEN("state"), scs); - protocol->store(states_names[singleton.state].str, - states_names[singleton.state].length, - scs); + if ((ret= protocol->write())) + break; + + /* thread_id */ + protocol->prepare_for_resend(); + protocol->store(STRING_WITH_LEN("thread_id"), scs); + if (thread_id) + { + int_string.set((longlong) thread_id, scs); + protocol->store(&int_string); + } + else + protocol->store_null(); + if ((ret= protocol->write())) + break; - ret= protocol->write(); - /* - If not initialized - don't show anything else. get_instance() - will otherwise implicitly initialize it. We don't want that. - */ - if (singleton.state >= INITIALIZED) - { /* last locked at*/ - /* - The first thing to do, or get_instance() will overwrite the values. - mutex_last_locked_at_line / mutex_last_unlocked_at_line - */ protocol->prepare_for_resend(); - protocol->store(STRING_WITH_LEN("last locked at"), scs); + protocol->store(STRING_WITH_LEN("scheduler last locked at"), scs); tmp_string.length(scs->cset->snprintf(scs, (char*) tmp_string.ptr(), - tmp_string.alloced_length(), "%s::%d", - singleton.mutex_last_locked_in_func, - singleton.mutex_last_locked_at_line)); + tmp_string.alloced_length(), "%s::%d", + mutex_last_locked_in_func, + mutex_last_locked_at_line)); protocol->store(&tmp_string); - ret= protocol->write(); + if ((ret= protocol->write())) + break; /* last unlocked at*/ protocol->prepare_for_resend(); - protocol->store(STRING_WITH_LEN("last unlocked at"), scs); + protocol->store(STRING_WITH_LEN("scheduler last unlocked at"), scs); tmp_string.length(scs->cset->snprintf(scs, (char*) tmp_string.ptr(), - tmp_string.alloced_length(), "%s::%d", - singleton.mutex_last_unlocked_in_func, - singleton.mutex_last_unlocked_at_line)); + tmp_string.alloced_length(), "%s::%d", + mutex_last_unlocked_in_func, + mutex_last_unlocked_at_line)); protocol->store(&tmp_string); - ret= protocol->write(); + if ((ret= protocol->write())) + break; /* waiting on */ protocol->prepare_for_resend(); - protocol->store(STRING_WITH_LEN("waiting on condition"), scs); - tmp_string.length(scs->cset-> - snprintf(scs, (char*) tmp_string.ptr(), - tmp_string.alloced_length(), "%s", - (singleton.cond_waiting_on != COND_NONE) ? - cond_vars_names[singleton.cond_waiting_on]: - "NONE")); - protocol->store(&tmp_string); - ret= protocol->write(); - - Event_scheduler *scheduler= get_instance(); + protocol->store(STRING_WITH_LEN("scheduler waiting on condition"), scs); + int_string.set((longlong) waiting_on_cond, scs); + protocol->store(&int_string); + if ((ret= protocol->write())) + break; /* workers_count */ protocol->prepare_for_resend(); - protocol->store(STRING_WITH_LEN("workers_count"), scs); - int_string.set((longlong) scheduler->workers_count(), scs); + protocol->store(STRING_WITH_LEN("scheduler workers count"), scs); + int_string.set((longlong) workers_count(), scs); protocol->store(&int_string); - ret= protocol->write(); + if ((ret= protocol->write())) + break; - /* queue.elements */ + /* workers_count */ protocol->prepare_for_resend(); - protocol->store(STRING_WITH_LEN("queue.elements"), scs); - int_string.set((longlong) scheduler->queue.elements, scs); + protocol->store(STRING_WITH_LEN("scheduler executed events"), scs); + int_string.set((longlong) started_events, scs); protocol->store(&int_string); - ret= protocol->write(); + if ((ret= protocol->write())) + break; /* scheduler_data_locked */ protocol->prepare_for_resend(); protocol->store(STRING_WITH_LEN("scheduler data locked"), scs); - int_string.set((longlong) scheduler->mutex_scheduler_data_locked, scs); + int_string.set((longlong) mutex_scheduler_data_locked, scs); protocol->store(&int_string); ret= protocol->write(); - } - send_eof(thd); + } while (0); #endif - DBUG_RETURN(0); + + DBUG_RETURN(ret); } diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h index 5ae310bab2a..cc6d0f7a20a 100644 --- a/sql/event_scheduler.h +++ b/sql/event_scheduler.h @@ -16,235 +16,113 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -class Event_timed; +class Event_queue; +class Event_job_data; -class THD; -typedef bool * (*event_timed_identifier_comparator)(Event_timed*, Event_timed*); +void +pre_init_event_thread(THD* thd); -int -events_init(); +bool +post_init_event_thread(THD* thd); void -events_shutdown(); +deinit_event_thread(THD *thd); class Event_scheduler { public: - /* Return codes */ - enum enum_error_code - { - OP_OK= 0, - OP_NOT_RUNNING, - OP_CANT_KILL, - OP_CANT_INIT, - OP_DISABLED_EVENT, - OP_LOAD_ERROR, - OP_ALREADY_EXISTS - }; + Event_scheduler(){} + ~Event_scheduler(){} enum enum_state { - UNINITIALIZED= 0, - INITIALIZED, - COMMENCING, - CANTSTART, + INITIALIZED = 0, RUNNING, - SUSPENDED, - IN_SHUTDOWN - }; - - enum enum_suspend_or_resume - { - SUSPEND= 1, - RESUME= 2 + STOPPING }; - /* Singleton access */ - static Event_scheduler* - get_instance(); - - /* Methods for queue management follow */ - - enum enum_error_code - create_event(THD *thd, Event_timed *et, bool check_existence); - - enum enum_error_code - update_event(THD *thd, Event_timed *et, LEX_STRING *new_schema, - LEX_STRING *new_name); - - bool - drop_event(THD *thd, Event_timed *et); - - - int - drop_schema_events(THD *thd, LEX_STRING *schema); - - int - drop_user_events(THD *thd, LEX_STRING *definer, uint *dropped_num) - { DBUG_ASSERT(0); return 0;} - - uint - events_count(); - /* State changing methods follow */ bool start(); - enum enum_error_code - stop(); - bool - start_suspended(); + stop(); + /* + Need to be public because has to be called from the function + passed to pthread_create. + */ bool run(THD *thd); - - enum enum_error_code - suspend_or_resume(enum enum_suspend_or_resume action); - bool - init(); + void + init_scheduler(Event_queue *queue); void - destroy(); + deinit_scheduler(); - static void + void init_mutexes(); - static void - destroy_mutexes(); - void - report_error_during_start(); + deinit_mutexes(); /* Information retrieving methods follow */ enum enum_state get_state(); - bool - initialized(); + void + queue_changed(); - static int + bool dump_internal_status(THD *thd); - static bool - check_system_tables(THD *thd); - private: - Event_timed * - find_event(Event_timed *etn, bool remove_from_q); - uint workers_count(); - bool - is_running_or_suspended(); - /* helper functions */ bool - execute_top(THD *thd); + execute_top(THD *thd, Event_job_data *job_data); + /* helper functions for working with mutexes & conditionals */ void - clean_queue(THD *thd); - - void - stop_all_running_events(THD *thd); - - enum enum_error_code - load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new); - - int - load_events_from_db(THD *thd); + lock_data(const char *func, uint line); void - drop_matching_events(THD *thd, LEX_STRING *pattern, - bool (*)(Event_timed *,LEX_STRING *)); - - bool - check_n_suspend_if_needed(THD *thd); + unlock_data(const char *func, uint line); - bool - check_n_wait_for_non_empty_queue(THD *thd); + void + cond_wait(THD *thd, struct timespec *abstime, const char* msg, + const char *func, uint line); - /* Singleton DP is used */ - Event_scheduler(); - - enum enum_cond_vars - { - COND_NONE= -1, - /* - COND_new_work is a conditional used to signal that there is a change - of the queue that should inform the executor thread that new event should - be executed sooner than previously expected, because of add/replace event. - */ - COND_new_work= 0, - /* - COND_started is a conditional used to synchronize the thread in which - ::start() was called and the spawned thread. ::start() spawns a new thread - and then waits on COND_started but also checks when awaken that `state` is - either RUNNING or CANTSTART. Then it returns back. - */ - COND_started_or_stopped, - /* - Conditional used for signalling from the scheduler thread back to the - thread that calls ::suspend() or ::resume. Synchronizing the calls. - */ - COND_suspend_or_resume, - /* Must be always last */ - COND_LAST - }; + pthread_mutex_t LOCK_scheduler_state; - /* Singleton instance */ - static Event_scheduler singleton; - - /* This is the current status of the life-cycle of the manager. */ + /* This is the current status of the life-cycle of the scheduler. */ enum enum_state state; - /* Set to start the scheduler in suspended state */ - bool start_scheduler_suspended; - /* - LOCK_scheduler_data is the mutex which protects the access to the - manager's queue as well as used when signalling COND_new_work, - COND_started and COND_shutdown. - */ - pthread_mutex_t LOCK_scheduler_data; - - /* - Holds the thread id of the executor thread or 0 if the executor is not + Holds the thread id of the executor thread or 0 if the scheduler is not running. It is used by ::shutdown() to know which thread to kill with kill_one_thread(). The latter wake ups a thread if it is waiting on a conditional variable and sets thd->killed to non-zero. */ ulong thread_id; - pthread_cond_t cond_vars[COND_LAST]; - static const char * const cond_vars_names[COND_LAST]; - - /* The MEM_ROOT of the object */ - MEM_ROOT scheduler_root; - - /* The sorted queue with the Event_timed objects */ - QUEUE queue; + pthread_cond_t COND_state; + Event_queue *queue; + uint mutex_last_locked_at_line; uint mutex_last_unlocked_at_line; const char* mutex_last_locked_in_func; const char* mutex_last_unlocked_in_func; - enum enum_cond_vars cond_waiting_on; bool mutex_scheduler_data_locked; - - /* helper functions for working with mutexes & conditionals */ - void - lock_data(const char *func, uint line); - - void - unlock_data(const char *func, uint line); - - int - cond_wait(enum enum_cond_vars, pthread_mutex_t *mutex); + bool waiting_on_cond; + + ulonglong started_events; private: /* Prevent use of these */ diff --git a/sql/event_timed.h b/sql/event_timed.h deleted file mode 100644 index 0652cece361..00000000000 --- a/sql/event_timed.h +++ /dev/null @@ -1,217 +0,0 @@ -#ifndef _EVENT_TIMED_H_ -#define _EVENT_TIMED_H_ -/* Copyright (C) 2004-2006 MySQL AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - - -#define 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_COMPILE_ERROR -19 -#define EVEX_GENERAL_ERROR -20 -#define EVEX_BAD_IDENTIFIER -21 -#define EVEX_BODY_TOO_LONG -22 -#define EVEX_BAD_PARAMS -23 -#define EVEX_NOT_RUNNING -24 -#define EVEX_MICROSECOND_UNSUP -25 -#define EVEX_CANT_KILL -26 - -#define EVENT_EXEC_NO_MORE (1L << 0) -#define EVENT_NOT_USED (1L << 1) -#define EVENT_FREE_WHEN_FINISHED (1L << 2) - - -class sp_head; - -class Event_timed -{ - Event_timed(const Event_timed &); /* Prevent use of these */ - void operator=(Event_timed &); - my_bool in_spawned_thread; - ulong locked_by_thread_id; - my_bool running; - ulong thread_id; - pthread_mutex_t LOCK_running; - pthread_cond_t COND_finished; - - bool status_changed; - bool last_executed_changed; - -public: - enum enum_status - { - ENABLED = 1, - DISABLED - }; - - enum enum_on_completion - { - ON_COMPLETION_DROP = 1, - ON_COMPLETION_PRESERVE - }; - - TIME last_executed; - - 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; - my_bool starts_null; - my_bool ends_null; - my_bool execute_at_null; - - longlong expression; - interval_type interval; - - ulonglong created; - ulonglong modified; - enum enum_on_completion on_completion; - enum enum_status status; - sp_head *sphead; - ulong sql_mode; - const uchar *body_begin; - - bool dropped; - bool free_sphead_on_delete; - uint flags;//all kind of purposes - - static void *operator new(size_t size) - { - void *p; - DBUG_ENTER("Event_timed::new(size)"); - p= my_malloc(size, MYF(0)); - DBUG_PRINT("info", ("alloc_ptr=0x%lx", p)); - DBUG_RETURN(p); - } - - static void *operator new(size_t size, MEM_ROOT *mem_root) - { return (void*) alloc_root(mem_root, (uint) size); } - - static void operator delete(void *ptr, size_t size) - { - DBUG_ENTER("Event_timed::delete(ptr,size)"); - DBUG_PRINT("enter", ("free_ptr=0x%lx", ptr)); - TRASH(ptr, size); - my_free((gptr) ptr, MYF(0)); - DBUG_VOID_RETURN; - } - - static void operator delete(void *ptr, MEM_ROOT *mem_root) - { - /* - Don't free the memory it will be done by the mem_root but - we need to call the destructor because we free other resources - which are not allocated on the root but on the heap, or we - deinit mutexes. - */ - DBUG_ASSERT(0); - } - - Event_timed(); - - ~Event_timed(); - - void - init(); - - void - deinit_mutexes(); - - int - init_definer(THD *thd); - - int - init_execute_at(THD *thd, Item *expr); - - int - init_interval(THD *thd, Item *expr, interval_type new_interval); - - void - init_name(THD *thd, sp_name *spn); - - int - init_starts(THD *thd, Item *starts); - - int - init_ends(THD *thd, Item *ends); - - void - init_body(THD *thd); - - void - init_comment(THD *thd, LEX_STRING *set_comment); - - int - load_from_row(MEM_ROOT *mem_root, TABLE *table); - - bool - compute_next_execution_time(); - - int - drop(THD *thd); - - void - mark_last_executed(THD *thd); - - bool - update_fields(THD *thd); - - int - get_create_event(THD *thd, String *buf); - - int - execute(THD *thd, MEM_ROOT *mem_root); - - int - compile(THD *thd, MEM_ROOT *mem_root); - - bool - is_running(); - - int - spawn_now(void * (*thread_func)(void*), void *arg); - - bool - spawn_thread_finish(THD *thd); - - void - free_sp(); - - bool - has_equal_db(Event_timed *etn); - - int - kill_thread(THD *thd); - - void - set_thread_id(ulong tid) { thread_id= tid; } -}; - -#endif /* _EVENT_H_ */ diff --git a/sql/events.cc b/sql/events.cc index 210cc2c4735..f75a362f947 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -15,11 +15,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mysql_priv.h" -#include "events_priv.h" #include "events.h" -#include "event_timed.h" +#include "event_data_objects.h" +#include "event_db_repository.h" +#include "event_queue.h" #include "event_scheduler.h" -#include "sp.h" #include "sp_head.h" /* @@ -41,16 +41,23 @@ - Add logging to file -Warning: - - For now parallel execution is not possible because the same sp_head cannot - be executed few times!!! There is still no lock attached to particular - event. */ -MEM_ROOT evex_mem_root; -time_t mysql_event_last_create_time= 0L; - +/* + If the user (un)intentionally removes an event directly from mysql.event + the following sequence has to be used to be able to remove the in-memory + counterpart. + 1. CREATE EVENT the_name ON SCHEDULE EVERY 1 SECOND DISABLE DO SELECT 1; + 2. DROP EVENT the_name + + In other words, the first one will create a row in mysql.event . In the + second step because there will be a line, disk based drop will pass and + the scheduler will remove the memory counterpart. The reason is that + in-memory queue does not check whether the event we try to drop from memory + is disabled. Disabled events are not kept in-memory because they are not + eligible for execution. +*/ const char *event_scheduler_state_names[]= { "OFF", "0", "ON", "1", "SUSPEND", "2", NullS }; @@ -63,122 +70,24 @@ TYPELIB Events::opt_typelib= NULL }; +Events Events::singleton; ulong Events::opt_event_scheduler= 2; -static -TABLE_FIELD_W_TYPE event_table_fields[Events::FIELD_COUNT] = { - { - {(char *) STRING_WITH_LEN("db")}, - {(char *) STRING_WITH_LEN("char(64)")}, - {(char *) STRING_WITH_LEN("utf8")} - }, - { - {(char *) STRING_WITH_LEN("name")}, - {(char *) STRING_WITH_LEN("char(64)")}, - {(char *) STRING_WITH_LEN("utf8")} - }, - { - {(char *) STRING_WITH_LEN("body")}, - {(char *) STRING_WITH_LEN("longblob")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("definer")}, - {(char *) STRING_WITH_LEN("char(77)")}, - {(char *) STRING_WITH_LEN("utf8")} - }, - { - {(char *) STRING_WITH_LEN("execute_at")}, - {(char *) STRING_WITH_LEN("datetime")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("interval_value")}, - {(char *) STRING_WITH_LEN("int(11)")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("interval_field")}, - {(char *) STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY'," - "'HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR'," - "'DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND'," - "'DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND'," - "'SECOND_MICROSECOND')")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("created")}, - {(char *) STRING_WITH_LEN("timestamp")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("modified")}, - {(char *) STRING_WITH_LEN("timestamp")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("last_executed")}, - {(char *) STRING_WITH_LEN("datetime")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("starts")}, - {(char *) STRING_WITH_LEN("datetime")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("ends")}, - {(char *) STRING_WITH_LEN("datetime")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("status")}, - {(char *) STRING_WITH_LEN("enum('ENABLED','DISABLED')")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("on_completion")}, - {(char *) STRING_WITH_LEN("enum('DROP','PRESERVE')")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("sql_mode")}, - {(char *) STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES'," - "'IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION'," - "'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB'," - "'NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40'," - "'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES'," - "'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES'," - "'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER'," - "'HIGH_NOT_PRECEDENCE')")}, - {NULL, 0} - }, - { - {(char *) STRING_WITH_LEN("comment")}, - {(char *) STRING_WITH_LEN("char(64)")}, - {(char *) STRING_WITH_LEN("utf8")} - } -}; - /* Compares 2 LEX strings regarding case. SYNOPSIS sortcmp_lex_string() - - s - first LEX_STRING - t - second LEX_STRING - cs - charset + s First LEX_STRING + t Second LEX_STRING + cs Charset RETURN VALUE - -1 - s < t - 0 - s == t - 1 - s > t - - Notes - TIME.second_part is not considered during comparison + -1 s < t + 0 s == t + 1 s > t */ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) @@ -189,6 +98,24 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) /* + Accessor for the singleton instance. + + SYNOPSIS + Events::get_instance() + + RETURN VALUE + address +*/ + +Events * +Events::get_instance() +{ + DBUG_ENTER("Events::get_instance"); + DBUG_RETURN(&singleton); +} + + +/* Reconstructs interval expression from interval type and expression value that is in form of a value of the smalles entity: For @@ -197,19 +124,18 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) SYNOPSIS Events::reconstruct_interval_expression() - buf - preallocated String buffer to add the value to - interval - the interval type (for instance YEAR_MONTH) - expression - the value in the lowest entity + buf Preallocated String buffer to add the value to + interval The interval type (for instance YEAR_MONTH) + expression The value in the lowest entity - RETURNS - 0 - OK - 1 - Error + RETURN VALUE + 0 OK + 1 Error */ int -Events::reconstruct_interval_expression(String *buf, - interval_type interval, - longlong expression) +Events::reconstruct_interval_expression(String *buf, interval_type interval, + longlong expression) { ulonglong expr= expression; char tmp_buff[128], *end; @@ -269,7 +195,7 @@ common_1_lev_code: expr= tmp_expr - (tmp_expr/60)*60; /* the code after the switch will finish */ } - break; + break; case INTERVAL_DAY_SECOND: { ulonglong tmp_expr= expr; @@ -323,7 +249,7 @@ common_1_lev_code: /* - Open mysql.event table for read + Opens mysql.event table with specified lock SYNOPSIS Events::open_event_table() @@ -341,545 +267,7 @@ int Events::open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table) { - TABLE_LIST tables; - DBUG_ENTER("open_events_table"); - - bzero((char*) &tables, sizeof(tables)); - tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*) "event"; - tables.lock_type= lock_type; - - if (simple_open_n_lock_tables(thd, &tables)) - DBUG_RETURN(1); - - if (table_check_intact(tables.table, Events::FIELD_COUNT, - event_table_fields, - &mysql_event_last_create_time, - ER_CANNOT_LOAD_FROM_TABLE)) - { - close_thread_tables(thd); - DBUG_RETURN(2); - } - *table= tables.table; - tables.table->use_all_columns(); - DBUG_RETURN(0); -} - - -/* - Find row in open mysql.event table representing event - - SYNOPSIS - evex_db_find_event_aux() - thd Thread context - et event_timed object containing dbname & name - table TABLE object for open mysql.event table. - - RETURN VALUE - 0 - Routine found - EVEX_KEY_NOT_FOUND - No routine with given name -*/ - -inline int -evex_db_find_event_aux(THD *thd, Event_timed *et, TABLE *table) -{ - return evex_db_find_event_by_name(thd, et->dbname, et->name, table); -} - - -/* - Find row in open mysql.event table representing event - - SYNOPSIS - evex_db_find_event_by_name() - thd Thread context - dbname Name of event's database - rname Name of the event inside the db - table TABLE object for open mysql.event table. - - RETURN VALUE - 0 - Routine found - EVEX_KEY_NOT_FOUND - No routine with given name -*/ - -int -evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, - const LEX_STRING ev_name, - TABLE *table) -{ - byte key[MAX_KEY_LENGTH]; - DBUG_ENTER("evex_db_find_event_by_name"); - DBUG_PRINT("enter", ("name: %.*s", ev_name.length, ev_name.str)); - - /* - Create key to find row. We have to use field->store() to be able to - handle VARCHAR and CHAR fields. - Assumption here is that the two first fields in the table are - 'db' and 'name' and the first key is the primary key over the - same fields. - */ - if (dbname.length > table->field[Events::FIELD_DB]->field_length || - ev_name.length > table->field[Events::FIELD_NAME]->field_length) - - DBUG_RETURN(EVEX_KEY_NOT_FOUND); - - table->field[Events::FIELD_DB]->store(dbname.str, dbname.length, - &my_charset_bin); - table->field[Events::FIELD_NAME]->store(ev_name.str, ev_name.length, - &my_charset_bin); - - key_copy(key, table->record[0], table->key_info, - table->key_info->key_length); - - if (table->file->index_read_idx(table->record[0], 0, key, - table->key_info->key_length, - HA_READ_KEY_EXACT)) - { - DBUG_PRINT("info", ("Row not found")); - DBUG_RETURN(EVEX_KEY_NOT_FOUND); - } - - DBUG_PRINT("info", ("Row found!")); - DBUG_RETURN(0); -} - - -/* - Puts some data common to CREATE and ALTER EVENT into a row. - - SYNOPSIS - evex_fill_row() - thd THD - table the row to fill out - et Event's data - - RETURN VALUE - 0 - OK - EVEX_GENERAL_ERROR - bad data - EVEX_GET_FIELD_FAILED - field count does not match. table corrupted? - - DESCRIPTION - Used both when an event is created and when it is altered. -*/ - -static int -evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update) -{ - CHARSET_INFO *scs= system_charset_info; - enum Events::enum_table_field field_num; - - DBUG_ENTER("evex_fill_row"); - - DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str)); - DBUG_PRINT("info", ("name =[%s]", et->name.str)); - DBUG_PRINT("info", ("body =[%s]", et->body.str)); - - if (table->field[field_num= Events::FIELD_DEFINER]-> - store(et->definer.str, et->definer.length, scs)) - goto err_truncate; - - if (table->field[field_num= Events::FIELD_DB]-> - store(et->dbname.str, et->dbname.length, scs)) - goto err_truncate; - - if (table->field[field_num= Events::FIELD_NAME]-> - store(et->name.str, et->name.length, scs)) - goto err_truncate; - - /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/ - table->field[Events::FIELD_ON_COMPLETION]-> - store((longlong)et->on_completion, true); - - table->field[Events::FIELD_STATUS]->store((longlong)et->status, true); - - /* - Change the SQL_MODE only if body was present in an ALTER EVENT and of course - always during CREATE EVENT. - */ - if (et->body.str) - { - table->field[Events::FIELD_SQL_MODE]-> - store((longlong)thd->variables.sql_mode, true); - - if (table->field[field_num= Events::FIELD_BODY]-> - store(et->body.str, et->body.length, scs)) - goto err_truncate; - } - - if (et->expression) - { - table->field[Events::FIELD_INTERVAL_EXPR]->set_notnull(); - table->field[Events::FIELD_INTERVAL_EXPR]-> - store((longlong)et->expression, true); - - table->field[Events::FIELD_TRANSIENT_INTERVAL]->set_notnull(); - /* - In the enum (C) intervals start from 0 but in mysql enum valid values start - from 1. Thus +1 offset is needed! - */ - table->field[Events::FIELD_TRANSIENT_INTERVAL]-> - store((longlong)et->interval+1, true); - - table->field[Events::FIELD_EXECUTE_AT]->set_null(); - - if (!et->starts_null) - { - table->field[Events::FIELD_STARTS]->set_notnull(); - table->field[Events::FIELD_STARTS]-> - store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME); - } - - if (!et->ends_null) - { - table->field[Events::FIELD_ENDS]->set_notnull(); - table->field[Events::FIELD_ENDS]-> - store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME); - } - } - else if (et->execute_at.year) - { - table->field[Events::FIELD_INTERVAL_EXPR]->set_null(); - table->field[Events::FIELD_TRANSIENT_INTERVAL]->set_null(); - table->field[Events::FIELD_STARTS]->set_null(); - table->field[Events::FIELD_ENDS]->set_null(); - - table->field[Events::FIELD_EXECUTE_AT]->set_notnull(); - table->field[Events::FIELD_EXECUTE_AT]-> - store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME); - } - else - { - DBUG_ASSERT(is_update); - /* - it is normal to be here when the action is update - this is an error if the action is create. something is borked - */ - } - - ((Field_timestamp *)table->field[Events::FIELD_MODIFIED])->set_time(); - - if (et->comment.str) - { - if (table->field[field_num= Events::FIELD_COMMENT]-> - store(et->comment.str, et->comment.length, scs)) - goto err_truncate; - } - - DBUG_RETURN(0); -err_truncate: - my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), table->field[field_num]->field_name); - DBUG_RETURN(EVEX_GENERAL_ERROR); -} - - -/* - Creates an event in mysql.event - - SYNOPSIS - db_create_event() - thd THD - et Event_timed object containing information for the event - create_if_not If an warning should be generated in case event exists - rows_affected How many rows were affected - - RETURN VALUE - 0 - OK - EVEX_GENERAL_ERROR - Failure - - DESCRIPTION - Creates an event. Relies on evex_fill_row which is shared with - db_update_event. The name of the event is inside "et". -*/ - -int -db_create_event(THD *thd, Event_timed *et, my_bool create_if_not, - uint *rows_affected) -{ - int ret= 0; - CHARSET_INFO *scs= system_charset_info; - TABLE *table; - char old_db_buf[NAME_LEN+1]; - LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; - bool dbchanged= FALSE; - DBUG_ENTER("db_create_event"); - DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str)); - - *rows_affected= 0; - DBUG_PRINT("info", ("open mysql.event for update")); - if (Events::open_event_table(thd, TL_WRITE, &table)) - { - my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - goto err; - } - - DBUG_PRINT("info", ("check existance of an event with the same name")); - if (!evex_db_find_event_aux(thd, et, table)) - { - if (create_if_not) - { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS), - et->name.str); - goto ok; - } - my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), et->name.str); - goto err; - } - - DBUG_PRINT("info", ("non-existant, go forward")); - if ((ret= sp_use_new_db(thd, et->dbname, &old_db, 0, &dbchanged))) - { - my_error(ER_BAD_DB_ERROR, MYF(0)); - goto err; - } - - restore_record(table, s->default_values); // Get default values for fields - - if (system_charset_info->cset->numchars(system_charset_info, et->dbname.str, - et->dbname.str + et->dbname.length) - > EVEX_DB_FIELD_LEN) - { - my_error(ER_TOO_LONG_IDENT, MYF(0), et->dbname.str); - goto err; - } - if (system_charset_info->cset->numchars(system_charset_info, et->name.str, - et->name.str + et->name.length) - > EVEX_DB_FIELD_LEN) - { - my_error(ER_TOO_LONG_IDENT, MYF(0), et->name.str); - goto err; - } - - if (et->body.length > table->field[Events::FIELD_BODY]->field_length) - { - my_error(ER_TOO_LONG_BODY, MYF(0), et->name.str); - goto err; - } - - if (!(et->expression) && !(et->execute_at.year)) - { - DBUG_PRINT("error", ("neither expression nor execute_at are set!")); - my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0)); - goto err; - } - - ((Field_timestamp *)table->field[Events::FIELD_CREATED])->set_time(); - - /* - evex_fill_row() calls my_error() in case of error so no need to - handle it here - */ - if ((ret= evex_fill_row(thd, table, et, false))) - goto err; - - if (table->file->ha_write_row(table->record[0])) - { - my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret); - goto err; - } - -#ifdef USE_THIS_CODE_AS_TEMPLATE_WHEN_EVENT_REPLICATION_IS_AGREED - if (mysql_bin_log.is_open()) - { - thd->clear_error(); - /* Such a statement can always go directly to binlog, no trans cache */ - thd->binlog_query(THD::MYSQL_QUERY_TYPE, thd->query, thd->query_length, - FALSE, FALSE); - } -#endif - - *rows_affected= 1; -ok: - if (dbchanged) - (void) mysql_change_db(thd, old_db.str, 1); - if (table) - close_thread_tables(thd); - DBUG_RETURN(EVEX_OK); - -err: - if (dbchanged) - (void) mysql_change_db(thd, old_db.str, 1); - if (table) - close_thread_tables(thd); - DBUG_RETURN(EVEX_GENERAL_ERROR); -} - - -/* - Used to execute ALTER EVENT. Pendant to Events::update_event(). - - SYNOPSIS - db_update_event() - thd THD - sp_name the name of the event to alter - et event's data - - RETURN VALUE - 0 OK - EVEX_GENERAL_ERROR Error occured (my_error() called) - - NOTES - sp_name is passed since this is the name of the event to - alter in case of RENAME TO. -*/ - -static int -db_update_event(THD *thd, Event_timed *et, sp_name *new_name) -{ - CHARSET_INFO *scs= system_charset_info; - TABLE *table; - int ret= EVEX_OPEN_TABLE_FAILED; - DBUG_ENTER("db_update_event"); - DBUG_PRINT("enter", ("dbname: %.*s", et->dbname.length, et->dbname.str)); - DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str)); - DBUG_PRINT("enter", ("user: %.*s", et->definer.length, et->definer.str)); - if (new_name) - DBUG_PRINT("enter", ("rename to: %.*s", new_name->m_name.length, - new_name->m_name.str)); - - if (Events::open_event_table(thd, TL_WRITE, &table)) - { - my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - goto err; - } - - /* first look whether we overwrite */ - if (new_name) - { - if (!sortcmp_lex_string(et->name, new_name->m_name, scs) && - !sortcmp_lex_string(et->dbname, new_name->m_db, scs)) - { - my_error(ER_EVENT_SAME_NAME, MYF(0), et->name.str); - goto err; - } - - if (!evex_db_find_event_by_name(thd,new_name->m_db,new_name->m_name,table)) - { - my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str); - goto err; - } - } - /* - ...and then if there is such an event. Don't exchange the blocks - because you will get error 120 from table handler because new_name will - overwrite the key and SE will tell us that it cannot find the already found - row (copied into record[1] later - */ - if (EVEX_KEY_NOT_FOUND == evex_db_find_event_aux(thd, et, table)) - { - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str); - goto err; - } - - store_record(table,record[1]); - - /* Don't update create on row update. */ - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - - /* - evex_fill_row() calls my_error() in case of error so no need to - handle it here - */ - if ((ret= evex_fill_row(thd, table, et, true))) - goto err; - - if (new_name) - { - table->field[Events::FIELD_DB]-> - store(new_name->m_db.str, new_name->m_db.length, scs); - table->field[Events::FIELD_NAME]-> - store(new_name->m_name.str, new_name->m_name.length, scs); - } - - if ((ret= table->file->ha_update_row(table->record[1], table->record[0]))) - { - my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret); - goto err; - } - - /* close mysql.event or we crash later when loading the event from disk */ - close_thread_tables(thd); - DBUG_RETURN(0); - -err: - if (table) - close_thread_tables(thd); - DBUG_RETURN(EVEX_GENERAL_ERROR); -} - - -/* - Looks for a named event in mysql.event and in case of success returns - an object will data loaded from the table. - - SYNOPSIS - db_find_event() - thd THD - name the name of the event to find - ett event's data if event is found - tbl TABLE object to use when not NULL - - NOTES - 1) Use sp_name for look up, return in **ett if found - 2) tbl is not closed at exit - - RETURN VALUE - 0 ok In this case *ett is set to the event - # error *ett == 0 -*/ - -int -db_find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl, - MEM_ROOT *root) -{ - TABLE *table; - int ret; - Event_timed *et= NULL; - DBUG_ENTER("db_find_event"); - DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - - if (!root) - root= &evex_mem_root; - - if (tbl) - table= tbl; - else if (Events::open_event_table(thd, TL_READ, &table)) - { - my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - ret= EVEX_GENERAL_ERROR; - goto done; - } - - if ((ret= evex_db_find_event_by_name(thd, name->m_db, name->m_name, table))) - { - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str); - goto done; - } - et= new Event_timed; - - /* - 1)The table should not be closed beforehand. ::load_from_row() only loads - and does not compile - - 2)::load_from_row() is silent on error therefore we emit error msg here - */ - if ((ret= et->load_from_row(root, table))) - { - my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0)); - goto done; - } - -done: - if (ret) - { - delete et; - et= 0; - } - /* don't close the table if we haven't opened it ourselves */ - if (!tbl && table) - close_thread_tables(thd); - *ett= et; - DBUG_RETURN(ret); + return db_repository->open_event_table(thd, lock_type, table); } @@ -888,41 +276,40 @@ done: SYNOPSIS Events::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 + thd [in] THD + et [in] Event's data from parsing stage + if_not_exists [in] Whether IF NOT EXISTS was specified in the DDL + rows_affected [out] How many rows were affected RETURN VALUE 0 OK - !0 Error + !0 Error (Reported) 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. + In case there is an event with the same name (db) and + IF NOT EXISTS is specified, an warning is put into the stack. */ int -Events::create_event(THD *thd, Event_timed *et, uint create_options, +Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists, uint *rows_affected) { int ret; - DBUG_ENTER("Events::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))) + pthread_mutex_lock(&LOCK_event_metadata); + /* On error conditions my_error() is called so no need to handle here */ + if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists, + rows_affected))) { - Event_scheduler *scheduler= Event_scheduler::get_instance(); - if (scheduler->initialized() && - (ret= scheduler->create_event(thd, et, true))) - my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret); + if ((ret= event_queue->create_event(thd, parse_data->dbname, + parse_data->name))) + { + DBUG_ASSERT(ret == OP_LOAD_ERROR); + my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0)); + } } - /* No need to close the table, it will be closed in sql_parse::do_command */ + pthread_mutex_unlock(&LOCK_event_metadata); DBUG_RETURN(ret); } @@ -933,9 +320,10 @@ Events::create_event(THD *thd, Event_timed *et, uint create_options, SYNOPSIS Events::update_event() - thd THD - et event's data - new_name set in case of RENAME TO. + thd [in] THD + et [in] Event's data from parsing stage + rename_to [in] Set in case of RENAME TO. + rows_affected [out] How many rows were affected. RETURN VALUE 0 OK @@ -943,32 +331,32 @@ Events::create_event(THD *thd, Event_timed *et, uint create_options, NOTES et contains data about dbname and event name. - new_name is the new name of the event, if not null (this means - that RENAME TO was specified in the query) + new_name is the new name of the event, if not null this means + that RENAME TO was specified in the query */ int -Events::update_event(THD *thd, Event_timed *et, sp_name *new_name, - uint *rows_affected) +Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to, + uint *rows_affected) { int ret; - DBUG_ENTER("Events::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))) + LEX_STRING *new_dbname= rename_to? &rename_to->m_db: NULL; + LEX_STRING *new_name= rename_to? &rename_to->m_name: NULL; + + pthread_mutex_lock(&LOCK_event_metadata); + /* On error conditions my_error() is called so no need to handle here */ + if (!(ret= db_repository->update_event(thd, parse_data, new_dbname, new_name))) { - Event_scheduler *scheduler= Event_scheduler::get_instance(); - if (scheduler->initialized() && - (ret= scheduler->update_event(thd, et, - new_name? &new_name->m_db: NULL, - new_name? &new_name->m_name: NULL))) - my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret); + if ((ret= event_queue->update_event(thd, parse_data->dbname, + parse_data->name, new_dbname, new_name))) + { + DBUG_ASSERT(ret == OP_LOAD_ERROR); + my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0)); + } } + pthread_mutex_unlock(&LOCK_event_metadata); + DBUG_RETURN(ret); } @@ -977,95 +365,69 @@ Events::update_event(THD *thd, Event_timed *et, sp_name *new_name, Drops an event SYNOPSIS - db_drop_event() - thd THD - et event's name - drop_if_exists if set and the event not existing => warning onto the stack - rows_affected affected number of rows is returned heres + Events::drop_event() + thd [in] THD + dbname [in] Event's schema + name [in] Event's name + if_exists [in] When set and the event does not exist => + warning onto the stack + rows_affected [out] Affected number of rows is returned here + only_from_disk [in] Whether to remove the event from the queue too. + In case of Event_job_data::drop() it's needed to + do only disk drop because Event_queue will handle + removal from memory queue. RETURN VALUE - 0 OK - !0 Error (my_error() called) + 0 OK + !0 Error (reported) */ -int db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists, - uint *rows_affected) +int +Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists, + uint *rows_affected, bool only_from_disk) { - TABLE *table; - Open_tables_state backup; int ret; + DBUG_ENTER("Events::drop_event"); - DBUG_ENTER("db_drop_event"); - ret= EVEX_OPEN_TABLE_FAILED; - - thd->reset_n_backup_open_tables_state(&backup); - if (Events::open_event_table(thd, TL_WRITE, &table)) - { - my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); - goto done; - } - - if (!(ret= evex_db_find_event_aux(thd, et, table))) + pthread_mutex_lock(&LOCK_event_metadata); + /* On error conditions my_error() is called so no need to handle here */ + if (!(ret= db_repository->drop_event(thd, dbname, name, if_exists, + rows_affected))) { - if ((ret= table->file->ha_delete_row(table->record[0]))) - { - my_error(ER_EVENT_CANNOT_DELETE, MYF(0)); - goto done; - } + if (!only_from_disk) + event_queue->drop_event(thd, dbname, name); } - else if (ret == EVEX_KEY_NOT_FOUND) - { - if (drop_if_exists) - { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), - "Event", et->name.str); - ret= 0; - } else - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str); - goto done; - } - - -done: - /* - evex_drop_event() is used by Event_timed::drop therefore - we have to close our thread tables. - */ - close_thread_tables(thd); - thd->restore_backup_open_tables_state(&backup); + pthread_mutex_unlock(&LOCK_event_metadata); DBUG_RETURN(ret); } /* - Drops an event + Drops all events from a schema SYNOPSIS - Events::drop_event() - thd THD - et event's name - drop_if_exists if set and the event not existing => warning onto the stack - rows_affected affected number of rows is returned heres + Events::drop_schema_events() + thd Thread + db ASCIIZ schema name RETURN VALUE - 0 OK - !0 Error (reported) + 0 OK + !0 Error */ int -Events::drop_event(THD *thd, Event_timed *et, bool drop_if_exists, - uint *rows_affected) +Events::drop_schema_events(THD *thd, char *db) { - int ret; + int ret= 0; + LEX_STRING db_lex= {db, strlen(db)}; + + DBUG_ENTER("Events::drop_schema_events"); + DBUG_PRINT("enter", ("dropping events from %s", db)); - DBUG_ENTER("Events::drop_event"); - if (!(ret= db_drop_event(thd, et, drop_if_exists, rows_affected))) - { - Event_scheduler *scheduler= Event_scheduler::get_instance(); - if (scheduler->initialized() && (ret= scheduler->drop_event(thd, et))) - my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret); - } + pthread_mutex_lock(&LOCK_event_metadata); + event_queue->drop_schema_events(thd, db_lex); + ret= db_repository->drop_schema_events(thd, db_lex); + pthread_mutex_unlock(&LOCK_event_metadata); DBUG_RETURN(ret); } @@ -1085,24 +447,22 @@ Events::drop_event(THD *thd, Event_timed *et, bool drop_if_exists, */ int -Events::show_create_event(THD *thd, sp_name *spn) +Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name) { + CHARSET_INFO *scs= system_charset_info; int ret; - Event_timed *et= NULL; - Open_tables_state backup; + Event_timed *et= new Event_timed(); - DBUG_ENTER("evex_update_event"); - DBUG_PRINT("enter", ("name: %*s", spn->m_name.length, spn->m_name.str)); + DBUG_ENTER("Events::show_create_event"); + DBUG_PRINT("enter", ("name: %s@%s", dbname.str, name.str)); - thd->reset_n_backup_open_tables_state(&backup); - ret= db_find_event(thd, spn, &et, NULL, thd->mem_root); - thd->restore_backup_open_tables_state(&backup); + ret= db_repository->load_named_event(thd, dbname, name, et); if (!ret) { Protocol *protocol= thd->protocol; - char show_str_buf[768]; - String show_str(show_str_buf, sizeof(show_str_buf), system_charset_info); + char show_str_buf[10 * STRING_BUFFER_USUAL_SIZE]; + String show_str(show_str_buf, sizeof(show_str_buf), scs); List<Item> field_list; byte *sql_mode_str; ulong sql_mode_len=0; @@ -1121,18 +481,19 @@ Events::show_create_event(THD *thd, sp_name *spn) field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len)); - field_list.push_back(new Item_empty_string("Create Event", - show_str.length())); + field_list. + push_back(new Item_empty_string("Create Event", show_str.length())); + if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) goto err; protocol->prepare_for_resend(); - protocol->store(et->name.str, et->name.length, system_charset_info); + protocol->store(et->name.str, et->name.length, scs); - protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info); + protocol->store((char*) sql_mode_str, sql_mode_len, scs); - protocol->store(show_str.c_ptr(), show_str.length(), system_charset_info); + protocol->store(show_str.c_ptr(), show_str.length(), scs); ret= protocol->write(); send_eof(thd); } @@ -1140,102 +501,47 @@ Events::show_create_event(THD *thd, sp_name *spn) DBUG_RETURN(ret); err: delete et; - DBUG_RETURN(1); + DBUG_RETURN(1); } /* - Drops all events from a schema + Proxy for Event_db_repository::fill_schema_events. + Callback for I_S from sql_show.cc SYNOPSIS - Events::drop_schema_events() - thd Thread - db ASCIIZ schema name + Events::fill_schema_events() + thd Thread + tables The schema table + cond Unused RETURN VALUE - 0 OK - !0 Error -*/ - -int -Events::drop_schema_events(THD *thd, char *db) -{ - int ret= 0; - LEX_STRING db_lex= {db, strlen(db)}; - - DBUG_ENTER("evex_drop_db_events"); - DBUG_PRINT("enter", ("dropping events from %s", db)); - - Event_scheduler *scheduler= Event_scheduler::get_instance(); - if (scheduler->initialized()) - ret= scheduler->drop_schema_events(thd, &db_lex); - else - ret= db_drop_events_from_table(thd, &db_lex); - - DBUG_RETURN(ret); -} - - -/* - Drops all events in the selected database, from mysql.event. - - SYNOPSIS - evex_drop_db_events_from_table() - thd Thread - db Schema name - - RETURN VALUE - 0 OK - !0 Error from ha_delete_row + 0 OK + !0 Error */ int -db_drop_events_from_table(THD *thd, LEX_STRING *db) +Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) { - int ret; - TABLE *table; - READ_RECORD read_record_info; - DBUG_ENTER("db_drop_events_from_table"); - DBUG_PRINT("info", ("dropping events from %s", db->str)); - - if ((ret= Events::open_event_table(thd, TL_WRITE, &table))) - { - if (my_errno != ENOENT) - sql_print_error("Table mysql.event is damaged. Got error %d on open", - my_errno); - DBUG_RETURN(ret); - } - /* only enabled events are in memory, so we go now and delete the rest */ - init_read_record(&read_record_info, thd, table, NULL, 1, 0); - while (!(read_record_info.read_record(&read_record_info)) && !ret) + char *db= NULL; + DBUG_ENTER("Events::fill_schema_events"); + /* + If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to + be NULL. Let's do an assert anyway. + */ + if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS) { - char *et_db= get_field(thd->mem_root, - table->field[Events::FIELD_DB]); - - LEX_STRING et_db_lex= {et_db, strlen(et_db)}; - DBUG_PRINT("info", ("Current event %s.%s", et_db, - get_field(thd->mem_root, - table->field[Events::FIELD_NAME]))); - - if (!sortcmp_lex_string(et_db_lex, *db, system_charset_info)) - { - DBUG_PRINT("info", ("Dropping")); - if ((ret= table->file->ha_delete_row(table->record[0]))) - my_error(ER_EVENT_DROP_FAILED, MYF(0), - get_field(thd->mem_root, - table->field[Events::FIELD_NAME])); - } + DBUG_ASSERT(thd->lex->select_lex.db); + if (check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0, + is_schema_db(thd->lex->select_lex.db))) + DBUG_RETURN(1); + db= thd->lex->select_lex.db; } - end_read_record(&read_record_info); - thd->version--; /* Force close to free memory */ - - close_thread_tables(thd); - - DBUG_RETURN(ret); + DBUG_RETURN(get_instance()->db_repository-> + fill_schema_events(thd, tables, db)); } - /* Inits the scheduler's structures. @@ -1247,25 +553,30 @@ db_drop_events_from_table(THD *thd, LEX_STRING *db) RETURN VALUE 0 OK - 1 Error + 1 Error in case the scheduler can't start */ -int +bool Events::init() { - int ret= 0; + int res; DBUG_ENTER("Events::init"); + if (event_queue->init_queue(db_repository, scheduler)) + { + sql_print_information("SCHEDULER: Error while loading from disk."); + DBUG_RETURN(TRUE); + } + scheduler->init_scheduler(event_queue); /* it should be an assignment! */ if (opt_event_scheduler) { - Event_scheduler *scheduler= Event_scheduler::get_instance(); DBUG_ASSERT(opt_event_scheduler == 1 || opt_event_scheduler == 2); - DBUG_RETURN(scheduler->init() || - (opt_event_scheduler == 1? scheduler->start(): - scheduler->start_suspended())); + if (opt_event_scheduler == 1) + DBUG_RETURN(scheduler->start()); } - DBUG_RETURN(0); + + DBUG_RETURN(FALSE); } @@ -1273,70 +584,161 @@ Events::init() Cleans up scheduler's resources. Called at server shutdown. SYNOPSIS - Events::shutdown() + Events::deinit() NOTES This function is not synchronized. */ void -Events::shutdown() +Events::deinit() { - DBUG_ENTER("Events::shutdown"); - Event_scheduler *scheduler= Event_scheduler::get_instance(); - if (scheduler->initialized()) - { - scheduler->stop(); - scheduler->destroy(); - } + DBUG_ENTER("Events::deinit"); + + scheduler->stop(); + scheduler->deinit_scheduler(); + + event_queue->deinit_queue(); DBUG_VOID_RETURN; } /* - Proxy for Event_scheduler::dump_internal_status + Inits Events mutexes + + SYNOPSIS + Events::init_mutexes() + thd Thread +*/ + +void +Events::init_mutexes() +{ + pthread_mutex_init(&LOCK_event_metadata, MY_MUTEX_INIT_FAST); + + db_repository= new Event_db_repository; + + event_queue= new Event_queue; + event_queue->init_mutexes(); + + scheduler= new Event_scheduler; + scheduler->init_mutexes(); +} + + +/* + Destroys Events mutexes + + SYNOPSIS + Events::destroy_mutexes() +*/ + +void +Events::destroy_mutexes() +{ + event_queue->deinit_mutexes(); + scheduler->deinit_mutexes(); + + delete scheduler; + delete db_repository; + delete event_queue; + + pthread_mutex_destroy(&LOCK_event_metadata); +} + + +/* + Dumps the internal status of the scheduler and the memory cache + into a table with two columns - Name & Value. Different properties + which could be useful for debugging for instance deadlocks are + returned. SYNOPSIS Events::dump_internal_status() thd Thread - + RETURN VALUE - 0 OK - !0 Error + FALSE OK + TRUE Error */ -int +bool Events::dump_internal_status(THD *thd) { - return Event_scheduler::dump_internal_status(thd); + DBUG_ENTER("Events::dump_internal_status"); + Protocol *protocol= thd->protocol; + List<Item> field_list; + + field_list.push_back(new Item_empty_string("Name", 30)); + field_list.push_back(new Item_empty_string("Value",20)); + if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | + Protocol::SEND_EOF)) + DBUG_RETURN(TRUE); + + if (scheduler->dump_internal_status(thd) || + event_queue->dump_internal_status(thd)) + DBUG_RETURN(TRUE); + + send_eof(thd); + DBUG_RETURN(FALSE); } /* - Inits Events mutexes + Starts execution of events by the scheduler SYNOPSIS - Events::init_mutexes() - thd Thread + Events::start_execution_of_events() + + RETURN VALUE + FALSE OK + TRUE Error */ -void -Events::init_mutexes() +bool +Events::start_execution_of_events() { - Event_scheduler::init_mutexes(); + DBUG_ENTER("Events::start_execution_of_events"); + DBUG_RETURN(scheduler->start()); } /* - Destroys Events mutexes + Stops execution of events by the scheduler. + Already running events will not be stopped. If the user needs + them stopped manual intervention is needed. SYNOPSIS - Events::destroy_mutexes() + Events::stop_execution_of_events() + + RETURN VALUE + FALSE OK + TRUE Error */ -void -Events::destroy_mutexes() +bool +Events::stop_execution_of_events() +{ + DBUG_ENTER("Events::stop_execution_of_events"); + DBUG_RETURN(scheduler->stop()); +} + + +/* + Checks whether the scheduler is running or not. + + SYNOPSIS + Events::is_started() + + RETURN VALUE + TRUE Yes + FALSE No +*/ + +bool +Events::is_started() { - Event_scheduler::destroy_mutexes(); + DBUG_ENTER("Events::is_started"); + DBUG_RETURN(scheduler->get_state() == Event_scheduler::RUNNING); } diff --git a/sql/events.h b/sql/events.h index 66cce6e7777..f14aed0fbd1 100644 --- a/sql/events.h +++ b/sql/events.h @@ -16,78 +16,112 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +class sp_name; +class Event_parse_data; +class Event_db_repository; +class Event_queue; +class Event_queue_element; +class Event_scheduler; + +/* Return codes */ +enum enum_events_error_code +{ + OP_OK= 0, + OP_NOT_RUNNING, + OP_CANT_KILL, + OP_CANT_INIT, + OP_DISABLED_EVENT, + OP_LOAD_ERROR, + OP_ALREADY_EXISTS +}; + +int +sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs); -class Event_timed; class Events { public: + friend class Event_queue_element; + /* + Quite NOT the best practice and will be removed once + Event_timed::drop() and Event_timed is fixed not do drop directly + or other scheme will be found. + */ + static ulong opt_event_scheduler; static TYPELIB opt_typelib; - enum enum_table_field - { - FIELD_DB = 0, - FIELD_NAME, - FIELD_BODY, - FIELD_DEFINER, - FIELD_EXECUTE_AT, - FIELD_INTERVAL_EXPR, - FIELD_TRANSIENT_INTERVAL, - FIELD_CREATED, - FIELD_MODIFIED, - FIELD_LAST_EXECUTED, - FIELD_STARTS, - FIELD_ENDS, - FIELD_STATUS, - FIELD_ON_COMPLETION, - FIELD_SQL_MODE, - FIELD_COMMENT, - FIELD_COUNT /* a cool trick to count the number of fields :) */ - }; + bool + init(); + + void + deinit(); - static int - create_event(THD *thd, Event_timed *et, uint create_options, + void + init_mutexes(); + + void + destroy_mutexes(); + + bool + start_execution_of_events(); + + bool + stop_execution_of_events(); + + bool + is_started(); + + static Events* + get_instance(); + + int + create_event(THD *thd, Event_parse_data *parse_data, bool if_exists, uint *rows_affected); - static int - update_event(THD *thd, Event_timed *et, sp_name *new_name, + int + update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to, uint *rows_affected); - static int - drop_event(THD *thd, Event_timed *et, bool drop_if_exists, - uint *rows_affected); + int + drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists, + uint *rows_affected, bool only_from_disk); - static int + int + drop_schema_events(THD *thd, char *db); + + int open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); - static int - show_create_event(THD *thd, sp_name *spn); + int + show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name); + /* Needed for both SHOW CREATE EVENT and INFORMATION_SCHEMA */ static int reconstruct_interval_expression(String *buf, interval_type interval, longlong expression); static int - drop_schema_events(THD *thd, char *db); + fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */); - static int + bool dump_internal_status(THD *thd); - - static int - init(); - - static void - shutdown(); - static void - init_mutexes(); - - static void - destroy_mutexes(); +private: + /* Singleton DP is used */ + Events(){} + ~Events(){} + /* Singleton instance */ + static Events singleton; + + Event_queue *event_queue; + Event_scheduler *scheduler; + Event_db_repository *db_repository; + + pthread_mutex_t LOCK_event_metadata; -private: /* Prevent use of these */ Events(const Events &); void operator=(Events &); diff --git a/sql/events_priv.h b/sql/events_priv.h deleted file mode 100644 index ed02cb7055b..00000000000 --- a/sql/events_priv.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef _EVENT_PRIV_H_ -#define _EVENT_PRIV_H_ -/* Copyright (C) 2004-2006 MySQL AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#define EVENT_EXEC_STARTED 0 -#define EVENT_EXEC_ALREADY_EXEC 1 -#define EVENT_EXEC_CANT_FORK 2 - -#define EVEX_DB_FIELD_LEN 64 -#define EVEX_NAME_FIELD_LEN 64 -#define EVEX_MAX_INTERVAL_VALUE 2147483647L - -class Event_timed; - -int -evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, - const LEX_STRING ev_name, - TABLE *table); - -int -db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists, - uint *rows_affected); -int -db_find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl, - MEM_ROOT *root); - -int -db_create_event(THD *thd, Event_timed *et, my_bool create_if_not, - uint *rows_affected); - -int -db_drop_events_from_table(THD *thd, LEX_STRING *db); - -int -sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs); - -/* Compares only the name part of the identifier */ -bool -event_timed_name_equal(Event_timed *et, LEX_STRING *name); - -/* Compares only the schema part of the identifier */ -bool -event_timed_db_equal(Event_timed *et, LEX_STRING *db); - -/* - Compares only the definer part of the identifier. Use during DROP USER - to drop user's events. (Still not implemented) -*/ -bool -event_timed_definer_equal(Event_timed *et, LEX_STRING *definer); - -/* Compares the whole identifier*/ -bool -event_timed_identifier_equal(Event_timed *a, Event_timed *b); - - -bool -change_security_context(THD *thd, LEX_STRING user, LEX_STRING host, - LEX_STRING db, Security_context *s_ctx, - Security_context **backup); - -void -restore_security_context(THD *thd, Security_context *backup); - -#endif /* _EVENT_PRIV_H_ */ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index b4558dec35e..58697202461 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -867,7 +867,7 @@ static void close_connections(void) DBUG_PRINT("quit",("Informing thread %ld that it's time to die", tmp->thread_id)); /* We skip slave threads & scheduler on this first loop through. */ - if (tmp->slave_thread || tmp->system_thread == SYSTEM_THREAD_EVENT_SCHEDULER) + if (tmp->slave_thread) continue; tmp->killed= THD::KILL_CONNECTION; @@ -886,7 +886,7 @@ static void close_connections(void) } (void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list - Events::shutdown(); + Events::get_instance()->deinit(); end_slave(); if (thread_count) @@ -1324,7 +1324,7 @@ static void clean_up_mutexes() (void) pthread_mutex_destroy(&LOCK_bytes_sent); (void) pthread_mutex_destroy(&LOCK_bytes_received); (void) pthread_mutex_destroy(&LOCK_user_conn); - Events::destroy_mutexes(); + Events::get_instance()->destroy_mutexes(); #ifdef HAVE_OPENSSL (void) pthread_mutex_destroy(&LOCK_des_key_file); #ifndef HAVE_YASSL @@ -2905,7 +2905,7 @@ static int init_thread_environment() (void) pthread_mutex_init(&LOCK_server_started, MY_MUTEX_INIT_FAST); (void) pthread_cond_init(&COND_server_started,NULL); sp_cache_init(); - Events::init_mutexes(); + Events::get_instance()->init_mutexes(); /* Parameter for threads created for connections */ (void) pthread_attr_init(&connection_attrib); (void) pthread_attr_setdetachstate(&connection_attrib, @@ -3696,7 +3696,8 @@ we force server id to 2, but this MySQL server will not act as a slave."); if (!opt_noacl) { - Events::init(); + if (Events::get_instance()->init()) + unireg_abort(1); } #if defined(__NT__) || defined(HAVE_SMEM) handle_connections_methods(); diff --git a/sql/set_var.cc b/sql/set_var.cc index 75a419019b8..5c7220da54d 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -57,7 +57,7 @@ #include <myisam.h> #include <my_dir.h> -#include "event_scheduler.h" +#include "events.h" /* WITH_BERKELEY_STORAGE_ENGINE */ extern bool berkeley_shared_data; @@ -3943,30 +3943,32 @@ byte *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b) bool sys_var_event_scheduler::update(THD *thd, set_var *var) { - enum Event_scheduler::enum_error_code res; - Event_scheduler *scheduler= Event_scheduler::get_instance(); + int res; /* here start the thread if not running. */ DBUG_ENTER("sys_var_event_scheduler::update"); - - DBUG_PRINT("new_value", ("%lu", (bool)var->save_result.ulong_value)); - if (!scheduler->initialized()) + if (Events::opt_event_scheduler == 0) { my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--event-scheduler=0"); - DBUG_RETURN(true); + DBUG_RETURN(TRUE); } + DBUG_PRINT("new_value", ("%lu", (bool)var->save_result.ulong_value)); + if (var->save_result.ulonglong_value < 1 || var->save_result.ulonglong_value > 2) { char buf[64]; my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "event_scheduler", llstr(var->save_result.ulonglong_value, buf)); - DBUG_RETURN(true); + DBUG_RETURN(TRUE); } - if ((res= scheduler->suspend_or_resume(var->save_result.ulonglong_value == 1? - Event_scheduler::RESUME : - Event_scheduler::SUSPEND))) - my_error(ER_EVENT_SET_VAR_ERROR, MYF(0), (uint) res); + if (var->save_result.ulonglong_value == 1) + res= Events::get_instance()->start_execution_of_events(); + else + res= Events::get_instance()->stop_execution_of_events(); + + if (res) + my_error(ER_EVENT_SET_VAR_ERROR, MYF(0)); DBUG_RETURN((bool) res); } @@ -3974,11 +3976,9 @@ sys_var_event_scheduler::update(THD *thd, set_var *var) byte *sys_var_event_scheduler::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { - Event_scheduler *scheduler= Event_scheduler::get_instance(); - - if (!scheduler->initialized()) + if (Events::opt_event_scheduler == 0) thd->sys_var_tmp.long_value= 0; - else if (scheduler->get_state() == Event_scheduler::RUNNING) + else if (Events::get_instance()->is_started()) thd->sys_var_tmp.long_value= 1; else thd->sys_var_tmp.long_value= 2; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index b88ff13cd7a..afc44845221 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -647,10 +647,12 @@ sp_head::create(THD *thd) sp_head::~sp_head() { + DBUG_ENTER("sp_head::~sp_head"); destroy(); delete m_next_cached_sp; if (m_thd) restore_thd_mem_root(m_thd); + DBUG_VOID_RETURN; } void diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 827a12e4ebd..0e8c5fc614e 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2084,6 +2084,7 @@ bool Security_context::set_user(char *user_arg) return user == 0; } + /**************************************************************************** Handling of open and locked tables states. diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 372a350566f..9bc11a2ddcd 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -949,7 +949,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) exit: (void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */ - error= Events::drop_schema_events(thd, db); + error= Events::get_instance()->drop_schema_events(thd, db); /* If this database was the client's selected database, we silently change the client's selected database to nothing (to have an empty diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index bf0da9d46ef..544dfd0814f 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -169,14 +169,15 @@ void lex_start(THD *thd, const uchar *buf, uint length) lex->sql_command= SQLCOM_END; lex->duplicates= DUP_ERROR; lex->ignore= 0; + lex->spname= NULL; lex->sphead= NULL; lex->spcont= NULL; lex->proc_list.first= 0; - lex->escape_used= lex->et_compile_phase= FALSE; + lex->escape_used= FALSE; lex->reset_query_tables_list(FALSE); lex->name= 0; - lex->et= NULL; + lex->event_parse_data= NULL; lex->nest_level=0 ; lex->allow_sum_func= 0; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 9f6df9861e2..53a6d9814a8 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -27,7 +27,7 @@ class sp_instr; class sp_pcontext; class st_alter_tablespace; class partition_info; -class Event_timed; +class Event_parse_data; #ifdef MYSQL_SERVER /* @@ -1035,8 +1035,7 @@ typedef struct st_lex : public Query_tables_list st_sp_chistics sp_chistics; - Event_timed *et; - bool et_compile_phase; + Event_parse_data *event_parse_data; bool only_view; /* used for SHOW CREATE TABLE/VIEW */ /* diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 9217c147143..a13851e93c1 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -27,7 +27,7 @@ #include "sp.h" #include "sp_cache.h" #include "events.h" -#include "event_timed.h" +#include "event_data_objects.h" #ifdef HAVE_OPENSSL /* @@ -3896,73 +3896,44 @@ end_with_restore_list: } 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 || - (lex->sql_command == SQLCOM_ALTER_EVENT && lex->spname && - !lex->spname->m_db.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)) || - (lex->sql_command == SQLCOM_ALTER_EVENT && lex->spname && - (check_access(thd, EVENT_ACL, lex->spname->m_db.str, 0, 0, 0, - is_schema_db(lex->spname->m_db.str))))) - break; - - if (end_active_trans(thd)) - { - res= -1; - break; - } - - switch (lex->sql_command) { - case SQLCOM_CREATE_EVENT: - res= Events::create_event(thd, lex->et, - (uint) lex->create_info.options, - &rows_affected); - break; - case SQLCOM_ALTER_EVENT: - res= Events::update_event(thd, lex->et, lex->spname, - &rows_affected); - break; - case SQLCOM_DROP_EVENT: - res= Events::drop_event(thd, lex->et, lex->drop_if_exists, - &rows_affected); - default:; - } - DBUG_PRINT("info", ("CREATE/ALTER/DROP returned error code=%d af_rows=%d", - res, rows_affected)); - if (!res) - send_ok(thd, rows_affected); + uint affected= 1; + DBUG_ASSERT(lex->event_parse_data); + switch (lex->sql_command) { + case SQLCOM_CREATE_EVENT: + res= Events::get_instance()-> + create_event(thd, lex->event_parse_data, + lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS, + &affected); + break; + case SQLCOM_ALTER_EVENT: + res= Events::get_instance()-> + update_event(thd, lex->event_parse_data, lex->spname, &affected); + break; + default:; + } + DBUG_PRINT("info",("DDL error code=%d affected=%d", res, affected)); + if (!res) + send_ok(thd, affected); - /* lex->unit.cleanup() is called outside, no need to call it here */ - } while (0); + /* Don't do it, if we are inside a SP */ if (!thd->spcont) { - lex->et->free_sphead_on_delete= true; - lex->et->free_sp(); - lex->et->deinit_mutexes(); + delete lex->sphead; + lex->sphead= NULL; } - + + /* lex->unit.cleanup() is called outside, no need to call it here */ break; } + case SQLCOM_DROP_EVENT: case SQLCOM_SHOW_CREATE_EVENT: { DBUG_ASSERT(lex->spname); - DBUG_ASSERT(lex->et); if (! lex->spname->m_db.str) { my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); - res= true; - break; + goto error; } if (check_access(thd, EVENT_ACL, lex->spname->m_db.str, 0, 0, 0, is_schema_db(lex->spname->m_db.str))) @@ -3971,15 +3942,30 @@ end_with_restore_list: if (lex->spname->m_name.length > NAME_LEN) { my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str); + /* this jumps to the end of the function and skips own messaging */ goto error; } - res= Events::show_create_event(thd, lex->spname); + + if (lex->sql_command == SQLCOM_SHOW_CREATE_EVENT) + res= Events::get_instance()->show_create_event(thd, lex->spname->m_db, + lex->spname->m_name); + else + { + uint affected= 1; + if (!(res= Events::get_instance()->drop_event(thd, + lex->spname->m_db, + lex->spname->m_name, + lex->drop_if_exists, + &affected, + FALSE))) + send_ok(thd, affected); + } break; } #ifndef DBUG_OFF case SQLCOM_SHOW_SCHEDULER_STATUS: { - res= Events::dump_internal_status(thd); + res= Events::get_instance()->dump_internal_status(thd); break; } #endif @@ -6059,14 +6045,6 @@ void mysql_parse(THD *thd, char *inBuf, uint length) { delete lex->sphead; lex->sphead= NULL; - if (lex->et) - { - lex->et->free_sphead_on_delete= true; - /* alloced on thd->mem_root so no real memory free but dtor call */ - lex->et->free_sp(); - lex->et->deinit_mutexes(); - lex->et= NULL; - } } else { @@ -6103,13 +6081,6 @@ void mysql_parse(THD *thd, char *inBuf, uint length) delete lex->sphead; lex->sphead= NULL; } - if (lex->et) - { - lex->et->free_sphead_on_delete= true; - lex->et->free_sp(); - lex->et->deinit_mutexes(); - lex->et= NULL; - } } thd->proc_info="freeing items"; thd->end_statement(); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index edbec84e37b..72a08c7ba15 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -27,7 +27,7 @@ #include "authors.h" #include "contributors.h" #include "events.h" -#include "event_timed.h" +#include "event_data_objects.h" #include <my_dir.h> #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -4232,7 +4232,7 @@ extern LEX_STRING interval_type_to_name[]; 1 Error */ -static int +int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) { const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS; @@ -4243,7 +4243,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) restore_record(sch_table, s->default_values); - if (et.load_from_row(thd->mem_root, event_table)) + if (et.load_from_row(event_table)) { my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0)); DBUG_RETURN(1); @@ -4367,183 +4367,6 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) } -/* - Performs an index scan of event_table (mysql.event) and fills schema_table. - - Synopsis - events_table_index_read_for_db() - thd Thread - schema_table The I_S.EVENTS table - event_table The event table to use for loading (mysql.event) - - Returns - 0 OK - 1 Error -*/ - -static -int events_table_index_read_for_db(THD *thd, TABLE *schema_table, - TABLE *event_table) -{ - int ret=0; - CHARSET_INFO *scs= system_charset_info; - KEY *key_info; - uint key_len; - byte *key_buf= NULL; - LINT_INIT(key_buf); - - DBUG_ENTER("schema_events_do_index_scan"); - - DBUG_PRINT("info", ("Using prefix scanning on PK")); - event_table->file->ha_index_init(0, 1); - event_table->field[Events::FIELD_DB]-> - store(thd->lex->select_lex.db, strlen(thd->lex->select_lex.db), scs); - key_info= event_table->key_info; - key_len= key_info->key_part[0].store_length; - - if (!(key_buf= (byte *)alloc_root(thd->mem_root, key_len))) - { - ret= 1; - /* don't send error, it would be done by sql_alloc_error_handler() */ - } - else - { - key_copy(key_buf, event_table->record[0], key_info, key_len); - if (!(ret= event_table->file->index_read(event_table->record[0], key_buf, - key_len, HA_READ_PREFIX))) - { - DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret)); - do - { - ret= copy_event_to_schema_table(thd, schema_table, event_table); - if (ret == 0) - ret= event_table->file->index_next_same(event_table->record[0], - key_buf, key_len); - } while (ret == 0); - } - DBUG_PRINT("info", ("Scan finished. ret=%d", ret)); - } - event_table->file->ha_index_end(); - /* ret is guaranteed to be != 0 */ - if (ret == HA_ERR_END_OF_FILE || ret == HA_ERR_KEY_NOT_FOUND) - DBUG_RETURN(0); - DBUG_RETURN(1); -} - - -/* - Performs a table scan of event_table (mysql.event) and fills schema_table. - - Synopsis - events_table_scan_all() - thd Thread - schema_table The I_S.EVENTS in memory table - event_table The event table to use for loading. - - Returns - 0 OK - 1 Error -*/ - -static -int events_table_scan_all(THD *thd, TABLE *schema_table, - TABLE *event_table) -{ - int ret; - READ_RECORD read_record_info; - - DBUG_ENTER("schema_events_do_table_scan"); - init_read_record(&read_record_info, thd, event_table, NULL, 1, 0); - - /* - rr_sequential, in read_record(), returns 137==HA_ERR_END_OF_FILE, - but rr_handle_error returns -1 for that reason. Thus, read_record() - returns -1 eventually. - */ - do - { - ret= read_record_info.read_record(&read_record_info); - if (ret == 0) - ret= copy_event_to_schema_table(thd, schema_table, event_table); - } - while (ret == 0); - - DBUG_PRINT("info", ("Scan finished. ret=%d", ret)); - end_read_record(&read_record_info); - - /* ret is guaranteed to be != 0 */ - DBUG_RETURN(ret == -1? 0:1); -} - - -/* - Fills I_S.EVENTS with data loaded from mysql.event. Also used by - SHOW EVENTS - - Synopsis - fill_schema_events() - thd Thread - tables The schema table - cond Unused - - Returns - 0 OK - 1 Error -*/ - -int fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) -{ - TABLE *schema_table= tables->table; - TABLE *event_table= NULL; - Open_tables_state backup; - int ret= 0; - - DBUG_ENTER("fill_schema_events"); - /* - If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to - be NULL. Let's do an assert anyway. - */ - if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS) - { - DBUG_ASSERT(thd->lex->select_lex.db); - if (check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0, - is_schema_db(thd->lex->select_lex.db))) - DBUG_RETURN(1); - } - - DBUG_PRINT("info",("db=%s", thd->lex->select_lex.db? - thd->lex->select_lex.db:"(null)")); - - thd->reset_n_backup_open_tables_state(&backup); - if (Events::open_event_table(thd, TL_READ, &event_table)) - { - sql_print_error("Table mysql.event is damaged."); - thd->restore_backup_open_tables_state(&backup); - DBUG_RETURN(1); - } - - /* - 1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order - thus we won't order it. OTOH, SHOW EVENTS will be - ordered. - 2. SHOW EVENTS => PRIMARY KEY with prefix scanning on (db) - Reasoning: Events are per schema, therefore a scan over an index - will save use from doing a table scan and comparing - every single row's `db` with the schema which we show. - */ - if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS) - ret= events_table_index_read_for_db(thd, schema_table, event_table); - else - ret= events_table_scan_all(thd, schema_table, event_table); - - close_thread_tables(thd); - thd->restore_backup_open_tables_state(&backup); - - DBUG_PRINT("info", ("Return code=%d", ret)); - DBUG_RETURN(ret); -} - - int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond) { DBUG_ENTER("fill_open_tables"); @@ -5646,7 +5469,7 @@ ST_SCHEMA_TABLE schema_tables[]= {"ENGINES", engines_fields_info, create_schema_table, fill_schema_engines, make_old_format, 0, -1, -1, 0}, {"EVENTS", events_fields_info, create_schema_table, - fill_schema_events, make_old_format, 0, -1, -1, 0}, + Events::fill_schema_events, make_old_format, 0, -1, -1, 0}, {"FILES", files_fields_info, create_schema_table, fill_schema_files, 0, 0, -1, -1, 0}, {"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table, diff --git a/sql/sql_show.h b/sql/sql_show.h index 6fce5e94ca3..681d1232b39 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -14,4 +14,6 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, HA_CREATE_INFO *create_info_arg); int view_store_create_info(THD *thd, TABLE_LIST *table, String *buff); +int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table); + #endif /* SQL_SHOW_H */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ba4c652efb7..25d804f18b5 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -38,7 +38,7 @@ #include "sp_pcontext.h" #include "sp_rcontext.h" #include "sp.h" -#include "event_timed.h" +#include "event_data_objects.h" #include <myisam.h> #include <myisammrg.h> @@ -880,7 +880,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa load_data opt_field_or_var_spec fields_or_vars opt_load_data_set_spec definer view_replace_or_algorithm view_replace view_algorithm_opt - view_algorithm view_or_trigger_or_sp view_or_trigger_or_sp_tail + view_algorithm view_or_trigger_or_sp_or_event + view_or_trigger_or_sp_or_event_tail view_suid view_tail view_list_opt view_list view_select view_check_option trigger_tail sp_tail install uninstall partition_entry binlog_base64_event @@ -1257,29 +1258,41 @@ create: lex->name=$4.str; lex->create_info.options=$3; } - | CREATE EVENT_SYM opt_if_not_exists sp_name + | CREATE + { + Lex->create_view_mode= VIEW_CREATE_NEW; + Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; + Lex->create_view_suid= TRUE; + } + view_or_trigger_or_sp_or_event + {} + | CREATE USER clear_privileges grant_list + { + Lex->sql_command = SQLCOM_CREATE_USER; + } + | CREATE LOGFILE_SYM GROUP logfile_group_info + { + Lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP; + } + | CREATE TABLESPACE tablespace_info + { + Lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE; + } + ; + + +event_tail: + EVENT_SYM opt_if_not_exists sp_name /* BE CAREFUL when you add a new rule to update the block where YYTHD->client_capabilities is set back to original value */ { - LEX *lex=Lex; + Lex->create_info.options= $2; - 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(YYTHD->mem_root) Event_timed())) // implicitly calls Event_timed::init() + if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) YYABORT; + Lex->event_parse_data->identifier= $3; /* We have to turn of CLIENT_MULTI_QUERIES while parsing a @@ -1289,11 +1302,8 @@ create: $<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); - if (!lex->et_compile_phase) - { - lex->et->init_name(YYTHD, $4); - lex->et->init_definer(YYTHD); - } + /* We need that for disallowing subqueries */ + Lex->sql_command= SQLCOM_CREATE_EVENT; } ON SCHEDULE_SYM ev_schedule_time opt_ev_on_completion @@ -1303,13 +1313,12 @@ create: { /* Restore flag if it was cleared above - $1 - CREATE - $2 - EVENT_SYM - $3 - opt_if_not_exists - $4 - sp_name - $5 - the block above + $1 - EVENT_SYM + $2 - opt_if_not_exists + $3 - sp_name + $4 - the block above */ - YYTHD->client_capabilities |= $<ulong_num>5; + YYTHD->client_capabilities |= $<ulong_num>4; /* sql_command is set here because some rules in ev_sql_stmt @@ -1317,146 +1326,48 @@ create: */ Lex->sql_command= SQLCOM_CREATE_EVENT; } - | CREATE - { - Lex->create_view_mode= VIEW_CREATE_NEW; - Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; - Lex->create_view_suid= TRUE; - } - view_or_trigger_or_sp - {} - | CREATE USER clear_privileges grant_list - { - Lex->sql_command = SQLCOM_CREATE_USER; - } - | CREATE LOGFILE_SYM GROUP logfile_group_info - { - LEX *lex= Lex; - lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP; - } - | CREATE TABLESPACE tablespace_info - { - LEX *lex= Lex; - lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE; - } - ; ev_schedule_time: EVERY_SYM expr interval { - LEX *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_OR_TOO_BIG, MYF(0)); - case EVEX_MICROSECOND_UNSUP: - my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND"); - YYABORT; - break; - } - } + Lex->event_parse_data->item_expression= $2; + Lex->event_parse_data->interval= $3; } 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 ER_WRONG_VALUE: - { - char buff[120]; - String str(buff,(uint32) sizeof(buff), system_charset_info); - String *str2= $2->val_str(&str); - my_error(ER_WRONG_VALUE, MYF(0), "AT", - str2? str2->c_ptr_safe():"NULL"); - YYABORT; - break; - } - case EVEX_BAD_PARAMS: - my_error(ER_EVENT_EXEC_TIME_IN_THE_PAST, MYF(0)); - YYABORT; - break; - } - } + Lex->event_parse_data->item_execute_at= $2; } ; opt_ev_status: /* empty */ { $$= 0; } | ENABLE_SYM { - LEX *lex=Lex; - if (!lex->et_compile_phase) - lex->et->status= Event_timed::ENABLED; + Lex->event_parse_data->status= Event_parse_data::ENABLED; $$= 1; } | DISABLE_SYM { - LEX *lex=Lex; - - if (!lex->et_compile_phase) - lex->et->status= Event_timed::DISABLED; + Lex->event_parse_data->status= Event_parse_data::DISABLED; $$= 1; } ; ev_starts: /* empty */ { - Lex->et->init_starts(YYTHD, new Item_func_now_local()); + Lex->event_parse_data->item_starts= new Item_func_now_local(); } | STARTS_SYM expr { - LEX *lex= Lex; - if (!lex->et_compile_phase) - { - - switch (lex->et->init_starts(YYTHD, $2)) { - case EVEX_PARSE_ERROR: - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; - break; - case EVEX_BAD_PARAMS: - { - char buff[20]; - String str(buff,(uint32) sizeof(buff), system_charset_info); - String *str2= $2->val_str(&str); - my_error(ER_WRONG_VALUE, MYF(0), "STARTS", - str2 ? str2->c_ptr_safe() : NULL); - YYABORT; - break; - } - } - } + Lex->event_parse_data->item_starts= $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; - } - } + Lex->event_parse_data->item_ends= $2; } ; @@ -1467,16 +1378,14 @@ opt_ev_on_completion: /* empty */ { $$= 0; } ev_on_completion: ON COMPLETION_SYM PRESERVE_SYM { - LEX *lex=Lex; - if (!lex->et_compile_phase) - lex->et->on_completion= Event_timed::ON_COMPLETION_PRESERVE; + Lex->event_parse_data->on_completion= + Event_parse_data::ON_COMPLETION_PRESERVE; $$= 1; } | ON COMPLETION_SYM NOT_SYM PRESERVE_SYM { - LEX *lex=Lex; - if (!lex->et_compile_phase) - lex->et->on_completion= Event_timed::ON_COMPLETION_DROP; + Lex->event_parse_data->on_completion= + Event_parse_data::ON_COMPLETION_DROP; $$= 1; } ; @@ -1484,64 +1393,65 @@ ev_on_completion: opt_ev_comment: /* empty */ { $$= 0; } | COMMENT_SYM TEXT_STRING_sys { - LEX *lex= Lex; - if (!lex->et_compile_phase) - { - lex->comment= $2; - lex->et->init_comment(YYTHD, &$2); - } - $$= 1; + Lex->comment= Lex->event_parse_data->comment= $2; } ; ev_sql_stmt: { LEX *lex= Lex; - sp_head *sp; - - $<sphead>$= lex->sphead; - if (!lex->sphead) + /* + This stops the following : + - CREATE EVENT ... DO CREATE EVENT ...; + - ALTER EVENT ... DO CREATE EVENT ...; + - CREATE EVENT ... DO ALTER EVENT DO ....; + - CREATE PROCEDURE ... BEGIN CREATE EVENT ... END| + This allows: + - CREATE EVENT ... DO DROP EVENT yyy; + - CREATE EVENT ... DO ALTER EVENT yyy; + (the nested ALTER EVENT can have anything but DO clause) + - ALTER EVENT ... DO ALTER EVENT yyy; + (the nested ALTER EVENT can have anything but DO clause) + - ALTER EVENT ... DO DROP EVENT yyy; + - CREATE PROCEDURE ... BEGIN ALTER EVENT ... END| + (the nested ALTER EVENT can have anything but DO clause) + - CREATE PROCEDURE ... BEGIN DROP EVENT ... END| + */ + if (lex->sphead) { - if (!(sp= new sp_head())) - YYABORT; - - sp->reset_thd_mem_root(YYTHD); - sp->init(lex); + my_error(ER_EVENT_RECURSIVITY_FORBIDDEN, MYF(0)); + YYABORT; + } + + if (!(lex->sphead= new sp_head())) + YYABORT; - sp->m_type= TYPE_ENUM_PROCEDURE; + lex->sphead->reset_thd_mem_root(YYTHD); + lex->sphead->init(lex); - lex->sphead= sp; + lex->sphead->m_type= TYPE_ENUM_PROCEDURE; - bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); - lex->sphead->m_chistics= &lex->sp_chistics; + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lex->ptr; - } + lex->sphead->m_body_begin= lex->ptr; + + Lex->event_parse_data->body_begin= lex->ptr; - if (!lex->et_compile_phase) - lex->et->body_begin= lex->ptr; } 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); - sp->restore_thd_mem_root(YYTHD); + // return back to the original memory root ASAP + lex->sphead->init_strings(YYTHD, lex, + Lex->event_parse_data->identifier); + lex->sphead->restore_thd_mem_root(YYTHD); - lex->sp_chistics.suid= SP_IS_SUID;//always the definer! + lex->sp_chistics.suid= SP_IS_SUID;//always the definer! - lex->et->sphead= lex->sphead; - lex->sphead= NULL; - } - if (!lex->et_compile_phase) - { - lex->et->init_body(YYTHD); - } + Lex->event_parse_data->init_body(YYTHD); } ; @@ -1613,17 +1523,6 @@ create_function_tail: RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys { LEX *lex=Lex; - if (lex->definer != NULL) - { - /* - DEFINER is a concept meaningful when interpreting SQL code. - UDF functions are compiled. - Using DEFINER with UDF has therefore no semantic, - and is considered a parsing error. - */ - my_error(ER_WRONG_USAGE, MYF(0), "SONAME", "DEFINER"); - YYABORT; - } lex->sql_command = SQLCOM_CREATE_FUNCTION; lex->udf.name = lex->spname->m_name; lex->udf.returns=(Item_result) $2; @@ -4767,37 +4666,29 @@ alter: 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. - */ - my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT"); - YYABORT; - } - lex->spname= 0;//defensive programming + /* + It is safe to use Lex->spname because + ALTER EVENT xxx RENATE TO yyy DO ALTER EVENT RENAME TO + is not allowed. Lex->spname is used in the case of RENAME TO + If it had to be supported spname had to be added to + Event_parse_data. + */ + Lex->spname= NULL; - if (!(et= new (YYTHD->mem_root) Event_timed()))// implicitly calls Event_timed::init() + if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) YYABORT; - lex->et = et; - - if (!lex->et_compile_phase) - { - et->init_definer(YYTHD); - et->init_name(YYTHD, $3); - } + Lex->event_parse_data->identifier= $3; /* - We have to turn of CLIENT_MULTI_QUERIES while parsing a - stored procedure, otherwise yylex will chop it into pieces - at each ';'. + We have to turn of CLIENT_MULTI_QUERIES while parsing a + stored procedure, otherwise yylex will chop it into pieces + at each ';'. */ $<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; + + /* we need that for disallowing subqueries */ + Lex->sql_command= SQLCOM_ALTER_EVENT; } ev_alter_on_schedule_completion opt_ev_rename_to @@ -4813,15 +4704,15 @@ alter: */ YYTHD->client_capabilities |= $<ulong_num>4; - /* - sql_command is set here because some rules in ev_sql_stmt - can overwrite it - */ if (!($5 || $6 || $7 || $8 || $9)) { yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } + /* + sql_command is set here because some rules in ev_sql_stmt + can overwrite it + */ Lex->sql_command= SQLCOM_ALTER_EVENT; } | ALTER TABLESPACE alter_tablespace_info @@ -4857,7 +4748,7 @@ opt_ev_rename_to: /* empty */ { $$= 0;} { LEX *lex=Lex; lex->spname= $3; //use lex's spname to hold the new name - //the original name is in the Event_timed object + //the original name is in the Event_parse_data object $$= 1; } ; @@ -7145,8 +7036,10 @@ select_derived2: { LEX *lex= Lex; lex->derived_tables|= DERIVED_SUBQUERY; - if (lex->sql_command == (int)SQLCOM_HA_READ || - lex->sql_command == (int)SQLCOM_KILL) + if (lex->sql_command == SQLCOM_HA_READ || + lex->sql_command == SQLCOM_KILL || + lex->sql_command == SQLCOM_CREATE_EVENT || + lex->sql_command == SQLCOM_ALTER_EVENT) { yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; @@ -7740,29 +7633,9 @@ drop: } | DROP EVENT_SYM if_exists sp_name { - LEX *lex=Lex; - - if (lex->et) - { - /* - Recursive events are not possible because recursive SPs - are not also possible. lex->sp_head is not stacked. - */ - my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT"); - YYABORT; - } - - if (!(lex->et= new (YYTHD->mem_root) Event_timed())) - YYABORT; - - if (!lex->et_compile_phase) - { - lex->et->init_name(YYTHD, $4); - lex->et->init_definer(YYTHD); - } - - lex->sql_command = SQLCOM_DROP_EVENT; - lex->drop_if_exists= $3; + Lex->drop_if_exists= $3; + Lex->spname= $4; + Lex->sql_command = SQLCOM_DROP_EVENT; } | DROP TRIGGER_SYM sp_name { @@ -8479,12 +8352,8 @@ show_param: } | CREATE EVENT_SYM sp_name { - Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT; Lex->spname= $3; - Lex->et= new (YYTHD->mem_root) Event_timed(); - if (!Lex->et) - YYABORT; - Lex->et->init_definer(YYTHD); + Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT; } ; @@ -10789,8 +10658,10 @@ subselect_start: '(' SELECT_SYM { LEX *lex=Lex; - if (lex->sql_command == (int)SQLCOM_HA_READ || - lex->sql_command == (int)SQLCOM_KILL) + if (lex->sql_command == SQLCOM_HA_READ || + lex->sql_command == SQLCOM_KILL || + lex->sql_command == SQLCOM_CREATE_EVENT || + lex->sql_command == SQLCOM_ALTER_EVENT) { yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; @@ -10814,20 +10685,22 @@ subselect_end: **************************************************************************/ -view_or_trigger_or_sp: - definer view_or_trigger_or_sp_tail +view_or_trigger_or_sp_or_event: + definer view_or_trigger_or_sp_or_event_tail {} | view_replace_or_algorithm definer view_tail {} ; -view_or_trigger_or_sp_tail: +view_or_trigger_or_sp_or_event_tail: view_tail {} | trigger_tail {} | sp_tail {} + | event_tail + {} ; /************************************************************************** |