diff options
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r-- | sql/sql_parse.cc | 87 |
1 files changed, 67 insertions, 20 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index d6f8e2f66c7..b3c17167a3a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -172,6 +172,7 @@ static int get_or_create_user_conn(THD *thd, const char *user, } } thd->user_connect=uc; + uc->connections++; end: (void) pthread_mutex_unlock(&LOCK_user_conn); return return_val; @@ -314,14 +315,15 @@ int check_user(THD *thd, enum enum_server_command command, thd->db_access=0; /* Don't allow user to connect if he has done too many queries */ - if ((ur.questions || ur.updates || - ur.connections || max_user_connections) && - get_or_create_user_conn(thd,thd->user,thd->host_or_ip,&ur)) - DBUG_RETURN(-1); - if (thd->user_connect && (thd->user_connect->user_resources.connections || - max_user_connections) && - check_for_max_user_connections(thd, thd->user_connect)) - DBUG_RETURN(-1); + if ((ur.questions || ur.updates || ur.connections || + max_user_connections) && + get_or_create_user_conn(thd,thd->user,thd->host_or_ip,&ur)) + DBUG_RETURN(-1); + if (thd->user_connect && + (thd->user_connect->user_resources.connections || + max_user_connections) && + check_for_max_user_connections(thd, thd->user_connect)) + DBUG_RETURN(-1); /* Change database if necessary: OK or FAIL is sent in mysql_change_db */ if (db && db[0]) @@ -386,42 +388,84 @@ void init_max_user_conn(void) } +/* + check if user has already too many connections + + SYNOPSIS + check_for_max_user_connections() + thd Thread handle + uc User connect object + + NOTES + If check fails, we decrease user connection count, which means one + shouldn't call decrease_user_connections() after this function. + + RETURN + 0 ok + 1 error +*/ + static int check_for_max_user_connections(THD *thd, USER_CONN *uc) { int error=0; DBUG_ENTER("check_for_max_user_connections"); + (void) pthread_mutex_lock(&LOCK_user_conn); if (max_user_connections && - (max_user_connections < (uint) uc->connections)) + max_user_connections < (uint) uc->connections) { net_printf(thd,ER_TOO_MANY_USER_CONNECTIONS, uc->user); error=1; goto end; } - uc->connections++; if (uc->user_resources.connections && - uc->conn_per_hour++ >= uc->user_resources.connections) + uc->user_resources.connections <= uc->conn_per_hour) { net_printf(thd, ER_USER_LIMIT_REACHED, uc->user, "max_connections", (long) uc->user_resources.connections); error=1; + goto end; } -end: + uc->conn_per_hour++; + + end: + if (error) + uc->connections--; // no need for decrease_user_connections() here + (void) pthread_mutex_unlock(&LOCK_user_conn); DBUG_RETURN(error); } +/* + Decrease user connection count + + SYNOPSIS + decrease_user_connections() + uc User connection object + + NOTES + If there is a n user connection object for a connection + (which only happens if 'max_user_connections' is defined or + if someone has created a resource grant for a user), then + the connection count is always incremented on connect. + + The user connect object is not freed if some users has + 'max connections per hour' defined as we need to be able to hold + count over the lifetime of the connection. +*/ + static void decrease_user_connections(USER_CONN *uc) { DBUG_ENTER("decrease_user_connections"); - if ((uc->connections && !--uc->connections) && !mqh_used) + (void) pthread_mutex_lock(&LOCK_user_conn); + DBUG_ASSERT(uc->connections); + if (!--uc->connections && !mqh_used) { /* Last connection for user; Delete it */ - (void) pthread_mutex_lock(&LOCK_user_conn); (void) hash_delete(&hash_user_connections,(byte*) uc); - (void) pthread_mutex_unlock(&LOCK_user_conn); } + (void) pthread_mutex_unlock(&LOCK_user_conn); DBUG_VOID_RETURN; } @@ -1227,15 +1271,17 @@ bool dispatch_command(enum enum_server_command command, THD *thd, char *save_user= thd->user; char *save_priv_user= thd->priv_user; char *save_db= thd->db; - USER_CONN *save_uc= thd->user_connect; - thd->user= my_strdup(user, MYF(0)); - if (!thd->user) + USER_CONN *save_user_connect= thd->user_connect; + + if (!(thd->user= my_strdup(user, MYF(0)))) { thd->user= save_user; send_error(thd, ER_OUT_OF_RESOURCES); break; } + /* Clear variables that are allocated */ + thd->user_connect= 0; int res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, false); if (res) @@ -1246,6 +1292,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, x_free(thd->user); thd->user= save_user; thd->priv_user= save_priv_user; + thd->user_connect= save_user_connect; thd->master_access= save_master_access; thd->db_access= save_db_access; thd->db= save_db; @@ -1254,8 +1301,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, else { /* we've authenticated new user */ - if (max_connections && save_uc) - decrease_user_connections(save_uc); + if (save_user_connect) + decrease_user_connections(save_user_connect); x_free((gptr) save_db); x_free((gptr) save_user); } |