diff options
-rw-r--r-- | mysql-test/r/system_mysql_db.result | 2 | ||||
-rw-r--r-- | mysql-test/r/user_limits-2.result | 2 | ||||
-rw-r--r-- | mysql-test/r/user_limits.result | 37 | ||||
-rw-r--r-- | mysql-test/r/variables.result | 3 | ||||
-rw-r--r-- | mysql-test/t/subselect_mat_cost-master.opt | 1 | ||||
-rw-r--r-- | mysql-test/t/user_limits-2.test | 11 | ||||
-rw-r--r-- | mysql-test/t/user_limits-master.opt | 1 | ||||
-rw-r--r-- | mysql-test/t/user_limits.test | 41 | ||||
-rw-r--r-- | mysql-test/t/variables-master.opt | 1 | ||||
-rw-r--r-- | mysql-test/t/variables.test | 3 | ||||
-rw-r--r-- | scripts/Makefile.am | 5 | ||||
-rw-r--r-- | scripts/mysql_system_tables.sql | 2 | ||||
-rw-r--r-- | scripts/mysql_system_tables_fix.sql | 5 | ||||
-rw-r--r-- | sql/item_func.cc | 8 | ||||
-rw-r--r-- | sql/log.cc | 11 | ||||
-rw-r--r-- | sql/mysql_priv.h | 3 | ||||
-rw-r--r-- | sql/mysqld.cc | 14 | ||||
-rw-r--r-- | sql/set_var.cc | 14 | ||||
-rw-r--r-- | sql/sql_acl.cc | 29 | ||||
-rw-r--r-- | sql/sql_connect.cc | 13 | ||||
-rw-r--r-- | sql/sql_show.cc | 2 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 10 | ||||
-rw-r--r-- | sql/structs.h | 9 |
23 files changed, 173 insertions, 54 deletions
diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result index d3cbc134de9..5079d72aaea 100644 --- a/mysql-test/r/system_mysql_db.result +++ b/mysql-test/r/system_mysql_db.result @@ -117,7 +117,7 @@ user CREATE TABLE `user` ( `max_questions` int(11) unsigned NOT NULL DEFAULT '0', `max_updates` int(11) unsigned NOT NULL DEFAULT '0', `max_connections` int(11) unsigned NOT NULL DEFAULT '0', - `max_user_connections` int(11) unsigned NOT NULL DEFAULT '0', + `max_user_connections` int(11) NOT NULL DEFAULT '0', `plugin` char(60) CHARACTER SET latin1 NOT NULL DEFAULT '', `auth_string` text COLLATE utf8_bin NOT NULL, PRIMARY KEY (`Host`,`User`) diff --git a/mysql-test/r/user_limits-2.result b/mysql-test/r/user_limits-2.result new file mode 100644 index 00000000000..d9daec5c089 --- /dev/null +++ b/mysql-test/r/user_limits-2.result @@ -0,0 +1,2 @@ +set global max_user_connections=100; +ERROR HY000: The MySQL server is running with the --max-user-connections=0 option so it cannot execute this statement diff --git a/mysql-test/r/user_limits.result b/mysql-test/r/user_limits.result index a94eb4616d1..776a25d55e6 100644 --- a/mysql-test/r/user_limits.result +++ b/mysql-test/r/user_limits.result @@ -1,3 +1,4 @@ +set @my_max_user_connections= @@global.max_user_connections; drop table if exists t1; create table t1 (i int); delete from mysql.user where user like 'mysqltest\_%'; @@ -12,9 +13,9 @@ i select * from t1; i select * from t1; -ERROR 42000: User 'mysqltest_1' has exceeded the 'max_questions' resource (current value: 2) +ERROR 42000: User 'mysqltest_1' has exceeded the 'max_queries_per_hour' resource (current value: 2) select * from t1; -ERROR 42000: User 'mysqltest_1' has exceeded the 'max_questions' resource (current value: 2) +ERROR 42000: User 'mysqltest_1' has exceeded the 'max_queries_per_hour' resource (current value: 2) drop user mysqltest_1@localhost; grant usage on *.* to mysqltest_1@localhost with max_updates_per_hour 2; flush user_resources; @@ -27,11 +28,11 @@ i delete from t1; delete from t1; delete from t1; -ERROR 42000: User 'mysqltest_1' has exceeded the 'max_updates' resource (current value: 2) +ERROR 42000: User 'mysqltest_1' has exceeded the 'max_updates_per_hour' resource (current value: 2) select * from t1; i delete from t1; -ERROR 42000: User 'mysqltest_1' has exceeded the 'max_updates' resource (current value: 2) +ERROR 42000: User 'mysqltest_1' has exceeded the 'max_updates_per_hour' resource (current value: 2) select * from t1; i drop user mysqltest_1@localhost; @@ -65,10 +66,20 @@ select * from t1; i connect(localhost,mysqltest_1,,test,MYSQL_PORT,MYSQL_SOCK); ERROR 42000: User 'mysqltest_1' has exceeded the 'max_user_connections' resource (current value: 3) +grant usage on *.* to mysqltest_1@localhost with max_user_connections -1; +show grants for mysqltest_1@localhost; +Grants for mysqltest_1@localhost +GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_USER_CONNECTIONS -1 +flush user_resources; +show grants for mysqltest_1@localhost; +Grants for mysqltest_1@localhost +GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_USER_CONNECTIONS -1 +connect(localhost,mysqltest_1,,test,MYSQL_PORT,MYSQL_SOCK); +ERROR 42000: User 'mysqltest_1' has exceeded the 'max_user_connections' resource (current value: -1) drop user mysqltest_1@localhost; select @@session.max_user_connections, @@global.max_user_connections; @@session.max_user_connections @@global.max_user_connections -0 0 +1000 1000 set session max_user_connections= 2; ERROR HY000: Variable 'max_user_connections' is a GLOBAL variable and should be set with SET GLOBAL set global max_user_connections= 2; @@ -92,5 +103,21 @@ select @@session.max_user_connections, @@global.max_user_connections; connect(localhost,mysqltest_1,,test,MYSQL_PORT,MYSQL_SOCK); ERROR 42000: User 'mysqltest_1' has exceeded the 'max_user_connections' resource (current value: 3) set global max_user_connections= 0; +grant usage on *.* to mysqltest_1@localhost with max_user_connections 0; +set global max_user_connections=-1; +show variables like "max_user_user_connections"; +Variable_name Value +select @@max_user_connections; +@@max_user_connections +-1 +select @@global.max_user_connections; +@@global.max_user_connections +-1 +connect(localhost,mysqltest_1,,test,MYSQL_PORT,MYSQL_SOCK); +ERROR 42000: User mysqltest_1 already has more than 'max_user_connections' active connections +set global max_user_connections=1; +connect(localhost,mysqltest_1,,test,MYSQL_PORT,MYSQL_SOCK); +ERROR 42000: User mysqltest_1 already has more than 'max_user_connections' active connections drop user mysqltest_1@localhost; drop table t1; +set global max_user_connections= @my_max_user_connections; diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result index f2eb58b5b4f..44eddb18667 100644 --- a/mysql-test/r/variables.result +++ b/mysql-test/r/variables.result @@ -14,6 +14,7 @@ set @my_max_delayed_threads =@@global.max_delayed_threads; set @my_max_heap_table_size =@@global.max_heap_table_size; set @my_max_insert_delayed_threads=@@global.max_insert_delayed_threads; set @my_max_join_size =@@global.max_join_size; +set @my_max_user_connections =@@global.max_user_connections; set @my_myisam_data_pointer_size =@@global.myisam_data_pointer_size; set @my_myisam_max_sort_file_size =@@global.myisam_max_sort_file_size; set @my_net_buffer_length =@@global.net_buffer_length; @@ -1049,7 +1050,7 @@ set global max_delayed_threads =@my_max_delayed_threads; set global max_heap_table_size =@my_max_heap_table_size; set global max_insert_delayed_threads=@my_max_insert_delayed_threads; set global max_join_size =@my_max_join_size; -set global max_user_connections =default; +set global max_user_connections =@my_max_user_connections; set global max_write_lock_count =default; set global myisam_data_pointer_size =@my_myisam_data_pointer_size; set global myisam_max_sort_file_size =@my_myisam_max_sort_file_size; diff --git a/mysql-test/t/subselect_mat_cost-master.opt b/mysql-test/t/subselect_mat_cost-master.opt new file mode 100644 index 00000000000..dc7ac6cc205 --- /dev/null +++ b/mysql-test/t/subselect_mat_cost-master.opt @@ -0,0 +1 @@ +--log-output=TABLE,FILE --log --log-slow-queries --slow-query-log=1 diff --git a/mysql-test/t/user_limits-2.test b/mysql-test/t/user_limits-2.test new file mode 100644 index 00000000000..a376c78a09b --- /dev/null +++ b/mysql-test/t/user_limits-2.test @@ -0,0 +1,11 @@ +# +# Test behavior of various per-account limits (aka quotas) +# +--source include/not_embedded.inc + +# +# We will get an error as it was set to 0 at startup +# +--error ER_OPTION_PREVENTS_STATEMENT +set global max_user_connections=100; + diff --git a/mysql-test/t/user_limits-master.opt b/mysql-test/t/user_limits-master.opt new file mode 100644 index 00000000000..107b2e4a27f --- /dev/null +++ b/mysql-test/t/user_limits-master.opt @@ -0,0 +1 @@ +--max-user-connections=1000 diff --git a/mysql-test/t/user_limits.test b/mysql-test/t/user_limits.test index 41af032b97e..becaa0963cc 100644 --- a/mysql-test/t/user_limits.test +++ b/mysql-test/t/user_limits.test @@ -8,6 +8,8 @@ # Save the initial number of concurrent sessions --source include/count_sessions.inc +set @my_max_user_connections= @@global.max_user_connections; + # Prepare play-ground --disable_warnings drop table if exists t1; @@ -120,8 +122,18 @@ select * from t1; --replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK --error ER_USER_LIMIT_REACHED connect (muc5, localhost, mysqltest_1,,); -# Clean up + connection default; +# Test with negative max_user_connections +grant usage on *.* to mysqltest_1@localhost with max_user_connections -1; +show grants for mysqltest_1@localhost; +flush user_resources; +show grants for mysqltest_1@localhost; +--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK +--error ER_USER_LIMIT_REACHED +connect (muc5, localhost, mysqltest_1,,); + +# Clean up disconnect muc2; disconnect muc3; disconnect muc4; @@ -165,12 +177,37 @@ disconnect muca1; disconnect muca2; disconnect muca3; set global max_user_connections= 0; -drop user mysqltest_1@localhost; --enable_ps_protocol +# +# Test setting negative values of max_user_connections +# +grant usage on *.* to mysqltest_1@localhost with max_user_connections 0; +set global max_user_connections=-1; +show variables like "max_user_user_connections"; +select @@max_user_connections; +select @@global.max_user_connections; +# Check that we can't connect anymore except as root +--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK +--error ER_TOO_MANY_USER_CONNECTIONS +connect (muca2, localhost, mysqltest_1,,); +connect (muca2, localhost, root,,); +disconnect muca2; +connection default; +set global max_user_connections=1; +# Check that we can connect one time, not two +connect (muca2, localhost, mysqltest_1,,); +--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK +--error ER_TOO_MANY_USER_CONNECTIONS +connect (muca3, localhost, mysqltest_1,,); +disconnect muca2; +connection default; +drop user mysqltest_1@localhost; + # Final cleanup drop table t1; # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc +set global max_user_connections= @my_max_user_connections; diff --git a/mysql-test/t/variables-master.opt b/mysql-test/t/variables-master.opt new file mode 100644 index 00000000000..e4b213a0323 --- /dev/null +++ b/mysql-test/t/variables-master.opt @@ -0,0 +1 @@ +--max-user-connections=1 diff --git a/mysql-test/t/variables.test b/mysql-test/t/variables.test index 38bbd2c9de8..6a9d0372c0a 100644 --- a/mysql-test/t/variables.test +++ b/mysql-test/t/variables.test @@ -23,6 +23,7 @@ set @my_max_delayed_threads =@@global.max_delayed_threads; set @my_max_heap_table_size =@@global.max_heap_table_size; set @my_max_insert_delayed_threads=@@global.max_insert_delayed_threads; set @my_max_join_size =@@global.max_join_size; +set @my_max_user_connections =@@global.max_user_connections; set @my_myisam_data_pointer_size =@@global.myisam_data_pointer_size; set @my_myisam_max_sort_file_size =@@global.myisam_max_sort_file_size; set @my_net_buffer_length =@@global.net_buffer_length; @@ -830,7 +831,7 @@ set global max_delayed_threads =@my_max_delayed_threads; set global max_heap_table_size =@my_max_heap_table_size; set global max_insert_delayed_threads=@my_max_insert_delayed_threads; set global max_join_size =@my_max_join_size; -set global max_user_connections =default; +set global max_user_connections =@my_max_user_connections; set global max_write_lock_count =default; set global myisam_data_pointer_size =@my_myisam_data_pointer_size; set global myisam_max_sort_file_size =@my_myisam_max_sort_file_size; diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 16180ce0f6a..e7504453fbc 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -117,7 +117,10 @@ mysqlbug: ${top_builddir}/config.status mysqlbug.sh mysql_fix_privilege_tables.sql: mysql_system_tables.sql \ mysql_system_tables_fix.sql @echo "Building $@"; - @cat mysql_system_tables.sql mysql_system_tables_fix.sql > $@ + @echo "-- This file is automaticly generated from" > $@ + @echo "-- mysql_system_tables.sql & mysql_system_tables_fix.sql" >> $@ + @echo "" >> $@ + @cat mysql_system_tables.sql mysql_system_tables_fix.sql >> $@ # # Build mysql_fix_privilege_tables_sql.c from diff --git a/scripts/mysql_system_tables.sql b/scripts/mysql_system_tables.sql index 468517d27cd..c21d9525bb2 100644 --- a/scripts/mysql_system_tables.sql +++ b/scripts/mysql_system_tables.sql @@ -13,7 +13,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, 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(60) CHARACTER SET latin1 DEFAULT '' NOT NULL, auth_string TEXT NOT NULL, 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, 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) DEFAULT 0 NOT NULL, plugin char(60) CHARACTER SET latin1 DEFAULT '' NOT NULL, auth_string TEXT 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; diff --git a/scripts/mysql_system_tables_fix.sql b/scripts/mysql_system_tables_fix.sql index 245bfe01c0a..7d791a5fc61 100644 --- a/scripts/mysql_system_tables_fix.sql +++ b/scripts/mysql_system_tables_fix.sql @@ -326,8 +326,11 @@ UPDATE host SET Create_routine_priv=Create_priv, Alter_routine_priv=Alter_priv, # # Add max_user_connections resource limit +# this is signed in MariaDB so that if one sets it's to -1 then the user +# can't connect anymore. # -ALTER TABLE user ADD max_user_connections int(11) unsigned DEFAULT '0' NOT NULL AFTER max_connections; +ALTER TABLE user ADD max_user_connections int(11) DEFAULT '0' NOT NULL AFTER max_connections; +ALTER TABLE user MODIFY max_user_connections int(11) DEFAULT '0' NOT NULL AFTER max_connections; # # user.Create_user_priv diff --git a/sql/item_func.cc b/sql/item_func.cc index b907d1f432e..ce43f90be8d 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -5063,12 +5063,16 @@ void Item_func_get_system_var::fix_length_and_dec() switch (var->show_type()) { case SHOW_LONG: - case SHOW_INT: case SHOW_HA_ROWS: unsigned_flag= TRUE; max_length= MY_INT64_NUM_DECIMAL_DIGITS; decimals=0; break; + case SHOW_INT: + unsigned_flag= FALSE; + max_length= MY_INT64_NUM_DECIMAL_DIGITS; + decimals=0; + break; case SHOW_LONGLONG: unsigned_flag= TRUE; max_length= MY_INT64_NUM_DECIMAL_DIGITS; @@ -5209,7 +5213,7 @@ longlong Item_func_get_system_var::val_int() switch (var->show_type()) { - case SHOW_INT: get_sys_var_safe (uint); + case SHOW_INT: get_sys_var_safe (int); case SHOW_LONG: get_sys_var_safe (ulong); case SHOW_LONGLONG: get_sys_var_safe (ulonglong); case SHOW_HA_ROWS: get_sys_var_safe (ha_rows); diff --git a/sql/log.cc b/sql/log.cc index 649db6a4c29..332cdce803e 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1062,17 +1062,6 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, query_length= command_name[thd->command].length; } - if (!query_length) - { - /* - Not a real query; Reset counts for slow query logging - (QQ: Wonder if this is really needed) - */ - thd->sent_row_count= thd->examined_row_count= 0; - thd->query_plan_flags= QPLAN_INIT; - thd->query_plan_fsort_passes= 0; - } - for (current_handler= slow_log_handler_list; *current_handler ;) error= (*current_handler++)->log_slow(thd, current_time, user_host_buff, user_host_len, diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 62fdef5aec6..bccd5b189ff 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -2151,7 +2151,8 @@ extern MYSQL_PLUGIN_IMPORT ulong max_connections; extern ulong max_connect_errors, connect_timeout; extern ulong extra_max_connections; extern ulong slave_net_timeout, slave_trans_retries; -extern uint max_user_connections; +extern int max_user_connections; +extern bool max_user_connections_checking; extern ulonglong denied_connections; extern ulong what_to_log,flush_time; extern ulong query_buff_size; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a54f845a762..09fc0258c1c 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -669,7 +669,8 @@ ulong extra_max_connections; */ ulong max_long_data_size; -uint max_user_connections= 0; +int max_user_connections= 0; +bool max_user_connections_checking=0; ulonglong denied_connections; /** Limit of the total number of prepared statements in the server. @@ -2142,6 +2143,7 @@ static bool cache_thread() */ thd->mysys_var->abort= 0; thd->thr_create_utime= microsecond_interval_timer(); + thd->start_utime= thd->thr_create_utime; threads.append(thd); return(1); } @@ -5263,7 +5265,7 @@ void create_thread_to_handle_connection(THD *thd) thread_created++; threads.append(thd); DBUG_PRINT("info",(("creating thread %lu"), thd->thread_id)); - thd->prior_thr_create_utime= thd->start_utime= microsecond_interval_timer(); + thd->prior_thr_create_utime= microsecond_interval_timer(); if ((error=pthread_create(&thd->real_id,&connection_attrib, handle_one_connection, (void*) thd))) @@ -7432,9 +7434,9 @@ each time the SQL thread starts.", &max_system_variables.max_tmp_tables, 0, GET_ULONG, REQUIRED_ARG, 32, 1, (longlong) ULONG_MAX, 0, 1, 0}, {"max_user_connections", OPT_MAX_USER_CONNECTIONS, - "The maximum number of active connections for a single user (0 = no limit).", - &max_user_connections, &max_user_connections, 0, GET_UINT, - REQUIRED_ARG, 0, 0, UINT_MAX, 0, 1, 0}, + "The maximum number of active connections for a single user (0 = no limit. In addition global max_user_connections counting and checking is permanently disabled).", + &max_user_connections, &max_user_connections, 0, GET_INT, + REQUIRED_ARG, 0, 0, INT_MAX, 0, 1, 0}, {"max_write_lock_count", OPT_MAX_WRITE_LOCK_COUNT, "After this many write locks, allow some read locks to run in between.", &max_write_lock_count, &max_write_lock_count, 0, GET_ULONG, @@ -9641,6 +9643,8 @@ static int get_options(int *argc,char **argv) if (!max_long_data_size_used) max_long_data_size= global_system_variables.max_allowed_packet; + /* Rember if max_user_connections was 0 at startup */ + max_user_connections_checking= max_user_connections != 0; return 0; } diff --git a/sql/set_var.cc b/sql/set_var.cc index 0a06b3254f6..10f3e20cc86 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -3082,7 +3082,19 @@ void sys_var_thd_time_zone::set_default(THD *thd, enum_var_type type) bool sys_var_max_user_conn::check(THD *thd, set_var *var) { if (var->type == OPT_GLOBAL) + { + if (! max_user_connections_checking) + { + /* + We can't change the value of max_user_connections from 0 as then + connect counting would be wrong. + */ + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), + "--max-user-connections=0"); + return TRUE; + } return sys_var_thd::check(thd, var); + } else { /* @@ -3098,7 +3110,7 @@ bool sys_var_max_user_conn::update(THD *thd, set_var *var) { DBUG_ASSERT(var->type == OPT_GLOBAL); pthread_mutex_lock(&LOCK_global_system_variables); - max_user_connections= (uint)var->save_result.ulonglong_value; + max_user_connections= (int) var->save_result.ulonglong_value; pthread_mutex_unlock(&LOCK_global_system_variables); return 0; } diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 61a6ac0cb87..ae286878cea 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2089,7 +2089,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, table->field[next_field+2]->store((longlong) mqh.conn_per_hour, TRUE); if (table->s->fields >= 36 && (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS)) - table->field[next_field+3]->store((longlong) mqh.user_conn, TRUE); + table->field[next_field+3]->store((longlong) mqh.user_conn, FALSE); mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour; next_field+=4; @@ -4578,7 +4578,8 @@ ulong get_column_grant(THD *thd, GRANT_INFO *grant, /* Help function for mysql_show_grants */ -static void add_user_option(String *grant, ulong value, const char *name) +static void add_user_option(String *grant, long value, const char *name, + my_bool is_signed) { if (value) { @@ -4586,7 +4587,7 @@ static void add_user_option(String *grant, ulong value, const char *name) grant->append(' '); grant->append(name, strlen(name)); grant->append(' '); - p=int10_to_str(value, buff, 10); + p=int10_to_str(value, buff, is_signed ? -10 : 10); grant->append(buff,p-buff); } } @@ -4768,13 +4769,13 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) if (want_access & GRANT_ACL) global.append(STRING_WITH_LEN(" GRANT OPTION")); add_user_option(&global, acl_user->user_resource.questions, - "MAX_QUERIES_PER_HOUR"); + "MAX_QUERIES_PER_HOUR", 0); add_user_option(&global, acl_user->user_resource.updates, - "MAX_UPDATES_PER_HOUR"); + "MAX_UPDATES_PER_HOUR", 0); add_user_option(&global, acl_user->user_resource.conn_per_hour, - "MAX_CONNECTIONS_PER_HOUR"); + "MAX_CONNECTIONS_PER_HOUR", 0); add_user_option(&global, acl_user->user_resource.user_conn, - "MAX_USER_CONNECTIONS"); + "MAX_USER_CONNECTIONS", 1); } protocol->prepare_for_resend(); protocol->store(global.ptr(),global.length(),global.charset()); @@ -8147,10 +8148,16 @@ bool acl_authenticate(THD *thd, uint connect_errors, DBUG_RETURN(1); } - /* 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 || + /* + Don't allow the user to connect if he has done too many queries. + As we are testing max_user_connections == 0 here, it means that we + can't let the user change max_user_connections from 0 in the server + without a restart as it would lead to wrong connect counting. + */ + if ((acl_user->user_resource.questions || + acl_user->user_resource.updates || acl_user->user_resource.conn_per_hour || - acl_user->user_resource.user_conn || max_user_connections) && + acl_user->user_resource.user_conn || max_user_connections_checking) && get_or_create_user_conn(thd, (opt_old_style_user_limits ? sctx->user : sctx->priv_user), (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host), @@ -8163,7 +8170,7 @@ bool acl_authenticate(THD *thd, uint connect_errors, if (thd->user_connect && (thd->user_connect->user_resources.conn_per_hour || thd->user_connect->user_resources.user_conn || - max_user_connections) && + max_user_connections_checking) && check_for_max_user_connections(thd, thd->user_connect)) { /* Ensure we don't decrement thd->user_connections->connections twice */ diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 5e0ab339418..cd51fd25558 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -113,8 +113,11 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc) DBUG_ENTER("check_for_max_user_connections"); (void) pthread_mutex_lock(&LOCK_user_conn); + + /* Root is not affected by the value of max_user_connections */ if (max_user_connections && !uc->user_resources.user_conn && - max_user_connections < (uint) uc->connections) + max_user_connections < uc->connections && + !(thd->security_ctx->master_access & SUPER_ACL)) { my_error(ER_TOO_MANY_USER_CONNECTIONS, MYF(0), uc->user); goto end; @@ -202,7 +205,7 @@ void time_out_user_resource_limits(THD *thd, USER_CONN *uc) /* If more than a hour since last check, reset resource checking */ if (check_time - uc->reset_utime >= LL(3600000000)) { - uc->questions=1; + uc->questions=0; uc->updates=0; uc->conn_per_hour=0; uc->reset_utime= check_time; @@ -231,7 +234,7 @@ bool check_mqh(THD *thd, uint check_command) if (uc->user_resources.questions && uc->questions++ >= uc->user_resources.questions) { - my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_questions", + my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_queries_per_hour", (long) uc->user_resources.questions); error=1; goto end; @@ -243,7 +246,7 @@ bool check_mqh(THD *thd, uint check_command) (sql_command_flags[check_command] & CF_CHANGES_DATA) && uc->updates++ >= uc->user_resources.updates) { - my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_updates", + my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_updates_per_hour", (long) uc->user_resources.updates); error=1; goto end; @@ -1131,6 +1134,8 @@ pthread_handler_t handle_one_connection(void *arg) THD *thd= (THD*) arg; thd->thr_create_utime= microsecond_interval_timer(); + /* We need to set this because of time_out_user_resource_limits */ + thd->start_utime= thd->thr_create_utime; if (thread_scheduler.init_new_connection_thread()) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 57d6d585997..062bfc347b3 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2439,7 +2439,7 @@ static bool show_status_array(THD *thd, const char *wild, end= strmov(buff, *(my_bool*) value ? "ON" : "OFF"); break; case SHOW_INT: - end= int10_to_str((long) *(uint32*) value, buff, 10); + end= int10_to_str((long) *(int*) value, buff, -10); break; case SHOW_HAVE: { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 08738f14cca..0d0e0ef98a4 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1350,7 +1350,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt optional_flush_tables_arguments opt_dyncol_type dyncol_type - opt_time_precision kill_type kill_option + opt_time_precision kill_type kill_option int_num %type <ulong_num> ulong_num real_ulong_num merge_insert_types @@ -9676,6 +9676,12 @@ delete_limit_clause: } ; +int_num: + NUM { int error; $$= (int) my_strtoll10($1.str, (char**) 0, &error); } + | '-' NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } + | '-' LONG_NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } + ; + ulong_num: NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); } @@ -13434,7 +13440,7 @@ grant_option: lex->mqh.conn_per_hour= $2; lex->mqh.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR; } - | MAX_USER_CONNECTIONS_SYM ulong_num + | MAX_USER_CONNECTIONS_SYM int_num { LEX *lex=Lex; lex->mqh.user_conn= $2; diff --git a/sql/structs.h b/sql/structs.h index 29ccde3fb03..18f90d5b8b1 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -222,8 +222,11 @@ typedef struct user_resources { uint updates; /* Maximum number of connections established per hour. */ uint conn_per_hour; - /* Maximum number of concurrent connections. */ - uint user_conn; + /* + Maximum number of concurrent connections. If -1 then no new + connections allowed + */ + int user_conn; /* Values of this enum and specified_limits member are used by the parser to store which user limits were specified in GRANT statement. @@ -256,7 +259,7 @@ typedef struct user_conn { /* Total length of the key. */ uint len; /* Current amount of concurrent connections for this account. */ - uint connections; + int connections; /* Current number of connections per hour, number of updating statements per hour and total number of statements per hour for this account. |