@@ -535,26 +535,27 @@ void free_global_client_stats(void)
535535 my_hash_free (&global_client_stats);
536536}
537537
538+
538539/*
539- Increments the global stats connection count for an entry from
540- global_client_stats or global_user_stats. Returns 0 on success
541- and 1 on error.
542- */
540+ Common code for increment_count_by_name, decrement_count_by_name, to fetch
541+ the USER_STATS corresponding to 'name'.
542+ */
543543
544- static bool increment_count_by_name (const char *name, size_t name_length,
545- const char *role_name,
546- HASH *users_or_clients, THD *thd)
544+ static USER_STATS* count_by_name_common (const char *name, size_t name_length,
545+ const char *role_name,
546+ HASH *users_or_clients, THD *thd)
547547{
548- USER_STATS *user_stats;
548+ USER_STATS *user_stats= nullptr ;
549549
550- if (!(user_stats= (USER_STATS*) my_hash_search (users_or_clients, (uchar*) name,
551- name_length)))
550+ if (!(user_stats= (USER_STATS*) my_hash_search (users_or_clients,
551+ (uchar*) name,
552+ name_length)))
552553 {
553554 /* First connection for this user or client */
554555 if (!(user_stats= ((USER_STATS*)
555556 my_malloc (PSI_INSTRUMENT_ME, sizeof (USER_STATS),
556557 MYF (MY_WME | MY_ZEROFILL)))))
557- return TRUE ; // Out of memory
558+ return nullptr ; // Out of memory
558559
559560 init_user_stats (user_stats, name, name_length, role_name,
560561 0 , 0 , 0 , // connections
@@ -573,12 +574,68 @@ static bool increment_count_by_name(const char *name, size_t name_length,
573574 if (my_hash_insert (users_or_clients, (uchar*)user_stats))
574575 {
575576 my_free (user_stats);
576- return TRUE ; // Out of memory
577+ return nullptr ; // Out of memory
577578 }
578579 }
579- user_stats->total_connections ++;
580+
581+ DBUG_ASSERT (user_stats);
582+ return user_stats;
583+ }
584+
585+
586+ /*
587+ Increments the global stats connection count for an entry from
588+ global_client_stats or global_user_stats. Returns FALSE on success
589+ and TRUE on error.
590+ */
591+
592+ static bool increment_count_by_name (const char *name, size_t name_length,
593+ const char *role_name,
594+ HASH *users_or_clients, THD *thd)
595+ {
596+ USER_STATS *user_stats= count_by_name_common (name, name_length, role_name,
597+ users_or_clients, thd);
598+ if (!user_stats)
599+ return TRUE ;
600+
601+ ++user_stats->total_connections ;
602+ #ifndef EMBEDDED_LIBRARY
603+ /*
604+ For the embedded library, we get here only because THD::update_all_stats
605+ is called after command dispatch, not because of any connection events
606+ (those are compiled-out for the embedded library). Maybe this entire
607+ function should do nothing in the case of embedded library? At least
608+ this makes it behave the same way it did before supporting
609+ concurrent_connections.
610+ */
611+ ++user_stats->concurrent_connections ;
612+ #endif
580613 if (thd->net .vio && thd->net .vio ->type == VIO_TYPE_SSL)
581- user_stats->total_ssl_connections ++;
614+ ++user_stats->total_ssl_connections ;
615+
616+ return FALSE ;
617+ }
618+
619+
620+ /*
621+ Decrements the global stats connection count for an entry from
622+ global_client_stats or global_user_stats. Returns FALSE on success
623+ and TRUE on error.
624+ */
625+
626+ #ifndef EMBEDDED_LIBRARY
627+ static bool decrement_count_by_name (const char *name, size_t name_length,
628+ const char *role_name,
629+ HASH *users_or_clients, THD *thd)
630+ {
631+ USER_STATS *user_stats= count_by_name_common (name, name_length, role_name,
632+ users_or_clients, thd);
633+ if (!user_stats)
634+ return TRUE ;
635+
636+ if (user_stats->concurrent_connections > 0 )
637+ --user_stats->concurrent_connections ;
638+
582639 return FALSE ;
583640}
584641
@@ -592,36 +649,65 @@ static bool increment_count_by_name(const char *name, size_t name_length,
592649 @retval 1 error.
593650*/
594651
595- #ifndef EMBEDDED_LIBRARY
596- static bool increment_connection_count (THD* thd, bool use_lock)
652+ static bool increment_connection_count (THD* thd)
597653{
598654 const char *user_string= get_valid_user_string (thd->main_security_ctx .user );
599655 const char *client_string= get_client_host (thd);
600- bool return_value= FALSE ;
601656
602657 if (!thd->userstat_running )
603658 return FALSE ;
604659
605- if (use_lock)
606- mysql_mutex_lock (&LOCK_global_user_client_stats);
660+ mysql_mutex_lock (&LOCK_global_user_client_stats);
661+ SCOPE_EXIT ([] () {
662+ mysql_mutex_unlock (&LOCK_global_user_client_stats);
663+ });
607664
608665 if (increment_count_by_name (user_string, strlen (user_string), user_string,
609666 &global_user_stats, thd))
610- {
611- return_value= TRUE ;
612- goto end;
613- }
667+ return TRUE ;
668+
614669 if (increment_count_by_name (client_string, strlen (client_string),
615670 user_string, &global_client_stats, thd))
616- {
617- return_value= TRUE ;
618- goto end;
619- }
671+ return TRUE ;
620672
621- end:
622- if (use_lock)
673+ return FALSE ;
674+ }
675+
676+ static bool decrement_connection_count (THD* thd)
677+ {
678+ const char *user_string= get_valid_user_string (thd->main_security_ctx .user );
679+ const char *client_string= get_client_host (thd);
680+
681+ /*
682+ THD::update_all_stats, called only from dispatch_command, clears
683+ thd->userstat_running to avoid double counting. thd->userstat_running
684+ is set during THD::init.
685+
686+ When a user connects for the first time, thd->userstat_running is set
687+ from the global variable opt_userstat_running during THD::init (indirectly
688+ called from THD::change_user). After each dispatched command, as noted
689+ above, it is cleared (even if the user maintains the connection). So for
690+ normal cases where the user disconnects after running a query, we need to
691+ check opt_userstat_running. We check thd->userstat_running for abnormal
692+ cases where the user disconnects during a dispatched command, before it
693+ reaches THD::update_all_stats.
694+ */
695+ if (!thd->userstat_running && !opt_userstat_running)
696+ return FALSE ;
697+
698+ mysql_mutex_lock (&LOCK_global_user_client_stats);
699+ SCOPE_EXIT ([] () {
623700 mysql_mutex_unlock (&LOCK_global_user_client_stats);
624- return return_value;
701+ });
702+
703+ if (decrement_count_by_name (user_string, strlen (user_string), user_string,
704+ &global_user_stats, thd))
705+ return TRUE ;
706+ if (decrement_count_by_name (client_string, strlen (client_string),
707+ user_string, &global_client_stats, thd))
708+ return TRUE ;
709+
710+ return FALSE ;
625711}
626712#endif
627713
@@ -1143,7 +1229,7 @@ bool login_connection(THD *thd)
11431229 my_net_set_write_timeout (net, thd->variables .net_write_timeout );
11441230
11451231 /* Updates global user connection stats. */
1146- if (increment_connection_count (thd, TRUE ))
1232+ if (increment_connection_count (thd))
11471233 {
11481234 my_error (ER_OUTOFMEMORY, MYF (0 ), (int ) (2 *sizeof (USER_STATS)));
11491235 error= 1 ;
@@ -1202,6 +1288,9 @@ void end_connection(THD *thd)
12021288 if (likely (!thd->killed ) && (net->error && net->vio != 0 ))
12031289 thd->print_aborted_warning (1 , thd->get_stmt_da ()->is_error ()
12041290 ? thd->get_stmt_da ()->message () : ER_THD (thd, ER_UNKNOWN_ERROR));
1291+
1292+ if (decrement_connection_count (thd))
1293+ my_error (ER_OUTOFMEMORY, MYF (ME_ERROR_LOG), (int ) (2 *sizeof (USER_STATS)));
12051294}
12061295
12071296
0 commit comments