1 # name : userstat.patch
2 # introduced : 11 or before
6 # Any small change to this file in the main branch
7 # should be done or reviewed by the maintainer!
8 --- a/include/mysql/plugin.h
9 +++ b/include/mysql/plugin.h
11 unsigned long thd_log_slow_verbosity(const MYSQL_THD thd);
12 int thd_opt_slow_log();
13 #define EXTENDED_SLOWLOG
15 +#define EXTENDED_FOR_USERSTAT
18 Create a temporary file.
20 --- a/include/mysql_com.h
21 +++ b/include/mysql_com.h
24 #define SERVER_VERSION_LENGTH 60
25 #define SQLSTATE_LENGTH 5
26 +#define LIST_PROCESS_HOST_LEN 64
29 Maximum length of comments
31 #define REFRESH_DES_KEY_FILE 0x40000L
32 #define REFRESH_USER_RESOURCES 0x80000L
33 #define REFRESH_QUERY_RESPONSE_TIME 0x100000L /* response time distibution */
34 +#define REFRESH_TABLE_STATS 0x200000L /* Refresh table stats my_hash table */
35 +#define REFRESH_INDEX_STATS 0x400000L /* Refresh index stats my_hash table */
36 +#define REFRESH_USER_STATS 0x800000L /* Refresh user stats my_hash table */
37 +#define REFRESH_CLIENT_STATS 0x1000000L /* Refresh client stats my_hash table */
38 +#define REFRESH_THREAD_STATS 0x2000000L /* Refresh thread stats my_hash table */
40 #define CLIENT_LONG_PASSWORD 1 /* new more secure passwords */
41 #define CLIENT_FOUND_ROWS 2 /* Found instead of affected rows */
43 +++ b/patch_info/userstats.patch
46 +Name=SHOW USER/TABLE/INDEX statistics
50 +Comment=Added INFORMATION_SCHEMA.*_STATISTICS
52 +YK: fix behavior for prepared statements
55 +YK: add switch variable "userstat" to control INFORMATION_SCHEMA.*_STATISTICS (default:OFF)
61 +Rename variable USERSTAT_RUNNING => USERSTAT
67 DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
69 + thd->diff_commit_trans++;
70 RUN_HOOK(transaction, after_commit, (thd, FALSE));
72 if (rw_trans && mdl_request.ticket)
74 /* Always cleanup. Even if nht==0. There may be savepoints. */
76 thd->transaction.cleanup();
78 + thd->diff_rollback_trans++;
80 thd->transaction_rollback_request= FALSE;
83 ha_info->reset(); /* keep it conveniently zero-filled */
85 trans->ha_list= sv->ha_list;
86 + thd->diff_rollback_trans++;
91 dup_ref=ref+ALIGN_SIZE(ref_length);
92 cached_table_flags= table_flags();
94 + rows_read= rows_changed= 0;
95 + memset(index_rows_read, 0, sizeof(index_rows_read));
99 @@ -3644,6 +3651,127 @@
103 +// Updates the global table stats with the TABLE this handler represents.
104 +void handler::update_global_table_stats()
108 + rows_read= rows_changed= 0;
112 + if (!rows_read && !rows_changed)
113 + return; // Nothing to update.
114 + // table_cache_key is db_name + '\0' + table_name + '\0'.
115 + if (!table->s || !table->s->table_cache_key.str || !table->s->table_name.str)
118 + TABLE_STATS* table_stats;
119 + char key[NAME_LEN * 2 + 2];
120 + // [db] + '.' + [table]
121 + sprintf(key, "%s.%s", table->s->table_cache_key.str, table->s->table_name.str);
123 + mysql_mutex_lock(&LOCK_global_table_stats);
124 + // Gets the global table stats, creating one if necessary.
125 + if (!(table_stats = (TABLE_STATS *) my_hash_search(&global_table_stats,
129 + if (!(table_stats = ((TABLE_STATS *)
130 + my_malloc(sizeof(TABLE_STATS), MYF(MY_WME | MY_ZEROFILL)))))
133 + sql_print_error("Allocating table stats failed.");
136 + strncpy(table_stats->table, key, sizeof(table_stats->table));
137 + table_stats->rows_read= 0;
138 + table_stats->rows_changed= 0;
139 + table_stats->rows_changed_x_indexes= 0;
140 + table_stats->engine_type= (int) ht->db_type;
142 + if (my_hash_insert(&global_table_stats, (uchar *) table_stats))
145 + sql_print_error("Inserting table stats failed.");
146 + my_free((char *) table_stats);
150 + // Updates the global table stats.
151 + table_stats->rows_read+= rows_read;
152 + table_stats->rows_changed+= rows_changed;
153 + table_stats->rows_changed_x_indexes+=
154 + rows_changed * (table->s->keys ? table->s->keys : 1);
155 + current_thd->diff_total_read_rows+= rows_read;
156 + rows_read= rows_changed= 0;
158 + mysql_mutex_unlock(&LOCK_global_table_stats);
161 +// Updates the global index stats with this handler's accumulated index reads.
162 +void handler::update_global_index_stats()
164 + // table_cache_key is db_name + '\0' + table_name + '\0'.
165 + if (!table->s || !table->s->table_cache_key.str || !table->s->table_name.str)
170 + for (uint x= 0; x < table->s->keys; ++x)
172 + index_rows_read[x]= 0;
177 + for (uint x = 0; x < table->s->keys; ++x)
179 + if (index_rows_read[x])
181 + // Rows were read using this index.
182 + KEY* key_info = &table->key_info[x];
184 + if (!key_info->name) continue;
186 + INDEX_STATS* index_stats;
187 + char key[NAME_LEN * 3 + 3];
188 + // [db] + '.' + [table] + '.' + [index]
189 + sprintf(key, "%s.%s.%s", table->s->table_cache_key.str,
190 + table->s->table_name.str, key_info->name);
192 + mysql_mutex_lock(&LOCK_global_index_stats);
193 + // Gets the global index stats, creating one if necessary.
194 + if (!(index_stats = (INDEX_STATS *) my_hash_search(&global_index_stats,
198 + if (!(index_stats = ((INDEX_STATS *)
199 + my_malloc(sizeof(INDEX_STATS), MYF(MY_WME | MY_ZEROFILL)))))
202 + sql_print_error("Allocating index stats failed.");
205 + strncpy(index_stats->index, key, sizeof(index_stats->index));
206 + index_stats->rows_read= 0;
208 + if (my_hash_insert(&global_index_stats, (uchar *) index_stats))
211 + sql_print_error("Inserting index stats failed.");
212 + my_free((char *) index_stats);
216 + // Updates the global index stats.
217 + index_stats->rows_read+= index_rows_read[x];
218 + index_rows_read[x]= 0;
220 + mysql_mutex_unlock(&LOCK_global_index_stats);
225 /****************************************************************************
226 ** Some general functions that isn't in the handler class
230 #include <ft_global.h>
231 #include <keycache.h>
234 +#error MAX_KEY is too large. Values up to 128 are supported.
237 // the following is for checking tables
239 #define HA_ADMIN_ALREADY_DONE 1
240 @@ -562,10 +566,12 @@
241 enum enum_schema_tables
246 SCH_COLLATION_CHARACTER_SET_APPLICABILITY,
248 SCH_COLUMN_PRIVILEGES,
254 SCH_TABLE_CONSTRAINTS,
256 SCH_TABLE_PRIVILEGES,
258 SCH_TEMPORARY_TABLES,
266 @@ -1233,6 +1242,9 @@
268 bool implicit_emptied; /* Can be !=0 only if HEAP */
269 const COND *pushed_cond;
270 + ulonglong rows_read;
271 + ulonglong rows_changed;
272 + ulonglong index_rows_read[MAX_KEY];
274 next_insert_id is the next value which should be inserted into the
275 auto_increment column: in a inserting-multi-row statement (like INSERT
276 @@ -1284,10 +1296,12 @@
277 ref_length(sizeof(my_off_t)),
278 ft_handler(0), inited(NONE),
279 locked(FALSE), implicit_emptied(0),
280 - pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
281 + pushed_cond(0), rows_read(0), rows_changed(0), next_insert_id(0), insert_id_for_cur_row(0),
282 auto_inc_intervals_count(0),
286 + memset(index_rows_read, 0, sizeof(index_rows_read));
288 virtual ~handler(void)
290 DBUG_ASSERT(locked == FALSE);
291 @@ -1410,6 +1424,8 @@
295 + rows_read = rows_changed= 0;
296 + memset(index_rows_read, 0, sizeof(index_rows_read));
298 virtual double scan_time()
299 { return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; }
300 @@ -1805,6 +1821,8 @@
301 virtual bool is_crashed() const { return 0; }
302 virtual bool auto_repair() const { return 0; }
304 + void update_global_table_stats();
305 + void update_global_index_stats();
307 #define CHF_CREATE_FLAG 0
308 #define CHF_DELETE_FLAG 1
312 { "CIPHER", SYM(CIPHER_SYM)},
313 { "CLASS_ORIGIN", SYM(CLASS_ORIGIN_SYM)},
314 { "CLIENT", SYM(CLIENT_SYM)},
315 + { "CLIENT_STATISTICS", SYM(CLIENT_STATS_SYM)},
316 { "CLOSE", SYM(CLOSE_SYM)},
317 { "COALESCE", SYM(COALESCE)},
318 { "CODE", SYM(CODE_SYM)},
320 { "IN", SYM(IN_SYM)},
321 { "INDEX", SYM(INDEX_SYM)},
322 { "INDEXES", SYM(INDEXES)},
323 + { "INDEX_STATISTICS", SYM(INDEX_STATS_SYM)},
324 { "INFILE", SYM(INFILE)},
325 { "INITIAL_SIZE", SYM(INITIAL_SIZE_SYM)},
326 { "INNER", SYM(INNER_SYM)},
327 @@ -550,12 +552,14 @@
328 { "TABLES", SYM(TABLES)},
329 { "TABLESPACE", SYM(TABLESPACE)},
330 { "TABLE_CHECKSUM", SYM(TABLE_CHECKSUM_SYM)},
331 + { "TABLE_STATISTICS", SYM(TABLE_STATS_SYM)},
332 { "TEMPORARY", SYM(TEMPORARY)},
333 { "TEMPTABLE", SYM(TEMPTABLE_SYM)},
334 { "TERMINATED", SYM(TERMINATED)},
335 { "TEXT", SYM(TEXT_SYM)},
336 { "THAN", SYM(THAN_SYM)},
337 { "THEN", SYM(THEN_SYM)},
338 + { "THREAD_STATISTICS", SYM(THREAD_STATS_SYM)},
339 { "TIME", SYM(TIME_SYM)},
340 { "TIMESTAMP", SYM(TIMESTAMP)},
341 { "TIMESTAMPADD", SYM(TIMESTAMP_ADD)},
343 { "USE", SYM(USE_SYM)},
344 { "USER", SYM(USER)},
345 { "USER_RESOURCES", SYM(RESOURCES)},
346 + { "USER_STATISTICS", SYM(USER_STATS_SYM)},
347 { "USE_FRM", SYM(USE_FRM)},
348 { "USING", SYM(USING)},
349 { "UTC_DATE", SYM(UTC_DATE_SYM)},
352 @@ -1007,6 +1007,13 @@
353 mysql_slow_log.reopen_file();
356 +void Log_to_file_event_handler::flush_slow_log()
358 + /* reopen slow log file */
360 + mysql_slow_log.reopen_file();
364 Log error with all enabled log event handlers
366 @@ -5062,6 +5069,8 @@
367 thd->first_successful_insert_id_in_prev_stmt_for_binlog);
370 + if (file == &log_file)
371 + thd->binlog_bytes_written+= e.data_written;
373 if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
375 @@ -5073,12 +5082,16 @@
379 + if (file == &log_file)
380 + thd->binlog_bytes_written+= e.data_written;
384 Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2);
387 + if (file == &log_file)
388 + thd->binlog_bytes_written+= e.data_written;
390 if (thd->user_var_events.elements)
392 @@ -5101,6 +5114,8 @@
396 + if (file == &log_file)
397 + thd->binlog_bytes_written+= e.data_written;
401 @@ -5112,6 +5127,8 @@
402 if (event_info->write(file) ||
403 DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0))
405 + if (file == &log_file)
406 + thd->binlog_bytes_written+= event_info->data_written;
410 @@ -5346,7 +5363,8 @@
411 be reset as a READ_CACHE to be able to read the contents from it.
414 -int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
415 +int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache,
416 + bool lock_log, bool sync_log)
418 Mutex_sentry sentry(lock_log ? &LOCK_log : NULL);
420 @@ -5393,6 +5411,7 @@
421 /* write the first half of the split header */
422 if (my_b_write(&log_file, header, carry))
423 return ER_ERROR_ON_WRITE;
424 + thd->binlog_bytes_written+= carry;
427 copy fixed second half of header to cache so the correct
428 @@ -5461,6 +5480,7 @@
429 /* Write data to the binary log file */
430 if (my_b_write(&log_file, cache->read_pos, length))
431 return ER_ERROR_ON_WRITE;
432 + thd->binlog_bytes_written+= length;
433 cache->read_pos=cache->read_end; // Mark buffer used up
434 } while ((length= my_b_fill(cache)));
436 @@ -5584,20 +5604,23 @@
437 Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), TRUE, FALSE, TRUE, 0);
438 if (qinfo.write(&log_file))
440 + thd->binlog_bytes_written+= qinfo.data_written;
441 DBUG_EXECUTE_IF("crash_before_writing_xid",
443 - if ((write_error= write_cache(cache, false, true)))
444 + if ((write_error= write_cache(thd, cache, false, true)))
445 DBUG_PRINT("info", ("error writing binlog cache: %d",
447 DBUG_PRINT("info", ("crashing before writing xid"));
451 - if ((write_error= write_cache(cache, false, false)))
452 + if ((write_error= write_cache(thd, cache, false, false)))
455 if (commit_event && commit_event->write(&log_file))
458 + thd->binlog_bytes_written+= commit_event->data_written;
460 if (incident && write_incident(thd, FALSE))
465 bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event, bool incident);
466 bool write_incident(THD *thd, bool lock);
468 - int write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync);
469 + int write_cache(THD *thd, IO_CACHE *cache,
470 + bool lock_log, bool flush_and_sync);
471 void set_write_error(THD *thd, bool is_transactional);
472 bool check_write_error(THD *thd);
475 const char *sql_text, uint sql_text_len,
476 CHARSET_INFO *client_cs);
478 + void flush_slow_log();
479 void init_pthread_objects();
480 MYSQL_QUERY_LOG *get_mysql_slow_log() { return &mysql_slow_log; }
481 MYSQL_QUERY_LOG *get_mysql_log() { return &mysql_log; }
485 MYSQL_PLUGIN_IMPORT uint opt_debug_sync_timeout= 0;
486 #endif /* defined(ENABLED_DEBUG_SYNC) */
487 my_bool opt_old_style_user_limits= 0, trust_function_creators= 0;
488 +my_bool opt_userstat= 0, opt_thread_statistics= 0;
489 my_bool opt_optimizer_fix= 0;
491 True if there is at least one per-hour limit for some user, so we should
493 ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
494 ulong binlog_stmt_cache_use= 0, binlog_stmt_cache_disk_use= 0;
495 ulong max_connections, max_connect_errors;
496 +ulonglong denied_connections= 0;
502 LOCK_global_system_variables,
503 LOCK_user_conn, LOCK_slave_list, LOCK_active_mi,
504 - LOCK_connection_count, LOCK_error_messages;
505 + LOCK_connection_count, LOCK_error_messages,
506 + LOCK_stats, LOCK_global_user_client_stats,
507 + LOCK_global_table_stats, LOCK_global_index_stats;
509 The below lock protects access to two global server variables:
510 max_prepared_stmt_count and prepared_stmt_count. These variables
511 @@ -1509,6 +1513,11 @@
512 #ifdef HAVE_RESPONSE_TIME_DISTRIBUTION
513 query_response_time_free();
514 #endif // HAVE_RESPONSE_TIME_DISTRIBUTION
515 + free_global_user_stats();
516 + free_global_client_stats();
517 + free_global_thread_stats();
518 + free_global_table_stats();
519 + free_global_index_stats();
520 #ifdef HAVE_REPLICATION
523 @@ -1612,6 +1621,10 @@
524 mysql_cond_destroy(&COND_thread_cache);
525 mysql_cond_destroy(&COND_flush_thread_cache);
526 mysql_cond_destroy(&COND_manager);
527 + mysql_mutex_destroy(&LOCK_stats);
528 + mysql_mutex_destroy(&LOCK_global_user_client_stats);
529 + mysql_mutex_destroy(&LOCK_global_table_stats);
530 + mysql_mutex_destroy(&LOCK_global_index_stats);
532 #endif /*EMBEDDED_LIBRARY*/
534 @@ -2938,6 +2951,7 @@
535 {"show_binlog_events", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOG_EVENTS]), SHOW_LONG_STATUS},
536 {"show_binlogs", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOGS]), SHOW_LONG_STATUS},
537 {"show_charsets", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CHARSETS]), SHOW_LONG_STATUS},
538 + {"show_client_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CLIENT_STATS]), SHOW_LONG_STATUS},
539 {"show_collations", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLLATIONS]), SHOW_LONG_STATUS},
540 {"show_contributors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CONTRIBUTORS]), SHOW_LONG_STATUS},
541 {"show_create_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_DB]), SHOW_LONG_STATUS},
542 @@ -2958,6 +2972,7 @@
544 {"show_function_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS_FUNC]), SHOW_LONG_STATUS},
545 {"show_grants", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS]), SHOW_LONG_STATUS},
546 + {"show_index_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_INDEX_STATS]), SHOW_LONG_STATUS},
547 {"show_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS},
548 {"show_master_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_MASTER_STAT]), SHOW_LONG_STATUS},
549 {"show_open_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_OPEN_TABLES]), SHOW_LONG_STATUS},
550 @@ -2976,10 +2991,13 @@
551 {"show_slave_status_nolock", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_NOLOCK_STAT]), SHOW_LONG_STATUS},
552 {"show_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS]), SHOW_LONG_STATUS},
553 {"show_storage_engines", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STORAGE_ENGINES]), SHOW_LONG_STATUS},
554 + {"show_table_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATS]), SHOW_LONG_STATUS},
555 {"show_table_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATUS]), SHOW_LONG_STATUS},
556 {"show_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLES]), SHOW_LONG_STATUS},
557 {"show_temporary_tables",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TEMPORARY_TABLES]), SHOW_LONG_STATUS},
558 + {"show_thread_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_THREAD_STATS]), SHOW_LONG_STATUS},
559 {"show_triggers", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TRIGGERS]), SHOW_LONG_STATUS},
560 + {"show_user_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_USER_STATS]), SHOW_LONG_STATUS},
561 {"show_variables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_VARIABLES]), SHOW_LONG_STATUS},
562 {"show_warnings", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_WARNS]), SHOW_LONG_STATUS},
563 {"slave_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS},
564 @@ -3517,6 +3535,13 @@
565 mysql_mutex_init(key_LOCK_server_started,
566 &LOCK_server_started, MY_MUTEX_INIT_FAST);
567 mysql_cond_init(key_COND_server_started, &COND_server_started, NULL);
568 + mysql_mutex_init(key_LOCK_stats, &LOCK_stats, MY_MUTEX_INIT_FAST);
569 + mysql_mutex_init(key_LOCK_global_user_client_stats,
570 + &LOCK_global_user_client_stats, MY_MUTEX_INIT_FAST);
571 + mysql_mutex_init(key_LOCK_global_table_stats,
572 + &LOCK_global_table_stats, MY_MUTEX_INIT_FAST);
573 + mysql_mutex_init(key_LOCK_global_index_stats,
574 + &LOCK_global_index_stats, MY_MUTEX_INIT_FAST);
576 #ifdef HAVE_EVENT_SCHEDULER
577 Events::init_mutexes();
578 @@ -3886,6 +3911,9 @@
579 query_response_time_init();
580 #endif // HAVE_RESPONSE_TIME_DISTRIBUTION
581 /* We have to initialize the storage engines before CSV logging */
582 + init_global_table_stats();
583 + init_global_index_stats();
587 sql_print_error("Can't init databases");
588 @@ -4022,6 +4050,9 @@
590 init_max_user_conn();
591 init_update_queries();
592 + init_global_user_stats();
593 + init_global_client_stats();
594 + init_global_thread_stats();
598 @@ -5087,6 +5118,7 @@
600 sql_print_warning("%s", ER_DEFAULT(ER_CON_COUNT_ERROR));
602 + statistic_increment(denied_connections, &LOCK_status);
606 @@ -7825,6 +7857,8 @@
607 key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
608 key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
609 key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
610 + key_LOCK_stats, key_LOCK_global_user_client_stats,
611 + key_LOCK_global_table_stats, key_LOCK_global_index_stats,
612 key_LOCK_gdl, key_LOCK_global_system_variables,
614 key_LOCK_prepared_stmt_count,
615 @@ -7864,6 +7898,13 @@
616 { &key_LOCK_delayed_insert, "LOCK_delayed_insert", PSI_FLAG_GLOBAL},
617 { &key_LOCK_delayed_status, "LOCK_delayed_status", PSI_FLAG_GLOBAL},
618 { &key_LOCK_error_log, "LOCK_error_log", PSI_FLAG_GLOBAL},
619 + { &key_LOCK_stats, "LOCK_stats", PSI_FLAG_GLOBAL},
620 + { &key_LOCK_global_user_client_stats,
621 + "LOCK_global_user_client_stats", PSI_FLAG_GLOBAL},
622 + { &key_LOCK_global_table_stats,
623 + "LOCK_global_table_stats", PSI_FLAG_GLOBAL},
624 + { &key_LOCK_global_index_stats,
625 + "LOCK_global_index_stats", PSI_FLAG_GLOBAL},
626 { &key_LOCK_gdl, "LOCK_gdl", PSI_FLAG_GLOBAL},
627 { &key_LOCK_global_system_variables, "LOCK_global_system_variables", PSI_FLAG_GLOBAL},
628 { &key_LOCK_manager, "LOCK_manager", PSI_FLAG_GLOBAL},
632 #include "my_atomic.h" /* my_atomic_rwlock_t */
633 #include "mysql/psi/mysql_file.h" /* MYSQL_FILE */
634 #include "sql_list.h" /* I_List */
640 extern ulonglong slave_type_conversions_options;
641 extern my_bool read_only, opt_readonly;
642 extern my_bool lower_case_file_system;
643 +extern my_bool opt_userstat, opt_thread_statistics;
644 extern my_bool opt_optimizer_fix;
645 extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
646 extern my_bool opt_secure_auth;
648 extern ulong slave_trans_retries;
649 extern uint slave_net_timeout;
650 extern uint max_user_connections;
651 +extern ulonglong denied_connections;
652 extern ulong what_to_log,flush_time;
653 extern ulong max_prepared_stmt_count, prepared_stmt_count;
654 extern ulong open_files_limit;
656 extern struct system_variables max_system_variables;
657 extern struct system_status_var global_status_var;
658 extern struct rand_struct sql_rand;
659 +extern HASH global_user_stats;
660 +extern HASH global_client_stats;
661 +extern HASH global_thread_stats;
662 +extern HASH global_table_stats;
663 +extern HASH global_index_stats;
664 extern const char *opt_date_time_formats[];
665 extern handlerton *partition_hton;
666 extern handlerton *myisam_hton;
668 key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
669 key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
670 key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
671 + key_LOCK_stats, key_LOCK_global_user_client_stats,
672 + key_LOCK_global_table_stats, key_LOCK_global_index_stats,
673 key_LOCK_gdl, key_LOCK_global_system_variables,
674 key_LOCK_logger, key_LOCK_manager,
675 key_LOCK_prepared_stmt_count,
677 LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
678 LOCK_slave_list, LOCK_active_mi, LOCK_manager,
679 LOCK_global_system_variables, LOCK_user_conn,
680 - LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count;
681 + LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count,
682 + LOCK_stats, LOCK_global_user_client_stats,
683 + LOCK_global_table_stats, LOCK_global_index_stats;
684 extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thread_count;
686 extern mysql_mutex_t LOCK_des_key_file;
691 +void init_global_user_stats(void);
692 +void init_global_table_stats(void);
693 +void init_global_index_stats(void);
694 +void init_global_client_stats(void);
695 +void init_global_thread_stats(void);
696 +void free_global_user_stats(void);
697 +void free_global_table_stats(void);
698 +void free_global_index_stats(void);
699 +void free_global_client_stats(void);
700 +void free_global_thread_stats(void);
703 TODO: Replace this with an inline function.
704 --- a/sql/sql_base.cc
705 +++ b/sql/sql_base.cc
706 @@ -1587,6 +1587,11 @@
707 table->mdl_ticket= NULL;
709 mysql_mutex_lock(&thd->LOCK_thd_data);
712 + table->file->update_global_table_stats();
713 + table->file->update_global_index_stats();
715 *table_ptr=table->next;
716 mysql_mutex_unlock(&thd->LOCK_thd_data);
718 @@ -2225,6 +2230,8 @@
719 DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
720 table->s->db.str, table->s->table_name.str));
722 + table->file->update_global_table_stats();
723 + table->file->update_global_index_stats();
724 free_io_cache(table);
727 --- a/sql/sql_class.cc
728 +++ b/sql/sql_class.cc
731 binlog_evt_union.do_union= FALSE;
737 + binlog_bytes_written= 0;
738 + updated_row_count= 0;
739 + sent_row_count_2= 0;
741 dbug_sentry=THD_SENTRY_MAGIC;
743 @@ -1357,6 +1364,7 @@
744 variables.option_bits|= OPTION_BIN_LOG;
746 variables.option_bits&= ~OPTION_BIN_LOG;
749 #if defined(ENABLED_DEBUG_SYNC)
750 /* Initialize the Debug Sync Facility. See debug_sync.cc. */
751 @@ -1366,6 +1374,94 @@
752 clear_slow_extended();
755 +// Resets stats in a THD.
756 +void THD::reset_stats(void)
758 + current_connect_time= time(NULL);
759 + last_global_update_time= current_connect_time;
760 + reset_diff_stats();
763 +// Resets the 'diff' stats, which are used to update global stats.
764 +void THD::reset_diff_stats(void)
766 + diff_total_busy_time= 0;
767 + diff_total_cpu_time= 0;
768 + diff_total_bytes_received= 0;
769 + diff_total_bytes_sent= 0;
770 + diff_total_binlog_bytes_written= 0;
771 + diff_total_sent_rows= 0;
772 + diff_total_updated_rows= 0;
773 + diff_total_read_rows= 0;
774 + diff_select_commands= 0;
775 + diff_update_commands= 0;
776 + diff_other_commands= 0;
777 + diff_commit_trans= 0;
778 + diff_rollback_trans= 0;
779 + diff_denied_connections= 0;
780 + diff_lost_connections= 0;
781 + diff_access_denied_errors= 0;
782 + diff_empty_queries= 0;
785 +// Updates 'diff' stats of a THD.
786 +void THD::update_stats(bool ran_command)
790 + diff_total_busy_time+= busy_time;
791 + diff_total_cpu_time+= cpu_time;
792 + diff_total_bytes_received+= bytes_received;
793 + diff_total_bytes_sent+= bytes_sent;
794 + diff_total_binlog_bytes_written+= binlog_bytes_written;
795 + diff_total_sent_rows+= sent_row_count_2;
796 + diff_total_updated_rows+= updated_row_count;
797 + // diff_total_read_rows is updated in handler.cc.
801 + // The replication thread has the COM_CONNECT command.
802 + if ((old_command == COM_QUERY || command == COM_CONNECT) &&
803 + (lex->sql_command >= 0 && lex->sql_command < SQLCOM_END))
806 + if (lex->sql_command == SQLCOM_SELECT)
808 + diff_select_commands++;
809 + if (!sent_row_count_2)
810 + diff_empty_queries++;
812 + else if (!sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND)
814 + // 'SHOW ' commands become SQLCOM_SELECT.
815 + diff_other_commands++;
816 + // 'SHOW ' commands shouldn't inflate total sent row count.
817 + diff_total_sent_rows-= sent_row_count_2;
818 + } else if (is_update_query(lex->sql_command)) {
819 + diff_update_commands++;
821 + diff_other_commands++;
825 + // diff_commit_trans is updated in handler.cc.
826 + // diff_rollback_trans is updated in handler.cc.
827 + // diff_denied_connections is updated in sql_parse.cc.
828 + // diff_lost_connections is updated in sql_parse.cc.
829 + // diff_access_denied_errors is updated in sql_parse.cc.
831 + /* reset counters to zero to avoid double-counting since values
832 + are already store in diff_total_*.
839 + binlog_bytes_written= 0;
840 + updated_row_count= 0;
841 + sent_row_count_2= 0;
845 Init THD for query processing.
846 @@ -2125,6 +2221,32 @@
850 +char *THD::get_client_host_port(THD *client)
852 + Security_context *client_sctx= client->security_ctx;
853 + char *client_host= NULL;
855 + if (client->peer_port && (client_sctx->host || client_sctx->ip) &&
856 + security_ctx->host_or_ip[0])
858 + if ((client_host= (char *) this->alloc(LIST_PROCESS_HOST_LEN+1)))
859 + my_snprintf((char *) client_host, LIST_PROCESS_HOST_LEN,
860 + "%s:%u", client_sctx->host_or_ip, client->peer_port);
863 + client_host= this->strdup(client_sctx->host_or_ip[0] ?
864 + client_sctx->host_or_ip :
865 + client_sctx->host ? client_sctx->host : "");
867 + return client_host;
870 +const char *get_client_host(THD *client)
872 + return client->security_ctx->host_or_ip[0] ?
873 + client->security_ctx->host_or_ip :
874 + client->security_ctx->host ? client->security_ctx->host : "";
877 struct Item_change_record: public ilink
879 @@ -2301,6 +2423,7 @@
882 thd->sent_row_count++;
883 + thd->sent_row_count_2++;
886 DBUG_RETURN(protocol->write());
887 @@ -2393,6 +2516,7 @@
888 select_export::~select_export()
890 thd->sent_row_count=row_count;
891 + thd->sent_row_count_2= row_count;
895 @@ -3416,6 +3540,7 @@
896 if (likely(thd != 0))
897 { /* current_thd==0 when close_connection() calls net_send_error() */
898 thd->status_var.bytes_sent+= length;
899 + thd->bytes_sent+= length;
903 @@ -3423,6 +3548,7 @@
904 void thd_increment_bytes_received(ulong length)
906 current_thd->status_var.bytes_received+= length;
907 + current_thd->bytes_received+= length;
911 --- a/sql/sql_class.h
912 +++ b/sql/sql_class.h
913 @@ -1705,6 +1705,8 @@
915 enum enum_server_command command;
917 + // Used to save the command, before it is set to COM_SLEEP.
918 + enum enum_server_command old_command;
919 uint32 file_id; // for LOAD DATA INFILE
920 /* remote (peer) port */
922 @@ -2214,6 +2216,8 @@
924 enum_tx_isolation tx_isolation;
925 enum_check_fields count_cuted_fields;
926 + ha_rows updated_row_count;
927 + ha_rows sent_row_count_2; /* for userstat */
929 DYNAMIC_ARRAY user_var_events; /* For user variables replication */
930 MEM_ROOT *user_var_events_alloc; /* Allocate above array elements here */
931 @@ -2308,6 +2312,49 @@
933 LOG_INFO* current_linfo;
934 NET* slave_net; // network connection from slave -> m.
937 + Used to update global user stats. The global user stats are updated
938 + occasionally with the 'diff' variables. After the update, the 'diff'
939 + variables are reset to 0.
941 + // Time when the current thread connected to MySQL.
942 + time_t current_connect_time;
943 + // Last time when THD stats were updated in global_user_stats.
944 + time_t last_global_update_time;
945 + // Busy (non-idle) time for just one command.
947 + // Busy time not updated in global_user_stats yet.
948 + double diff_total_busy_time;
949 + // Cpu (non-idle) time for just one thread.
951 + // Cpu time not updated in global_user_stats yet.
952 + double diff_total_cpu_time;
953 + /* bytes counting */
954 + ulonglong bytes_received;
955 + ulonglong diff_total_bytes_received;
956 + ulonglong bytes_sent;
957 + ulonglong diff_total_bytes_sent;
958 + ulonglong binlog_bytes_written;
959 + ulonglong diff_total_binlog_bytes_written;
961 + // Number of rows not reflected in global_user_stats yet.
962 + ha_rows diff_total_sent_rows, diff_total_updated_rows, diff_total_read_rows;
963 + // Number of commands not reflected in global_user_stats yet.
964 + ulonglong diff_select_commands, diff_update_commands, diff_other_commands;
965 + // Number of transactions not reflected in global_user_stats yet.
966 + ulonglong diff_commit_trans, diff_rollback_trans;
967 + // Number of connection errors not reflected in global_user_stats yet.
968 + ulonglong diff_denied_connections, diff_lost_connections;
969 + // Number of db access denied, not reflected in global_user_stats yet.
970 + ulonglong diff_access_denied_errors;
971 + // Number of queries that return 0 rows
972 + ulonglong diff_empty_queries;
974 + // Per account query delay in miliseconds. When not 0, sleep this number of
975 + // milliseconds before every SQL command.
976 + ulonglong query_delay_millis;
978 /* Used by the sys_var class to store temporary values */
981 @@ -2388,6 +2435,11 @@
984 void init_for_queries();
985 + void reset_stats(void);
986 + void reset_diff_stats(void);
987 + // ran_command is true when this is called immediately after a
988 + // command has been run.
989 + void update_stats(bool ran_command);
990 void change_user(void);
992 void cleanup_after_query();
993 @@ -2860,6 +2912,15 @@
995 thd_scheduler scheduler;
997 + /* Returns string as 'IP:port' for the client-side
998 + of the connnection represented
999 + by 'client' as displayed by SHOW PROCESSLIST.
1000 + Allocates memory from the heap of
1001 + this THD and that is not reclaimed
1002 + immediately, so use sparingly. May return NULL.
1004 + char *get_client_host_port(THD *client);
1007 inline Internal_error_handler *get_internal_handler()
1008 { return m_internal_handler; }
1009 @@ -3060,6 +3121,10 @@
1010 LEX_STRING invoker_host;
1013 +/* Returns string as 'IP' for the client-side of the connection represented by
1014 + 'client'. Does not allocate memory. May return "".
1016 +const char *get_client_host(THD *client);
1018 /** A short cut for thd->stmt_da->set_ok_status(). */
1020 --- a/sql/sql_connect.cc
1021 +++ b/sql/sql_connect.cc
1023 #define MIN_HANDSHAKE_SIZE 6
1024 #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
1026 +// Increments connection count for user.
1027 +static int increment_connection_count(THD* thd, bool use_lock);
1029 +// Uses the THD to update the global stats by user name and client IP
1030 +void update_global_user_stats(THD* thd, bool create_user, time_t now);
1032 +HASH global_user_stats;
1033 +HASH global_client_stats;
1034 +HASH global_thread_stats;
1035 +// Protects global_user_stats and global_client_stats
1036 +extern mysql_mutex_t LOCK_global_user_client_stats;
1038 +HASH global_table_stats;
1039 +extern mysql_mutex_t LOCK_global_table_stats;
1041 +HASH global_index_stats;
1042 +extern mysql_mutex_t LOCK_global_index_stats;
1045 Get structure for logging connection data for the current user
1047 @@ -113,6 +131,586 @@
1051 +extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
1052 + my_bool not_used __attribute__((unused)))
1054 + *length= strlen(user_stats->user);
1055 + return (uchar*) user_stats->user;
1058 +extern "C" uchar *get_key_thread_stats(THREAD_STATS *thread_stats, size_t *length,
1059 + my_bool not_used __attribute__((unused)))
1061 + *length= sizeof(my_thread_id);
1062 + return (uchar *) &(thread_stats->id);
1065 +void free_user_stats(USER_STATS* user_stats)
1067 + my_free((char *) user_stats);
1070 +void free_thread_stats(THREAD_STATS* thread_stats)
1072 + my_free((char *) thread_stats);
1075 +void init_user_stats(USER_STATS *user_stats,
1077 + const char *priv_user,
1078 + uint total_connections,
1079 + uint concurrent_connections,
1080 + time_t connected_time,
1083 + ulonglong bytes_received,
1084 + ulonglong bytes_sent,
1085 + ulonglong binlog_bytes_written,
1086 + ha_rows rows_fetched,
1087 + ha_rows rows_updated,
1088 + ha_rows rows_read,
1089 + ulonglong select_commands,
1090 + ulonglong update_commands,
1091 + ulonglong other_commands,
1092 + ulonglong commit_trans,
1093 + ulonglong rollback_trans,
1094 + ulonglong denied_connections,
1095 + ulonglong lost_connections,
1096 + ulonglong access_denied_errors,
1097 + ulonglong empty_queries)
1099 + DBUG_ENTER("init_user_stats");
1100 + DBUG_PRINT("info",
1101 + ("Add user_stats entry for user %s - priv_user %s",
1102 + user, priv_user));
1103 + strncpy(user_stats->user, user, sizeof(user_stats->user));
1104 + strncpy(user_stats->priv_user, priv_user, sizeof(user_stats->priv_user));
1106 + user_stats->total_connections= total_connections;
1107 + user_stats->concurrent_connections= concurrent_connections;
1108 + user_stats->connected_time= connected_time;
1109 + user_stats->busy_time= busy_time;
1110 + user_stats->cpu_time= cpu_time;
1111 + user_stats->bytes_received= bytes_received;
1112 + user_stats->bytes_sent= bytes_sent;
1113 + user_stats->binlog_bytes_written= binlog_bytes_written;
1114 + user_stats->rows_fetched= rows_fetched;
1115 + user_stats->rows_updated= rows_updated;
1116 + user_stats->rows_read= rows_read;
1117 + user_stats->select_commands= select_commands;
1118 + user_stats->update_commands= update_commands;
1119 + user_stats->other_commands= other_commands;
1120 + user_stats->commit_trans= commit_trans;
1121 + user_stats->rollback_trans= rollback_trans;
1122 + user_stats->denied_connections= denied_connections;
1123 + user_stats->lost_connections= lost_connections;
1124 + user_stats->access_denied_errors= access_denied_errors;
1125 + user_stats->empty_queries= empty_queries;
1129 +void init_thread_stats(THREAD_STATS *thread_stats,
1131 + uint total_connections,
1132 + uint concurrent_connections,
1133 + time_t connected_time,
1136 + ulonglong bytes_received,
1137 + ulonglong bytes_sent,
1138 + ulonglong binlog_bytes_written,
1139 + ha_rows rows_fetched,
1140 + ha_rows rows_updated,
1141 + ha_rows rows_read,
1142 + ulonglong select_commands,
1143 + ulonglong update_commands,
1144 + ulonglong other_commands,
1145 + ulonglong commit_trans,
1146 + ulonglong rollback_trans,
1147 + ulonglong denied_connections,
1148 + ulonglong lost_connections,
1149 + ulonglong access_denied_errors,
1150 + ulonglong empty_queries)
1152 + DBUG_ENTER("init_thread_stats");
1153 + DBUG_PRINT("info",
1154 + ("Add thread_stats entry for thread %lu",
1156 + thread_stats->id= id;
1158 + thread_stats->total_connections= total_connections;
1159 + thread_stats->concurrent_connections= concurrent_connections;
1160 + thread_stats->connected_time= connected_time;
1161 + thread_stats->busy_time= busy_time;
1162 + thread_stats->cpu_time= cpu_time;
1163 + thread_stats->bytes_received= bytes_received;
1164 + thread_stats->bytes_sent= bytes_sent;
1165 + thread_stats->binlog_bytes_written= binlog_bytes_written;
1166 + thread_stats->rows_fetched= rows_fetched;
1167 + thread_stats->rows_updated= rows_updated;
1168 + thread_stats->rows_read= rows_read;
1169 + thread_stats->select_commands= select_commands;
1170 + thread_stats->update_commands= update_commands;
1171 + thread_stats->other_commands= other_commands;
1172 + thread_stats->commit_trans= commit_trans;
1173 + thread_stats->rollback_trans= rollback_trans;
1174 + thread_stats->denied_connections= denied_connections;
1175 + thread_stats->lost_connections= lost_connections;
1176 + thread_stats->access_denied_errors= access_denied_errors;
1177 + thread_stats->empty_queries= empty_queries;
1181 +void add_user_stats(USER_STATS *user_stats,
1182 + uint total_connections,
1183 + uint concurrent_connections,
1184 + time_t connected_time,
1187 + ulonglong bytes_received,
1188 + ulonglong bytes_sent,
1189 + ulonglong binlog_bytes_written,
1190 + ha_rows rows_fetched,
1191 + ha_rows rows_updated,
1192 + ha_rows rows_read,
1193 + ulonglong select_commands,
1194 + ulonglong update_commands,
1195 + ulonglong other_commands,
1196 + ulonglong commit_trans,
1197 + ulonglong rollback_trans,
1198 + ulonglong denied_connections,
1199 + ulonglong lost_connections,
1200 + ulonglong access_denied_errors,
1201 + ulonglong empty_queries)
1203 + user_stats->total_connections+= total_connections;
1204 + user_stats->concurrent_connections+= concurrent_connections;
1205 + user_stats->connected_time+= connected_time;
1206 + user_stats->busy_time+= busy_time;
1207 + user_stats->cpu_time+= cpu_time;
1208 + user_stats->bytes_received+= bytes_received;
1209 + user_stats->bytes_sent+= bytes_sent;
1210 + user_stats->binlog_bytes_written+= binlog_bytes_written;
1211 + user_stats->rows_fetched+= rows_fetched;
1212 + user_stats->rows_updated+= rows_updated;
1213 + user_stats->rows_read+= rows_read;
1214 + user_stats->select_commands+= select_commands;
1215 + user_stats->update_commands+= update_commands;
1216 + user_stats->other_commands+= other_commands;
1217 + user_stats->commit_trans+= commit_trans;
1218 + user_stats->rollback_trans+= rollback_trans;
1219 + user_stats->denied_connections+= denied_connections;
1220 + user_stats->lost_connections+= lost_connections;
1221 + user_stats->access_denied_errors+= access_denied_errors;
1222 + user_stats->empty_queries+= empty_queries;
1225 +void add_thread_stats(THREAD_STATS *thread_stats,
1226 + uint total_connections,
1227 + uint concurrent_connections,
1228 + time_t connected_time,
1231 + ulonglong bytes_received,
1232 + ulonglong bytes_sent,
1233 + ulonglong binlog_bytes_written,
1234 + ha_rows rows_fetched,
1235 + ha_rows rows_updated,
1236 + ha_rows rows_read,
1237 + ulonglong select_commands,
1238 + ulonglong update_commands,
1239 + ulonglong other_commands,
1240 + ulonglong commit_trans,
1241 + ulonglong rollback_trans,
1242 + ulonglong denied_connections,
1243 + ulonglong lost_connections,
1244 + ulonglong access_denied_errors,
1245 + ulonglong empty_queries)
1247 + thread_stats->total_connections+= total_connections;
1248 + thread_stats->concurrent_connections+= concurrent_connections;
1249 + thread_stats->connected_time+= connected_time;
1250 + thread_stats->busy_time+= busy_time;
1251 + thread_stats->cpu_time+= cpu_time;
1252 + thread_stats->bytes_received+= bytes_received;
1253 + thread_stats->bytes_sent+= bytes_sent;
1254 + thread_stats->binlog_bytes_written+= binlog_bytes_written;
1255 + thread_stats->rows_fetched+= rows_fetched;
1256 + thread_stats->rows_updated+= rows_updated;
1257 + thread_stats->rows_read+= rows_read;
1258 + thread_stats->select_commands+= select_commands;
1259 + thread_stats->update_commands+= update_commands;
1260 + thread_stats->other_commands+= other_commands;
1261 + thread_stats->commit_trans+= commit_trans;
1262 + thread_stats->rollback_trans+= rollback_trans;
1263 + thread_stats->denied_connections+= denied_connections;
1264 + thread_stats->lost_connections+= lost_connections;
1265 + thread_stats->access_denied_errors+= access_denied_errors;
1266 + thread_stats->empty_queries+= empty_queries;
1269 +void init_global_user_stats(void)
1271 + if (my_hash_init(&global_user_stats, system_charset_info, max_connections,
1272 + 0, 0, (my_hash_get_key)get_key_user_stats,
1273 + (my_hash_free_key)free_user_stats, 0)) {
1274 + sql_print_error("Initializing global_user_stats failed.");
1279 +void init_global_client_stats(void)
1281 + if (my_hash_init(&global_client_stats, system_charset_info, max_connections,
1282 + 0, 0, (my_hash_get_key)get_key_user_stats,
1283 + (my_hash_free_key)free_user_stats, 0)) {
1284 + sql_print_error("Initializing global_client_stats failed.");
1289 +void init_global_thread_stats(void)
1291 + if (my_hash_init(&global_thread_stats, &my_charset_bin, max_connections,
1292 + 0, 0, (my_hash_get_key) get_key_thread_stats,
1293 + (my_hash_free_key) free_thread_stats, 0))
1295 + sql_print_error("Initializing global_client_stats failed.");
1300 +extern "C" uchar *get_key_table_stats(TABLE_STATS *table_stats, size_t *length,
1301 + my_bool not_used __attribute__((unused)))
1303 + *length= strlen(table_stats->table);
1304 + return (uchar*) table_stats->table;
1307 +extern "C" void free_table_stats(TABLE_STATS* table_stats)
1309 + my_free((char*) table_stats);
1312 +void init_global_table_stats(void)
1314 + if (my_hash_init(&global_table_stats, system_charset_info, max_connections,
1315 + 0, 0, (my_hash_get_key)get_key_table_stats,
1316 + (my_hash_free_key)free_table_stats, 0)) {
1317 + sql_print_error("Initializing global_table_stats failed.");
1322 +extern "C" uchar *get_key_index_stats(INDEX_STATS *index_stats, size_t *length,
1323 + my_bool not_used __attribute__((unused)))
1325 + *length= strlen(index_stats->index);
1326 + return (uchar*) index_stats->index;
1329 +extern "C" void free_index_stats(INDEX_STATS* index_stats)
1331 + my_free((char*) index_stats);
1334 +void init_global_index_stats(void)
1336 + if (my_hash_init(&global_index_stats, system_charset_info, max_connections,
1337 + 0, 0, (my_hash_get_key)get_key_index_stats,
1338 + (my_hash_free_key)free_index_stats, 0)) {
1339 + sql_print_error("Initializing global_index_stats failed.");
1344 +void free_global_user_stats(void)
1346 + my_hash_free(&global_user_stats);
1349 +void free_global_thread_stats(void)
1351 + my_hash_free(&global_thread_stats);
1354 +void free_global_table_stats(void)
1356 + my_hash_free(&global_table_stats);
1359 +void free_global_index_stats(void)
1361 + my_hash_free(&global_index_stats);
1364 +void free_global_client_stats(void)
1366 + my_hash_free(&global_client_stats);
1369 +// 'mysql_system_user' is used for when the user is not defined for a THD.
1370 +static char mysql_system_user[] = "#mysql_system#";
1372 +// Returns 'user' if it's not NULL. Returns 'mysql_system_user' otherwise.
1373 +static char* get_valid_user_string(char* user) {
1374 + return user ? user : mysql_system_user;
1377 +// Increments the global stats connection count for an entry from
1378 +// global_client_stats or global_user_stats. Returns 0 on success
1380 +static int increment_count_by_name(const char *name, const char *role_name,
1381 + HASH *users_or_clients, THD *thd)
1383 + USER_STATS* user_stats;
1385 + if (!(user_stats = (USER_STATS *) my_hash_search(users_or_clients,
1389 + // First connection for this user or client
1390 + if (!(user_stats = ((USER_STATS *)
1391 + my_malloc(sizeof(USER_STATS), MYF(MY_WME | MY_ZEROFILL)))))
1393 + return 1; // Out of memory
1396 + init_user_stats(user_stats, name, role_name,
1397 + 0, 0, // connections
1399 + 0, 0, 0, // bytes sent, received and written
1400 + 0, 0, 0, // rows fetched, updated and read
1401 + 0, 0, 0, // select, update and other commands
1402 + 0, 0, // commit and rollback trans
1403 + thd->diff_denied_connections,
1404 + 0, // lost connections
1405 + 0, // access denied errors
1406 + 0); // empty queries
1408 + if (my_hash_insert(users_or_clients, (uchar *) user_stats))
1410 + my_free((char *) user_stats);
1411 + return 1; // Out of memory
1414 + user_stats->total_connections++;
1418 +static int increment_count_by_id(my_thread_id id,
1419 + HASH *users_or_clients, THD *thd)
1421 + THREAD_STATS* thread_stats;
1423 + if (!(thread_stats = (THREAD_STATS *) my_hash_search(users_or_clients,
1425 + sizeof(my_thread_id))))
1427 + // First connection for this user or client
1428 + if (!(thread_stats = ((THREAD_STATS *)
1429 + my_malloc(sizeof(THREAD_STATS), MYF(MY_WME | MY_ZEROFILL)))))
1431 + return 1; // Out of memory
1434 + init_thread_stats(thread_stats, id,
1435 + 0, 0, // connections
1437 + 0, 0, 0, // bytes sent, received and written
1438 + 0, 0, 0, // rows fetched, updated and read
1439 + 0, 0, 0, // select, update and other commands
1440 + 0, 0, // commit and rollback trans
1441 + thd->diff_denied_connections,
1442 + 0, // lost connections
1443 + 0, // access denied errors
1444 + 0); // empty queries
1446 + if (my_hash_insert(users_or_clients, (uchar *) thread_stats))
1448 + my_free((char *) thread_stats);
1449 + return 1; // Out of memory
1452 + thread_stats->total_connections++;
1456 +/* Increments the global user and client stats connection count. If 'use_lock'
1457 + is true, LOCK_global_user_client_stats will be locked/unlocked. Returns
1458 + 0 on success, 1 on error.
1460 +static int increment_connection_count(THD* thd, bool use_lock)
1462 + char* user_string= get_valid_user_string(thd->main_security_ctx.user);
1463 + const char* client_string= get_client_host(thd);
1464 + int return_value= 0;
1466 + if (!opt_userstat)
1467 + return return_value;
1470 + mysql_mutex_lock(&LOCK_global_user_client_stats);
1472 + if (increment_count_by_name(user_string, user_string,
1473 + &global_user_stats, thd))
1478 + if (increment_count_by_name(client_string,
1480 + &global_client_stats, thd))
1485 + if (opt_thread_statistics)
1487 + if (increment_count_by_id(thd->thread_id, &global_thread_stats, thd))
1496 + mysql_mutex_unlock(&LOCK_global_user_client_stats);
1497 + return return_value;
1500 +// Used to update the global user and client stats.
1501 +static void update_global_user_stats_with_user(THD* thd,
1502 + USER_STATS* user_stats,
1505 + user_stats->connected_time+= now - thd->last_global_update_time;
1506 +//thd->last_global_update_time= now;
1507 + user_stats->busy_time+= thd->diff_total_busy_time;
1508 + user_stats->cpu_time+= thd->diff_total_cpu_time;
1509 + user_stats->bytes_received+= thd->diff_total_bytes_received;
1510 + user_stats->bytes_sent+= thd->diff_total_bytes_sent;
1511 + user_stats->binlog_bytes_written+= thd->diff_total_binlog_bytes_written;
1512 + user_stats->rows_fetched+= thd->diff_total_sent_rows;
1513 + user_stats->rows_updated+= thd->diff_total_updated_rows;
1514 + user_stats->rows_read+= thd->diff_total_read_rows;
1515 + user_stats->select_commands+= thd->diff_select_commands;
1516 + user_stats->update_commands+= thd->diff_update_commands;
1517 + user_stats->other_commands+= thd->diff_other_commands;
1518 + user_stats->commit_trans+= thd->diff_commit_trans;
1519 + user_stats->rollback_trans+= thd->diff_rollback_trans;
1520 + user_stats->denied_connections+= thd->diff_denied_connections;
1521 + user_stats->lost_connections+= thd->diff_lost_connections;
1522 + user_stats->access_denied_errors+= thd->diff_access_denied_errors;
1523 + user_stats->empty_queries+= thd->diff_empty_queries;
1526 +static void update_global_thread_stats_with_thread(THD* thd,
1527 + THREAD_STATS* thread_stats,
1530 + thread_stats->connected_time+= now - thd->last_global_update_time;
1531 +//thd->last_global_update_time= now;
1532 + thread_stats->busy_time+= thd->diff_total_busy_time;
1533 + thread_stats->cpu_time+= thd->diff_total_cpu_time;
1534 + thread_stats->bytes_received+= thd->diff_total_bytes_received;
1535 + thread_stats->bytes_sent+= thd->diff_total_bytes_sent;
1536 + thread_stats->binlog_bytes_written+= thd->diff_total_binlog_bytes_written;
1537 + thread_stats->rows_fetched+= thd->diff_total_sent_rows;
1538 + thread_stats->rows_updated+= thd->diff_total_updated_rows;
1539 + thread_stats->rows_read+= thd->diff_total_read_rows;
1540 + thread_stats->select_commands+= thd->diff_select_commands;
1541 + thread_stats->update_commands+= thd->diff_update_commands;
1542 + thread_stats->other_commands+= thd->diff_other_commands;
1543 + thread_stats->commit_trans+= thd->diff_commit_trans;
1544 + thread_stats->rollback_trans+= thd->diff_rollback_trans;
1545 + thread_stats->denied_connections+= thd->diff_denied_connections;
1546 + thread_stats->lost_connections+= thd->diff_lost_connections;
1547 + thread_stats->access_denied_errors+= thd->diff_access_denied_errors;
1548 + thread_stats->empty_queries+= thd->diff_empty_queries;
1551 +// Updates the global stats of a user or client
1552 +void update_global_user_stats(THD* thd, bool create_user, time_t now)
1556 + char* user_string= get_valid_user_string(thd->main_security_ctx.user);
1557 + const char* client_string= get_client_host(thd);
1559 + USER_STATS* user_stats;
1560 + THREAD_STATS* thread_stats;
1561 + mysql_mutex_lock(&LOCK_global_user_client_stats);
1563 + // Update by user name
1564 + if ((user_stats = (USER_STATS *) my_hash_search(&global_user_stats,
1565 + (uchar *) user_string,
1566 + strlen(user_string))))
1569 + update_global_user_stats_with_user(thd, user_stats, now);
1573 + // Create the entry
1576 + increment_count_by_name(user_string, user_string,
1577 + &global_user_stats, thd);
1581 + // Update by client IP
1582 + if ((user_stats = (USER_STATS *) my_hash_search(&global_client_stats,
1583 + (uchar *) client_string,
1584 + strlen(client_string))))
1586 + // Found by client IP
1587 + update_global_user_stats_with_user(thd, user_stats, now);
1591 + // Create the entry
1594 + increment_count_by_name(client_string,
1596 + &global_client_stats, thd);
1600 + if (opt_thread_statistics)
1602 + // Update by thread ID
1603 + if ((thread_stats = (THREAD_STATS *) my_hash_search(&global_thread_stats,
1604 + (uchar *) &(thd->thread_id),
1605 + sizeof(my_thread_id))))
1607 + // Found by thread ID
1608 + update_global_thread_stats_with_thread(thd, thread_stats, now);
1612 + // Create the entry
1615 + increment_count_by_id(thd->thread_id,
1616 + &global_thread_stats, thd);
1621 + thd->last_global_update_time = now;
1622 + thd->reset_diff_stats();
1624 + mysql_mutex_unlock(&LOCK_global_user_client_stats);
1628 + thd->reset_diff_stats();
1633 check if user has already too many connections
1637 uc->connections--; // no need for decrease_user_connections() here
1638 + statistic_increment(denied_connections, &LOCK_status);
1640 The thread may returned back to the pool and assigned to a user
1641 that doesn't have a limit. Ensure the user is not using resources
1642 @@ -589,11 +1188,18 @@
1643 my_sleep(1000); /* must wait after eof() */
1645 statistic_increment(aborted_connects,&LOCK_status);
1646 + thd->diff_denied_connections++;
1649 /* Connect completed, set read/write timeouts back to default */
1650 my_net_set_read_timeout(net, thd->variables.net_read_timeout);
1651 my_net_set_write_timeout(net, thd->variables.net_write_timeout);
1653 + thd->reset_stats();
1654 + // Updates global user connection stats.
1655 + if (increment_connection_count(thd, true))
1661 @@ -623,6 +1229,7 @@
1662 if (thd->killed || (net->error && net->vio != 0))
1664 statistic_increment(aborted_threads,&LOCK_status);
1665 + thd->diff_lost_connections++;
1668 if (net->error && net->vio != 0)
1669 @@ -787,10 +1394,14 @@
1673 + bool create_user= TRUE;
1675 rc= thd_prepare_connection(thd);
1678 + create_user= FALSE;
1682 while (thd_is_connection_alive(thd))
1684 @@ -802,6 +1413,8 @@
1687 close_connection(thd);
1688 + thd->update_stats(false);
1689 + update_global_user_stats(thd, create_user, time(NULL));
1690 if (MYSQL_CALLBACK_ELSE(thread_scheduler, end_thread, (thd, 1), 0))
1691 return; // Probably no-threads
1693 --- a/sql/sql_delete.cc
1694 +++ b/sql/sql_delete.cc
1696 my_ok(thd, deleted);
1697 DBUG_PRINT("info",("%ld records deleted",(long) deleted));
1699 + thd->updated_row_count+= deleted;
1700 DBUG_RETURN(error >= 0 || thd->is_error());
1703 @@ -1005,6 +1006,7 @@
1705 ::my_ok(thd, deleted);
1707 + thd->updated_row_count+= deleted;
1711 --- a/sql/sql_insert.cc
1712 +++ b/sql/sql_insert.cc
1713 @@ -1072,13 +1072,14 @@
1717 + ha_rows row_count;
1718 if (values_list.elements == 1 && (!(thd->variables.option_bits & OPTION_WARNINGS) ||
1719 !thd->cuted_fields))
1721 - my_ok(thd, info.copied + info.deleted +
1722 + row_count= info.copied + info.deleted +
1723 ((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
1724 - info.touched : info.updated),
1726 + info.touched : info.updated);
1727 + my_ok(thd, row_count, id);
1731 @@ -1094,8 +1095,10 @@
1732 sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
1733 (ulong) (info.deleted + updated),
1734 (ulong) thd->warning_info->statement_warn_count());
1735 - ::my_ok(thd, info.copied + info.deleted + updated, id, buff);
1736 + row_count= info.copied + info.deleted + updated;
1737 + ::my_ok(thd, row_count, id, buff);
1739 + thd->updated_row_count+= row_count;
1740 thd->abort_on_warning= 0;
1743 @@ -3540,6 +3543,7 @@
1744 thd->first_successful_insert_id_in_prev_stmt :
1745 (info.copied ? autoinc_value_of_last_inserted_row : 0));
1746 ::my_ok(thd, row_count, id, buff);
1747 + thd->updated_row_count+= row_count;
1754 in "struct show_var_st status_vars[]= {" ...
1756 SQLCOM_SHOW_TEMPORARY_TABLES,
1757 + // TODO(mcallaghan): update status_vars in mysqld to export these
1758 + SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS,
1759 + SQLCOM_SHOW_CLIENT_STATS, SQLCOM_SHOW_THREAD_STATS,
1760 /* This should be the last !!! */
1763 --- a/sql/sql_parse.cc
1764 +++ b/sql/sql_parse.cc
1766 static void sql_kill(THD *thd, ulong id, bool only_kill_query);
1767 static inline ulonglong get_query_exec_time(THD *thd, ulonglong cur_utime);
1769 +// Uses the THD to update the global stats by user name and client IP
1770 +void update_global_user_stats(THD* thd, bool create_user, time_t now);
1772 const char *any_db="*any*"; // Special symbol for check_access
1774 const LEX_STRING command_name[]={
1775 @@ -703,6 +706,12 @@
1777 thd->clear_error(); // Clear error message
1778 thd->stmt_da->reset_diagnostics_area();
1779 + thd->updated_row_count= 0;
1780 + thd->busy_time= 0;
1782 + thd->bytes_received= 0;
1783 + thd->bytes_sent= 0;
1784 + thd->binlog_bytes_written= 0;
1786 net_new_transaction(net);
1788 @@ -888,6 +897,10 @@
1789 (char *) thd->security_ctx->host_or_ip);
1791 thd->command=command;
1792 + /* To increment the corrent command counter for user stats, 'command' must
1793 + be saved because it is set to COM_SLEEP at the end of this function.
1795 + thd->old_command= command;
1797 Commands which always take a long time are logged into
1798 the slow log only if opt_log_slow_admin_statements is set.
1799 @@ -1683,6 +1696,13 @@
1800 thd->profiling.discard_current_query();
1803 + case SCH_USER_STATS:
1804 + case SCH_CLIENT_STATS:
1805 + case SCH_THREAD_STATS:
1806 + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
1808 + case SCH_TABLE_STATS:
1809 + case SCH_INDEX_STATS:
1810 case SCH_OPEN_TABLES:
1813 @@ -1857,6 +1877,7 @@
1814 thd->security_ctx->priv_host)) &&
1815 check_global_access(thd, SUPER_ACL))
1817 + thd->diff_access_denied_errors++;
1818 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
1821 @@ -4892,6 +4913,7 @@
1822 case ACL_INTERNAL_ACCESS_DENIED:
1825 + thd->diff_access_denied_errors++;
1826 my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
1827 sctx->priv_user, sctx->priv_host, db);
1829 @@ -4942,6 +4964,7 @@
1830 DBUG_PRINT("error",("No possible access"));
1833 + thd->diff_access_denied_errors++;
1834 if (thd->password == 2)
1835 my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
1837 @@ -5058,6 +5081,7 @@
1839 if (!thd->col_access && check_grant_db(thd, dst_db_name))
1841 + thd->diff_access_denied_errors++;
1842 my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
1843 thd->security_ctx->priv_user,
1844 thd->security_ctx->priv_host,
1845 @@ -5328,6 +5352,7 @@
1846 if ((thd->security_ctx->master_access & want_access))
1848 get_privilege_desc(command, sizeof(command), want_access);
1849 + thd->diff_access_denied_errors++;
1850 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
1853 @@ -5695,6 +5720,32 @@
1855 mysql_reset_thd_for_next_command(thd);
1857 + int start_time_error= 0;
1858 + int end_time_error= 0;
1859 + struct timeval start_time, end_time;
1860 + double start_usecs= 0;
1861 + double end_usecs= 0;
1863 + int cputime_error= 0;
1864 + struct timespec tp;
1865 + double start_cpu_nsecs= 0;
1866 + double end_cpu_nsecs= 0;
1870 +#ifdef HAVE_CLOCK_GETTIME
1871 + /* get start cputime */
1872 + if (!(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
1873 + start_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
1876 + // Gets the start time, in order to measure how long this command takes.
1877 + if (!(start_time_error = gettimeofday(&start_time, NULL)))
1879 + start_usecs = start_time.tv_sec * 1000000.0 + start_time.tv_usec;
1883 if (query_cache_send_result_to_client(thd, rawbuf, length) <= 0)
1886 @@ -5763,6 +5814,52 @@
1887 DBUG_ASSERT(thd->change_list.is_empty());
1892 + // Gets the end time.
1893 + if (!(end_time_error= gettimeofday(&end_time, NULL)))
1895 + end_usecs= end_time.tv_sec * 1000000.0 + end_time.tv_usec;
1898 + // Calculates the difference between the end and start times.
1899 + if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error)
1901 + thd->busy_time= (end_usecs - start_usecs) / 1000000;
1902 + // In case there are bad values, 2629743 is the #seconds in a month.
1903 + if (thd->busy_time > 2629743)
1905 + thd->busy_time= 0;
1910 + // end time went back in time, or gettimeofday() failed.
1911 + thd->busy_time= 0;
1914 +#ifdef HAVE_CLOCK_GETTIME
1915 + /* get end cputime */
1916 + if (!cputime_error &&
1917 + !(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
1918 + end_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
1920 + if (start_cpu_nsecs && !cputime_error)
1922 + thd->cpu_time = (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
1923 + // In case there are bad values, 2629743 is the #seconds in a month.
1924 + if (thd->cpu_time > 2629743)
1926 + thd->cpu_time = 0;
1930 + thd->cpu_time = 0;
1932 + // Updates THD stats and the global user stats.
1933 + thd->update_stats(true);
1934 + update_global_user_stats(thd, true, time(NULL));
1939 --- a/sql/sql_prepare.cc
1940 +++ b/sql/sql_prepare.cc
1943 #include "lock.h" // MYSQL_OPEN_FORCE_SHARED_MDL
1945 +// Uses the THD to update the global stats by user name and client IP
1946 +void update_global_user_stats(THD* thd, bool create_user, time_t now);
1949 A result class used to send cursor rows using the binary protocol.
1951 @@ -2173,8 +2176,34 @@
1952 /* First of all clear possible warnings from the previous command */
1953 mysql_reset_thd_for_next_command(thd);
1955 + int start_time_error= 0;
1956 + int end_time_error= 0;
1957 + struct timeval start_time, end_time;
1958 + double start_usecs= 0;
1959 + double end_usecs= 0;
1961 + int cputime_error= 0;
1962 + struct timespec tp;
1963 + double start_cpu_nsecs= 0;
1964 + double end_cpu_nsecs= 0;
1968 +#ifdef HAVE_CLOCK_GETTIME
1969 + /* get start cputime */
1970 + if (!(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
1971 + start_cpu_nsecs= tp.tv_sec * 1000000000.0 + tp.tv_nsec;
1974 + // Gets the start time, in order to measure how long this command takes.
1975 + if (!(start_time_error= gettimeofday(&start_time, NULL)))
1977 + start_usecs= start_time.tv_sec * 1000000.0 + start_time.tv_usec;
1981 if (! (stmt= new Prepared_statement(thd)))
1982 - DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */
1983 + goto end; /* out of memory: error is set in Sql_alloc */
1985 if (thd->stmt_map.insert(thd, stmt))
1987 @@ -2182,7 +2211,7 @@
1988 The error is set in the insert. The statement itself
1989 will be also deleted there (this is how the hash works).
1995 thd->protocol= &thd->protocol_binary;
1996 @@ -2196,6 +2225,53 @@
1997 thd->protocol= save_protocol;
1999 /* check_prepared_statemnt sends the metadata packet in case of success */
2003 + // Gets the end time.
2004 + if (!(end_time_error= gettimeofday(&end_time, NULL)))
2006 + end_usecs= end_time.tv_sec * 1000000.0 + end_time.tv_usec;
2009 + // Calculates the difference between the end and start times.
2010 + if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error)
2012 + thd->busy_time= (end_usecs - start_usecs) / 1000000;
2013 + // In case there are bad values, 2629743 is the #seconds in a month.
2014 + if (thd->busy_time > 2629743)
2016 + thd->busy_time= 0;
2021 + // end time went back in time, or gettimeofday() failed.
2022 + thd->busy_time= 0;
2025 +#ifdef HAVE_CLOCK_GETTIME
2026 + /* get end cputime */
2027 + if (!cputime_error &&
2028 + !(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2029 + end_cpu_nsecs= tp.tv_sec*1000000000.0+tp.tv_nsec;
2031 + if (start_cpu_nsecs && !cputime_error)
2033 + thd->cpu_time= (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
2034 + // In case there are bad values, 2629743 is the #seconds in a month.
2035 + if (thd->cpu_time > 2629743)
2041 + thd->cpu_time = 0;
2043 + // Updates THD stats and the global user stats.
2044 + thd->update_stats(true);
2045 + update_global_user_stats(thd, true, time(NULL));
2050 @@ -2540,12 +2616,38 @@
2051 /* First of all clear possible warnings from the previous command */
2052 mysql_reset_thd_for_next_command(thd);
2054 + int start_time_error= 0;
2055 + int end_time_error= 0;
2056 + struct timeval start_time, end_time;
2057 + double start_usecs= 0;
2058 + double end_usecs= 0;
2060 + int cputime_error= 0;
2061 + struct timespec tp;
2062 + double start_cpu_nsecs= 0;
2063 + double end_cpu_nsecs= 0;
2067 +#ifdef HAVE_CLOCK_GETTIME
2068 + /* get start cputime */
2069 + if (!(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2070 + start_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
2073 + // Gets the start time, in order to measure how long this command takes.
2074 + if (!(start_time_error = gettimeofday(&start_time, NULL)))
2076 + start_usecs = start_time.tv_sec * 1000000.0 + start_time.tv_usec;
2080 if (!(stmt= find_prepared_statement(thd, stmt_id)))
2083 my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(sizeof(llbuf)),
2084 llstr(stmt_id, llbuf), "mysqld_stmt_execute");
2089 #if defined(ENABLED_PROFILING)
2090 @@ -2563,6 +2665,53 @@
2091 /* Close connection socket; for use with client testing (Bug#43560). */
2092 DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio););
2097 + // Gets the end time.
2098 + if (!(end_time_error= gettimeofday(&end_time, NULL)))
2100 + end_usecs= end_time.tv_sec * 1000000.0 + end_time.tv_usec;
2103 + // Calculates the difference between the end and start times.
2104 + if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error)
2106 + thd->busy_time= (end_usecs - start_usecs) / 1000000;
2107 + // In case there are bad values, 2629743 is the #seconds in a month.
2108 + if (thd->busy_time > 2629743)
2110 + thd->busy_time= 0;
2115 + // end time went back in time, or gettimeofday() failed.
2116 + thd->busy_time= 0;
2119 +#ifdef HAVE_CLOCK_GETTIME
2120 + /* get end cputime */
2121 + if (!cputime_error &&
2122 + !(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2123 + end_cpu_nsecs= tp.tv_sec*1000000000.0+tp.tv_nsec;
2125 + if (start_cpu_nsecs && !cputime_error)
2127 + thd->cpu_time= (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
2128 + // In case there are bad values, 2629743 is the #seconds in a month.
2129 + if (thd->cpu_time > 2629743)
2135 + thd->cpu_time = 0;
2137 + // Updates THD stats and the global user stats.
2138 + thd->update_stats(true);
2139 + update_global_user_stats(thd, true, time(NULL));
2144 @@ -2635,20 +2784,47 @@
2146 /* First of all clear possible warnings from the previous command */
2147 mysql_reset_thd_for_next_command(thd);
2149 + int start_time_error= 0;
2150 + int end_time_error= 0;
2151 + struct timeval start_time, end_time;
2152 + double start_usecs= 0;
2153 + double end_usecs= 0;
2155 + int cputime_error= 0;
2156 + struct timespec tp;
2157 + double start_cpu_nsecs= 0;
2158 + double end_cpu_nsecs= 0;
2162 +#ifdef HAVE_CLOCK_GETTIME
2163 + /* get start cputime */
2164 + if (!(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2165 + start_cpu_nsecs= tp.tv_sec*1000000000.0+tp.tv_nsec;
2168 + // Gets the start time, in order to measure how long this command takes.
2169 + if (!(start_time_error= gettimeofday(&start_time, NULL)))
2171 + start_usecs= start_time.tv_sec * 1000000.0 + start_time.tv_usec;
2175 status_var_increment(thd->status_var.com_stmt_fetch);
2176 if (!(stmt= find_prepared_statement(thd, stmt_id)))
2179 my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(sizeof(llbuf)),
2180 llstr(stmt_id, llbuf), "mysqld_stmt_fetch");
2185 cursor= stmt->cursor;
2188 my_error(ER_STMT_HAS_NO_OPEN_CURSOR, MYF(0), stmt_id);
2193 thd->stmt_arena= stmt;
2194 @@ -2665,6 +2841,52 @@
2195 thd->restore_backup_statement(stmt, &stmt_backup);
2196 thd->stmt_arena= thd;
2201 + // Gets the end time.
2202 + if (!(end_time_error = gettimeofday(&end_time, NULL)))
2204 + end_usecs = end_time.tv_sec * 1000000.0 + end_time.tv_usec;
2207 + // Calculates the difference between the end and start times.
2208 + if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error)
2210 + thd->busy_time= (end_usecs - start_usecs) / 1000000;
2211 + // In case there are bad values, 2629743 is the #seconds in a month.
2212 + if (thd->busy_time > 2629743)
2214 + thd->busy_time= 0;
2219 + // end time went back in time, or gettimeofday() failed.
2220 + thd->busy_time= 0;
2223 +#ifdef HAVE_CLOCK_GETTIME
2224 + /* get end cputime */
2225 + if (!cputime_error &&
2226 + !(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2227 + end_cpu_nsecs= tp.tv_sec*1000000000.0+tp.tv_nsec;
2229 + if (start_cpu_nsecs && !cputime_error)
2231 + thd->cpu_time= (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
2232 + // In case there are bad values, 2629743 is the #seconds in a month.
2233 + if (thd->cpu_time > 2629743)
2240 + // Updates THD stats and the global user stats.
2241 + thd->update_stats(true);
2242 + update_global_user_stats(thd, true, time(NULL));
2247 @@ -2695,13 +2917,39 @@
2248 /* First of all clear possible warnings from the previous command */
2249 mysql_reset_thd_for_next_command(thd);
2251 + int start_time_error= 0;
2252 + int end_time_error= 0;
2253 + struct timeval start_time, end_time;
2254 + double start_usecs= 0;
2255 + double end_usecs= 0;
2257 + int cputime_error= 0;
2258 + struct timespec tp;
2259 + double start_cpu_nsecs= 0;
2260 + double end_cpu_nsecs= 0;
2264 +#ifdef HAVE_CLOCK_GETTIME
2265 + /* get start cputime */
2266 + if (!(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2267 + start_cpu_nsecs= tp.tv_sec * 1000000000.0+tp.tv_nsec;
2270 + // Gets the start time, in order to measure how long this command takes.
2271 + if (!(start_time_error= gettimeofday(&start_time, NULL)))
2273 + start_usecs= start_time.tv_sec * 1000000.0 + start_time.tv_usec;
2277 status_var_increment(thd->status_var.com_stmt_reset);
2278 if (!(stmt= find_prepared_statement(thd, stmt_id)))
2281 my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(sizeof(llbuf)),
2282 llstr(stmt_id, llbuf), "mysqld_stmt_reset");
2287 stmt->close_cursor();
2288 @@ -2718,6 +2966,53 @@
2295 + // Gets the end time.
2296 + if (!(end_time_error = gettimeofday(&end_time, NULL)))
2298 + end_usecs = end_time.tv_sec * 1000000.0 + end_time.tv_usec;
2301 + // Calculates the difference between the end and start times.
2302 + if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error)
2304 + thd->busy_time= (end_usecs - start_usecs) / 1000000;
2305 + // In case there are bad values, 2629743 is the #seconds in a month.
2306 + if (thd->busy_time > 2629743)
2308 + thd->busy_time= 0;
2313 + // end time went back in time, or gettimeofday() failed.
2314 + thd->busy_time= 0;
2317 +#ifdef HAVE_CLOCK_GETTIME
2318 + /* get end cputime */
2319 + if (!cputime_error &&
2320 + !(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2321 + end_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
2323 + if (start_cpu_nsecs && !cputime_error)
2325 + thd->cpu_time = (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
2326 + // In case there are bad values, 2629743 is the #seconds in a month.
2327 + if (thd->cpu_time > 2629743)
2335 + // Updates THD stats and the global user stats.
2336 + thd->update_stats(true);
2337 + update_global_user_stats(thd, true, time(NULL));
2342 --- a/sql/sql_reload.cc
2343 +++ b/sql/sql_reload.cc
2344 @@ -320,14 +320,48 @@
2345 mysql_mutex_unlock(&LOCK_active_mi);
2348 - if (options & REFRESH_USER_RESOURCES)
2349 - reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
2350 #ifdef HAVE_RESPONSE_TIME_DISTRIBUTION
2351 if (options & REFRESH_QUERY_RESPONSE_TIME)
2353 query_response_time_flush();
2355 #endif // HAVE_RESPONSE_TIME_DISTRIBUTION
2356 + if (options & REFRESH_USER_RESOURCES)
2357 + reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
2358 + if (options & REFRESH_TABLE_STATS)
2360 + mysql_mutex_lock(&LOCK_global_table_stats);
2361 + free_global_table_stats();
2362 + init_global_table_stats();
2363 + mysql_mutex_unlock(&LOCK_global_table_stats);
2365 + if (options & REFRESH_INDEX_STATS)
2367 + mysql_mutex_lock(&LOCK_global_index_stats);
2368 + free_global_index_stats();
2369 + init_global_index_stats();
2370 + mysql_mutex_unlock(&LOCK_global_index_stats);
2372 + if (options & (REFRESH_USER_STATS | REFRESH_CLIENT_STATS | REFRESH_THREAD_STATS))
2374 + mysql_mutex_lock(&LOCK_global_user_client_stats);
2375 + if (options & REFRESH_USER_STATS)
2377 + free_global_user_stats();
2378 + init_global_user_stats();
2380 + if (options & REFRESH_CLIENT_STATS)
2382 + free_global_client_stats();
2383 + init_global_client_stats();
2385 + if (options & REFRESH_THREAD_STATS)
2387 + free_global_thread_stats();
2388 + init_global_thread_stats();
2390 + mysql_mutex_unlock(&LOCK_global_user_client_stats);
2392 if (*write_to_binlog != -1)
2393 *write_to_binlog= tmp_write_to_binlog;
2395 --- a/sql/sql_show.cc
2396 +++ b/sql/sql_show.cc
2397 @@ -114,6 +114,43 @@
2399 static COND * make_cond_for_info_schema(COND *cond, TABLE_LIST *table);
2402 + * Solaris 10 does not have strsep().
2404 + * based on getToken from http://www.winehq.org/pipermail/wine-patches/2001-November/001322.html
2408 +#ifndef HAVE_STRSEP
2409 +static char* strsep(char** str, const char* delims)
2415 + /* No more tokens */
2420 + while (**str != '\0')
2422 + if (strchr(delims, **str) != NULL)
2431 + /* There is not another token */
2438 /***************************************************************************
2439 ** List all table types supported
2440 ***************************************************************************/
2442 sctx->master_access);
2443 if (!(db_access & DB_ACLS) && check_grant_db(thd,dbname))
2445 + thd->diff_access_denied_errors++;
2446 my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
2447 sctx->priv_user, sctx->host_or_ip, dbname);
2448 general_log_print(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
2449 @@ -2359,6 +2397,284 @@
2454 + Write result to network for SHOW USER_STATISTICS
2458 + all_user_stats - values to return
2465 +int send_user_stats(THD* thd, HASH *all_user_stats, TABLE *table)
2467 + DBUG_ENTER("send_user_stats");
2468 + for (uint i = 0; i < all_user_stats->records; ++i)
2470 + restore_record(table, s->default_values);
2471 + USER_STATS *user_stats = (USER_STATS *) my_hash_element(all_user_stats, i);
2472 + table->field[0]->store(user_stats->user, strlen(user_stats->user), system_charset_info);
2473 + table->field[1]->store((longlong)user_stats->total_connections);
2474 + table->field[2]->store((longlong)user_stats->concurrent_connections);
2475 + table->field[3]->store((longlong)user_stats->connected_time);
2476 + table->field[4]->store((longlong)user_stats->busy_time);
2477 + table->field[5]->store((longlong)user_stats->cpu_time);
2478 + table->field[6]->store((longlong)user_stats->bytes_received);
2479 + table->field[7]->store((longlong)user_stats->bytes_sent);
2480 + table->field[8]->store((longlong)user_stats->binlog_bytes_written);
2481 + table->field[9]->store((longlong)user_stats->rows_fetched);
2482 + table->field[10]->store((longlong)user_stats->rows_updated);
2483 + table->field[11]->store((longlong)user_stats->rows_read);
2484 + table->field[12]->store((longlong)user_stats->select_commands);
2485 + table->field[13]->store((longlong)user_stats->update_commands);
2486 + table->field[14]->store((longlong)user_stats->other_commands);
2487 + table->field[15]->store((longlong)user_stats->commit_trans);
2488 + table->field[16]->store((longlong)user_stats->rollback_trans);
2489 + table->field[17]->store((longlong)user_stats->denied_connections);
2490 + table->field[18]->store((longlong)user_stats->lost_connections);
2491 + table->field[19]->store((longlong)user_stats->access_denied_errors);
2492 + table->field[20]->store((longlong)user_stats->empty_queries);
2493 + if (schema_table_store_record(thd, table))
2495 + DBUG_PRINT("error", ("store record error"));
2502 +int send_thread_stats(THD* thd, HASH *all_thread_stats, TABLE *table)
2504 + DBUG_ENTER("send_thread_stats");
2505 + for (uint i = 0; i < all_thread_stats->records; ++i)
2507 + restore_record(table, s->default_values);
2508 + THREAD_STATS *user_stats = (THREAD_STATS *) my_hash_element(all_thread_stats, i);
2509 + table->field[0]->store((longlong)user_stats->id);
2510 + table->field[1]->store((longlong)user_stats->total_connections);
2511 + table->field[2]->store((longlong)user_stats->concurrent_connections);
2512 + table->field[3]->store((longlong)user_stats->connected_time);
2513 + table->field[4]->store((longlong)user_stats->busy_time);
2514 + table->field[5]->store((longlong)user_stats->cpu_time);
2515 + table->field[6]->store((longlong)user_stats->bytes_received);
2516 + table->field[7]->store((longlong)user_stats->bytes_sent);
2517 + table->field[8]->store((longlong)user_stats->binlog_bytes_written);
2518 + table->field[9]->store((longlong)user_stats->rows_fetched);
2519 + table->field[10]->store((longlong)user_stats->rows_updated);
2520 + table->field[11]->store((longlong)user_stats->rows_read);
2521 + table->field[12]->store((longlong)user_stats->select_commands);
2522 + table->field[13]->store((longlong)user_stats->update_commands);
2523 + table->field[14]->store((longlong)user_stats->other_commands);
2524 + table->field[15]->store((longlong)user_stats->commit_trans);
2525 + table->field[16]->store((longlong)user_stats->rollback_trans);
2526 + table->field[17]->store((longlong)user_stats->denied_connections);
2527 + table->field[18]->store((longlong)user_stats->lost_connections);
2528 + table->field[19]->store((longlong)user_stats->access_denied_errors);
2529 + table->field[20]->store((longlong)user_stats->empty_queries);
2530 + if (schema_table_store_record(thd, table))
2532 + DBUG_PRINT("error", ("store record error"));
2540 + Process SHOW USER_STATISTICS
2543 + mysqld_show_user_stats
2544 + thd - current thread
2545 + wild - limit results to the entry for this user
2546 + with_roles - when true, display role for mapped users
2554 +int fill_schema_user_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2556 + TABLE *table= tables->table;
2557 + DBUG_ENTER("fill_schema_user_stats");
2559 + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
2562 + // Iterates through all the global stats and sends them to the client.
2563 + // Pattern matching on the client IP is supported.
2565 + mysql_mutex_lock(&LOCK_global_user_client_stats);
2566 + int result= send_user_stats(thd, &global_user_stats, table);
2567 + mysql_mutex_unlock(&LOCK_global_user_client_stats);
2571 + DBUG_PRINT("exit", ("fill_schema_user_stats result is 0"));
2575 + DBUG_PRINT("exit", ("fill_schema_user_stats result is 1"));
2580 + Process SHOW CLIENT_STATISTICS
2583 + mysqld_show_client_stats
2584 + thd - current thread
2585 + wild - limit results to the entry for this client
2593 +int fill_schema_client_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2595 + TABLE *table= tables->table;
2596 + DBUG_ENTER("fill_schema_client_stats");
2598 + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
2601 + // Iterates through all the global stats and sends them to the client.
2602 + // Pattern matching on the client IP is supported.
2604 + mysql_mutex_lock(&LOCK_global_user_client_stats);
2605 + int result= send_user_stats(thd, &global_client_stats, table);
2606 + mysql_mutex_unlock(&LOCK_global_user_client_stats);
2610 + DBUG_PRINT("exit", ("mysqld_show_client_stats result is 0"));
2614 + DBUG_PRINT("exit", ("mysqld_show_client_stats result is 1"));
2618 +int fill_schema_thread_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2620 + TABLE *table= tables->table;
2621 + DBUG_ENTER("fill_schema_thread_stats");
2623 + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
2626 + // Iterates through all the global stats and sends them to the client.
2627 + // Pattern matching on the client IP is supported.
2629 + mysql_mutex_lock(&LOCK_global_user_client_stats);
2630 + int result= send_thread_stats(thd, &global_thread_stats, table);
2631 + mysql_mutex_unlock(&LOCK_global_user_client_stats);
2635 + DBUG_PRINT("exit", ("mysqld_show_thread_stats result is 0"));
2639 + DBUG_PRINT("exit", ("mysqld_show_thread_stats result is 1"));
2643 +// Sends the global table stats back to the client.
2644 +int fill_schema_table_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2646 + TABLE *table= tables->table;
2647 + DBUG_ENTER("fill_schema_table_stats");
2648 + char *table_full_name, *table_schema;
2650 + mysql_mutex_lock(&LOCK_global_table_stats);
2651 + for (uint i = 0; i < global_table_stats.records; ++i)
2653 + restore_record(table, s->default_values);
2654 + TABLE_STATS *table_stats =
2655 + (TABLE_STATS *) my_hash_element(&global_table_stats, i);
2657 + table_full_name= thd->strdup(table_stats->table);
2658 + table_schema= strsep(&table_full_name, ".");
2660 + TABLE_LIST tmp_table;
2661 + bzero((char *) &tmp_table,sizeof(tmp_table));
2662 + tmp_table.table_name= table_full_name;
2663 + tmp_table.db= table_schema;
2664 + tmp_table.grant.privilege= 0;
2665 + if (check_access(thd, SELECT_ACL, tmp_table.db,
2666 + &tmp_table.grant.privilege, 0, 0,
2667 + is_infoschema_db(table_schema)) ||
2668 + check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, 1))
2671 + table->field[0]->store(table_schema, strlen(table_schema), system_charset_info);
2672 + table->field[1]->store(table_full_name, strlen(table_full_name), system_charset_info);
2673 + table->field[2]->store((longlong)table_stats->rows_read, TRUE);
2674 + table->field[3]->store((longlong)table_stats->rows_changed, TRUE);
2675 + table->field[4]->store((longlong)table_stats->rows_changed_x_indexes, TRUE);
2677 + if (schema_table_store_record(thd, table))
2679 + mysql_mutex_unlock(&LOCK_global_table_stats);
2683 + mysql_mutex_unlock(&LOCK_global_table_stats);
2687 +// Sends the global index stats back to the client.
2688 +int fill_schema_index_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2690 + TABLE *table= tables->table;
2691 + DBUG_ENTER("fill_schema_index_stats");
2692 + char *index_full_name, *table_schema, *table_name;
2694 + mysql_mutex_lock(&LOCK_global_index_stats);
2695 + for (uint i = 0; i < global_index_stats.records; ++i)
2697 + restore_record(table, s->default_values);
2698 + INDEX_STATS *index_stats =
2699 + (INDEX_STATS *) my_hash_element(&global_index_stats, i);
2701 + index_full_name= thd->strdup(index_stats->index);
2702 + table_schema= strsep(&index_full_name, ".");
2703 + table_name= strsep(&index_full_name, ".");
2705 + TABLE_LIST tmp_table;
2706 + bzero((char *) &tmp_table,sizeof(tmp_table));
2707 + tmp_table.table_name= table_name;
2708 + tmp_table.db= table_schema;
2709 + tmp_table.grant.privilege= 0;
2710 + if (check_access(thd, SELECT_ACL, tmp_table.db,
2711 + &tmp_table.grant.privilege, 0, 0,
2712 + is_infoschema_db(table_schema)) ||
2713 + check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, 1))
2716 + table->field[0]->store(table_schema, strlen(table_schema), system_charset_info);
2717 + table->field[1]->store(table_name, strlen(table_name), system_charset_info);
2718 + table->field[2]->store(index_full_name, strlen(index_full_name), system_charset_info);
2719 + table->field[3]->store((longlong)index_stats->rows_read, TRUE);
2721 + if (schema_table_store_record(thd, table))
2723 + mysql_mutex_unlock(&LOCK_global_index_stats);
2727 + mysql_mutex_unlock(&LOCK_global_index_stats);
2732 /* collect status for all running threads */
2734 @@ -7712,6 +8028,104 @@
2738 +ST_FIELD_INFO user_stats_fields_info[]=
2740 + {"USER", USERNAME_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User", SKIP_OPEN_TABLE},
2741 + {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections", SKIP_OPEN_TABLE},
2742 + {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections", SKIP_OPEN_TABLE},
2743 + {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time", SKIP_OPEN_TABLE},
2744 + {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Busy_time", SKIP_OPEN_TABLE},
2745 + {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Cpu_time", SKIP_OPEN_TABLE},
2746 + {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_received", SKIP_OPEN_TABLE},
2747 + {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_sent", SKIP_OPEN_TABLE},
2748 + {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Binlog_bytes_written", SKIP_OPEN_TABLE},
2749 + {"ROWS_FETCHED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_fetched", SKIP_OPEN_TABLE},
2750 + {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated", SKIP_OPEN_TABLE},
2751 + {"TABLE_ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Table_rows_read", SKIP_OPEN_TABLE},
2752 + {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Select_commands", SKIP_OPEN_TABLE},
2753 + {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Update_commands", SKIP_OPEN_TABLE},
2754 + {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Other_commands", SKIP_OPEN_TABLE},
2755 + {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Commit_transactions", SKIP_OPEN_TABLE},
2756 + {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rollback_transactions", SKIP_OPEN_TABLE},
2757 + {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Denied_connections", SKIP_OPEN_TABLE},
2758 + {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Lost_connections", SKIP_OPEN_TABLE},
2759 + {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Access_denied", SKIP_OPEN_TABLE},
2760 + {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Empty_queries", SKIP_OPEN_TABLE},
2761 + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2764 +ST_FIELD_INFO client_stats_fields_info[]=
2766 + {"CLIENT", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Client", SKIP_OPEN_TABLE},
2767 + {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections", SKIP_OPEN_TABLE},
2768 + {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections", SKIP_OPEN_TABLE},
2769 + {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time", SKIP_OPEN_TABLE},
2770 + {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Busy_time", SKIP_OPEN_TABLE},
2771 + {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Cpu_time", SKIP_OPEN_TABLE},
2772 + {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_received", SKIP_OPEN_TABLE},
2773 + {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_sent", SKIP_OPEN_TABLE},
2774 + {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Binlog_bytes_written", SKIP_OPEN_TABLE},
2775 + {"ROWS_FETCHED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_fetched", SKIP_OPEN_TABLE},
2776 + {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated", SKIP_OPEN_TABLE},
2777 + {"TABLE_ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Table_rows_read", SKIP_OPEN_TABLE},
2778 + {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Select_commands", SKIP_OPEN_TABLE},
2779 + {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Update_commands", SKIP_OPEN_TABLE},
2780 + {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Other_commands", SKIP_OPEN_TABLE},
2781 + {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Commit_transactions", SKIP_OPEN_TABLE},
2782 + {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rollback_transactions", SKIP_OPEN_TABLE},
2783 + {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Denied_connections", SKIP_OPEN_TABLE},
2784 + {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Lost_connections", SKIP_OPEN_TABLE},
2785 + {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Access_denied", SKIP_OPEN_TABLE},
2786 + {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Empty_queries", SKIP_OPEN_TABLE},
2787 + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2790 +ST_FIELD_INFO thread_stats_fields_info[]=
2792 + {"THREAD_ID", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Thread_id", SKIP_OPEN_TABLE},
2793 + {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections", SKIP_OPEN_TABLE},
2794 + {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections", SKIP_OPEN_TABLE},
2795 + {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time", SKIP_OPEN_TABLE},
2796 + {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Busy_time", SKIP_OPEN_TABLE},
2797 + {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Cpu_time", SKIP_OPEN_TABLE},
2798 + {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_received", SKIP_OPEN_TABLE},
2799 + {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_sent", SKIP_OPEN_TABLE},
2800 + {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Binlog_bytes_written", SKIP_OPEN_TABLE},
2801 + {"ROWS_FETCHED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_fetched", SKIP_OPEN_TABLE},
2802 + {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated", SKIP_OPEN_TABLE},
2803 + {"TABLE_ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Table_rows_read", SKIP_OPEN_TABLE},
2804 + {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Select_commands", SKIP_OPEN_TABLE},
2805 + {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Update_commands", SKIP_OPEN_TABLE},
2806 + {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Other_commands", SKIP_OPEN_TABLE},
2807 + {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Commit_transactions", SKIP_OPEN_TABLE},
2808 + {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rollback_transactions", SKIP_OPEN_TABLE},
2809 + {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Denied_connections", SKIP_OPEN_TABLE},
2810 + {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Lost_connections", SKIP_OPEN_TABLE},
2811 + {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Access_denied", SKIP_OPEN_TABLE},
2812 + {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Empty_queries", SKIP_OPEN_TABLE},
2813 + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2816 +ST_FIELD_INFO table_stats_fields_info[]=
2818 + {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_schema", SKIP_OPEN_TABLE},
2819 + {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name", SKIP_OPEN_TABLE},
2820 + {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read", SKIP_OPEN_TABLE},
2821 + {"ROWS_CHANGED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_changed", SKIP_OPEN_TABLE},
2822 + {"ROWS_CHANGED_X_INDEXES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_changed_x_#indexes", SKIP_OPEN_TABLE},
2823 + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2826 +ST_FIELD_INFO index_stats_fields_info[]=
2828 + {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_schema", SKIP_OPEN_TABLE},
2829 + {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name", SKIP_OPEN_TABLE},
2830 + {"INDEX_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Index_name", SKIP_OPEN_TABLE},
2831 + {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read", SKIP_OPEN_TABLE},
2832 + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2836 ST_FIELD_INFO processlist_fields_info[]=
2838 {"ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Id", SKIP_OPEN_TABLE},
2839 @@ -7901,6 +8315,8 @@
2841 {"CHARACTER_SETS", charsets_fields_info, create_schema_table,
2842 fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0, 0},
2843 + {"CLIENT_STATISTICS", client_stats_fields_info, create_schema_table,
2844 + fill_schema_client_stats, make_old_format, 0, -1, -1, 0, 0},
2845 {"COLLATIONS", collation_fields_info, create_schema_table,
2846 fill_schema_collation, make_old_format, 0, -1, -1, 0, 0},
2847 {"COLLATION_CHARACTER_SET_APPLICABILITY", coll_charset_app_fields_info,
2848 @@ -7910,6 +8326,8 @@
2849 OPTIMIZE_I_S_TABLE|OPEN_VIEW_FULL},
2850 {"COLUMN_PRIVILEGES", column_privileges_fields_info, create_schema_table,
2851 fill_schema_column_privileges, 0, 0, -1, -1, 0, 0},
2852 + {"INDEX_STATISTICS", index_stats_fields_info, create_schema_table,
2853 + fill_schema_index_stats, make_old_format, 0, -1, -1, 0, 0},
2854 {"ENGINES", engines_fields_info, create_schema_table,
2855 fill_schema_engines, make_old_format, 0, -1, -1, 0, 0},
2856 #ifdef HAVE_EVENT_SCHEDULER
2857 @@ -7982,14 +8400,20 @@
2858 get_all_tables, make_table_names_old_format, 0, 1, 2, 1, 0},
2859 {"TABLE_PRIVILEGES", table_privileges_fields_info, create_schema_table,
2860 fill_schema_table_privileges, 0, 0, -1, -1, 0, 0},
2861 + {"TABLE_STATISTICS", table_stats_fields_info, create_schema_table,
2862 + fill_schema_table_stats, make_old_format, 0, -1, -1, 0, 0},
2863 {"TEMPORARY_TABLES", temporary_table_fields_info, create_schema_table,
2864 fill_temporary_tables, make_temporary_tables_old_format, 0, 2, 3, 0,
2865 OPEN_TABLE_ONLY|OPTIMIZE_I_S_TABLE},
2866 + {"THREAD_STATISTICS", thread_stats_fields_info, create_schema_table,
2867 + fill_schema_thread_stats, make_old_format, 0, -1, -1, 0, 0},
2868 {"TRIGGERS", triggers_fields_info, create_schema_table,
2869 get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0,
2870 OPEN_TRIGGER_ONLY|OPTIMIZE_I_S_TABLE},
2871 {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table,
2872 fill_schema_user_privileges, 0, 0, -1, -1, 0, 0},
2873 + {"USER_STATISTICS", user_stats_fields_info, create_schema_table,
2874 + fill_schema_user_stats, make_old_format, 0, -1, -1, 0, 0},
2875 {"VARIABLES", variables_fields_info, create_schema_table, fill_variables,
2876 make_old_format, 0, 0, -1, 1, 0},
2877 {"VIEWS", view_fields_info, create_schema_table,
2878 --- a/sql/sql_update.cc
2879 +++ b/sql/sql_update.cc
2880 @@ -900,8 +900,10 @@
2881 my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), (ulong) found,
2883 (ulong) thd->warning_info->statement_warn_count());
2884 - my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
2886 + ha_rows row_count=
2887 + (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
2888 + my_ok(thd, row_count, id, buff);
2889 + thd->updated_row_count += row_count;
2890 DBUG_PRINT("info",("%ld records updated", (long) updated));
2892 thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */
2893 @@ -2252,7 +2254,9 @@
2894 thd->first_successful_insert_id_in_prev_stmt : 0;
2895 my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO),
2896 (ulong) found, (ulong) updated, (ulong) thd->cuted_fields);
2897 - ::my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
2899 + ha_rows row_count=
2900 + (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
2901 + ::my_ok(thd, row_count, id, buff);
2902 + thd->updated_row_count+= row_count;
2905 --- a/sql/sql_yacc.yy
2906 +++ b/sql/sql_yacc.yy
2909 %token CLASS_ORIGIN_SYM /* SQL-2003-N */
2911 +%token CLIENT_STATS_SYM
2912 %token CLOSE_SYM /* SQL-2003-R */
2913 %token COALESCE /* SQL-2003-N */
2915 @@ -1018,6 +1019,7 @@
2919 +%token INDEX_STATS_SYM
2921 %token INITIAL_SIZE_SYM
2922 %token INNER_SYM /* SQL-2003-R */
2923 @@ -1316,6 +1318,7 @@
2925 %token TABLE_REF_PRIORITY
2926 %token TABLE_SYM /* SQL-2003-R */
2927 +%token TABLE_STATS_SYM
2928 %token TABLE_CHECKSUM_SYM
2929 %token TABLE_NAME_SYM /* SQL-2003-N */
2930 %token TEMPORARY /* SQL-2003-N */
2931 @@ -1325,6 +1328,7 @@
2934 %token THEN_SYM /* SQL-2003-R */
2935 +%token THREAD_STATS_SYM
2936 %token TIMESTAMP /* SQL-2003-R */
2937 %token TIMESTAMP_ADD
2938 %token TIMESTAMP_DIFF
2939 @@ -1362,6 +1366,7 @@
2941 %token USAGE /* SQL-2003-N */
2942 %token USER /* SQL-2003-R */
2943 +%token USER_STATS_SYM
2946 %token USING /* SQL-2003-R */
2947 @@ -11126,6 +11131,41 @@
2949 #endif // HAVE_RESPONSE_TIME_DISTRIBUTION
2951 + | CLIENT_STATS_SYM wild_and_where
2954 + Lex->sql_command= SQLCOM_SELECT;
2955 + if (prepare_schema_table(YYTHD, lex, 0, SCH_CLIENT_STATS))
2958 + | USER_STATS_SYM wild_and_where
2961 + lex->sql_command= SQLCOM_SELECT;
2962 + if (prepare_schema_table(YYTHD, lex, 0, SCH_USER_STATS))
2965 + | THREAD_STATS_SYM wild_and_where
2968 + Lex->sql_command= SQLCOM_SELECT;
2969 + if (prepare_schema_table(YYTHD, lex, 0, SCH_THREAD_STATS))
2972 + | TABLE_STATS_SYM wild_and_where
2975 + lex->sql_command= SQLCOM_SELECT;
2976 + if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_STATS))
2979 + | INDEX_STATS_SYM wild_and_where
2982 + lex->sql_command= SQLCOM_SELECT;
2983 + if (prepare_schema_table(YYTHD, lex, 0, SCH_INDEX_STATS))
2986 | CREATE PROCEDURE_SYM sp_name
2989 @@ -11371,6 +11411,16 @@
2990 Lex->type|= REFRESH_QUERY_RESPONSE_TIME;
2991 #endif // HAVE_RESPONSE_TIME_DISTRIBUTION
2993 + | CLIENT_STATS_SYM
2994 + { Lex->type|= REFRESH_CLIENT_STATS; }
2996 + { Lex->type|= REFRESH_USER_STATS; }
2997 + | THREAD_STATS_SYM
2998 + { Lex->type|= REFRESH_THREAD_STATS; }
3000 + { Lex->type|= REFRESH_TABLE_STATS; }
3002 + { Lex->type|= REFRESH_INDEX_STATS; }
3004 { Lex->type|= REFRESH_MASTER; }
3006 @@ -12515,6 +12565,7 @@
3010 + | CLIENT_STATS_SYM {}
3012 | CLASS_ORIGIN_SYM {}
3014 @@ -12583,6 +12634,7 @@
3018 + | INDEX_STATS_SYM {}
3019 | IGNORE_SERVER_IDS_SYM {}
3022 @@ -12734,6 +12786,7 @@
3026 + | TABLE_STATS_SYM {}
3029 | TABLE_CHECKSUM_SYM {}
3030 @@ -12759,6 +12812,7 @@
3034 + | USER_STATS_SYM {}
3041 #include "my_time.h" /* enum_mysql_timestamp_type */
3042 #include "thr_lock.h" /* thr_lock_type */
3043 #include "my_base.h" /* ha_rows, ha_key_alg */
3044 +#include "mysql_com.h"
3048 @@ -218,6 +219,171 @@
3049 USER_RESOURCES user_resources;
3052 +typedef struct st_user_stats {
3053 + char user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1];
3054 + // Account name the user is mapped to when this is a user from mapped_user.
3055 + // Otherwise, the same value as user.
3056 + char priv_user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1];
3057 + uint total_connections;
3058 + uint concurrent_connections;
3059 + time_t connected_time; // in seconds
3060 + double busy_time; // in seconds
3061 + double cpu_time; // in seconds
3062 + ulonglong bytes_received;
3063 + ulonglong bytes_sent;
3064 + ulonglong binlog_bytes_written;
3065 + ha_rows rows_fetched, rows_updated, rows_read;
3066 + ulonglong select_commands, update_commands, other_commands;
3067 + ulonglong commit_trans, rollback_trans;
3068 + ulonglong denied_connections, lost_connections;
3069 + ulonglong access_denied_errors;
3070 + ulonglong empty_queries;
3073 +/* Lookup function for my_hash tables with USER_STATS entries */
3074 +extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
3075 + my_bool not_used __attribute__((unused)));
3077 +/* Free all memory for a my_hash table with USER_STATS entries */
3078 +extern void free_user_stats(USER_STATS* user_stats);
3080 +/* Intialize an instance of USER_STATS */
3082 +init_user_stats(USER_STATS *user_stats,
3084 + const char *priv_user,
3085 + uint total_connections,
3086 + uint concurrent_connections,
3087 + time_t connected_time,
3090 + ulonglong bytes_received,
3091 + ulonglong bytes_sent,
3092 + ulonglong binlog_bytes_written,
3093 + ha_rows rows_fetched,
3094 + ha_rows rows_updated,
3095 + ha_rows rows_read,
3096 + ulonglong select_commands,
3097 + ulonglong update_commands,
3098 + ulonglong other_commands,
3099 + ulonglong commit_trans,
3100 + ulonglong rollback_trans,
3101 + ulonglong denied_connections,
3102 + ulonglong lost_connections,
3103 + ulonglong access_denied_errors,
3104 + ulonglong empty_queries);
3106 +/* Increment values of an instance of USER_STATS */
3108 +add_user_stats(USER_STATS *user_stats,
3109 + uint total_connections,
3110 + uint concurrent_connections,
3111 + time_t connected_time,
3114 + ulonglong bytes_received,
3115 + ulonglong bytes_sent,
3116 + ulonglong binlog_bytes_written,
3117 + ha_rows rows_fetched,
3118 + ha_rows rows_updated,
3119 + ha_rows rows_read,
3120 + ulonglong select_commands,
3121 + ulonglong update_commands,
3122 + ulonglong other_commands,
3123 + ulonglong commit_trans,
3124 + ulonglong rollback_trans,
3125 + ulonglong denied_connections,
3126 + ulonglong lost_connections,
3127 + ulonglong access_denied_errors,
3128 + ulonglong empty_queries);
3130 +typedef struct st_thread_stats {
3132 + uint total_connections;
3133 + uint concurrent_connections;
3134 + time_t connected_time; // in seconds
3135 + double busy_time; // in seconds
3136 + double cpu_time; // in seconds
3137 + ulonglong bytes_received;
3138 + ulonglong bytes_sent;
3139 + ulonglong binlog_bytes_written;
3140 + ha_rows rows_fetched, rows_updated, rows_read;
3141 + ulonglong select_commands, update_commands, other_commands;
3142 + ulonglong commit_trans, rollback_trans;
3143 + ulonglong denied_connections, lost_connections;
3144 + ulonglong access_denied_errors;
3145 + ulonglong empty_queries;
3148 +/* Lookup function for my_hash tables with THREAD_STATS entries */
3149 +extern "C" uchar *get_key_thread_stats(THREAD_STATS *thread_stats, size_t *length,
3150 + my_bool not_used __attribute__((unused)));
3152 +/* Free all memory for a my_hash table with THREAD_STATS entries */
3153 +extern void free_thread_stats(THREAD_STATS* thread_stats);
3155 +/* Intialize an instance of THREAD_STATS */
3157 +init_thread_stats(THREAD_STATS *thread_stats,
3159 + uint total_connections,
3160 + uint concurrent_connections,
3161 + time_t connected_time,
3164 + ulonglong bytes_received,
3165 + ulonglong bytes_sent,
3166 + ulonglong binlog_bytes_written,
3167 + ha_rows rows_fetched,
3168 + ha_rows rows_updated,
3169 + ha_rows rows_read,
3170 + ulonglong select_commands,
3171 + ulonglong update_commands,
3172 + ulonglong other_commands,
3173 + ulonglong commit_trans,
3174 + ulonglong rollback_trans,
3175 + ulonglong denied_connections,
3176 + ulonglong lost_connections,
3177 + ulonglong access_denied_errors,
3178 + ulonglong empty_queries);
3180 +/* Increment values of an instance of THREAD_STATS */
3182 +add_thread_stats(THREAD_STATS *thread_stats,
3183 + uint total_connections,
3184 + uint concurrent_connections,
3185 + time_t connected_time,
3188 + ulonglong bytes_received,
3189 + ulonglong bytes_sent,
3190 + ulonglong binlog_bytes_written,
3191 + ha_rows rows_fetched,
3192 + ha_rows rows_updated,
3193 + ha_rows rows_read,
3194 + ulonglong select_commands,
3195 + ulonglong update_commands,
3196 + ulonglong other_commands,
3197 + ulonglong commit_trans,
3198 + ulonglong rollback_trans,
3199 + ulonglong denied_connections,
3200 + ulonglong lost_connections,
3201 + ulonglong access_denied_errors,
3202 + ulonglong empty_queries);
3204 +typedef struct st_table_stats {
3205 + char table[NAME_LEN * 2 + 2]; // [db] + '.' + [table] + '\0'
3206 + ulonglong rows_read, rows_changed;
3207 + ulonglong rows_changed_x_indexes;
3208 + /* Stores enum db_type, but forward declarations cannot be done */
3212 +typedef struct st_index_stats {
3213 + char index[NAME_LEN * 3 + 3]; // [db] + '.' + [table] + '.' + [index] + '\0'
3214 + ulonglong rows_read;
3217 /* Bits in form->update */
3218 #define REG_MAKE_DUPP 1 /* Make a copy of record when read */
3219 #define REG_NEW_RECORD 2 /* Write a new record if not found */
3220 --- a/sql/sys_vars.cc
3221 +++ b/sql/sys_vars.cc
3222 @@ -1711,6 +1711,17 @@
3223 NO_MUTEX_GUARD, NOT_IN_BINLOG,
3224 ON_CHECK(check_read_only), ON_UPDATE(fix_read_only));
3226 +static Sys_var_mybool Sys_userstat(
3228 + "Control USER_STATISTICS, CLIENT_STATISTICS, THREAD_STATISTICS, "
3229 + "INDEX_STATISTICS and TABLE_STATISTICS running",
3230 + GLOBAL_VAR(opt_userstat), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
3232 +static Sys_var_mybool Sys_thread_statistics(
3233 + "thread_statistics",
3234 + "Control TABLE_STATISTICS running, when userstat is enabled",
3235 + GLOBAL_VAR(opt_thread_statistics), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
3237 // Small lower limit to be able to test MRR
3238 static Sys_var_ulong Sys_read_rnd_buff_size(
3239 "read_rnd_buffer_size",
3240 --- a/storage/myisam/ha_myisam.cc
3241 +++ b/storage/myisam/ha_myisam.cc
3244 int ha_myisam::write_row(uchar *buf)
3247 ha_statistic_increment(&SSV::ha_write_count);
3249 /* If we have a timestamp column, update it to the current time */
3250 @@ -782,11 +783,13 @@
3252 if (table->next_number_field && buf == table->record[0])
3255 if ((error= update_auto_increment()))
3258 - return mi_write(file,buf);
3259 + error=mi_write(file,buf);
3265 int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
3266 @@ -1553,16 +1556,24 @@
3268 int ha_myisam::update_row(const uchar *old_data, uchar *new_data)
3271 ha_statistic_increment(&SSV::ha_update_count);
3272 if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
3273 table->timestamp_field->set_time();
3274 - return mi_update(file,old_data,new_data);
3275 + error=mi_update(file,old_data,new_data);
3281 int ha_myisam::delete_row(const uchar *buf)
3284 ha_statistic_increment(&SSV::ha_delete_count);
3285 - return mi_delete(file,buf);
3286 + error=mi_delete(file,buf);
3292 int ha_myisam::index_read_map(uchar *buf, const uchar *key,
3293 @@ -1574,6 +1585,14 @@
3294 ha_statistic_increment(&SSV::ha_read_key_count);
3295 int error=mi_rkey(file, buf, active_index, key, keypart_map, find_flag);
3296 table->status=error ? STATUS_NOT_FOUND: 0;
3301 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3302 + if (inx >= 0 && inx < MAX_KEY)
3303 + index_rows_read[inx]++;
3305 MYSQL_INDEX_READ_ROW_DONE(error);
3308 @@ -1586,6 +1605,14 @@
3309 ha_statistic_increment(&SSV::ha_read_key_count);
3310 int error=mi_rkey(file, buf, index, key, keypart_map, find_flag);
3311 table->status=error ? STATUS_NOT_FOUND: 0;
3317 + if (inx >= 0 && inx < MAX_KEY)
3318 + index_rows_read[inx]++;
3320 MYSQL_INDEX_READ_ROW_DONE(error);
3323 @@ -1600,6 +1627,14 @@
3324 int error=mi_rkey(file, buf, active_index, key, keypart_map,
3325 HA_READ_PREFIX_LAST);
3326 table->status=error ? STATUS_NOT_FOUND: 0;
3331 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3332 + if (inx >= 0 && inx < MAX_KEY)
3333 + index_rows_read[inx]++;
3335 MYSQL_INDEX_READ_ROW_DONE(error);
3338 @@ -1611,6 +1646,13 @@
3339 ha_statistic_increment(&SSV::ha_read_next_count);
3340 int error=mi_rnext(file,buf,active_index);
3341 table->status=error ? STATUS_NOT_FOUND: 0;
3345 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3346 + if (inx >= 0 && inx < MAX_KEY)
3347 + index_rows_read[inx]++;
3349 MYSQL_INDEX_READ_ROW_DONE(error);
3352 @@ -1622,6 +1664,13 @@
3353 ha_statistic_increment(&SSV::ha_read_prev_count);
3354 int error=mi_rprev(file,buf, active_index);
3355 table->status=error ? STATUS_NOT_FOUND: 0;
3359 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3360 + if (inx >= 0 && inx < MAX_KEY)
3361 + index_rows_read[inx]++;
3363 MYSQL_INDEX_READ_ROW_DONE(error);
3366 @@ -1633,6 +1682,14 @@
3367 ha_statistic_increment(&SSV::ha_read_first_count);
3368 int error=mi_rfirst(file, buf, active_index);
3369 table->status=error ? STATUS_NOT_FOUND: 0;
3374 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3375 + if (inx >= 0 && inx < MAX_KEY)
3376 + index_rows_read[inx]++;
3378 MYSQL_INDEX_READ_ROW_DONE(error);
3381 @@ -1644,6 +1701,14 @@
3382 ha_statistic_increment(&SSV::ha_read_last_count);
3383 int error=mi_rlast(file, buf, active_index);
3384 table->status=error ? STATUS_NOT_FOUND: 0;
3389 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3390 + if (inx >= 0 && inx < MAX_KEY)
3391 + index_rows_read[inx]++;
3393 MYSQL_INDEX_READ_ROW_DONE(error);
3396 @@ -1661,6 +1726,14 @@
3397 error= mi_rnext_same(file,buf);
3398 } while (error == HA_ERR_RECORD_DELETED);
3399 table->status=error ? STATUS_NOT_FOUND: 0;
3404 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3405 + if (inx >= 0 && inx < MAX_KEY)
3406 + index_rows_read[inx]++;
3408 MYSQL_INDEX_READ_ROW_DONE(error);
3411 @@ -1680,6 +1753,8 @@
3412 ha_statistic_increment(&SSV::ha_read_rnd_next_count);
3413 int error=mi_scan(file, buf);
3414 table->status=error ? STATUS_NOT_FOUND: 0;
3417 MYSQL_READ_ROW_DONE(error);
3420 @@ -1696,6 +1771,8 @@
3421 ha_statistic_increment(&SSV::ha_read_rnd_count);
3422 int error=mi_rrnd(file, buf, my_get_ptr(pos,ref_length));
3423 table->status=error ? STATUS_NOT_FOUND: 0;
3426 MYSQL_READ_ROW_DONE(error);
3430 +++ b/mysql-test/r/userstat_bug602047.result
3432 +DROP TABLE IF EXISTS t1;
3433 +SET GLOBAL userstat=ON;
3434 +CREATE TABLE t1 ( id int(10), PRIMARY KEY (id)) ENGINE=InnoDB;
3435 +INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
3436 +SELECT COUNT(*) FROM t1;
3439 +SELECT ROWS_READ FROM information_schema.table_statistics WHERE TABLE_NAME='t1';
3442 +SELECT ROWS_READ FROM information_schema.index_statistics WHERE TABLE_NAME='t1';
3445 +SET GLOBAL userstat=OFF;
3448 +++ b/mysql-test/t/userstat_bug602047.test
3451 +DROP TABLE IF EXISTS t1;
3453 +SET GLOBAL userstat=ON;
3454 +CREATE TABLE t1 ( id int(10), PRIMARY KEY (id)) ENGINE=InnoDB;
3455 +INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
3456 +SELECT COUNT(*) FROM t1;
3457 +SELECT ROWS_READ FROM information_schema.table_statistics WHERE TABLE_NAME='t1';
3458 +SELECT ROWS_READ FROM information_schema.index_statistics WHERE TABLE_NAME='t1';
3459 +SET GLOBAL userstat=OFF;
3461 \ No newline at end of file
3462 --- a/mysql-test/r/mysqld--help-notwin.result
3463 +++ b/mysql-test/r/mysqld--help-notwin.result
3465 Define threads usage for handling queries, one of
3466 one-thread-per-connection, no-threads, loaded-dynamically
3467 --thread-stack=# The stack size for each thread
3468 + --thread-statistics Control TABLE_STATISTICS running, when userstat is
3470 --time-format=name The TIME format (ignored)
3471 --timed-mutexes Specify whether to time mutexes (only InnoDB mutexes are
3472 currently supported)
3474 of the underlying table and the query uses a LIMIT clause
3475 (usually get from GUI tools)
3476 -u, --user=name Run mysqld daemon as user.
3477 + --userstat Control USER_STATISTICS, CLIENT_STATISTICS,
3478 + THREAD_STATISTICS, INDEX_STATISTICS and TABLE_STATISTICS
3480 -v, --verbose Used with --help option for detailed help.
3481 -V, --version Output version information and exit.
3482 --wait-timeout=# The number of seconds the server waits for activity on a
3483 @@ -1005,6 +1010,7 @@
3485 thread-handling one-thread-per-connection
3487 +thread-statistics FALSE
3488 time-format %H:%i:%s
3490 tmp-table-size 16777216
3491 @@ -1012,6 +1018,7 @@
3492 transaction-isolation REPEATABLE-READ
3493 transaction-prealloc-size 4096
3494 updatable-views-with-limit YES
3498 xtradb-admin-command ON