Allow one to block an account by using GRANT max_user_connections = -1

One can set @@global.max_user_connections to -1 to block anyone, except SUPER user, to login.
If max_user_connection is 0, one can't change it without a restart (needed to get user connections counting to work correctly)


mysql-test/r/system_mysql_db.result:
  Changed max_user_connections to handle negative numbers.
mysql-test/r/user_limits-2.result:
  New test case that one can't change max_user_connection if it was 0
mysql-test/r/user_limits.result:
  Fixed wrong error messages.
mysql-test/r/variables.result:
  Store / restore max_user_connections (needed as there is now a --master.opt file that changes it)
mysql-test/t/subselect_mat_cost-master.opt:
  Enable slow query log (as this test found some errors in slow query logging)
mysql-test/t/user_limits-2.test:
  New test case that one can't change max_user_connection if it was 0
mysql-test/t/user_limits-master.opt:
  Set max_user_connections (as one can't change it if it was 0)
mysql-test/t/user_limits.test:
  Test max_user_connections -1
mysql-test/t/variables-master.opt:
  Set max_user_connections (as one can't change it if it was 0)
mysql-test/t/variables.test:
  Set/restore max_user_connections
scripts/Makefile.am:
  Add a text message to mysql_fix_privilege_tables.sql that it's automaticly generated
scripts/mysql_system_tables.sql:
  Change max_user_connections to signed
scripts/mysql_system_tables_fix.sql:
  Change max_user_connections to signed
sql/item_func.cc:
  Change SHOW_INT to be signed.
  (Needed for max_user_connections and it's probably a bug that it was not originally signed)
sql/log.cc:
  Remove some code that was not needed (All these variables are reset at start of query)
sql/mysql_priv.h:
  Made max_user_connections signed.
  Added max_user_connections_checking
sql/mysqld.cc:
  Added max_user_connections_checking so that we know if max_user_connections was 0 at startup
  (Which means that we will not do connection counting for accounts that don't have user resource limits)
  Set thd->start_utime at same time as thr_create_utime. (Before start_utime could be < thr_create_utime which lead to wrong query counting)
sql/set_var.cc:
  Don't allow one to change 'max_user_connections' if it was 0 at startup.
sql/sql_acl.cc:
  Change user_connection counting to be negative.
sql/sql_connect.cc:
  If max_user_connections is < 0 then only SUPER user can login.
  Fixed wrong variable names for error messages.
  Fixed wrong initial value for questions.
  Set thd->start_utime and thd->thr_create_utime at startup.  Needed to get time_out_user_resource_limits() to work.
sql/sql_show.cc:
  SHOW_INT is now negative
sql/sql_yacc.yy:
  Support negative values for MAX_USER_CONNECTIONS
sql/structs.h:
  Make user connect counting work with signed numbers.
This commit is contained in:
Michael Widenius 2011-09-26 20:26:47 +03:00
parent f0c6ce9ade
commit 7800d93bc3
23 changed files with 173 additions and 54 deletions

View File

@ -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`)

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1 @@
--log-output=TABLE,FILE --log --log-slow-queries --slow-query-log=1

View File

@ -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;

View File

@ -0,0 +1 @@
--max-user-connections=1000

View File

@ -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;

View File

@ -0,0 +1 @@
--max-user-connections=1

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */

View File

@ -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())
{

View File

@ -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:
{

View File

@ -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;

View File

@ -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.