Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'MYSQL-15' into twitter-dev

  • Loading branch information...
commit 8efbad7c19e89b81a0f6b752d8088a27c6944c8e 2 parents d47f9d1 + 7fc0322
Davi Arnaut authored
Showing with 2,356 additions and 40 deletions.
  1. +4 −0 config.h.cmake
  2. +17 −0 configure.cmake
  3. +14 −3 include/my_global.h
  4. +69 −0 include/my_timer.h
  5. +7 −0 include/mysql/plugin.h
  6. +1 −0  include/mysql/plugin_audit.h.pp
  7. +1 −0  include/mysql/plugin_auth.h.pp
  8. +1 −0  include/mysql/plugin_ftparser.h.pp
  9. +3 −0  libmysqld/CMakeLists.txt
  10. +4 −0 mysql-test/include/have_statement_timeout.inc
  11. +3 −0  mysql-test/r/grant.result
  12. +2 −0  mysql-test/r/have_statement_timeout.require
  13. +157 −0 mysql-test/r/max_statement_time.result
  14. +3 −3 mysql-test/r/ps.result
  15. +1 −0  mysql-test/r/system_mysql_db.result
  16. +2 −0  mysql-test/suite/funcs_1/r/is_columns_mysql.result
  17. +33 −0 mysql-test/suite/funcs_1/r/is_user_privileges.result
  18. 0  mysql-test/suite/randgen/r/max_statement_time.result
  19. +16 −0 mysql-test/suite/randgen/t/max_statement_time.test
  20. +53 −0 mysql-test/suite/sys_vars/r/have_statement_timeout_basic.result
  21. +91 −0 mysql-test/suite/sys_vars/r/max_statement_time_basic.result
  22. +66 −0 mysql-test/suite/sys_vars/r/max_statement_time_func.result
  23. +55 −0 mysql-test/suite/sys_vars/t/have_statement_timeout_basic.test
  24. +78 −0 mysql-test/suite/sys_vars/t/max_statement_time_basic.test
  25. +75 −0 mysql-test/suite/sys_vars/t/max_statement_time_func.test
  26. +220 −0 mysql-test/t/max_statement_time.test
  27. +8 −0 mysys/CMakeLists.txt
  28. +219 −0 mysys/kqueue_timers.c
  29. +256 −0 mysys/posix_timers.c
  30. +1 −1  scripts/mysql_system_tables.sql
  31. +4 −4 scripts/mysql_system_tables_data.sql
  32. +4 −0 scripts/mysql_system_tables_fix.sql
  33. +4 −0 sql/CMakeLists.txt
  34. +18 −0 sql/handler.cc
  35. +4 −0 sql/handler.h
  36. +1 −0  sql/lex.h
  37. +22 −0 sql/mysqld.cc
  38. +1 −0  sql/set_var.h
  39. +4 −1 sql/share/errmsg-utf8.txt
  40. +3 −0  sql/signal_handler.cc
  41. +40 −2 sql/sql_acl.cc
  42. +42 −16 sql/sql_class.cc
  43. +11 −0 sql/sql_class.h
  44. +3 −0  sql/sql_lex.cc
  45. +3 −0  sql/sql_lex.h
  46. +92 −1 sql/sql_parse.cc
  47. +233 −0 sql/sql_timer.cc
  48. +50 −0 sql/sql_timer.h
  49. +37 −5 sql/sql_yacc.yy
  50. +3 −1 sql/structs.h
  51. +10 −0 sql/sys_vars.cc
  52. +34 −3 storage/innobase/handler/ha_innodb.cc
  53. +4 −0 unittest/mysys/CMakeLists.txt
  54. +269 −0 unittest/mysys/my_timer-t.c
View
4 config.h.cmake
@@ -308,6 +308,10 @@
#cmakedefine STRUCT_DIRENT_HAS_D_NAMLEN 1
#cmakedefine SPRINTF_RETURNS_INT 1
+#cmakedefine HAVE_POSIX_TIMERS 1
+#cmakedefine HAVE_KQUEUE_TIMERS 1
+#cmakedefine HAVE_MY_TIMER 1
+
#define USE_MB 1
#define USE_MB_IDENT 1
View
17 configure.cmake
@@ -147,6 +147,7 @@ IF(UNIX)
IF(NOT LIBRT)
MY_SEARCH_LIBS(clock_gettime rt LIBRT)
ENDIF()
+ MY_SEARCH_LIBS(timer_create rt LIBRT)
FIND_PACKAGE(Threads)
SET(CMAKE_REQUIRED_LIBRARIES
@@ -454,6 +455,9 @@ CHECK_FUNCTION_EXISTS (valloc HAVE_VALLOC)
CHECK_FUNCTION_EXISTS (memalign HAVE_MEMALIGN)
CHECK_FUNCTION_EXISTS (chown HAVE_CHOWN)
CHECK_FUNCTION_EXISTS (nl_langinfo HAVE_NL_LANGINFO)
+CHECK_FUNCTION_EXISTS (timer_create HAVE_TIMER_CREATE)
+CHECK_FUNCTION_EXISTS (timer_settime HAVE_TIMER_SETTIME)
+CHECK_FUNCTION_EXISTS (kqueue HAVE_KQUEUE)
#--------------------------------------------------------------------
# Support for WL#2373 (Use cycle counter for timing)
@@ -495,6 +499,8 @@ CHECK_SYMBOL_EXISTS(FIONREAD "sys/ioctl.h" FIONREAD_IN_SYS_IOCTL)
CHECK_SYMBOL_EXISTS(TIOCSTAT "sys/ioctl.h" TIOCSTAT_IN_SYS_IOCTL)
CHECK_SYMBOL_EXISTS(FIONREAD "sys/filio.h" FIONREAD_IN_SYS_FILIO)
CHECK_SYMBOL_EXISTS(gettimeofday "sys/time.h" HAVE_GETTIMEOFDAY)
+CHECK_SYMBOL_EXISTS(SIGEV_THREAD_ID "signal.h;time.h" HAVE_SIGEV_THREAD_ID)
+CHECK_SYMBOL_EXISTS(EVFILT_TIMER "sys/types.h;sys/event.h;sys/time.h" HAVE_EVFILT_TIMER)
CHECK_SYMBOL_EXISTS(finite "math.h" HAVE_FINITE_IN_MATH_H)
IF(HAVE_FINITE_IN_MATH_H)
@@ -514,7 +520,18 @@ int main() {
return 0;
}" HAVE_ISINF)
+# Check for the Linux-specific POSIX timers API.
+IF(HAVE_TIMER_CREATE AND HAVE_TIMER_SETTIME AND HAVE_SIGEV_THREAD_ID)
+ SET(HAVE_POSIX_TIMERS 1 CACHE INTERNAL "Have POSIX timer-related functions")
+ENDIF()
+
+IF(HAVE_KQUEUE AND HAVE_EVFILT_TIMER)
+ SET(HAVE_KQUEUE_TIMERS 1 CACHE INTERNAL "Have kqueue timer-related filter")
+ENDIF()
+IF(HAVE_POSIX_TIMERS OR HAVE_KQUEUE_TIMERS)
+ SET(HAVE_MY_TIMER 1 CACHE INTERNAL "Have mysys timer-related functions")
+ENDIF()
#
# Test for endianess
View
17 include/my_global.h
@@ -377,6 +377,19 @@ C_MODE_END
#include <crypt.h>
#endif
+/**
+ Cast a member of a structure to the structure that contains it.
+
+ @param ptr Pointer to the member.
+ @param type Type of the structure that contains the member.
+ @param member Name of the member within the structure.
+*/
+#define my_container_of(ptr, type, member) \
+ ({ \
+ const typeof(((type *)0)->member) *__mptr= (ptr); \
+ (type *)((char *)__mptr - offsetof(type, member)); \
+ })
+
/*
A lot of our programs uses asserts, so better to always include it
This also fixes a problem when people uses DBUG_ASSERT without including
@@ -1418,9 +1431,7 @@ do { doubleget_union _tmp; \
#define NEED_EXPLICIT_SYNC_DIR 1
#endif
-#if !defined(__cplusplus) && !defined(bool)
-#define bool In_C_you_should_use_my_bool_instead()
-#endif
+#include <stdbool.h>
/* Provide __func__ macro definition for platforms that miss it. */
#if __STDC_VERSION__ < 199901L
View
69 include/my_timer.h
@@ -0,0 +1,69 @@
+/* Copyright (c) 2012, Twitter, Inc. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#ifndef MY_TIMER_H
+#define MY_TIMER_H
+
+#include "my_global.h" /* C_MODE_START, C_MODE_END */
+#include "my_config.h" /* HAVE_*_TIMERS */
+
+/* POSIX timers API. */
+#ifdef HAVE_POSIX_TIMERS
+# include <time.h> /* timer_t */
+ typedef timer_t os_timer_t;
+#elif HAVE_KQUEUE_TIMERS
+# include <sys/types.h> /* uintptr_t */
+ typedef uintptr_t os_timer_t;
+#endif
+
+/* Whether timer API is implemented. */
+#ifdef HAVE_MY_TIMER
+
+C_MODE_START
+
+typedef struct st_my_timer my_timer_t;
+
+/** Non-copyable timer object. */
+struct st_my_timer
+{
+ /** Timer ID used to identify the timer in timer requests. */
+ os_timer_t id;
+
+ /** Timer expiration notification function. */
+ void (*notify_function)(my_timer_t *);
+};
+
+/* Initialize internal components. */
+int my_timer_init_ext(void);
+
+/* Release any resources acquired. */
+void my_timer_deinit(void);
+
+/* Create a timer object. */
+int my_timer_create(my_timer_t *timer);
+
+/* Set the time (in milliseconds) until the next expiration of the timer. */
+int my_timer_set(my_timer_t *timer, unsigned long time);
+
+/* Reset the time until the next expiration of the timer. */
+int my_timer_reset(my_timer_t *timer, bool *state);
+
+/* Delete a timer object. */
+void my_timer_delete(my_timer_t *timer);
+
+C_MODE_END
+
+#endif /* HAVE_MY_TIMER */
+#endif /* MY_TIMER_H */
View
7 include/mysql/plugin.h
@@ -584,6 +584,13 @@ int thd_killed(const MYSQL_THD thd);
/**
+ Set the killed status of the current statement.
+
+ @param thd user thread connection handle
+*/
+void thd_set_kill_status(const MYSQL_THD thd);
+
+/**
Return the thread id of a user thread
@param thd user thread connection handle
View
1  include/mysql/plugin_audit.h.pp
@@ -189,6 +189,7 @@
int mysql_tmpfile(const char *prefix);
void* thd_get_current_thd();
int thd_killed(const void* thd);
+void thd_set_kill_status(const void* thd);
unsigned long thd_get_thread_id(const void* thd);
void thd_get_xid(const void* thd, MYSQL_XID *xid);
void mysql_query_cache_invalidate4(void* thd,
View
1  include/mysql/plugin_auth.h.pp
@@ -189,6 +189,7 @@
int mysql_tmpfile(const char *prefix);
void* thd_get_current_thd();
int thd_killed(const void* thd);
+void thd_set_kill_status(const void* thd);
unsigned long thd_get_thread_id(const void* thd);
void thd_get_xid(const void* thd, MYSQL_XID *xid);
void mysql_query_cache_invalidate4(void* thd,
View
1  include/mysql/plugin_ftparser.h.pp
@@ -142,6 +142,7 @@
int mysql_tmpfile(const char *prefix);
void* thd_get_current_thd();
int thd_killed(const void* thd);
+void thd_set_kill_status(const void* thd);
unsigned long thd_get_thread_id(const void* thd);
void thd_get_xid(const void* thd, MYSQL_XID *xid);
void mysql_query_cache_invalidate4(void* thd,
View
3  libmysqld/CMakeLists.txt
@@ -92,6 +92,9 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
${MYSYS_LIBWRAP_SOURCE}
)
+IF(HAVE_MY_TIMER)
+ SET(SQL_EMBEDDED_SOURCES ${SQL_EMBEDDED_SOURCES} ../sql/sql_timer.cc)
+ENDIF()
ADD_CONVENIENCE_LIBRARY(sql_embedded ${SQL_EMBEDDED_SOURCES})
DTRACE_INSTRUMENT(sql_embedded)
View
4 mysql-test/include/have_statement_timeout.inc
@@ -0,0 +1,4 @@
+-- require r/have_statement_timeout.require
+disable_query_log;
+show variables like 'have_statement_timeout';
+enable_query_log;
View
3  mysql-test/r/grant.result
@@ -55,6 +55,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
show grants for mysqltest_1@localhost;
Grants for mysqltest_1@localhost
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3-SHA'
@@ -126,6 +127,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
show grants for mysqltest_1@localhost;
Grants for mysqltest_1@localhost
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10
@@ -173,6 +175,7 @@ max_connections 30
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
show grants for mysqltest_1@localhost;
Grants for mysqltest_1@localhost
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 20 MAX_CONNECTIONS_PER_HOUR 30
View
2  mysql-test/r/have_statement_timeout.require
@@ -0,0 +1,2 @@
+Variable_name Value
+have_statement_timeout YES
View
157 mysql-test/r/max_statement_time.result
@@ -0,0 +1,157 @@
+
+# Test MAX_STATEMENT_TIME option syntax.
+
+CREATE TABLE t1 (a INT);
+SET @var = (SELECT MAX_STATEMENT_TIME=0 1);
+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 '1)' at line 1
+SELECT 1 FROM t1 WHERE a IN (SELECT MAX_STATEMENT_TIME=0 1);
+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 '1)' at line 1
+SELECT (SELECT MAX_STATEMENT_TIME=0 a FROM t1);
+ERROR 42S22: Unknown column 'MAX_STATEMENT_TIME' in 'field list'
+SELECT a FROM t1 WHERE a IN (SELECT MAX_STATEMENT_TIME=0 a FROM t1);
+ERROR 42S22: Unknown column 'MAX_STATEMENT_TIME' in 'field list'
+SELECT * FROM t1 WHERE a IN (SELECT MAX_STATEMENT_TIME=0 a FROM t1);
+ERROR 42S22: Unknown column 'MAX_STATEMENT_TIME' in 'field list'
+SELECT MAX_STATEMENT_TIME=0 * FROM t1
+WHERE a IN (SELECT MAX_STATEMENT_TIME=0 a FROM t1);
+ERROR 42S22: Unknown column 'MAX_STATEMENT_TIME' in 'field list'
+SELECT * FROM t1
+WHERE a IN (SELECT a FROM t1 UNION SELECT MAX_STATEMENT_TIME=0 a FROM t1);
+ERROR 42S22: Unknown column 'MAX_STATEMENT_TIME' in 'field list'
+SELECT MAX_STATEMENT_TIME=0 * FROM t1
+WHERE a IN (SELECT a FROM t1 UNION SELECT MAX_STATEMENT_TIME=0 a FROM t1);
+ERROR 42S22: Unknown column 'MAX_STATEMENT_TIME' in 'field list'
+SELECT * FROM t1 UNION SELECT MAX_STATEMENT_TIME=0 * FROM t1;
+ERROR 42000: Incorrect usage/placement of 'MAX_STATEMENT_TIME'
+SELECT MAX_STATEMENT_TIME=0 * FROM t1
+UNION SELECT MAX_STATEMENT_TIME=0 * FROM t1;
+ERROR 42000: Incorrect usage/placement of 'MAX_STATEMENT_TIME'
+INSERT INTO t1 SELECT MAX_STATEMENT_TIME=0 * FROM t1;
+ERROR 42000: Incorrect usage/placement of 'MAX_STATEMENT_TIME'
+CREATE TABLE t1 AS SELECT MAX_STATEMENT_TIME=0 * FROM t1;
+ERROR 42000: Incorrect usage/placement of 'MAX_STATEMENT_TIME'
+CREATE TABLE t1 AS SELECT 1 A UNION SELECT 2 UNION SELECT MAX_STATEMENT_TIME=0 3;
+ERROR 42000: Incorrect usage/placement of 'MAX_STATEMENT_TIME'
+SELECT MAX_STATEMENT_TIME=0 * FROM t1;
+a
+DROP TABLE t1;
+
+# Test the MAX_STATEMENT_TIME option.
+
+SELECT MAX_STATEMENT_TIME=1 SLEEP(1);
+SLEEP(1)
+1
+CREATE TABLE t1 (a INT, b VARCHAR(300));
+INSERT INTO t1 VALUES (1, 'string');
+SELECT 996;
+996
+996
+DROP TABLE t1;
+
+# Test the MAX_STATEMENT_TIME option with SF.
+
+CREATE TABLE t1 (a INT, b VARCHAR(300));
+INSERT INTO t1 VALUES (1, 'string');
+CREATE FUNCTION f1() RETURNS INT
+BEGIN
+WHILE true DO
+INSERT INTO t1 SELECT * FROM t1;
+END WHILE;
+RETURN 1;
+END|
+SELECT MAX_STATEMENT_TIME=500 f1();
+ERROR 70100: Query execution was interrupted, max_statement_time exceeded
+DROP FUNCTION f1;
+DROP TABLE t1;
+
+# MAX_STATEMENT_TIME option takes precedence over @@max_statement_time.
+
+SET @@SESSION.max_statement_time = 100;
+SELECT MAX_STATEMENT_TIME=2000 SLEEP(1);
+SLEEP(1)
+0
+SET @@SESSION.max_statement_time = DEFAULT;
+
+# MAX_STATEMENT_TIME account resource
+
+GRANT USAGE ON *.* TO user1@localhost WITH MAX_STATEMENT_TIME 5000;
+# con1
+SELECT @@max_statement_time;
+@@max_statement_time
+5000
+# restart and reconnect
+SELECT @@max_statement_time;
+@@max_statement_time
+5000
+DROP USER user1@localhost;
+
+# MAX_STATEMENT_TIME status variables.
+
+SELECT CONVERT(VARIABLE_VALUE, UNSIGNED) INTO @time_set
+FROM INFORMATION_SCHEMA.GLOBAL_STATUS
+WHERE VARIABLE_NAME = 'MAX_STATEMENT_TIME_SET';
+SELECT CONVERT(VARIABLE_VALUE, UNSIGNED) INTO @time_exceeded
+FROM INFORMATION_SCHEMA.GLOBAL_STATUS
+WHERE VARIABLE_NAME = 'MAX_STATEMENT_TIME_EXCEEDED';
+SELECT MAX_STATEMENT_TIME=10 SLEEP(1);
+SLEEP(1)
+1
+# Ensure that the counters for:
+# - statements that are time limited; and
+# - statements that exceeded their maximum execution time
+# are incremented.
+SELECT 1 AS STATUS FROM INFORMATION_SCHEMA.GLOBAL_STATUS
+WHERE VARIABLE_NAME = 'MAX_STATEMENT_TIME_SET'
+ AND CONVERT(VARIABLE_VALUE, UNSIGNED) > @time_set;
+STATUS
+1
+SELECT 1 AS STATUS FROM INFORMATION_SCHEMA.GLOBAL_STATUS
+WHERE VARIABLE_NAME = 'MAX_STATEMENT_TIME_EXCEEDED'
+ AND CONVERT(VARIABLE_VALUE, UNSIGNED) > @time_exceeded;
+STATUS
+1
+
+# Check that the appropriate error status is set.
+
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1);
+START TRANSACTION;
+SELECT * FROM t1 FOR UPDATE;
+a
+1
+SET @@SESSION.max_statement_time = 500;
+UPDATE t1 SET a = 2;
+ERROR 70100: Query execution was interrupted, max_statement_time exceeded
+SHOW WARNINGS;
+Level Code Message
+Error 996 Query execution was interrupted, max_statement_time exceeded
+Error 996 Query execution was interrupted, max_statement_time exceeded
+ROLLBACK;
+DROP TABLE t1;
+
+# Test interaction with lock waits.
+
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1);
+SET @@SESSION.max_statement_time = 500;
+LOCK TABLES t1 WRITE;
+LOCK TABLES t1 READ;
+ERROR 70100: Query execution was interrupted, max_statement_time exceeded
+UNLOCK TABLES;
+BEGIN;
+SELECT * FROM t1;
+a
+1
+ALTER TABLE t1 ADD COLUMN b INT;
+ERROR 70100: Query execution was interrupted, max_statement_time exceeded
+ROLLBACK;
+SELECT GET_LOCK('lock', 1);
+GET_LOCK('lock', 1)
+1
+SELECT GET_LOCK('lock', 1);
+GET_LOCK('lock', 1)
+NULL
+SELECT RELEASE_LOCK('lock');
+RELEASE_LOCK('lock')
+1
+DROP TABLE t1;
View
6 mysql-test/r/ps.result
@@ -1194,13 +1194,13 @@ SET @aux= "SELECT COUNT(*)
prepare my_stmt from @aux;
execute my_stmt;
COUNT(*)
-42
+43
execute my_stmt;
COUNT(*)
-42
+43
execute my_stmt;
COUNT(*)
-42
+43
deallocate prepare my_stmt;
drop procedure if exists p1|
drop table if exists t1|
View
1  mysql-test/r/system_mysql_db.result
@@ -122,6 +122,7 @@ user CREATE TABLE `user` (
`max_user_connections` int(11) unsigned NOT NULL DEFAULT '0',
`plugin` char(64) COLLATE utf8_bin DEFAULT '',
`authentication_string` text COLLATE utf8_bin,
+ `max_statement_time` int(11) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`Host`,`User`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges'
show create table func;
View
2  mysql-test/suite/funcs_1/r/is_columns_mysql.result
@@ -204,6 +204,7 @@ def mysql user Insert_priv 5 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('
def mysql user Lock_tables_priv 21 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references
def mysql user max_connections 39 0 NO int NULL NULL 10 0 NULL NULL int(11) unsigned select,insert,update,references
def mysql user max_questions 37 0 NO int NULL NULL 10 0 NULL NULL int(11) unsigned select,insert,update,references
+def mysql user max_statement_time 43 0 NO int NULL NULL 10 0 NULL NULL int(11) unsigned select,insert,update,references
def mysql user max_updates 38 0 NO int NULL NULL 10 0 NULL NULL int(11) unsigned select,insert,update,references
def mysql user max_user_connections 40 0 NO int NULL NULL 10 0 NULL NULL int(11) unsigned select,insert,update,references
def mysql user Password 3 NO char 41 41 NULL NULL latin1 latin1_bin char(41) select,insert,update,references
@@ -518,3 +519,4 @@ NULL mysql user max_connections int NULL NULL NULL NULL int(11) unsigned
NULL mysql user max_user_connections int NULL NULL NULL NULL int(11) unsigned
3.0000 mysql user plugin char 64 192 utf8 utf8_bin char(64)
1.0000 mysql user authentication_string text 65535 65535 utf8 utf8_bin text
+NULL mysql user max_statement_time int NULL NULL NULL NULL int(11) unsigned
View
33 mysql-test/suite/funcs_1/r/is_user_privileges.result
@@ -129,6 +129,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser2
Password
@@ -171,6 +172,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser3
Password
@@ -213,6 +215,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
#
# Add GRANT OPTION db_datadict.* to testuser1;
GRANT UPDATE ON db_datadict.* TO 'testuser1'@'localhost' WITH GRANT OPTION;
@@ -279,6 +282,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser2
Password
@@ -321,6 +325,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser3
Password
@@ -363,6 +368,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
# Establish connection testuser1 (user=testuser1)
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
@@ -415,6 +421,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser2
Password
@@ -457,6 +464,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser3
Password
@@ -499,6 +507,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
SHOW GRANTS;
Grants for testuser1@localhost
GRANT USAGE ON *.* TO 'testuser1'@'localhost'
@@ -573,6 +582,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser2
Password
@@ -615,6 +625,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser3
Password
@@ -657,6 +668,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
GRANT SELECT ON *.* TO 'testuser1'@'localhost' WITH GRANT OPTION;
#
# Here <SELECT YES> is shown correctly for testuser1;
@@ -723,6 +735,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser2
Password
@@ -765,6 +778,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser3
Password
@@ -807,6 +821,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
# Switch to connection testuser1
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
@@ -859,6 +874,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser2
Password
@@ -901,6 +917,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser3
Password
@@ -943,6 +960,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
SHOW GRANTS;
Grants for testuser1@localhost
GRANT SELECT ON *.* TO 'testuser1'@'localhost' WITH GRANT OPTION
@@ -1047,6 +1065,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser2
Password
@@ -1089,6 +1108,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser3
Password
@@ -1131,6 +1151,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
# Switch to connection testuser1
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
@@ -1230,6 +1251,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser2
Password
@@ -1272,6 +1294,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser3
Password
@@ -1314,6 +1337,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
# Switch to connection testuser1
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
@@ -1366,6 +1390,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser2
Password
@@ -1408,6 +1433,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser3
Password
@@ -1450,6 +1476,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
SHOW GRANTS;
Grants for testuser1@localhost
GRANT USAGE ON *.* TO 'testuser1'@'localhost'
@@ -1509,6 +1536,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser2
Password
@@ -1551,6 +1579,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser3
Password
@@ -1593,6 +1622,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
SHOW GRANTS;
Grants for testuser1@localhost
GRANT USAGE ON *.* TO 'testuser1'@'localhost'
@@ -1667,6 +1697,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser2
Password
@@ -1709,6 +1740,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
Host localhost
User testuser3
Password
@@ -1751,6 +1783,7 @@ max_connections 0
max_user_connections 0
plugin
authentication_string NULL
+max_statement_time 0
# Switch to connection testuser1
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
View
0  mysql-test/suite/randgen/r/max_statement_time.result
No changes.
View
16 mysql-test/suite/randgen/t/max_statement_time.test
@@ -0,0 +1,16 @@
+--source include/have_statement_timeout.inc
+--source suite/randgen/include/setup_rqg.inc
+
+let RQG_LOG_FILE = $RQG_LOG_DIR/rqg_example.log;
+
+exec perl $RQG_HOME/gentest.pl
+ --dsn=dbi:mysql:host=127.0.0.1:port=$MASTER_MYPORT:user=root:database=test
+ --logconf=$MYSQLTEST_VARDIR/std_data/randgen/log.conf
+ --gendata=$RQG_HOME/conf/twitter/max_statement_time.zz
+ --grammar=$RQG_HOME/conf/twitter/max_statement_time.yy
+ --engine=$RQG_ENGINE
+ --queries=100K
+ --threads=25
+ --duration=600;
+
+--source suite/randgen/include/cleanup.inc
View
53 mysql-test/suite/sys_vars/r/have_statement_timeout_basic.result
@@ -0,0 +1,53 @@
+# Displaying default value
+SELECT COUNT(@@GLOBAL.have_statement_timeout);
+COUNT(@@GLOBAL.have_statement_timeout)
+1
+1 Expected
+# Check if Value can set
+SET @@GLOBAL.have_statement_timeout=1;
+ERROR HY000: Variable 'have_statement_timeout' is a read only variable
+Expected error 'Read only variable'
+SELECT COUNT(@@GLOBAL.have_statement_timeout);
+COUNT(@@GLOBAL.have_statement_timeout)
+1
+1 Expected
+# Check if the value in GLOBAL Table matches value in variable
+SELECT @@GLOBAL.have_statement_timeout = VARIABLE_VALUE
+FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES
+WHERE VARIABLE_NAME='have_statement_timeout';
+@@GLOBAL.have_statement_timeout = VARIABLE_VALUE
+1
+1 Expected
+SELECT COUNT(@@GLOBAL.have_statement_timeout);
+COUNT(@@GLOBAL.have_statement_timeout)
+1
+1 Expected
+SELECT COUNT(VARIABLE_VALUE)
+FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES
+WHERE VARIABLE_NAME='have_statement_timeout';
+COUNT(VARIABLE_VALUE)
+1
+1 Expected
+# Check if accessing variable with and without GLOBAL point to same variable
+SELECT @@have_statement_timeout = @@GLOBAL.have_statement_timeout;
+@@have_statement_timeout = @@GLOBAL.have_statement_timeout
+1
+1 Expected
+# Check if have_statement_timeout can be accessed with and without @@ sign
+SELECT COUNT(@@have_statement_timeout);
+COUNT(@@have_statement_timeout)
+1
+1 Expected
+SELECT COUNT(@@local.have_statement_timeout);
+ERROR HY000: Variable 'have_statement_timeout' is a GLOBAL variable
+Expected error 'Variable is a GLOBAL variable'
+SELECT COUNT(@@SESSION.have_statement_timeout);
+ERROR HY000: Variable 'have_statement_timeout' is a GLOBAL variable
+Expected error 'Variable is a GLOBAL variable'
+SELECT COUNT(@@GLOBAL.have_statement_timeout);
+COUNT(@@GLOBAL.have_statement_timeout)
+1
+1 Expected
+SELECT have_statement_timeout = @@SESSION.have_statement_timeout;
+ERROR 42S22: Unknown column 'have_statement_timeout' in 'field list'
+Expected error 'Readonly variable'
View
91 mysql-test/suite/sys_vars/r/max_statement_time_basic.result
@@ -0,0 +1,91 @@
+# Save initial value
+SET @start_session_value = @@session.max_statement_time;
+SELECT @start_session_value;
+@start_session_value
+0
+# Display the DEFAULT value of max_statement_time
+SET @@session.max_statement_time = 20000;
+SET @@session.max_statement_time = DEFAULT;
+SELECT @@session.max_statement_time;
+@@session.max_statement_time
+0
+# Check the DEFAULT value of max_statement_time
+SET @@session.max_statement_time = DEFAULT;
+SELECT @@session.max_statement_time = 28800;
+@@session.max_statement_time = 28800
+0
+# Change the value of max_statement_time to a valid value for SESSION scope
+SET @@session.max_statement_time = 10000;
+SELECT @@session.max_statement_time;
+@@session.max_statement_time
+10000
+SET @@session.max_statement_time = 50050;
+SELECT @@session.max_statement_time;
+@@session.max_statement_time
+50050
+SET @@session.max_statement_time = 65535;
+SELECT @@session.max_statement_time;
+@@session.max_statement_time
+65535
+# Change the value of max_statement_time to an invalid value #
+SET @@session.max_statement_time = 0;
+SELECT @@session.max_statement_time;
+@@session.max_statement_time
+0
+SET @@session.max_statement_time = -2;
+Warnings:
+Warning 1292 Truncated incorrect max_statement_time value: '-2'
+SELECT @@session.max_statement_time;
+@@session.max_statement_time
+0
+SET @@session.max_statement_time = 65530.34;
+ERROR 42000: Incorrect argument type to variable 'max_statement_time'
+SET @@session.max_statement_time = 100000000;
+SELECT @@session.max_statement_time;
+@@session.max_statement_time
+100000000
+SET @@session.max_statement_time = test;
+ERROR 42000: Incorrect argument type to variable 'max_statement_time'
+SELECT @@session.max_statement_time;
+@@session.max_statement_time
+100000000
+# Check if the value in SESSION Table matches value in variable
+SELECT @@session.max_statement_time = VARIABLE_VALUE
+FROM INFORMATION_SCHEMA.SESSION_VARIABLES
+WHERE VARIABLE_NAME='max_statement_time';
+@@session.max_statement_time = VARIABLE_VALUE
+1
+# Check if TRUE and FALSE values can be used on variable
+SET @@session.max_statement_time = TRUE;
+SELECT @@session.max_statement_time;
+@@session.max_statement_time
+1
+SET @@session.max_statement_time = FALSE;
+SELECT @@session.max_statement_time;
+@@session.max_statement_time
+0
+# Check if accessing variable with SESSION,LOCAL and without SCOPE points to same session variable
+SET @@max_statement_time = 10000;
+SELECT @@max_statement_time = @@local.max_statement_time;
+@@max_statement_time = @@local.max_statement_time
+1
+SELECT @@local.max_statement_time = @@session.max_statement_time;
+@@local.max_statement_time = @@session.max_statement_time
+1
+# Check if max_statement_time can be accessed with and without @@ sign
+SET max_statement_time = 10000;
+SELECT @@max_statement_time;
+@@max_statement_time
+10000
+SELECT local.max_statement_time;
+ERROR 42S02: Unknown table 'local' in field list
+SELECT session.max_statement_time;
+ERROR 42S02: Unknown table 'session' in field list
+# Check that the variable is a SESSION-only variable
+SET @@global.max_statement_time = 0;
+ERROR HY000: Variable 'max_statement_time' is a SESSION variable and can't be used with SET GLOBAL
+# Restore initial value
+SET @@session.max_statement_time = @start_session_value;
+SELECT @@session.max_statement_time;
+@@session.max_statement_time
+0
View
66 mysql-test/suite/sys_vars/r/max_statement_time_func.result
@@ -0,0 +1,66 @@
+#
+# Test max statement time interruption precision.
+# Note that if SLEEP() is interrupted, it returns 1.
+#
+SET @@SESSION.max_statement_time = 100;
+SELECT SLEEP(1);
+SLEEP(1)
+1
+SET @@SESSION.max_statement_time = 250;
+SELECT SLEEP(1);
+SLEEP(1)
+1
+SET @@SESSION.max_statement_time = 500;
+SELECT SLEEP(1);
+SLEEP(1)
+1
+SET @@SESSION.max_statement_time = 750;
+SELECT SLEEP(1);
+SLEEP(1)
+1
+SET @@SESSION.max_statement_time = 1250;
+SELECT SLEEP(1);
+SLEEP(1)
+0
+SET @@SESSION.max_statement_time = 1500;
+SELECT SLEEP(1);
+SLEEP(1)
+0
+SET @@SESSION.max_statement_time = 0;
+#
+# Timeout only applies to the top-level statement.
+#
+CREATE PROCEDURE p1()
+BEGIN
+SELECT SLEEP(0.5); -- 0.5
+SELECT SLEEP(0.5); -- 1.0
+SELECT SLEEP(1.5); -- 1.5
+END|
+SET @@SESSION.max_statement_time = 750;
+CALL p1();
+SLEEP(0.5)
+0
+SLEEP(0.5)
+1
+SLEEP(1.5)
+0
+SET @@SESSION.max_statement_time = 0;
+DROP PROCEDURE p1;
+#
+# Interrupt a statement that changes data.
+#
+CREATE TABLE t1 (a INT);
+CREATE FUNCTION f1() RETURNS INT
+BEGIN
+WHILE true DO
+INSERT INTO t1 VALUES (1);
+END WHILE;
+RETURN 1;
+END|
+SET @@SESSION.max_statement_time = 500;
+SELECT f1();
+ERROR 70100: Query execution was interrupted, max_statement_time exceeded
+SET @@SESSION.max_statement_time = 0;
+DROP FUNCTION f1;
+DROP TABLE t1;
+SET @@SESSION.max_statement_time = default;
View
55 mysql-test/suite/sys_vars/t/have_statement_timeout_basic.test
@@ -0,0 +1,55 @@
+--source include/have_statement_timeout.inc
+
+--echo # Displaying default value
+
+SELECT COUNT(@@GLOBAL.have_statement_timeout);
+--echo 1 Expected
+
+--echo # Check if Value can set
+
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SET @@GLOBAL.have_statement_timeout=1;
+--echo Expected error 'Read only variable'
+
+SELECT COUNT(@@GLOBAL.have_statement_timeout);
+--echo 1 Expected
+
+--echo # Check if the value in GLOBAL Table matches value in variable
+
+SELECT @@GLOBAL.have_statement_timeout = VARIABLE_VALUE
+FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES
+WHERE VARIABLE_NAME='have_statement_timeout';
+--echo 1 Expected
+
+SELECT COUNT(@@GLOBAL.have_statement_timeout);
+--echo 1 Expected
+
+SELECT COUNT(VARIABLE_VALUE)
+FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES
+WHERE VARIABLE_NAME='have_statement_timeout';
+--echo 1 Expected
+
+--echo # Check if accessing variable with and without GLOBAL point to same variable
+
+SELECT @@have_statement_timeout = @@GLOBAL.have_statement_timeout;
+--echo 1 Expected
+
+--echo # Check if have_statement_timeout can be accessed with and without @@ sign
+
+SELECT COUNT(@@have_statement_timeout);
+--echo 1 Expected
+
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SELECT COUNT(@@local.have_statement_timeout);
+--echo Expected error 'Variable is a GLOBAL variable'
+
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SELECT COUNT(@@SESSION.have_statement_timeout);
+--echo Expected error 'Variable is a GLOBAL variable'
+
+SELECT COUNT(@@GLOBAL.have_statement_timeout);
+--echo 1 Expected
+
+--error ER_BAD_FIELD_ERROR
+SELECT have_statement_timeout = @@SESSION.have_statement_timeout;
+--echo Expected error 'Readonly variable'
View
78 mysql-test/suite/sys_vars/t/max_statement_time_basic.test
@@ -0,0 +1,78 @@
+--source include/have_statement_timeout.inc
+
+--echo # Save initial value
+
+SET @start_session_value = @@session.max_statement_time;
+SELECT @start_session_value;
+
+--echo # Display the DEFAULT value of max_statement_time
+
+SET @@session.max_statement_time = 20000;
+SET @@session.max_statement_time = DEFAULT;
+SELECT @@session.max_statement_time;
+
+--echo # Check the DEFAULT value of max_statement_time
+
+SET @@session.max_statement_time = DEFAULT;
+SELECT @@session.max_statement_time = 28800;
+
+--echo # Change the value of max_statement_time to a valid value for SESSION scope
+
+SET @@session.max_statement_time = 10000;
+SELECT @@session.max_statement_time;
+SET @@session.max_statement_time = 50050;
+SELECT @@session.max_statement_time;
+SET @@session.max_statement_time = 65535;
+SELECT @@session.max_statement_time;
+
+--echo # Change the value of max_statement_time to an invalid value #
+
+SET @@session.max_statement_time = 0;
+SELECT @@session.max_statement_time;
+SET @@session.max_statement_time = -2;
+SELECT @@session.max_statement_time;
+--error ER_WRONG_TYPE_FOR_VAR
+SET @@session.max_statement_time = 65530.34;
+SET @@session.max_statement_time = 100000000;
+SELECT @@session.max_statement_time;
+
+--error ER_WRONG_TYPE_FOR_VAR
+SET @@session.max_statement_time = test;
+SELECT @@session.max_statement_time;
+
+--echo # Check if the value in SESSION Table matches value in variable
+
+SELECT @@session.max_statement_time = VARIABLE_VALUE
+FROM INFORMATION_SCHEMA.SESSION_VARIABLES
+WHERE VARIABLE_NAME='max_statement_time';
+
+--echo # Check if TRUE and FALSE values can be used on variable
+
+SET @@session.max_statement_time = TRUE;
+SELECT @@session.max_statement_time;
+SET @@session.max_statement_time = FALSE;
+SELECT @@session.max_statement_time;
+
+--echo # Check if accessing variable with SESSION,LOCAL and without SCOPE points to same session variable
+
+SET @@max_statement_time = 10000;
+SELECT @@max_statement_time = @@local.max_statement_time;
+SELECT @@local.max_statement_time = @@session.max_statement_time;
+
+--echo # Check if max_statement_time can be accessed with and without @@ sign
+
+SET max_statement_time = 10000;
+SELECT @@max_statement_time;
+--error ER_UNKNOWN_TABLE
+SELECT local.max_statement_time;
+--error ER_UNKNOWN_TABLE
+SELECT session.max_statement_time;
+
+--echo # Check that the variable is a SESSION-only variable
+--error ER_LOCAL_VARIABLE
+SET @@global.max_statement_time = 0;
+
+--echo # Restore initial value
+
+SET @@session.max_statement_time = @start_session_value;
+SELECT @@session.max_statement_time;
View
75 mysql-test/suite/sys_vars/t/max_statement_time_func.test
@@ -0,0 +1,75 @@
+--source include/have_statement_timeout.inc
+
+--echo #
+--echo # Test max statement time interruption precision.
+--echo # Note that if SLEEP() is interrupted, it returns 1.
+--echo #
+
+SET @@SESSION.max_statement_time = 100;
+SELECT SLEEP(1);
+
+SET @@SESSION.max_statement_time = 250;
+SELECT SLEEP(1);
+
+SET @@SESSION.max_statement_time = 500;
+SELECT SLEEP(1);
+
+SET @@SESSION.max_statement_time = 750;
+SELECT SLEEP(1);
+
+SET @@SESSION.max_statement_time = 1250;
+SELECT SLEEP(1);
+
+SET @@SESSION.max_statement_time = 1500;
+SELECT SLEEP(1);
+
+SET @@SESSION.max_statement_time = 0;
+
+--echo #
+--echo # Timeout only applies to the top-level statement.
+--echo #
+
+DELIMITER |;
+
+CREATE PROCEDURE p1()
+BEGIN
+ SELECT SLEEP(0.5); -- 0.5
+ SELECT SLEEP(0.5); -- 1.0
+ SELECT SLEEP(1.5); -- 1.5
+END|
+
+DELIMITER ;|
+
+SET @@SESSION.max_statement_time = 750;
+CALL p1();
+SET @@SESSION.max_statement_time = 0;
+
+DROP PROCEDURE p1;
+
+--echo #
+--echo # Interrupt a statement that changes data.
+--echo #
+
+CREATE TABLE t1 (a INT);
+
+DELIMITER |;
+
+CREATE FUNCTION f1() RETURNS INT
+BEGIN
+ WHILE true DO
+ INSERT INTO t1 VALUES (1);
+ END WHILE;
+ RETURN 1;
+END|
+
+DELIMITER ;|
+
+SET @@SESSION.max_statement_time = 500;
+--error ER_QUERY_TIMEOUT
+SELECT f1();
+SET @@SESSION.max_statement_time = 0;
+
+DROP FUNCTION f1;
+DROP TABLE t1;
+
+SET @@SESSION.max_statement_time = default;
View
220 mysql-test/t/max_statement_time.test
@@ -0,0 +1,220 @@
+#
+# Test behavior of MAX_STATEMENT_TIME.
+#
+
+--source include/have_statement_timeout.inc
+--source include/not_embedded.inc
+--source include/have_innodb.inc
+
+--echo
+--echo # Test MAX_STATEMENT_TIME option syntax.
+--echo
+
+CREATE TABLE t1 (a INT);
+
+--error ER_PARSE_ERROR
+SET @var = (SELECT MAX_STATEMENT_TIME=0 1);
+--error ER_PARSE_ERROR
+SELECT 1 FROM t1 WHERE a IN (SELECT MAX_STATEMENT_TIME=0 1);
+
+--error ER_BAD_FIELD_ERROR
+SELECT (SELECT MAX_STATEMENT_TIME=0 a FROM t1);
+--error ER_BAD_FIELD_ERROR
+SELECT a FROM t1 WHERE a IN (SELECT MAX_STATEMENT_TIME=0 a FROM t1);
+--error ER_BAD_FIELD_ERROR
+SELECT * FROM t1 WHERE a IN (SELECT MAX_STATEMENT_TIME=0 a FROM t1);
+--error ER_BAD_FIELD_ERROR
+SELECT MAX_STATEMENT_TIME=0 * FROM t1
+ WHERE a IN (SELECT MAX_STATEMENT_TIME=0 a FROM t1);
+--error ER_BAD_FIELD_ERROR
+SELECT * FROM t1
+ WHERE a IN (SELECT a FROM t1 UNION SELECT MAX_STATEMENT_TIME=0 a FROM t1);
+--error ER_BAD_FIELD_ERROR
+SELECT MAX_STATEMENT_TIME=0 * FROM t1
+ WHERE a IN (SELECT a FROM t1 UNION SELECT MAX_STATEMENT_TIME=0 a FROM t1);
+
+--error ER_CANT_USE_OPTION_HERE
+SELECT * FROM t1 UNION SELECT MAX_STATEMENT_TIME=0 * FROM t1;
+--error ER_CANT_USE_OPTION_HERE
+SELECT MAX_STATEMENT_TIME=0 * FROM t1
+ UNION SELECT MAX_STATEMENT_TIME=0 * FROM t1;
+--error ER_CANT_USE_OPTION_HERE
+INSERT INTO t1 SELECT MAX_STATEMENT_TIME=0 * FROM t1;
+--error ER_CANT_USE_OPTION_HERE
+CREATE TABLE t1 AS SELECT MAX_STATEMENT_TIME=0 * FROM t1;
+--error ER_CANT_USE_OPTION_HERE
+CREATE TABLE t1 AS SELECT 1 A UNION SELECT 2 UNION SELECT MAX_STATEMENT_TIME=0 3;
+
+SELECT MAX_STATEMENT_TIME=0 * FROM t1;
+
+DROP TABLE t1;
+
+--echo
+--echo # Test the MAX_STATEMENT_TIME option.
+--echo
+
+SELECT MAX_STATEMENT_TIME=1 SLEEP(1);
+
+CREATE TABLE t1 (a INT, b VARCHAR(300));
+
+INSERT INTO t1 VALUES (1, 'string');
+
+--disable_result_log
+--disable_query_log
+
+WHILE (! $mysql_errno)
+{
+ INSERT INTO t1 SELECT * FROM t1;
+ --error 0,996
+ SELECT MAX_STATEMENT_TIME=250 COUNT(*) FROM t1 WHERE b LIKE '%z%';
+}
+
+--enable_query_log
+--enable_result_log
+
+eval SELECT $mysql_errno;
+
+DROP TABLE t1;
+
+--echo
+--echo # Test the MAX_STATEMENT_TIME option with SF.
+--echo
+
+CREATE TABLE t1 (a INT, b VARCHAR(300));
+
+INSERT INTO t1 VALUES (1, 'string');
+
+DELIMITER |;
+
+CREATE FUNCTION f1() RETURNS INT
+BEGIN
+ WHILE true DO
+ INSERT INTO t1 SELECT * FROM t1;
+ END WHILE;
+ RETURN 1;
+END|
+
+DELIMITER ;|
+
+--error ER_QUERY_TIMEOUT
+SELECT MAX_STATEMENT_TIME=500 f1();
+
+DROP FUNCTION f1;
+
+DROP TABLE t1;
+
+--echo
+--echo # MAX_STATEMENT_TIME option takes precedence over @@max_statement_time.
+--echo
+
+SET @@SESSION.max_statement_time = 100;
+SELECT MAX_STATEMENT_TIME=2000 SLEEP(1);
+SET @@SESSION.max_statement_time = DEFAULT;
+
+--echo
+--echo # MAX_STATEMENT_TIME account resource
+--echo
+
+GRANT USAGE ON *.* TO user1@localhost WITH MAX_STATEMENT_TIME 5000;
+
+--echo # con1
+connect(con1,localhost,user1,,test,,);
+SELECT @@max_statement_time;
+disconnect con1;
+
+--echo # restart and reconnect
+connection default;
+source include/restart_mysqld.inc;
+
+connect(con1,localhost,user1,,test,,);
+SELECT @@max_statement_time;
+disconnect con1;
+
+connection default;
+DROP USER user1@localhost;
+
+--echo
+--echo # MAX_STATEMENT_TIME status variables.
+--echo
+
+SELECT CONVERT(VARIABLE_VALUE, UNSIGNED) INTO @time_set
+ FROM INFORMATION_SCHEMA.GLOBAL_STATUS
+ WHERE VARIABLE_NAME = 'MAX_STATEMENT_TIME_SET';
+
+SELECT CONVERT(VARIABLE_VALUE, UNSIGNED) INTO @time_exceeded
+ FROM INFORMATION_SCHEMA.GLOBAL_STATUS
+ WHERE VARIABLE_NAME = 'MAX_STATEMENT_TIME_EXCEEDED';
+
+SELECT MAX_STATEMENT_TIME=10 SLEEP(1);
+
+--echo # Ensure that the counters for:
+--echo # - statements that are time limited; and
+--echo # - statements that exceeded their maximum execution time
+--echo # are incremented.
+
+SELECT 1 AS STATUS FROM INFORMATION_SCHEMA.GLOBAL_STATUS
+ WHERE VARIABLE_NAME = 'MAX_STATEMENT_TIME_SET'
+ AND CONVERT(VARIABLE_VALUE, UNSIGNED) > @time_set;
+
+SELECT 1 AS STATUS FROM INFORMATION_SCHEMA.GLOBAL_STATUS
+ WHERE VARIABLE_NAME = 'MAX_STATEMENT_TIME_EXCEEDED'
+ AND CONVERT(VARIABLE_VALUE, UNSIGNED) > @time_exceeded;
+
+--echo
+--echo # Check that the appropriate error status is set.
+--echo
+
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1);
+
+START TRANSACTION;
+SELECT * FROM t1 FOR UPDATE;
+
+connect (con1,localhost,root,,test,,);
+SET @@SESSION.max_statement_time = 500;
+--error ER_QUERY_TIMEOUT
+UPDATE t1 SET a = 2;
+SHOW WARNINGS;
+disconnect con1;
+
+connection default;
+ROLLBACK;
+DROP TABLE t1;
+
+--echo
+--echo # Test interaction with lock waits.
+--echo
+
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1);
+
+connect (con1,localhost,root,,test,,);
+SET @@SESSION.max_statement_time = 500;
+
+connection default;
+LOCK TABLES t1 WRITE;
+
+connection con1;
+--error ER_QUERY_TIMEOUT
+LOCK TABLES t1 READ;
+
+connection default;
+UNLOCK TABLES;
+BEGIN;
+SELECT * FROM t1;
+
+connection con1;
+--error ER_QUERY_TIMEOUT
+ALTER TABLE t1 ADD COLUMN b INT;
+
+connection default;
+ROLLBACK;
+SELECT GET_LOCK('lock', 1);
+
+connection con1;
+SELECT GET_LOCK('lock', 1);
+
+disconnect con1;
+connection default;
+SELECT RELEASE_LOCK('lock');
+DROP TABLE t1;
View
8 mysys/CMakeLists.txt
@@ -44,6 +44,14 @@ IF(HAVE_ALARM)
SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_alarm.c)
ENDIF()
+IF(HAVE_POSIX_TIMERS)
+ SET(MYSYS_SOURCES ${MYSYS_SOURCES} posix_timers.c)
+ENDIF()
+
+IF(HAVE_KQUEUE_TIMERS)
+ SET(MYSYS_SOURCES ${MYSYS_SOURCES} kqueue_timers.c)
+ENDIF()
+
IF(NOT HAVE_CXX_NEW)
# gcc as C++ compiler does not have new/delete
SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_new.cc)
View
219 mysys/kqueue_timers.c
@@ -0,0 +1,219 @@
+/* Copyright (c) 2012, Twitter, Inc. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "my_timer.h" /* my_timer_t */
+#include "my_pthread.h" /* my_thread_init, my_thread_end */
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include <assert.h>
+#include <errno.h>
+
+static int kq_fd= -1;
+static pthread_t thread;
+
+/**
+ Timer expiration notification thread.
+
+ @param arg Unused.
+*/
+
+static void *
+timer_notify_thread(void *arg __attribute__((unused)))
+{
+ my_timer_t *timer;
+ struct kevent kev;
+
+ my_thread_init();
+
+ while (1)
+ {
+ if (kevent(kq_fd, NULL, 0, &kev, 1, NULL) < 0)
+ continue;
+
+ if (kev.filter == EVFILT_TIMER)
+ {
+ timer= kev.udata;
+ assert(timer->id == kev.ident);
+ timer->notify_function(timer);
+ }
+ else if (kev.filter == EVFILT_USER)
+ break;
+ }
+
+ my_thread_end();
+
+ return NULL;
+}
+
+
+/**
+ Create a helper thread to dispatch timer expiration notifications.
+
+ @return On success, 0. On error, -1 is returned.
+*/
+
+static int
+start_helper_thread(void)
+{
+ struct kevent kev;
+
+ EV_SET(&kev, 0, EVFILT_USER, EV_ADD, 0, 0, 0);
+
+ if (kevent(kq_fd, &kev, 1, NULL, 0, NULL) < 0)
+ return -1;
+
+ return pthread_create(&thread, NULL, timer_notify_thread, NULL);
+}
+
+
+/**
+ Initialize internal components.
+
+ @return On success, 0.
+ On error, -1 is returned, and errno is set to indicate the error.
+*/
+
+int
+my_timer_init_ext(void)
+{
+ int rc;
+
+ /* Create a file descriptor for event notification. */
+ if ((kq_fd= kqueue()) < 0)
+ return -1;
+
+ /* Create a helper thread. */
+ if ((rc= start_helper_thread()))
+ close(kq_fd);
+
+ return rc;
+}
+
+
+/**
+ Release any resources that were allocated as part of initialization.
+*/
+
+void
+my_timer_deinit(void)
+{
+ struct kevent kev;
+
+ EV_SET(&kev, 0, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0);
+
+ /* There's not much to do if triggering the event fails. */
+ if (kevent(kq_fd, &kev, 1, NULL, 0, NULL) > -1)
+ pthread_join(thread, NULL);
+
+ close(kq_fd);
+}
+
+
+/**
+ Create a timer object.
+
+ @param timer Timer object.
+
+ @return On success, 0.
+ On error, -1 is returned, and errno is set to indicate the error.
+*/
+
+int
+my_timer_create(my_timer_t *timer)
+{
+ assert(kq_fd >= 0);
+
+ timer->id= (uintptr_t) timer;
+
+ return 0;
+}
+
+
+/**
+ Set the time until the next expiration of the timer.
+
+ @param timer Timer object.
+ @param time Amount of time (in milliseconds) before the timer expires.
+
+ @return On success, 0.
+ On error, -1 is returned, and errno is set to indicate the error.
+*/
+
+int
+my_timer_set(my_timer_t *timer, unsigned long time)
+{
+ struct kevent kev;
+
+ EV_SET(&kev, timer->id, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, time, timer);
+
+ return kevent(kq_fd, &kev, 1, NULL, 0, NULL);
+}
+
+
+/**
+ Reset the time until the next expiration of the timer.
+
+ @param timer Timer object.
+ @param state The state of the timer at the time of cancellation, either
+ signaled (false) or nonsignaled (true).
+
+ @return On success, 0.
+ On error, -1 is returned, and errno is set to indicate the error.
+*/
+
+int
+my_timer_reset(my_timer_t *timer, bool *state)
+{
+ int status;
+ struct kevent kev;
+
+ EV_SET(&kev, timer->id, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
+
+ status= kevent(kq_fd, &kev, 1, NULL, 0, NULL);
+
+ /*
+ If the event was retrieved from the kqueue (at which point we
+ consider it to be signaled), the timer was automatically deleted.
+ */
+ if (!status)
+ *state= 1;
+ else if (errno == ENOENT)
+ {
+ *state= 0;
+ status= 0;
+ }
+
+ return status;
+}
+
+
+/**
+ Delete a timer object.
+
+ @param timer Timer object.
+*/
+
+void
+my_timer_delete(my_timer_t *timer)
+{
+ struct kevent kev;
+
+ EV_SET(&kev, timer->id, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
+
+ kevent(kq_fd, &kev, 1, NULL, 0, NULL);
+}
+
View
256 mysys/posix_timers.c
@@ -0,0 +1,256 @@
+/* Copyright (c) 2012, Twitter, Inc. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "my_timer.h" /* my_timer_t */
+#include "my_pthread.h" /* my_thread_init, my_thread_end */
+#include <sys/syscall.h> /* SYS_gettid */
+#include <string.h> /* memset */
+
+#ifndef sigev_notify_thread_id
+#define sigev_notify_thread_id _sigev_un._tid
+#endif
+
+#define MY_TIMER_EVENT_SIGNO (SIGRTMIN)
+#define MY_TIMER_KILL_SIGNO (SIGRTMIN+1)
+
+/* Timer thread object. */
+static pthread_t thread;
+
+/* Timer thread ID (TID). */
+static pid_t thread_id;
+
+/**
+ Timer expiration notification function.
+
+ @param sigev_value Signal (notification) value.
+
+ @remark The notification function is usually run in a helper thread
+ and is called each time the timer expires.
+*/
+
+static void
+timer_notify_function(sigval_t sigev_value)
+{
+ my_timer_t *timer= sigev_value.sival_ptr;
+ timer->notify_function(timer);
+}
+
+
+/**
+ Timer expiration notification thread.
+
+ @param arg Barrier object.
+*/
+
+static void *
+timer_notify_thread(void *arg)
+{
+ sigset_t set;
+ siginfo_t info;
+ pthread_barrier_t *barrier= arg;
+
+ my_thread_init();
+
+ sigemptyset(&set);
+ sigaddset(&set, MY_TIMER_EVENT_SIGNO);
+ sigaddset(&set, MY_TIMER_KILL_SIGNO);
+
+ /* Get the thread ID of the current thread. */
+ thread_id= (pid_t) syscall(SYS_gettid);
+
+ /* Wake up parent thread, thread_id is available. */
+ pthread_barrier_wait(barrier);
+
+ while (1)
+ {
+ if (sigwaitinfo(&set, &info) < 0)
+ continue;
+
+ if (info.si_signo == MY_TIMER_EVENT_SIGNO)
+ timer_notify_function(info.si_value);
+ else if (info.si_signo == MY_TIMER_KILL_SIGNO)
+ break;
+ }
+
+ my_thread_end();
+
+ return NULL;
+}
+
+
+/**
+ Create a helper thread to dispatch timer expiration notifications.
+
+ @return On success, 0. On error, -1 is returned.
+*/
+
+static int
+start_helper_thread(void)
+{
+ pthread_barrier_t barrier;
+
+ if (pthread_barrier_init(&barrier, NULL, 2))
+ return -1;
+
+ if (pthread_create(&thread, NULL, timer_notify_thread, &barrier))
+ return -1;
+
+ pthread_barrier_wait(&barrier);
+ pthread_barrier_destroy(&barrier);
+
+ return 0;
+}
+
+
+/**
+ Initialize internal components.
+
+ @return On success, 0.
+ On error, -1 is returned, and errno is set to indicate the error.
+*/
+
+int
+my_timer_init_ext(void)
+{
+ int rc;
+ sigset_t set, old_set;
+
+ if (sigfillset(&set))
+ return -1;
+
+ /*
+ Temporarily block all signals. New thread will inherit signal
+ mask of the current thread.
+ */
+ if (pthread_sigmask(SIG_BLOCK, &set, &old_set))
+ return -1;
+
+ /* Create a helper thread. */
+ rc= start_helper_thread();
+
+ /* Restore the signal mask. */
+ pthread_sigmask(SIG_SETMASK, &old_set, NULL);
+
+ return rc;
+}
+
+
+/**
+ Release any resources that were allocated as part of initialization.
+*/
+
+void
+my_timer_deinit(void)
+{
+ /* Kill helper thread. */
+ pthread_kill(thread, MY_TIMER_KILL_SIGNO);
+
+ /* Wait for helper thread termination. */
+ pthread_join(thread, NULL);
+}
+
+
+/**
+ Create a timer object.
+
+ @param timer Location where the timer ID is returned.
+
+ @return On success, 0.
+ On error, -1 is returned, and errno is set to indicate the error.
+*/
+
+int
+my_timer_create(my_timer_t *timer)
+{
+ struct sigevent sigev;
+
+ memset(&sigev, 0, sizeof(sigev));
+
+ sigev.sigev_value.sival_ptr= timer;
+ sigev.sigev_signo= MY_TIMER_EVENT_SIGNO;
+ sigev.sigev_notify= SIGEV_SIGNAL | SIGEV_THREAD_ID;
+ sigev.sigev_notify_thread_id= thread_id;
+
+ return timer_create(CLOCK_MONOTONIC, &sigev, &timer->id);
+}
+
+
+/**
+ Set the time until the next expiration of the timer.
+
+ @param timer Timer object.
+ @param time Amount of time (in milliseconds) before the timer expires.
+
+ @return On success, 0.
+ On error, -1 is returned, and errno is set to indicate the error.
+*/
+
+int
+my_timer_set(my_timer_t *timer, unsigned long time)
+{
+ const struct itimerspec spec= {
+ .it_interval= {.tv_sec= 0, .tv_nsec= 0},
+ .it_value= {.tv_sec= time / 1000,
+ .tv_nsec= (time % 1000) * 1000000}
+ };
+
+ return timer_settime(timer->id, 0, &spec, NULL);
+}
+
+
+/**
+ Reset the time until the next expiration of the timer.
+
+ @param timer Timer object.
+ @param state The state of the timer at the time of cancellation, either
+ signaled (false) or nonsignaled (true).
+
+ @return On success, 0.
+ On error, -1 is returned, and errno is set to indicate the error.
+*/
+
+int
+my_timer_reset(my_timer_t *timer, bool *state)
+{
+ int status;
+ struct itimerspec old_spec;
+
+ /* A zeroed initial expiration value disarms the timer. */
+ const struct timespec zero_time= { .tv_sec= 0, .tv_nsec= 0 };
+ const struct itimerspec zero_spec= { .it_value= zero_time };
+
+ /*
+ timer_settime returns the amount of time before the timer
+ would have expired or zero if the timer was disarmed.
+ */
+ if (! (status= timer_settime(timer->id, 0, &zero_spec, &old_spec)))
+ *state= old_spec.it_value.tv_sec || old_spec.it_value.tv_nsec;
+
+ return status;
+}
+
+
+/**
+ Delete a timer object.
+
+ @param timer Timer object.
+*/
+
+void
+my_timer_delete(my_timer_t *timer)
+{
+ timer_delete(timer->id);
+}
+
View
2  scripts/mysql_system_tables.sql
@@ -28,7 +28,7 @@ set @had_db_table= @@warning_count != 0;
CREATE TABLE IF NOT EXISTS host ( Host char(60) binary DEFAULT '' NOT NULL, Db char(64) binary DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, PRIMARY KEY Host (Host,Db) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Host privileges; Merged with database privileges';
-CREATE TABLE IF NOT EXISTS user ( Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Password char(41) character set latin1 collate latin1_bin DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Reload_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Shutdown_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Process_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, File_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_db_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Super_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_slave_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_client_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_user_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tablespace_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL, ssl_cipher BLOB NOT NULL, x509_issuer BLOB NOT NULL, x509_subject BLOB NOT NULL, max_questions int(11) unsigned DEFAULT 0 NOT NULL, max_updates int(11) unsigned DEFAULT 0 NOT NULL, max_connections int(11) unsigned DEFAULT 0 NOT NULL, max_user_connections int(11) unsigned DEFAULT 0 NOT NULL, plugin char(64) DEFAULT '', authentication_string TEXT, PRIMARY KEY Host (Host,User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges';
+CREATE TABLE IF NOT EXISTS user ( Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Password char(41) character set latin1 collate latin1_bin DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Reload_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Shutdown_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Process_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, File_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_db_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Super_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_slave_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_client_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_user_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tablespace_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL, ssl_cipher BLOB NOT NULL, x509_issuer BLOB NOT NULL, x509_subject BLOB NOT NULL, max_questions int(11) unsigned DEFAULT 0 NOT NULL, max_updates int(11) unsigned DEFAULT 0 NOT NULL, max_connections int(11) unsigned DEFAULT 0 NOT NULL, max_user_connections int(11) unsigned DEFAULT 0 NOT NULL, plugin char(64) DEFAULT '', authentication_string TEXT, max_statement_time int(11) unsigned DEFAULT 0 NOT NULL, PRIMARY KEY Host (Host,User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges';
-- Remember for later if user table already existed
set @had_user_table= @@warning_count != 0;
View
8 scripts/mysql_system_tables_data.sql
@@ -36,10 +36,10 @@ DROP TABLE tmp_db;
-- from local machine if "users" table didn't exist before
CREATE TEMPORARY TABLE tmp_user LIKE user;
set @current_hostname= @@hostname;
-INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','');
-REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','' FROM dual WHERE LOWER( @current_hostname) != 'localhost';
-REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','');
-REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','');
+INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','', 0);
+REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','',0 FROM dual WHERE LOWER( @current_hostname) != 'localhost';
+REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','',0);
+REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','',0);
INSERT INTO tmp_user (host,user) VALUES ('localhost','');
INSERT INTO tmp_user (host,user) SELECT @current_hostname,'' FROM dual WHERE LOWER(@current_hostname ) != 'localhost';
INSERT INTO user SELECT * FROM tmp_user WHERE @had_user_table=0;
View
4 scripts/mysql_system_tables_fix.sql
@@ -652,6 +652,10 @@ INSERT INTO tmp_proxies_priv VALUES ('localhost', 'root', '', '', TRUE, '', now(
INSERT INTO proxies_priv SELECT * FROM tmp_proxies_priv WHERE @had_proxies_priv_table=0;
DROP TABLE tmp_proxies_priv;
+--
+-- Custom columns should be the last columns of the user table.
+--
+ALTER TABLE user ADD max_statement_time int(11) unsigned DEFAULT 0 NOT NULL;
# Activate the new, possible modified privilege tables
# This should not be needed, but gives us some extra testing that the above
View
4 sql/CMakeLists.txt
@@ -80,6 +80,10 @@ SET (SQL_SOURCE
${GEN_SOURCES}
${MYSYS_LIBWRAP_SOURCE})
+IF(HAVE_MY_TIMER)
+ SET(SQL_SOURCE ${SQL_SOURCE} sql_timer.cc)
+ENDIF()
+
MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT STATIC_ONLY
RECOMPILE_FOR_EMBEDDED)
View
18 sql/handler.cc
@@ -651,6 +651,24 @@ void ha_close_connection(THD* thd)
plugin_foreach(thd, closecon_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, 0);
}
+static my_bool kill_handlerton(THD *thd, plugin_ref plugin, void *)
+{
+ handlerton *hton= plugin_data(plugin, handlerton *);
+
+ if (hton->state == SHOW_OPTION_YES && hton->kill_connection)
+ {
+ if (thd_get_ha_data(thd, hton))
+ hton->kill_connection(hton, thd);
+ }
+
+ return FALSE;
+}
+
+void ha_kill_connection(THD *thd)
+{
+ plugin_foreach(thd, kill_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, 0);
+}
+
/* ========================================================================
======================= TRANSACTIONS ===================================*/
View
4 sql/handler.h
@@ -788,6 +788,9 @@ struct handlerton
longlong (*control)(handlerton *hton, const char *cmd,
Item **args, uint arg_count);
+ /* Terminate connection/statement notification. */
+ void (*kill_connection)(handlerton *hton, THD *thd);
+
/*
Iterators creator.
Presence of the pointer should be checked before using
@@ -2222,6 +2225,7 @@ int ha_finalize_handlerton(st_plugin_int *plugin);
TYPELIB *ha_known_exts(void);
int ha_panic(enum ha_panic_function flag);
void ha_close_connection(THD* thd);
+void ha_kill_connection(THD *thd);
bool ha_flush_logs(handlerton *db_type);
void ha_drop_database(char* path);
int ha_create_table(THD *thd, const char *path,
View
1  sql/lex.h
@@ -335,6 +335,7 @@ static SYMBOL symbols[] = {
{ "MATCH", SYM(MATCH)},
{ "MAX_CONNECTIONS_PER_HOUR", SYM(MAX_CONNECTIONS_PER_HOUR)},
{ "MAX_QUERIES_PER_HOUR", SYM(MAX_QUERIES_PER_HOUR)},
+ { "MAX_STATEMENT_TIME", SYM(MAX_STATEMENT_TIME_SYM)},
{ "MAX_ROWS", SYM(MAX_ROWS)},
{ "MAX_SIZE", SYM(MAX_SIZE_SYM)},
{ "MAX_UPDATES_PER_HOUR", SYM(MAX_UPDATES_PER_HOUR)},
View
22 sql/mysqld.cc
@@ -92,6 +92,8 @@
#include "sp_cache.h"
#include "sql_reload.h" // reload_acl_and_cache
+#include "my_timer.h" // my_timer_init_ext, my_timer_deinit
+
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
@@ -621,6 +623,8 @@ SHOW_COMP_OPTION have_geometry, have_rtree_keys;
SHOW_COMP_OPTION have_crypt, have_compress;
SHOW_COMP_OPTION have_profiling;
+SHOW_COMP_OPTION have_statement_timeout;
+
/* Thread specific variables */
pthread_key(MEM_ROOT**,THR_MALLOC);
@@ -1525,6 +1529,11 @@ void clean_up(bool print_message)
mysql_mutex_unlock(&LOCK_thread_count);
sys_var_end();
+#ifdef HAVE_MY_TIMER
+ if (have_statement_timeout == SHOW_OPTION_YES)
+ my_timer_deinit();
+#endif
+
/*
The following lines may never be executed as the main thread may have
killed us
@@ -3642,6 +3651,16 @@ static int init_server_components()
if (table_def_init() | hostname_cache_init())
unireg_abort(1);
+#ifdef HAVE_MY_TIMER
+ have_statement_timeout= my_timer_init_ext() ?
+ SHOW_OPTION_DISABLED : SHOW_OPTION_YES;
+
+ if (have_statement_timeout == SHOW_OPTION_DISABLED)
+ sql_print_error("Failed to initialize timer component (errno %d).", errno);
+#else
+ have_statement_timeout= SHOW_OPTION_NO;
+#endif
+
query_cache_set_min_res_unit(query_cache_min_res_unit);
query_cache_init();
query_cache_resize(query_cache_size);
@@ -6426,6 +6445,9 @@ SHOW_VAR status_vars[]= {
{"Key_writes", (char*) offsetof(KEY_CACHE, global_cache_write), SHOW_KEY_CACHE_LONGLONG},
{"Last_query_cost", (char*) offsetof(STATUS_VAR, last_query_cost), SHOW_DOUBLE_STATUS},
{"Max_used_connections", (char*) &max_used_connections, SHOW_LONG},
+ {"Max_statement_time_exceeded", (char*) offsetof(STATUS_VAR, max_statement_time_exceeded), SHOW_LONG_STATUS},
+ {"Max_statement_time_set", (char*) offsetof(STATUS_VAR, max_statement_time_set), SHOW_LONG_STATUS},
+ {"Max_statement_time_set_failed", (char*) offsetof(STATUS_VAR, max_statement_time_set_failed), SHOW_LONG_STATUS},
{"Not_flushed_delayed_rows", (char*) &delayed_rows_in_use, SHOW_LONG_NOFLUSH},
{"Open_files", (char*) &my_file_opened, SHOW_LONG_NOFLUSH},
{"Open_streams", (char*) &my_stream_opened, SHOW_LONG_NOFLUSH},
View
1  sql/set_var.h
@@ -297,6 +297,7 @@ extern SHOW_COMP_OPTION have_query_cache;
extern SHOW_COMP_OPTION have_geometry, have_rtree_keys;
extern SHOW_COMP_OPTION have_crypt;
extern SHOW_COMP_OPTION have_compress;
+extern SHOW_COMP_OPTION have_statement_timeout;
/*
Prototypes for helper functions
View
5 sql/share/errmsg-utf8.txt
@@ -2,7 +2,10 @@ languages czech=cze latin2, danish=dan latin1, dutch=nla latin1, english=eng lat
default-language eng
-start-error-number 997
+start-error-number 996
+
+ER_QUERY_TIMEOUT 70100
+ eng "Query execution was interrupted, max_statement_time exceeded"
ER_HTON_CONTROL_INVALID_ARGUMENT
eng "Invalid control command argument"
View
3  sql/signal_handler.cc
@@ -176,6 +176,9 @@ extern "C" sig_handler handle_fatal_signal(int sig)
case THD::KILL_QUERY:
kreason= "KILL_QUERY";
break;
+ case THD::KILL_TIMEOUT:
+ kreason= "KILL_TIMEOUT";
+ break;
case THD::KILLED_NO_VALUE:
kreason= "KILLED_NO_VALUE";
break;
View
42 sql/sql_acl.cc
@@ -925,6 +925,22 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
user.auth_string.length= strlen(user.auth_string.str);
}
}
+
+ /*
+ Find custom fields by name as these should always be located
+ at the end of the table as to avoid messing with the index
+ of fields that are added later. In other words, custom fields
+ have no specific position within a table and should always be
+ the last columns of a table.
+ */
+ {
+ Field *field;
+
+ if ((field= find_field_in_table_sef(table, "max_statement_time")))
+ user.user_resource.statement_timeout= (uint) field->val_int();
+ else
+ user.user_resource.statement_timeout= 0;
+ }
}
else
{
@@ -1418,6 +1434,8 @@ static void acl_update_user(const char *user, const char *host,
acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
acl_user->user_resource.user_conn= mqh->user_conn;
+ if (mqh->specified_limits & USER_RESOURCES::STATEMENT_TIMEOUT)
+ acl_user->user_resource.statement_timeout= mqh->statement_timeout;
if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
{
acl_user->ssl_type= ssl_type;
@@ -2396,6 +2414,18 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
goto end;
}
}
+
+ /*
+ Find custom fields by name as they have no specific position
+ within a table.
+ */
+ {
+ Field *field;
+
+ if ((field= find_field_in_table_sef(table, "max_statement_time")) &&
+ mqh.specified_limits & USER_RESOURCES::STATEMENT_TIMEOUT)
+ field->store(mqh.statement_timeout, TRUE);
+ }
}
if (old_row_exists)
@@ -5290,7 +5320,8 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
(acl_user->user_resource.questions ||
acl_user->user_resource.updates ||
acl_user->user_resource.conn_per_hour ||
- acl_user->user_resource.user_conn))
+ acl_user->user_resource.user_conn) ||
+ acl_user->user_resource.statement_timeout)
{
global.append(STRING_WITH_LEN(" WITH"));
if (want_access & GRANT_ACL)
@@ -5303,6 +5334,8 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
"MAX_CONNECTIONS_PER_HOUR");
add_user_option(&global, acl_user->user_resource.user_conn,
"MAX_USER_CONNECTIONS");
+ add_user_option(&global, acl_user->user_resource.statement_timeout,
+ "MAX_STATEMENT_TIME");
}
protocol->prepare_for_resend();
protocol->store(global.ptr(),global.length(),global.charset());
@@ -9447,7 +9480,8 @@ acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len)
/* Don't allow the user to connect if he has done too many queries */
if ((acl_user->user_resource.questions || acl_user->user_resource.updates ||
acl_user->user_resource.conn_per_hour ||
- acl_user->user_resource.user_conn ||
+ acl_user->user_resource.user_conn ||
+ acl_user->user_resource.statement_timeout ||
global_system_variables.max_user_connections) &&
get_or_create_user_conn(thd,
(opt_old_style_user_limits ? sctx->user : sctx->priv_user),
@@ -9531,6 +9565,10 @@ acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len)
thd->net.skip_big_packet= TRUE;
#endif
+ if (thd->user_connect)
+ thd->variables.max_statement_time=
+ thd->user_connect->user_resources.statement_timeout;
+
/* Ready to handle queries */
DBUG_RETURN(0);
}
View
58 sql/sql_class.cc
@@ -61,6 +61,8 @@
#include "sql_parse.h" // is_update_query
#include "sql_callback.h"