1 # name : userstat.patch
2 # introduced : 11 or before
3 # maintainer : Yasufumi
6 # Any small change to this file in the main branch
7 # should be done or reviewed by the maintainer!
8 diff -ruN a/configure b/configure
9 diff -ruN a/configure.in b/configure.in
10 --- a/configure.in 2010-10-12 00:34:15.000000000 +0400
11 +++ b/configure.in 2010-11-24 18:00:58.000000000 +0300
12 @@ -2095,13 +2095,16 @@
13 realpath rename rint rwlock_init setupterm \
14 shmget shmat shmdt shmctl sigaction sigemptyset sigaddset \
15 sighold sigset sigthreadmask port_create sleep \
16 - snprintf socket stpcpy strcasecmp strerror strsignal strnlen strpbrk strstr \
17 + snprintf socket strsep stpcpy strcasecmp strerror strsignal strnlen strpbrk strstr \
18 strtol strtoll strtoul strtoull tell tempnam thr_setconcurrency vidattr \
19 posix_fallocate backtrace backtrace_symbols backtrace_symbols_fd printstack)
24 +# The following change can be safely null-merged to 5.5
25 +# since configure.cmake in 5.5 does the same check
26 +AC_CHECK_LIB(rt, clock_gettime)
29 # (grr) aix 4.3 has a stub for clock_gettime, (returning ENOSYS)
30 diff -ruN a/include/config.h.in b/include/config.h.in
31 --- a/include/config.h.in 2010-10-12 00:39:59.000000000 +0400
32 +++ b/include/config.h.in 2010-11-24 17:53:34.000000000 +0300
34 /* Define to 1 if you have the `pthread' library (-lpthread). */
35 #undef HAVE_LIBPTHREAD
37 +/* Define to 1 if you have the `rt' library (-lrt). */
40 /* Define if have -lwrap */
44 /* Define to 1 if you have the `strpbrk' function. */
47 +/* Define to 1 if you have the `strsep' function. */
50 /* Define to 1 if you have the `strsignal' function. */
54 /* Define to 1 if you have the `strtoull' function. */
57 -/* Define to 1 if `st_rdev' is member of `struct stat'. */
58 +/* Define to 1 if `st_rdev' is a member of `struct stat'. */
59 #undef HAVE_STRUCT_STAT_ST_RDEV
61 /* Define to 1 if your `struct stat' has `st_rdev'. Deprecated, use
63 /* Define to the one symbol short name of this package. */
64 #undef PACKAGE_TARNAME
66 +/* Define to the home page for this package. */
69 /* Define to the version of this package. */
70 #undef PACKAGE_VERSION
72 diff -ruN a/include/mysql/plugin.h b/include/mysql/plugin.h
73 --- a/include/mysql/plugin.h 2010-11-24 17:24:51.000000000 +0300
74 +++ b/include/mysql/plugin.h 2010-11-24 17:24:52.000000000 +0300
76 unsigned long thd_log_slow_verbosity(const MYSQL_THD thd);
77 int thd_opt_slow_log();
78 #define EXTENDED_SLOWLOG
80 +#define EXTENDED_FOR_USERSTAT
83 Create a temporary file.
85 diff -ruN a/include/mysql_com.h b/include/mysql_com.h
86 --- a/include/mysql_com.h 2010-10-12 00:34:28.000000000 +0400
87 +++ b/include/mysql_com.h 2010-11-24 17:28:26.000000000 +0300
90 #define SERVER_VERSION_LENGTH 60
91 #define SQLSTATE_LENGTH 5
92 +#define LIST_PROCESS_HOST_LEN 64
95 USER_HOST_BUFF_SIZE -- length of string buffer, that is enough to contain
98 #define REFRESH_MASTER 128 /* Remove all bin logs in the index
99 and truncate the index */
100 +#define REFRESH_TABLE_STATS 256 /* Refresh table stats hash table */
101 +#define REFRESH_INDEX_STATS 512 /* Refresh index stats hash table */
102 +#define REFRESH_USER_STATS 1024 /* Refresh user stats hash table */
103 +#define REFRESH_SLOW_QUERY_LOG 2048 /* Flush slow query log and rotate*/
104 +#define REFRESH_CLIENT_STATS 4096 /* Refresh client stats hash table */
105 +#define REFRESH_THREAD_STATS 8192 /* Refresh thread stats hash table */
107 /* The following can't be set with mysql_refresh() */
108 #define REFRESH_READ_LOCK 16384 /* Lock tables for read */
109 diff -ruN /dev/null b/patch_info/userstats.info
110 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
111 +++ b/patch_info/userstats.info 2010-11-24 17:24:52.000000000 +0300
113 +File=userstats.patch
114 +Name=SHOW USER/TABLE/INDEX statistics
118 +Comment=Added INFORMATION_SCHEMA.*_STATISTICS
120 +YK: fix behavior for prepared statements
123 +YK: add switch variable "userstat_running" to control INFORMATION_SCHEMA.*_STATISTICS (default:OFF)
124 diff -ruN a/sql/handler.cc b/sql/handler.cc
125 --- a/sql/handler.cc 2010-10-12 00:34:25.000000000 +0400
126 +++ b/sql/handler.cc 2010-11-24 17:24:52.000000000 +0300
127 @@ -1194,6 +1194,8 @@
129 tc_log->unlog(cookie, xid);
130 DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
132 + thd->diff_commit_trans++;
135 start_waiting_global_read_lock(thd);
136 @@ -1324,6 +1326,8 @@
137 /* Always cleanup. Even if there nht==0. There may be savepoints. */
139 thd->transaction.cleanup();
141 + thd->diff_rollback_trans++;
142 #endif /* USING_TRANSACTIONS */
144 thd->transaction_rollback_request= FALSE;
145 @@ -1762,6 +1766,7 @@
146 ha_info->reset(); /* keep it conveniently zero-filled */
148 trans->ha_list= sv->ha_list;
149 + thd->diff_rollback_trans++;
153 @@ -2122,6 +2127,8 @@
154 dup_ref=ref+ALIGN_SIZE(ref_length);
155 cached_table_flags= table_flags();
157 + rows_read = rows_changed = 0;
158 + memset(index_rows_read, 0, sizeof(index_rows_read));
162 @@ -3571,6 +3578,111 @@
166 +// Updates the global table stats with the TABLE this handler represents.
167 +void handler::update_global_table_stats() {
168 + if (!opt_userstat_running) {
169 + rows_read = rows_changed = 0;
173 + if (!rows_read && !rows_changed) return; // Nothing to update.
174 + // table_cache_key is db_name + '\0' + table_name + '\0'.
175 + if (!table->s || !table->s->table_cache_key.str || !table->s->table_name.str) return;
177 + TABLE_STATS* table_stats;
178 + char key[NAME_LEN * 2 + 2];
179 + // [db] + '.' + [table]
180 + sprintf(key, "%s.%s", table->s->table_cache_key.str, table->s->table_name.str);
182 + pthread_mutex_lock(&LOCK_global_table_stats);
183 + // Gets the global table stats, creating one if necessary.
184 + if (!(table_stats = (TABLE_STATS*)hash_search(&global_table_stats,
187 + if (!(table_stats = ((TABLE_STATS*)
188 + my_malloc(sizeof(TABLE_STATS), MYF(MY_WME | MY_ZEROFILL))))) {
190 + sql_print_error("Allocating table stats failed.");
193 + strncpy(table_stats->table, key, sizeof(table_stats->table));
194 + table_stats->rows_read = 0;
195 + table_stats->rows_changed = 0;
196 + table_stats->rows_changed_x_indexes = 0;
197 + table_stats->engine_type = (int) ht->db_type;
199 + if (my_hash_insert(&global_table_stats, (uchar*)table_stats)) {
201 + sql_print_error("Inserting table stats failed.");
202 + my_free((char*)table_stats, 0);
206 + // Updates the global table stats.
207 + table_stats->rows_read += rows_read;
208 + table_stats->rows_changed += rows_changed;
209 + table_stats->rows_changed_x_indexes +=
210 + rows_changed * (table->s->keys ? table->s->keys : 1);
211 + current_thd->diff_total_read_rows += rows_read;
212 + rows_read = rows_changed = 0;
214 + pthread_mutex_unlock(&LOCK_global_table_stats);
217 +// Updates the global index stats with this handler's accumulated index reads.
218 +void handler::update_global_index_stats() {
219 + // table_cache_key is db_name + '\0' + table_name + '\0'.
220 + if (!table->s || !table->s->table_cache_key.str || !table->s->table_name.str) return;
222 + if (!opt_userstat_running) {
223 + for (uint x = 0; x < table->s->keys; x++) {
224 + index_rows_read[x] = 0;
229 + for (uint x = 0; x < table->s->keys; x++) {
230 + if (index_rows_read[x]) {
231 + // Rows were read using this index.
232 + KEY* key_info = &table->key_info[x];
234 + if (!key_info->name) continue;
236 + INDEX_STATS* index_stats;
237 + char key[NAME_LEN * 3 + 3];
238 + // [db] + '.' + [table] + '.' + [index]
239 + sprintf(key, "%s.%s.%s", table->s->table_cache_key.str,
240 + table->s->table_name.str, key_info->name);
242 + pthread_mutex_lock(&LOCK_global_index_stats);
243 + // Gets the global index stats, creating one if necessary.
244 + if (!(index_stats = (INDEX_STATS*)hash_search(&global_index_stats,
247 + if (!(index_stats = ((INDEX_STATS*)
248 + my_malloc(sizeof(INDEX_STATS), MYF(MY_WME | MY_ZEROFILL))))) {
250 + sql_print_error("Allocating index stats failed.");
253 + strncpy(index_stats->index, key, sizeof(index_stats->index));
254 + index_stats->rows_read = 0;
256 + if (my_hash_insert(&global_index_stats, (uchar*)index_stats)) {
258 + sql_print_error("Inserting index stats failed.");
259 + my_free((char*)index_stats, 0);
263 + // Updates the global index stats.
264 + index_stats->rows_read += index_rows_read[x];
265 + index_rows_read[x] = 0;
267 + pthread_mutex_unlock(&LOCK_global_index_stats);
272 /****************************************************************************
273 ** Some general functions that isn't in the handler class
274 diff -ruN a/sql/handler.h b/sql/handler.h
275 --- a/sql/handler.h 2010-10-12 00:34:25.000000000 +0400
276 +++ b/sql/handler.h 2010-11-24 17:28:49.000000000 +0300
279 #define USING_TRANSACTIONS
282 +#error MAX_KEY is too large. Values up to 128 are supported.
285 // the following is for checking tables
287 #define HA_ADMIN_ALREADY_DONE 1
288 @@ -1121,6 +1125,9 @@
290 bool implicit_emptied; /* Can be !=0 only if HEAP */
291 const COND *pushed_cond;
292 + ulonglong rows_read;
293 + ulonglong rows_changed;
294 + ulonglong index_rows_read[MAX_KEY];
296 next_insert_id is the next value which should be inserted into the
297 auto_increment column: in a inserting-multi-row statement (like INSERT
298 @@ -1158,9 +1165,11 @@
299 ref_length(sizeof(my_off_t)),
300 ft_handler(0), inited(NONE),
301 locked(FALSE), implicit_emptied(0),
302 - pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
303 + pushed_cond(0), rows_read(0), rows_changed(0), next_insert_id(0), insert_id_for_cur_row(0),
304 auto_inc_intervals_count(0)
307 + memset(index_rows_read, 0, sizeof(index_rows_read));
309 virtual ~handler(void)
311 DBUG_ASSERT(locked == FALSE);
312 @@ -1284,6 +1293,8 @@
316 + rows_read = rows_changed = 0;
317 + memset(index_rows_read, 0, sizeof(index_rows_read));
319 virtual double scan_time()
320 { return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; }
321 @@ -1628,6 +1639,8 @@
322 virtual bool is_crashed() const { return 0; }
323 virtual bool auto_repair() const { return 0; }
325 + void update_global_table_stats();
326 + void update_global_index_stats();
328 #define CHF_CREATE_FLAG 0
329 #define CHF_DELETE_FLAG 1
330 diff -ruN a/sql/lex.h b/sql/lex.h
331 --- a/sql/lex.h 2010-11-24 17:24:51.000000000 +0300
332 +++ b/sql/lex.h 2010-11-24 17:28:26.000000000 +0300
334 { "CHECKSUM", SYM(CHECKSUM_SYM)},
335 { "CIPHER", SYM(CIPHER_SYM)},
336 { "CLIENT", SYM(CLIENT_SYM)},
337 + { "CLIENT_STATISTICS", SYM(CLIENT_STATS_SYM)},
338 { "CLOSE", SYM(CLOSE_SYM)},
339 { "COALESCE", SYM(COALESCE)},
340 { "CODE", SYM(CODE_SYM)},
342 { "IN", SYM(IN_SYM)},
343 { "INDEX", SYM(INDEX_SYM)},
344 { "INDEXES", SYM(INDEXES)},
345 + { "INDEX_STATISTICS", SYM(INDEX_STATS_SYM)},
346 { "INFILE", SYM(INFILE)},
347 { "INITIAL_SIZE", SYM(INITIAL_SIZE_SYM)},
348 { "INNER", SYM(INNER_SYM)},
350 { "SIGNED", SYM(SIGNED_SYM)},
351 { "SIMPLE", SYM(SIMPLE_SYM)},
352 { "SLAVE", SYM(SLAVE)},
353 + { "SLOW", SYM(SLOW_SYM)},
354 { "SNAPSHOT", SYM(SNAPSHOT_SYM)},
355 { "SMALLINT", SYM(SMALLINT)},
356 { "SOCKET", SYM(SOCKET_SYM)},
357 @@ -527,12 +530,14 @@
358 { "TABLES", SYM(TABLES)},
359 { "TABLESPACE", SYM(TABLESPACE)},
360 { "TABLE_CHECKSUM", SYM(TABLE_CHECKSUM_SYM)},
361 + { "TABLE_STATISTICS", SYM(TABLE_STATS_SYM)},
362 { "TEMPORARY", SYM(TEMPORARY)},
363 { "TEMPTABLE", SYM(TEMPTABLE_SYM)},
364 { "TERMINATED", SYM(TERMINATED)},
365 { "TEXT", SYM(TEXT_SYM)},
366 { "THAN", SYM(THAN_SYM)},
367 { "THEN", SYM(THEN_SYM)},
368 + { "THREAD_STATISTICS", SYM(THREAD_STATS_SYM)},
369 { "TIME", SYM(TIME_SYM)},
370 { "TIMESTAMP", SYM(TIMESTAMP)},
371 { "TIMESTAMPADD", SYM(TIMESTAMP_ADD)},
373 { "USE", SYM(USE_SYM)},
374 { "USER", SYM(USER)},
375 { "USER_RESOURCES", SYM(RESOURCES)},
376 + { "USER_STATISTICS", SYM(USER_STATS_SYM)},
377 { "USE_FRM", SYM(USE_FRM)},
378 { "USING", SYM(USING)},
379 { "UTC_DATE", SYM(UTC_DATE_SYM)},
380 diff -ruN a/sql/log.cc b/sql/log.cc
381 --- a/sql/log.cc 2010-11-24 17:24:51.000000000 +0300
382 +++ b/sql/log.cc 2010-11-24 17:24:52.000000000 +0300
384 mysql_slow_log.reopen_file();
387 +void Log_to_file_event_handler::flush_slow_log()
389 + /* reopen slow log file */
391 + mysql_slow_log.reopen_file();
395 Log error with all enabled log event handlers
401 +bool LOGGER::flush_slow_log(THD *thd)
404 + Now we lock logger, as nobody should be able to use logging routines while
405 + log tables are closed
407 + logger.lock_exclusive();
409 + /* reopen log files */
410 + file_log_handler->flush_slow_log();
412 + /* end of log flush */
418 Log slow query with all enabled log event handlers
419 @@ -4495,6 +4517,8 @@
420 thd->first_successful_insert_id_in_prev_stmt_for_binlog);
423 + if (file == &log_file)
424 + thd->binlog_bytes_written += e.data_written;
426 if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
428 @@ -4506,12 +4530,16 @@
432 + if (file == &log_file)
433 + thd->binlog_bytes_written += e.data_written;
437 Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2);
440 + if (file == &log_file)
441 + thd->binlog_bytes_written += e.data_written;
443 if (thd->user_var_events.elements)
445 @@ -4527,6 +4555,8 @@
446 user_var_event->charset_number);
449 + if (file == &log_file)
450 + thd->binlog_bytes_written += e.data_written;
454 @@ -4539,6 +4569,8 @@
455 if (event_info->write(file) ||
456 DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0))
458 + if (file == &log_file)
459 + thd->binlog_bytes_written += event_info->data_written;
461 if (file == &log_file) // we are writing to the real log (disk)
463 @@ -4684,7 +4716,7 @@
464 be reset as a READ_CACHE to be able to read the contents from it.
467 -int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
468 +int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache, bool lock_log, bool sync_log)
470 Mutex_sentry sentry(lock_log ? &LOCK_log : NULL);
472 @@ -4732,6 +4764,7 @@
473 /* write the first half of the split header */
474 if (my_b_write(&log_file, header, carry))
475 return ER_ERROR_ON_WRITE;
476 + thd->binlog_bytes_written += carry;
479 copy fixed second half of header to cache so the correct
480 @@ -4800,6 +4833,7 @@
481 /* Write data to the binary log file */
482 if (my_b_write(&log_file, cache->read_pos, length))
483 return ER_ERROR_ON_WRITE;
484 + thd->binlog_bytes_written += length;
485 cache->read_pos=cache->read_end; // Mark buffer used up
486 } while ((length= my_b_fill(cache)));
488 @@ -4922,21 +4956,24 @@
490 if (qinfo.write(&log_file))
492 + thd->binlog_bytes_written += qinfo.data_written;
494 DBUG_EXECUTE_IF("crash_before_writing_xid",
496 - if ((write_error= write_cache(cache, false, true)))
497 + if ((write_error= write_cache(thd, cache, false, true)))
498 DBUG_PRINT("info", ("error writing binlog cache: %d",
500 DBUG_PRINT("info", ("crashing before writing xid"));
504 - if ((write_error= write_cache(cache, false, false)))
505 + if ((write_error= write_cache(thd, cache, false, false)))
508 if (commit_event && commit_event->write(&log_file))
511 + thd->binlog_bytes_written += commit_event->data_written;
513 if (incident && write_incident(thd, FALSE))
515 diff -ruN a/sql/log.h b/sql/log.h
516 --- a/sql/log.h 2010-11-24 17:24:51.000000000 +0300
517 +++ b/sql/log.h 2010-11-24 17:24:52.000000000 +0300
519 bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event, bool incident);
521 bool write_incident(THD *thd, bool lock);
522 - int write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync);
523 + int write_cache(THD *thd, IO_CACHE *cache, bool lock_log, bool flush_and_sync);
524 void set_write_error(THD *thd);
525 bool check_write_error(THD *thd);
528 const char *sql_text, uint sql_text_len,
529 CHARSET_INFO *client_cs);
531 + void flush_slow_log();
532 void init_pthread_objects();
533 MYSQL_QUERY_LOG *get_mysql_slow_log() { return &mysql_slow_log; }
534 MYSQL_QUERY_LOG *get_mysql_log() { return &mysql_log; }
537 void init_log_tables();
538 bool flush_logs(THD *thd);
539 + bool flush_slow_log(THD *thd);
540 /* Perform basic logger cleanup. this will leave e.g. error log open. */
542 /* Free memory. Nothing could be logged after this function is called */
543 diff -ruN a/sql/mysql_priv.h b/sql/mysql_priv.h
544 --- a/sql/mysql_priv.h 2010-11-24 17:24:51.000000000 +0300
545 +++ b/sql/mysql_priv.h 2010-11-24 17:31:34.000000000 +0300
546 @@ -1139,7 +1139,17 @@
547 bool multi_delete_set_locks_and_link_aux_tables(LEX *lex);
548 void init_max_user_conn(void);
549 void init_update_queries(void);
550 +void init_global_user_stats(void);
551 +void init_global_table_stats(void);
552 +void init_global_index_stats(void);
553 +void init_global_client_stats(void);
554 +void init_global_thread_stats(void);
555 void free_max_user_conn(void);
556 +void free_global_user_stats(void);
557 +void free_global_table_stats(void);
558 +void free_global_index_stats(void);
559 +void free_global_client_stats(void);
560 +void free_global_thread_stats(void);
561 pthread_handler_t handle_bootstrap(void *arg);
562 int mysql_execute_command(THD *thd);
563 bool do_command(THD *thd);
564 @@ -2015,6 +2025,7 @@
565 extern ulong max_connect_errors, connect_timeout;
566 extern ulong slave_net_timeout, slave_trans_retries;
567 extern uint max_user_connections;
568 +extern ulonglong denied_connections;
569 extern ulong what_to_log,flush_time;
570 extern ulong query_buff_size;
571 extern ulong max_prepared_stmt_count, prepared_stmt_count;
572 @@ -2068,6 +2079,7 @@
573 extern my_bool opt_slave_compressed_protocol, use_temp_pool;
574 extern ulong slave_exec_mode_options;
575 extern my_bool opt_readonly, lower_case_file_system;
576 +extern my_bool opt_userstat_running, opt_thread_statistics;
577 extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
578 extern my_bool opt_secure_auth;
579 extern char* opt_secure_file_priv;
580 @@ -2132,6 +2144,15 @@
581 extern struct system_variables max_system_variables;
582 extern struct system_status_var global_status_var;
583 extern struct rand_struct sql_rand;
584 +extern HASH global_user_stats;
585 +extern HASH global_client_stats;
586 +extern HASH global_thread_stats;
587 +extern pthread_mutex_t LOCK_global_user_client_stats;
588 +extern HASH global_table_stats;
589 +extern pthread_mutex_t LOCK_global_table_stats;
590 +extern HASH global_index_stats;
591 +extern pthread_mutex_t LOCK_global_index_stats;
592 +extern pthread_mutex_t LOCK_stats;
594 extern const char *opt_date_time_formats[];
595 extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
596 diff -ruN a/sql/mysqld.cc b/sql/mysqld.cc
597 --- a/sql/mysqld.cc 2010-11-24 17:24:51.000000000 +0300
598 +++ b/sql/mysqld.cc 2010-11-24 17:31:34.000000000 +0300
600 uint opt_debug_sync_timeout= 0;
601 #endif /* defined(ENABLED_DEBUG_SYNC) */
602 my_bool opt_old_style_user_limits= 0, trust_function_creators= 0;
603 +my_bool opt_userstat_running= 0, opt_thread_statistics= 0;
605 True if there is at least one per-hour limit for some user, so we should
606 check them before each query (and possibly reset counters when hour is
608 ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
609 ulong max_connections, max_connect_errors;
610 uint max_user_connections= 0;
611 +ulonglong denied_connections = 0;
613 Limit of the total number of prepared statements in the server.
614 Is necessary to protect the server against out-of-memory attacks.
616 LOCK_global_system_variables,
617 LOCK_user_conn, LOCK_slave_list, LOCK_active_mi,
618 LOCK_connection_count;
619 +pthread_mutex_t LOCK_stats;
620 +pthread_mutex_t LOCK_global_user_client_stats;
621 +pthread_mutex_t LOCK_global_table_stats;
622 +pthread_mutex_t LOCK_global_index_stats;
624 The below lock protects access to two global server variables:
625 max_prepared_stmt_count and prepared_stmt_count. These variables
626 @@ -1367,6 +1373,11 @@
627 x_free(opt_secure_file_priv);
628 bitmap_free(&temp_pool);
629 free_max_user_conn();
630 + free_global_user_stats();
631 + free_global_client_stats();
632 + free_global_thread_stats();
633 + free_global_table_stats();
634 + free_global_index_stats();
635 #ifdef HAVE_REPLICATION
638 @@ -1483,6 +1494,10 @@
639 (void) pthread_cond_destroy(&COND_thread_cache);
640 (void) pthread_cond_destroy(&COND_flush_thread_cache);
641 (void) pthread_cond_destroy(&COND_manager);
642 + (void) pthread_mutex_destroy(&LOCK_stats);
643 + (void) pthread_mutex_destroy(&LOCK_global_user_client_stats);
644 + (void) pthread_mutex_destroy(&LOCK_global_table_stats);
645 + (void) pthread_mutex_destroy(&LOCK_global_index_stats);
648 #endif /*EMBEDDED_LIBRARY*/
649 @@ -3172,6 +3187,7 @@
650 {"show_binlog_events", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOG_EVENTS]), SHOW_LONG_STATUS},
651 {"show_binlogs", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOGS]), SHOW_LONG_STATUS},
652 {"show_charsets", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CHARSETS]), SHOW_LONG_STATUS},
653 + {"show_client_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CLIENT_STATS]), SHOW_LONG_STATUS},
654 {"show_collations", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLLATIONS]), SHOW_LONG_STATUS},
655 {"show_column_types", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLUMN_TYPES]), SHOW_LONG_STATUS},
656 {"show_contributors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CONTRIBUTORS]), SHOW_LONG_STATUS},
657 @@ -3193,6 +3209,7 @@
659 {"show_function_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS_FUNC]), SHOW_LONG_STATUS},
660 {"show_grants", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS]), SHOW_LONG_STATUS},
661 + {"show_index_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_INDEX_STATS]), SHOW_LONG_STATUS},
662 {"show_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS},
663 {"show_master_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_MASTER_STAT]), SHOW_LONG_STATUS},
664 {"show_new_master", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_NEW_MASTER]), SHOW_LONG_STATUS},
665 @@ -3211,9 +3228,12 @@
666 {"show_slave_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_STAT]), SHOW_LONG_STATUS},
667 {"show_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS]), SHOW_LONG_STATUS},
668 {"show_storage_engines", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STORAGE_ENGINES]), SHOW_LONG_STATUS},
669 + {"show_table_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATS]), SHOW_LONG_STATUS},
670 {"show_table_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATUS]), SHOW_LONG_STATUS},
671 {"show_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLES]), SHOW_LONG_STATUS},
672 + {"show_thread_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_THREAD_STATS]), SHOW_LONG_STATUS},
673 {"show_triggers", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TRIGGERS]), SHOW_LONG_STATUS},
674 + {"show_user_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_USER_STATS]), SHOW_LONG_STATUS},
675 {"show_variables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_VARIABLES]), SHOW_LONG_STATUS},
676 {"show_warnings", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_WARNS]), SHOW_LONG_STATUS},
677 {"slave_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS},
678 @@ -3652,6 +3672,10 @@
680 (void) pthread_mutex_init(&LOCK_server_started, MY_MUTEX_INIT_FAST);
681 (void) pthread_cond_init(&COND_server_started,NULL);
682 + (void) pthread_mutex_init(&LOCK_stats, MY_MUTEX_INIT_FAST);
683 + (void) pthread_mutex_init(&LOCK_global_user_client_stats, MY_MUTEX_INIT_FAST);
684 + (void) pthread_mutex_init(&LOCK_global_table_stats, MY_MUTEX_INIT_FAST);
685 + (void) pthread_mutex_init(&LOCK_global_index_stats, MY_MUTEX_INIT_FAST);
687 #ifdef HAVE_EVENT_SCHEDULER
688 Events::init_mutexes();
689 @@ -4053,6 +4077,9 @@
693 + init_global_table_stats();
694 + init_global_index_stats();
696 /* We have to initialize the storage engines before CSV logging */
699 @@ -4199,6 +4226,9 @@
701 init_max_user_conn();
702 init_update_queries();
703 + init_global_user_stats();
704 + init_global_client_stats();
705 + init_global_thread_stats();
709 @@ -5016,6 +5046,7 @@
711 DBUG_PRINT("error",("Too many connections"));
712 close_connection(thd, ER_CON_COUNT_ERROR, 1);
713 + statistic_increment(denied_connections, &LOCK_status);
717 @@ -5800,6 +5831,8 @@
719 OPT_GENERAL_LOG_FILE,
720 OPT_SLOW_QUERY_LOG_FILE,
721 + OPT_USERSTAT_RUNNING,
722 + OPT_THREAD_STATISTICS,
723 OPT_USE_GLOBAL_LONG_QUERY_TIME,
724 OPT_USE_GLOBAL_LOG_SLOW_CONTROL,
725 OPT_SLOW_QUERY_LOG_MICROSECONDS_TIMESTAMP,
726 @@ -7292,6 +7325,14 @@
727 &max_system_variables.net_wait_timeout, 0, GET_ULONG,
728 REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT),
730 + {"userstat_running", OPT_USERSTAT_RUNNING,
731 + "Control USER_STATISTICS, CLIENT_STATISTICS, THREAD_STATISTICS, INDEX_STATISTICS and TABLE_STATISTICS running",
732 + (uchar**) &opt_userstat_running, (uchar**) &opt_userstat_running,
733 + 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 1, 0},
734 + {"thread_statistics", OPT_THREAD_STATISTICS,
735 + "Control TABLE_STATISTICS running, when userstat_running is enabled",
736 + (uchar**) &opt_thread_statistics, (uchar**) &opt_thread_statistics,
737 + 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 1, 0},
738 {"binlog-direct-non-transactional-updates", OPT_BINLOG_DIRECT_NON_TRANS_UPDATE,
739 "Causes updates to non-transactional engines using statement format to be "
740 "written directly to binary log. Before using this option, make sure that "
741 diff -ruN a/sql/set_var.cc b/sql/set_var.cc
742 --- a/sql/set_var.cc 2010-11-24 17:24:51.000000000 +0300
743 +++ b/sql/set_var.cc 2010-11-24 17:31:34.000000000 +0300
745 static sys_var_thd_ulong sys_read_buff_size(&vars, "read_buffer_size",
746 &SV::read_buff_size);
747 static sys_var_opt_readonly sys_readonly(&vars, "read_only", &opt_readonly);
748 +static sys_var_bool_ptr sys_userstat_running(&vars, "userstat_running",
749 + &opt_userstat_running);
750 +static sys_var_bool_ptr sys_thread_statistics(&vars, "thread_statistics",
751 + &opt_thread_statistics);
752 static sys_var_thd_ulong sys_read_rnd_buff_size(&vars, "read_rnd_buffer_size",
753 &SV::read_rnd_buff_size);
754 static sys_var_thd_ulong sys_div_precincrement(&vars, "div_precision_increment",
755 diff -ruN a/sql/sql_base.cc b/sql/sql_base.cc
756 --- a/sql/sql_base.cc 2010-10-12 00:34:33.000000000 +0400
757 +++ b/sql/sql_base.cc 2010-11-24 17:29:05.000000000 +0300
758 @@ -1382,6 +1382,12 @@
759 DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str,
760 table->s->table_name.str, (long) table));
764 + table->file->update_global_table_stats();
765 + table->file->update_global_index_stats();
768 *table_ptr=table->next;
770 When closing a MERGE parent or child table, detach the children first.
771 @@ -1922,6 +1928,8 @@
772 DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
773 table->s->db.str, table->s->table_name.str));
775 + table->file->update_global_table_stats();
776 + table->file->update_global_index_stats();
777 free_io_cache(table);
780 diff -ruN a/sql/sql_class.cc b/sql/sql_class.cc
781 --- a/sql/sql_class.cc 2010-11-24 17:24:51.000000000 +0300
782 +++ b/sql/sql_class.cc 2010-11-24 17:31:33.000000000 +0300
785 binlog_evt_union.do_union= FALSE;
789 + bytes_received = 0;
791 + binlog_bytes_written = 0;
792 + updated_row_count = 0;
793 + sent_row_count_2 = 0;
795 dbug_sentry=THD_SENTRY_MAGIC;
798 reset_current_stmt_binlog_row_based();
799 bzero((char *) &status_var, sizeof(status_var));
800 sql_log_bin_toplevel= options & OPTION_BIN_LOG;
803 #if defined(ENABLED_DEBUG_SYNC)
804 /* Initialize the Debug Sync Facility. See debug_sync.cc. */
806 #endif /* defined(ENABLED_DEBUG_SYNC) */
809 +// Resets stats in a THD.
810 +void THD::reset_stats(void) {
811 + current_connect_time = time(NULL);
812 + last_global_update_time = current_connect_time;
813 + reset_diff_stats();
816 +// Resets the 'diff' stats, which are used to update global stats.
817 +void THD::reset_diff_stats(void) {
818 + diff_total_busy_time = 0;
819 + diff_total_cpu_time = 0;
820 + diff_total_bytes_received = 0;
821 + diff_total_bytes_sent = 0;
822 + diff_total_binlog_bytes_written = 0;
823 + diff_total_sent_rows = 0;
824 + diff_total_updated_rows = 0;
825 + diff_total_read_rows = 0;
826 + diff_select_commands = 0;
827 + diff_update_commands = 0;
828 + diff_other_commands = 0;
829 + diff_commit_trans = 0;
830 + diff_rollback_trans = 0;
831 + diff_denied_connections = 0;
832 + diff_lost_connections = 0;
833 + diff_access_denied_errors = 0;
834 + diff_empty_queries = 0;
837 +// Updates 'diff' stats of a THD.
838 +void THD::update_stats(bool ran_command) {
839 + if (opt_userstat_running) {
840 + diff_total_busy_time += busy_time;
841 + diff_total_cpu_time += cpu_time;
842 + diff_total_bytes_received += bytes_received;
843 + diff_total_bytes_sent += bytes_sent;
844 + diff_total_binlog_bytes_written += binlog_bytes_written;
845 + diff_total_sent_rows += sent_row_count_2;
846 + diff_total_updated_rows += updated_row_count;
847 + // diff_total_read_rows is updated in handler.cc.
850 + // The replication thread has the COM_CONNECT command.
851 + if ((old_command == COM_QUERY || command == COM_CONNECT) &&
852 + (lex->sql_command >= 0 && lex->sql_command < SQLCOM_END)) {
854 + if (lex->sql_command == SQLCOM_SELECT) {
855 + diff_select_commands++;
856 + if (!sent_row_count_2)
857 + diff_empty_queries++;
858 + } else if (! sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) {
859 + // 'SHOW ' commands become SQLCOM_SELECT.
860 + diff_other_commands++;
861 + // 'SHOW ' commands shouldn't inflate total sent row count.
862 + diff_total_sent_rows -= sent_row_count_2;
863 + } else if (is_update_query(lex->sql_command)) {
864 + diff_update_commands++;
866 + diff_other_commands++;
870 + // diff_commit_trans is updated in handler.cc.
871 + // diff_rollback_trans is updated in handler.cc.
872 + // diff_denied_connections is updated in sql_parse.cc.
873 + // diff_lost_connections is updated in sql_parse.cc.
874 + // diff_access_denied_errors is updated in sql_parse.cc.
876 + /* reset counters to zero to avoid double-counting since values
877 + are already store in diff_total_*. */
881 + bytes_received = 0;
883 + binlog_bytes_written = 0;
884 + updated_row_count = 0;
885 + sent_row_count_2 = 0;
889 Init THD for query processing.
890 @@ -1547,6 +1633,32 @@
894 +char *THD::get_client_host_port(THD *client)
896 + Security_context *client_sctx= client->security_ctx;
897 + char *client_host= NULL;
899 + if (client->peer_port && (client_sctx->host || client_sctx->ip) &&
900 + security_ctx->host_or_ip[0])
902 + if ((client_host= (char *) this->alloc(LIST_PROCESS_HOST_LEN+1)))
903 + my_snprintf((char *) client_host, LIST_PROCESS_HOST_LEN,
904 + "%s:%u", client_sctx->host_or_ip, client->peer_port);
907 + client_host= this->strdup(client_sctx->host_or_ip[0] ?
908 + client_sctx->host_or_ip :
909 + client_sctx->host ? client_sctx->host : "");
911 + return client_host;
914 +const char *get_client_host(THD *client)
916 + return client->security_ctx->host_or_ip[0] ?
917 + client->security_ctx->host_or_ip :
918 + client->security_ctx->host ? client->security_ctx->host : "";
921 struct Item_change_record: public ilink
923 @@ -1734,6 +1846,7 @@
924 buffer.set(buff, sizeof(buff), &my_charset_bin);
926 thd->sent_row_count++;
927 + thd->sent_row_count_2++;
930 protocol->remove_last_row();
931 @@ -1838,6 +1951,7 @@
932 select_export::~select_export()
934 thd->sent_row_count=row_count;
935 + thd->sent_row_count_2=row_count;
939 @@ -2870,6 +2984,7 @@
940 if (likely(thd != 0))
941 { /* current_thd==0 when close_connection() calls net_send_error() */
942 thd->status_var.bytes_sent+= length;
943 + thd->bytes_sent+= length;
947 @@ -2877,6 +2992,7 @@
948 void thd_increment_bytes_received(ulong length)
950 current_thd->status_var.bytes_received+= length;
951 + current_thd->bytes_received+= length;
955 diff -ruN a/sql/sql_class.h b/sql/sql_class.h
956 --- a/sql/sql_class.h 2010-11-24 17:24:51.000000000 +0300
957 +++ b/sql/sql_class.h 2010-11-24 17:28:57.000000000 +0300
958 @@ -1435,6 +1435,8 @@
959 first byte of the packet in do_command()
961 enum enum_server_command command;
962 + // Used to save the command, before it is set to COM_SLEEP.
963 + enum enum_server_command old_command;
965 uint32 file_id; // for LOAD DATA INFILE
966 /* remote (peer) port */
967 @@ -1828,6 +1830,8 @@
968 /* variables.transaction_isolation is reset to this after each commit */
969 enum_tx_isolation session_tx_isolation;
970 enum_check_fields count_cuted_fields;
971 + ha_rows updated_row_count;
972 + ha_rows sent_row_count_2; /* for userstat */
974 DYNAMIC_ARRAY user_var_events; /* For user variables replication */
975 MEM_ROOT *user_var_events_alloc; /* Allocate above array elements here */
976 @@ -1916,6 +1920,49 @@
978 LOG_INFO* current_linfo;
979 NET* slave_net; // network connection from slave -> m.
982 + Used to update global user stats. The global user stats are updated
983 + occasionally with the 'diff' variables. After the update, the 'diff'
984 + variables are reset to 0.
986 + // Time when the current thread connected to MySQL.
987 + time_t current_connect_time;
988 + // Last time when THD stats were updated in global_user_stats.
989 + time_t last_global_update_time;
990 + // Busy (non-idle) time for just one command.
992 + // Busy time not updated in global_user_stats yet.
993 + double diff_total_busy_time;
994 + // Cpu (non-idle) time for just one thread.
996 + // Cpu time not updated in global_user_stats yet.
997 + double diff_total_cpu_time;
998 + /* bytes counting */
999 + ulonglong bytes_received;
1000 + ulonglong diff_total_bytes_received;
1001 + ulonglong bytes_sent;
1002 + ulonglong diff_total_bytes_sent;
1003 + ulonglong binlog_bytes_written;
1004 + ulonglong diff_total_binlog_bytes_written;
1006 + // Number of rows not reflected in global_user_stats yet.
1007 + ha_rows diff_total_sent_rows, diff_total_updated_rows, diff_total_read_rows;
1008 + // Number of commands not reflected in global_user_stats yet.
1009 + ulonglong diff_select_commands, diff_update_commands, diff_other_commands;
1010 + // Number of transactions not reflected in global_user_stats yet.
1011 + ulonglong diff_commit_trans, diff_rollback_trans;
1012 + // Number of connection errors not reflected in global_user_stats yet.
1013 + ulonglong diff_denied_connections, diff_lost_connections;
1014 + // Number of db access denied, not reflected in global_user_stats yet.
1015 + ulonglong diff_access_denied_errors;
1016 + // Number of queries that return 0 rows
1017 + ulonglong diff_empty_queries;
1019 + // Per account query delay in miliseconds. When not 0, sleep this number of
1020 + // milliseconds before every SQL command.
1021 + ulonglong query_delay_millis;
1023 /* Used by the sys_var class to store temporary values */
1026 @@ -1981,6 +2028,11 @@
1029 void init_for_queries();
1030 + void reset_stats(void);
1031 + void reset_diff_stats(void);
1032 + // ran_command is true when this is called immediately after a
1033 + // command has been run.
1034 + void update_stats(bool ran_command);
1035 void change_user(void);
1037 void cleanup_after_query();
1038 @@ -2351,9 +2403,15 @@
1039 *p_db= strmake(db, db_length);
1040 *p_db_length= db_length;
1043 + // Returns string as 'IP:port' for the client-side of the connnection represented
1044 + // by 'client' as displayed by SHOW PROCESSLIST. Allocates memory from the heap of
1045 + // this THD and that is not reclaimed immediately, so use sparingly. May return NULL.
1047 thd_scheduler scheduler;
1049 + char *get_client_host_port(THD *client);
1052 inline Internal_error_handler *get_internal_handler()
1053 { return m_internal_handler; }
1054 @@ -2437,6 +2495,9 @@
1055 LEX_STRING invoker_host;
1058 +// Returns string as 'IP' for the client-side of the connection represented by
1059 +// 'client'. Does not allocate memory. May return "".
1060 +const char *get_client_host(THD *client);
1062 /** A short cut for thd->main_da.set_ok_status(). */
1064 diff -ruN a/sql/sql_connect.cc b/sql/sql_connect.cc
1065 --- a/sql/sql_connect.cc 2010-11-24 17:24:51.000000000 +0300
1066 +++ b/sql/sql_connect.cc 2010-11-24 17:24:52.000000000 +0300
1068 extern void win_install_sigabrt_handler();
1071 +// Increments connection count for user.
1072 +static int increment_connection_count(THD* thd, bool use_lock);
1074 +// Uses the THD to update the global stats by user name and client IP
1075 +void update_global_user_stats(THD* thd, bool create_user, time_t now);
1077 +HASH global_user_stats;
1078 +HASH global_client_stats;
1079 +HASH global_thread_stats;
1080 +// Protects global_user_stats and global_client_stats
1081 +extern pthread_mutex_t LOCK_global_user_client_stats;
1083 +HASH global_table_stats;
1084 +extern pthread_mutex_t LOCK_global_table_stats;
1086 +HASH global_index_stats;
1087 +extern pthread_mutex_t LOCK_global_index_stats;
1090 Get structure for logging connection data for the current user
1092 @@ -99,6 +117,563 @@
1096 +extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
1097 + my_bool not_used __attribute__((unused)))
1099 + *length = strlen(user_stats->user);
1100 + return (uchar*)user_stats->user;
1103 +extern "C" uchar *get_key_thread_stats(THREAD_STATS *thread_stats, size_t *length,
1104 + my_bool not_used __attribute__((unused)))
1106 + *length = sizeof(my_thread_id);
1107 + return (uchar*)&(thread_stats->id);
1110 +void free_user_stats(USER_STATS* user_stats)
1112 + my_free((char*)user_stats, MYF(0));
1115 +void free_thread_stats(THREAD_STATS* thread_stats)
1117 + my_free((char*)thread_stats, MYF(0));
1120 +void init_user_stats(USER_STATS *user_stats,
1122 + const char *priv_user,
1123 + uint total_connections,
1124 + uint concurrent_connections,
1125 + time_t connected_time,
1128 + ulonglong bytes_received,
1129 + ulonglong bytes_sent,
1130 + ulonglong binlog_bytes_written,
1131 + ha_rows rows_fetched,
1132 + ha_rows rows_updated,
1133 + ha_rows rows_read,
1134 + ulonglong select_commands,
1135 + ulonglong update_commands,
1136 + ulonglong other_commands,
1137 + ulonglong commit_trans,
1138 + ulonglong rollback_trans,
1139 + ulonglong denied_connections,
1140 + ulonglong lost_connections,
1141 + ulonglong access_denied_errors,
1142 + ulonglong empty_queries)
1144 + DBUG_ENTER("init_user_stats");
1145 + DBUG_PRINT("info",
1146 + ("Add user_stats entry for user %s - priv_user %s",
1147 + user, priv_user));
1148 + strncpy(user_stats->user, user, sizeof(user_stats->user));
1149 + strncpy(user_stats->priv_user, priv_user, sizeof(user_stats->priv_user));
1151 + user_stats->total_connections = total_connections;
1152 + user_stats->concurrent_connections = concurrent_connections;
1153 + user_stats->connected_time = connected_time;
1154 + user_stats->busy_time = busy_time;
1155 + user_stats->cpu_time = cpu_time;
1156 + user_stats->bytes_received = bytes_received;
1157 + user_stats->bytes_sent = bytes_sent;
1158 + user_stats->binlog_bytes_written = binlog_bytes_written;
1159 + user_stats->rows_fetched = rows_fetched;
1160 + user_stats->rows_updated = rows_updated;
1161 + user_stats->rows_read = rows_read;
1162 + user_stats->select_commands = select_commands;
1163 + user_stats->update_commands = update_commands;
1164 + user_stats->other_commands = other_commands;
1165 + user_stats->commit_trans = commit_trans;
1166 + user_stats->rollback_trans = rollback_trans;
1167 + user_stats->denied_connections = denied_connections;
1168 + user_stats->lost_connections = lost_connections;
1169 + user_stats->access_denied_errors = access_denied_errors;
1170 + user_stats->empty_queries = empty_queries;
1174 +void init_thread_stats(THREAD_STATS *thread_stats,
1176 + uint total_connections,
1177 + uint concurrent_connections,
1178 + time_t connected_time,
1181 + ulonglong bytes_received,
1182 + ulonglong bytes_sent,
1183 + ulonglong binlog_bytes_written,
1184 + ha_rows rows_fetched,
1185 + ha_rows rows_updated,
1186 + ha_rows rows_read,
1187 + ulonglong select_commands,
1188 + ulonglong update_commands,
1189 + ulonglong other_commands,
1190 + ulonglong commit_trans,
1191 + ulonglong rollback_trans,
1192 + ulonglong denied_connections,
1193 + ulonglong lost_connections,
1194 + ulonglong access_denied_errors,
1195 + ulonglong empty_queries)
1197 + DBUG_ENTER("init_thread_stats");
1198 + DBUG_PRINT("info",
1199 + ("Add thread_stats entry for thread %lu",
1201 + thread_stats->id = id;
1203 + thread_stats->total_connections = total_connections;
1204 + thread_stats->concurrent_connections = concurrent_connections;
1205 + thread_stats->connected_time = connected_time;
1206 + thread_stats->busy_time = busy_time;
1207 + thread_stats->cpu_time = cpu_time;
1208 + thread_stats->bytes_received = bytes_received;
1209 + thread_stats->bytes_sent = bytes_sent;
1210 + thread_stats->binlog_bytes_written = binlog_bytes_written;
1211 + thread_stats->rows_fetched = rows_fetched;
1212 + thread_stats->rows_updated = rows_updated;
1213 + thread_stats->rows_read = rows_read;
1214 + thread_stats->select_commands = select_commands;
1215 + thread_stats->update_commands = update_commands;
1216 + thread_stats->other_commands = other_commands;
1217 + thread_stats->commit_trans = commit_trans;
1218 + thread_stats->rollback_trans = rollback_trans;
1219 + thread_stats->denied_connections = denied_connections;
1220 + thread_stats->lost_connections = lost_connections;
1221 + thread_stats->access_denied_errors = access_denied_errors;
1222 + thread_stats->empty_queries = empty_queries;
1226 +void add_user_stats(USER_STATS *user_stats,
1227 + uint total_connections,
1228 + uint concurrent_connections,
1229 + time_t connected_time,
1232 + ulonglong bytes_received,
1233 + ulonglong bytes_sent,
1234 + ulonglong binlog_bytes_written,
1235 + ha_rows rows_fetched,
1236 + ha_rows rows_updated,
1237 + ha_rows rows_read,
1238 + ulonglong select_commands,
1239 + ulonglong update_commands,
1240 + ulonglong other_commands,
1241 + ulonglong commit_trans,
1242 + ulonglong rollback_trans,
1243 + ulonglong denied_connections,
1244 + ulonglong lost_connections,
1245 + ulonglong access_denied_errors,
1246 + ulonglong empty_queries)
1248 + user_stats->total_connections += total_connections;
1249 + user_stats->concurrent_connections += concurrent_connections;
1250 + user_stats->connected_time += connected_time;
1251 + user_stats->busy_time += busy_time;
1252 + user_stats->cpu_time += cpu_time;
1253 + user_stats->bytes_received += bytes_received;
1254 + user_stats->bytes_sent += bytes_sent;
1255 + user_stats->binlog_bytes_written += binlog_bytes_written;
1256 + user_stats->rows_fetched += rows_fetched;
1257 + user_stats->rows_updated += rows_updated;
1258 + user_stats->rows_read += rows_read;
1259 + user_stats->select_commands += select_commands;
1260 + user_stats->update_commands += update_commands;
1261 + user_stats->other_commands += other_commands;
1262 + user_stats->commit_trans += commit_trans;
1263 + user_stats->rollback_trans += rollback_trans;
1264 + user_stats->denied_connections += denied_connections;
1265 + user_stats->lost_connections += lost_connections;
1266 + user_stats->access_denied_errors += access_denied_errors;
1267 + user_stats->empty_queries += empty_queries;
1270 +void add_thread_stats(THREAD_STATS *thread_stats,
1271 + uint total_connections,
1272 + uint concurrent_connections,
1273 + time_t connected_time,
1276 + ulonglong bytes_received,
1277 + ulonglong bytes_sent,
1278 + ulonglong binlog_bytes_written,
1279 + ha_rows rows_fetched,
1280 + ha_rows rows_updated,
1281 + ha_rows rows_read,
1282 + ulonglong select_commands,
1283 + ulonglong update_commands,
1284 + ulonglong other_commands,
1285 + ulonglong commit_trans,
1286 + ulonglong rollback_trans,
1287 + ulonglong denied_connections,
1288 + ulonglong lost_connections,
1289 + ulonglong access_denied_errors,
1290 + ulonglong empty_queries)
1292 + thread_stats->total_connections += total_connections;
1293 + thread_stats->concurrent_connections += concurrent_connections;
1294 + thread_stats->connected_time += connected_time;
1295 + thread_stats->busy_time += busy_time;
1296 + thread_stats->cpu_time += cpu_time;
1297 + thread_stats->bytes_received += bytes_received;
1298 + thread_stats->bytes_sent += bytes_sent;
1299 + thread_stats->binlog_bytes_written += binlog_bytes_written;
1300 + thread_stats->rows_fetched += rows_fetched;
1301 + thread_stats->rows_updated += rows_updated;
1302 + thread_stats->rows_read += rows_read;
1303 + thread_stats->select_commands += select_commands;
1304 + thread_stats->update_commands += update_commands;
1305 + thread_stats->other_commands += other_commands;
1306 + thread_stats->commit_trans += commit_trans;
1307 + thread_stats->rollback_trans += rollback_trans;
1308 + thread_stats->denied_connections += denied_connections;
1309 + thread_stats->lost_connections += lost_connections;
1310 + thread_stats->access_denied_errors += access_denied_errors;
1311 + thread_stats->empty_queries += empty_queries;
1314 +void init_global_user_stats(void)
1316 + if (hash_init(&global_user_stats, system_charset_info, max_connections,
1317 + 0, 0, (hash_get_key)get_key_user_stats,
1318 + (hash_free_key)free_user_stats, 0)) {
1319 + sql_print_error("Initializing global_user_stats failed.");
1324 +void init_global_client_stats(void)
1326 + if (hash_init(&global_client_stats, system_charset_info, max_connections,
1327 + 0, 0, (hash_get_key)get_key_user_stats,
1328 + (hash_free_key)free_user_stats, 0)) {
1329 + sql_print_error("Initializing global_client_stats failed.");
1334 +void init_global_thread_stats(void)
1336 + if (hash_init(&global_thread_stats, &my_charset_bin, max_connections,
1337 + 0, 0, (hash_get_key)get_key_thread_stats,
1338 + (hash_free_key)free_thread_stats, 0)) {
1339 + sql_print_error("Initializing global_client_stats failed.");
1344 +extern "C" uchar *get_key_table_stats(TABLE_STATS *table_stats, size_t *length,
1345 + my_bool not_used __attribute__((unused)))
1347 + *length = strlen(table_stats->table);
1348 + return (uchar*)table_stats->table;
1351 +extern "C" void free_table_stats(TABLE_STATS* table_stats)
1353 + my_free((char*)table_stats, MYF(0));
1356 +void init_global_table_stats(void)
1358 + if (hash_init(&global_table_stats, system_charset_info, max_connections,
1359 + 0, 0, (hash_get_key)get_key_table_stats,
1360 + (hash_free_key)free_table_stats, 0)) {
1361 + sql_print_error("Initializing global_table_stats failed.");
1366 +extern "C" uchar *get_key_index_stats(INDEX_STATS *index_stats, size_t *length,
1367 + my_bool not_used __attribute__((unused)))
1369 + *length = strlen(index_stats->index);
1370 + return (uchar*)index_stats->index;
1373 +extern "C" void free_index_stats(INDEX_STATS* index_stats)
1375 + my_free((char*)index_stats, MYF(0));
1378 +void init_global_index_stats(void)
1380 + if (hash_init(&global_index_stats, system_charset_info, max_connections,
1381 + 0, 0, (hash_get_key)get_key_index_stats,
1382 + (hash_free_key)free_index_stats, 0)) {
1383 + sql_print_error("Initializing global_index_stats failed.");
1388 +void free_global_user_stats(void)
1390 + hash_free(&global_user_stats);
1393 +void free_global_thread_stats(void)
1395 + hash_free(&global_thread_stats);
1398 +void free_global_table_stats(void)
1400 + hash_free(&global_table_stats);
1403 +void free_global_index_stats(void)
1405 + hash_free(&global_index_stats);
1408 +void free_global_client_stats(void)
1410 + hash_free(&global_client_stats);
1413 +// 'mysql_system_user' is used for when the user is not defined for a THD.
1414 +static char mysql_system_user[] = "#mysql_system#";
1416 +// Returns 'user' if it's not NULL. Returns 'mysql_system_user' otherwise.
1417 +static char* get_valid_user_string(char* user) {
1418 + return user ? user : mysql_system_user;
1421 +// Increments the global stats connection count for an entry from
1422 +// global_client_stats or global_user_stats. Returns 0 on success
1424 +static int increment_count_by_name(const char *name, const char *role_name,
1425 + HASH *users_or_clients, THD *thd)
1427 + USER_STATS* user_stats;
1429 + if (!(user_stats = (USER_STATS*)hash_search(users_or_clients, (uchar*) name,
1432 + // First connection for this user or client
1433 + if (!(user_stats = ((USER_STATS*)
1434 + my_malloc(sizeof(USER_STATS), MYF(MY_WME | MY_ZEROFILL)))))
1436 + return 1; // Out of memory
1439 + init_user_stats(user_stats, name, role_name,
1440 + 0, 0, // connections
1442 + 0, 0, 0, // bytes sent, received and written
1443 + 0, 0, 0, // rows fetched, updated and read
1444 + 0, 0, 0, // select, update and other commands
1445 + 0, 0, // commit and rollback trans
1446 + thd->diff_denied_connections,
1447 + 0, // lost connections
1448 + 0, // access denied errors
1449 + 0); // empty queries
1451 + if (my_hash_insert(users_or_clients, (uchar*)user_stats))
1453 + my_free((char*)user_stats, 0);
1454 + return 1; // Out of memory
1457 + user_stats->total_connections++;
1461 +static int increment_count_by_id(my_thread_id id,
1462 + HASH *users_or_clients, THD *thd)
1464 + THREAD_STATS* thread_stats;
1466 + if (!(thread_stats = (THREAD_STATS*)hash_search(users_or_clients, (uchar*) &id,
1467 + sizeof(my_thread_id))))
1469 + // First connection for this user or client
1470 + if (!(thread_stats = ((THREAD_STATS*)
1471 + my_malloc(sizeof(THREAD_STATS), MYF(MY_WME | MY_ZEROFILL)))))
1473 + return 1; // Out of memory
1476 + init_thread_stats(thread_stats, id,
1477 + 0, 0, // connections
1479 + 0, 0, 0, // bytes sent, received and written
1480 + 0, 0, 0, // rows fetched, updated and read
1481 + 0, 0, 0, // select, update and other commands
1482 + 0, 0, // commit and rollback trans
1483 + thd->diff_denied_connections,
1484 + 0, // lost connections
1485 + 0, // access denied errors
1486 + 0); // empty queries
1488 + if (my_hash_insert(users_or_clients, (uchar*)thread_stats))
1490 + my_free((char*)thread_stats, 0);
1491 + return 1; // Out of memory
1494 + thread_stats->total_connections++;
1498 +// Increments the global user and client stats connection count. If 'use_lock'
1499 +// is true, LOCK_global_user_client_stats will be locked/unlocked. Returns
1500 +// 0 on success, 1 on error.
1501 +static int increment_connection_count(THD* thd, bool use_lock)
1503 + char* user_string = get_valid_user_string(thd->main_security_ctx.user);
1504 + const char* client_string = get_client_host(thd);
1505 + int return_value = 0;
1507 + if (!opt_userstat_running)
1508 + return return_value;
1510 + if (use_lock) pthread_mutex_lock(&LOCK_global_user_client_stats);
1512 + if (increment_count_by_name(user_string, user_string,
1513 + &global_user_stats, thd))
1518 + if (increment_count_by_name(client_string,
1520 + &global_client_stats, thd))
1525 + if (opt_thread_statistics) {
1526 + if (increment_count_by_id(thd->thread_id, &global_thread_stats, thd))
1534 + if (use_lock) pthread_mutex_unlock(&LOCK_global_user_client_stats);
1535 + return return_value;
1538 +// Used to update the global user and client stats.
1539 +static void update_global_user_stats_with_user(THD* thd,
1540 + USER_STATS* user_stats,
1543 + user_stats->connected_time += now - thd->last_global_update_time;
1544 +// thd->last_global_update_time = now;
1545 + user_stats->busy_time += thd->diff_total_busy_time;
1546 + user_stats->cpu_time += thd->diff_total_cpu_time;
1547 + user_stats->bytes_received += thd->diff_total_bytes_received;
1548 + user_stats->bytes_sent += thd->diff_total_bytes_sent;
1549 + user_stats->binlog_bytes_written += thd->diff_total_binlog_bytes_written;
1550 + user_stats->rows_fetched += thd->diff_total_sent_rows;
1551 + user_stats->rows_updated += thd->diff_total_updated_rows;
1552 + user_stats->rows_read += thd->diff_total_read_rows;
1553 + user_stats->select_commands += thd->diff_select_commands;
1554 + user_stats->update_commands += thd->diff_update_commands;
1555 + user_stats->other_commands += thd->diff_other_commands;
1556 + user_stats->commit_trans += thd->diff_commit_trans;
1557 + user_stats->rollback_trans += thd->diff_rollback_trans;
1558 + user_stats->denied_connections += thd->diff_denied_connections;
1559 + user_stats->lost_connections += thd->diff_lost_connections;
1560 + user_stats->access_denied_errors += thd->diff_access_denied_errors;
1561 + user_stats->empty_queries += thd->diff_empty_queries;
1564 +static void update_global_thread_stats_with_thread(THD* thd,
1565 + THREAD_STATS* thread_stats,
1568 + thread_stats->connected_time += now - thd->last_global_update_time;
1569 +// thd->last_global_update_time = now;
1570 + thread_stats->busy_time += thd->diff_total_busy_time;
1571 + thread_stats->cpu_time += thd->diff_total_cpu_time;
1572 + thread_stats->bytes_received += thd->diff_total_bytes_received;
1573 + thread_stats->bytes_sent += thd->diff_total_bytes_sent;
1574 + thread_stats->binlog_bytes_written += thd->diff_total_binlog_bytes_written;
1575 + thread_stats->rows_fetched += thd->diff_total_sent_rows;
1576 + thread_stats->rows_updated += thd->diff_total_updated_rows;
1577 + thread_stats->rows_read += thd->diff_total_read_rows;
1578 + thread_stats->select_commands += thd->diff_select_commands;
1579 + thread_stats->update_commands += thd->diff_update_commands;
1580 + thread_stats->other_commands += thd->diff_other_commands;
1581 + thread_stats->commit_trans += thd->diff_commit_trans;
1582 + thread_stats->rollback_trans += thd->diff_rollback_trans;
1583 + thread_stats->denied_connections += thd->diff_denied_connections;
1584 + thread_stats->lost_connections += thd->diff_lost_connections;
1585 + thread_stats->access_denied_errors += thd->diff_access_denied_errors;
1586 + thread_stats->empty_queries += thd->diff_empty_queries;
1589 +// Updates the global stats of a user or client
1590 +void update_global_user_stats(THD* thd, bool create_user, time_t now)
1592 + if (opt_userstat_running) {
1593 + char* user_string = get_valid_user_string(thd->main_security_ctx.user);
1594 + const char* client_string = get_client_host(thd);
1596 + USER_STATS* user_stats;
1597 + THREAD_STATS* thread_stats;
1598 + pthread_mutex_lock(&LOCK_global_user_client_stats);
1600 + // Update by user name
1601 + if ((user_stats = (USER_STATS*)hash_search(&global_user_stats,
1602 + (uchar*)user_string,
1603 + strlen(user_string)))) {
1605 + update_global_user_stats_with_user(thd, user_stats, now);
1607 + // Create the entry
1608 + if (create_user) {
1609 + increment_count_by_name(user_string, user_string,
1610 + &global_user_stats, thd);
1614 + // Update by client IP
1615 + if ((user_stats = (USER_STATS*)hash_search(&global_client_stats,
1616 + (uchar*)client_string,
1617 + strlen(client_string)))) {
1618 + // Found by client IP
1619 + update_global_user_stats_with_user(thd, user_stats, now);
1621 + // Create the entry
1622 + if (create_user) {
1623 + increment_count_by_name(client_string,
1625 + &global_client_stats, thd);
1629 + if (opt_thread_statistics) {
1630 + // Update by thread ID
1631 + if ((thread_stats = (THREAD_STATS*)hash_search(&global_thread_stats,
1632 + (uchar*) &(thd->thread_id),
1633 + sizeof(my_thread_id)))) {
1634 + // Found by thread ID
1635 + update_global_thread_stats_with_thread(thd, thread_stats, now);
1637 + // Create the entry
1638 + if (create_user) {
1639 + increment_count_by_id(thd->thread_id,
1640 + &global_thread_stats, thd);
1645 + thd->last_global_update_time = now;
1646 + thd->reset_diff_stats();
1648 + pthread_mutex_unlock(&LOCK_global_user_client_stats);
1650 + thd->reset_diff_stats();
1655 check if user has already too many connections
1656 @@ -154,7 +729,10 @@
1661 uc->connections--; // no need for decrease_user_connections() here
1662 + statistic_increment(denied_connections, &LOCK_status);
1664 (void) pthread_mutex_unlock(&LOCK_user_conn);
1667 @@ -490,6 +1068,7 @@
1668 general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
1671 + thd->diff_access_denied_errors++;
1672 my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
1673 thd->main_security_ctx.user,
1674 thd->main_security_ctx.host_or_ip,
1675 @@ -971,11 +1550,20 @@
1676 my_sleep(1000); /* must wait after eof() */
1678 statistic_increment(aborted_connects,&LOCK_status);
1679 + thd->diff_denied_connections++;
1682 /* Connect completed, set read/write timeouts back to default */
1683 my_net_set_read_timeout(net, thd->variables.net_read_timeout);
1684 my_net_set_write_timeout(net, thd->variables.net_write_timeout);
1686 + thd->reset_stats();
1687 + // Updates global user connection stats.
1688 + if (increment_connection_count(thd, true)) {
1689 + net_send_error(thd, ER_OUTOFMEMORY); // Out of memory
1696 @@ -997,6 +1585,7 @@
1697 if (thd->killed || (net->error && net->vio != 0))
1699 statistic_increment(aborted_threads,&LOCK_status);
1700 + thd->diff_lost_connections++;
1703 if (net->error && net->vio != 0)
1704 @@ -1123,10 +1712,14 @@
1707 NET *net= &thd->net;
1708 + bool create_user= TRUE;
1711 if (login_connection(thd))
1713 + create_user= FALSE;
1717 prepare_new_connection_state(thd);
1719 @@ -1149,6 +1742,8 @@
1722 close_connection(thd, 0, 1);
1723 + thd->update_stats(false);
1724 + update_global_user_stats(thd, create_user, time(NULL));
1725 if (thread_scheduler.end_thread(thd,1))
1726 return 0; // Probably no-threads
1728 diff -ruN a/sql/sql_delete.cc b/sql/sql_delete.cc
1729 --- a/sql/sql_delete.cc 2010-10-12 00:34:33.000000000 +0400
1730 +++ b/sql/sql_delete.cc 2010-11-24 17:24:52.000000000 +0300
1732 my_ok(thd, (ha_rows) thd->row_count_func);
1733 DBUG_PRINT("info",("%ld records deleted",(long) deleted));
1735 + thd->updated_row_count += deleted;
1736 DBUG_RETURN(error >= 0 || thd->is_error());
1739 @@ -1059,6 +1060,7 @@
1740 thd->row_count_func= deleted;
1741 ::my_ok(thd, (ha_rows) thd->row_count_func);
1743 + thd->updated_row_count += deleted;
1747 diff -ruN a/sql/sql_insert.cc b/sql/sql_insert.cc
1748 --- a/sql/sql_insert.cc 2010-10-12 00:34:16.000000000 +0400
1749 +++ b/sql/sql_insert.cc 2010-11-24 17:24:52.000000000 +0300
1751 thd->row_count_func= info.copied + info.deleted + updated;
1752 ::my_ok(thd, (ulong) thd->row_count_func, id, buff);
1754 + thd->updated_row_count += thd->row_count_func;
1755 thd->abort_on_warning= 0;
1758 @@ -3309,6 +3310,7 @@
1759 thd->first_successful_insert_id_in_prev_stmt :
1760 (info.copied ? autoinc_value_of_last_inserted_row : 0));
1761 ::my_ok(thd, (ulong) thd->row_count_func, id, buff);
1762 + thd->updated_row_count += thd->row_count_func;
1766 diff -ruN a/sql/sql_lex.h b/sql/sql_lex.h
1767 --- a/sql/sql_lex.h 2010-11-24 17:24:51.000000000 +0300
1768 +++ b/sql/sql_lex.h 2010-11-24 17:31:33.000000000 +0300
1770 When a command is added here, be sure it's also added in mysqld.cc
1771 in "struct show_var_st status_vars[]= {" ...
1773 + // TODO(mcallaghan): update status_vars in mysqld to export these
1774 + SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS,
1775 + SQLCOM_SHOW_CLIENT_STATS, SQLCOM_SHOW_THREAD_STATS,
1776 /* This should be the last !!! */
1779 diff -ruN a/sql/sql_parse.cc b/sql/sql_parse.cc
1780 --- a/sql/sql_parse.cc 2010-11-24 17:24:51.000000000 +0300
1781 +++ b/sql/sql_parse.cc 2010-11-24 17:45:19.000000000 +0300
1783 static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
1784 static bool check_show_create_table_access(THD *thd, TABLE_LIST *table);
1786 +// Uses the THD to update the global stats by user name and client IP
1787 +void update_global_user_stats(THD* thd, bool create_user, time_t now);
1789 const char *any_db="*any*"; // Special symbol for check_access
1791 const LEX_STRING command_name[]={
1792 @@ -825,6 +828,12 @@
1794 thd->clear_error(); // Clear error message
1795 thd->main_da.reset_diagnostics_area();
1796 + thd->updated_row_count=0;
1799 + thd->bytes_received=0;
1800 + thd->bytes_sent=0;
1801 + thd->binlog_bytes_written=0;
1803 net_new_transaction(net);
1805 @@ -994,6 +1003,9 @@
1806 DBUG_PRINT("info",("packet: '%*.s'; command: %d", packet_length, packet, command));
1808 thd->command=command;
1809 + // To increment the corrent command counter for user stats, 'command' must
1810 + // be saved because it is set to COM_SLEEP at the end of this function.
1811 + thd->old_command = command;
1813 Commands which always take a long time are logged into
1814 the slow log only if opt_log_slow_admin_statements is set.
1815 @@ -1865,6 +1877,13 @@
1816 thd->profiling.discard_current_query();
1819 + case SCH_USER_STATS:
1820 + case SCH_CLIENT_STATS:
1821 + case SCH_THREAD_STATS:
1822 + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
1824 + case SCH_TABLE_STATS:
1825 + case SCH_INDEX_STATS:
1826 case SCH_OPEN_TABLES:
1829 @@ -2021,6 +2040,7 @@
1830 thd->security_ctx->priv_host)) &&
1831 check_global_access(thd, SUPER_ACL))
1833 + thd->diff_access_denied_errors++;
1834 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
1837 @@ -5348,6 +5368,7 @@
1840 const char *db_name= db ? db : thd->db;
1841 + thd->diff_access_denied_errors++;
1842 my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
1843 sctx->priv_user, sctx->priv_host, db_name);
1845 @@ -5380,12 +5401,15 @@
1846 { // We can never grant this
1847 DBUG_PRINT("error",("No possible access"));
1850 + thd->diff_access_denied_errors++;
1851 my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
1856 ER(ER_NO))); /* purecov: tested */
1858 DBUG_RETURN(TRUE); /* purecov: tested */
1861 @@ -5411,11 +5435,15 @@
1863 DBUG_PRINT("error",("Access denied"));
1866 + // increment needs !no_errors condition, otherwise double counting.
1867 + thd->diff_access_denied_errors++;
1868 my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
1869 sctx->priv_user, sctx->priv_host,
1870 (db ? db : (thd->db ?
1872 "unknown"))); /* purecov: tested */
1874 DBUG_RETURN(TRUE); /* purecov: tested */
1877 @@ -5444,6 +5472,7 @@
1879 if (!thd->col_access && check_grant_db(thd, dst_db_name))
1881 + thd->diff_access_denied_errors++;
1882 my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
1883 thd->security_ctx->priv_user,
1884 thd->security_ctx->priv_host,
1885 @@ -5525,9 +5554,12 @@
1886 (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
1890 + thd->diff_access_denied_errors++;
1891 my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
1892 sctx->priv_user, sctx->priv_host,
1893 INFORMATION_SCHEMA_NAME.str);
1898 @@ -5690,6 +5722,7 @@
1899 if ((thd->security_ctx->master_access & want_access))
1901 get_privilege_desc(command, sizeof(command), want_access);
1902 + thd->diff_access_denied_errors++;
1903 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
1906 @@ -6071,6 +6104,30 @@
1908 mysql_reset_thd_for_next_command(thd);
1910 + int start_time_error = 0;
1911 + int end_time_error = 0;
1912 + struct timeval start_time, end_time;
1913 + double start_usecs = 0;
1914 + double end_usecs = 0;
1916 + int cputime_error = 0;
1917 + struct timespec tp;
1918 + double start_cpu_nsecs = 0;
1919 + double end_cpu_nsecs = 0;
1921 + if (opt_userstat_running) {
1922 +#ifdef HAVE_CLOCK_GETTIME
1923 + /* get start cputime */
1924 + if (!(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
1925 + start_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
1928 + // Gets the start time, in order to measure how long this command takes.
1929 + if (!(start_time_error = gettimeofday(&start_time, NULL))) {
1930 + start_usecs = start_time.tv_sec * 1000000.0 + start_time.tv_usec;
1934 if (query_cache_send_result_to_client(thd, rawbuf, length) <= 0)
1937 @@ -6151,6 +6208,43 @@
1938 *found_semicolon= NULL;
1941 + if (opt_userstat_running) {
1942 + // Gets the end time.
1943 + if (!(end_time_error = gettimeofday(&end_time, NULL))) {
1944 + end_usecs = end_time.tv_sec * 1000000.0 + end_time.tv_usec;
1947 + // Calculates the difference between the end and start times.
1948 + if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error) {
1949 + thd->busy_time = (end_usecs - start_usecs) / 1000000;
1950 + // In case there are bad values, 2629743 is the #seconds in a month.
1951 + if (thd->busy_time > 2629743) {
1952 + thd->busy_time = 0;
1955 + // end time went back in time, or gettimeofday() failed.
1956 + thd->busy_time = 0;
1959 +#ifdef HAVE_CLOCK_GETTIME
1960 + /* get end cputime */
1961 + if (!cputime_error &&
1962 + !(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
1963 + end_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
1965 + if (start_cpu_nsecs && !cputime_error) {
1966 + thd->cpu_time = (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
1967 + // In case there are bad values, 2629743 is the #seconds in a month.
1968 + if (thd->cpu_time > 2629743) {
1969 + thd->cpu_time = 0;
1972 + thd->cpu_time = 0;
1974 + // Updates THD stats and the global user stats.
1975 + thd->update_stats(true);
1976 + update_global_user_stats(thd, true, time(NULL));
1981 @@ -7016,6 +7110,13 @@
1982 if (flush_error_log())
1985 + if (((options & (REFRESH_SLOW_QUERY_LOG | REFRESH_LOG)) ==
1986 + REFRESH_SLOW_QUERY_LOG))
1988 + /* We are only flushing slow query log */
1989 + logger.flush_slow_log(thd);
1992 #ifdef HAVE_QUERY_CACHE
1993 if (options & REFRESH_QUERY_CACHE_FREE)
1995 @@ -7116,6 +7217,40 @@
1997 if (options & REFRESH_USER_RESOURCES)
1998 reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
1999 + if (options & REFRESH_TABLE_STATS)
2001 + pthread_mutex_lock(&LOCK_global_table_stats);
2002 + free_global_table_stats();
2003 + init_global_table_stats();
2004 + pthread_mutex_unlock(&LOCK_global_table_stats);
2006 + if (options & REFRESH_INDEX_STATS)
2008 + pthread_mutex_lock(&LOCK_global_index_stats);
2009 + free_global_index_stats();
2010 + init_global_index_stats();
2011 + pthread_mutex_unlock(&LOCK_global_index_stats);
2013 + if (options & (REFRESH_USER_STATS | REFRESH_CLIENT_STATS | REFRESH_THREAD_STATS))
2015 + pthread_mutex_lock(&LOCK_global_user_client_stats);
2016 + if (options & REFRESH_USER_STATS)
2018 + free_global_user_stats();
2019 + init_global_user_stats();
2021 + if (options & REFRESH_CLIENT_STATS)
2023 + free_global_client_stats();
2024 + init_global_client_stats();
2026 + if (options & REFRESH_THREAD_STATS)
2028 + free_global_thread_stats();
2029 + init_global_thread_stats();
2031 + pthread_mutex_unlock(&LOCK_global_user_client_stats);
2033 *write_to_binlog= tmp_write_to_binlog;
2035 If the query was killed then this function must fail.
2036 diff -ruN a/sql/sql_prepare.cc b/sql/sql_prepare.cc
2037 --- a/sql/sql_prepare.cc 2010-11-24 17:24:51.000000000 +0300
2038 +++ b/sql/sql_prepare.cc 2010-11-24 17:45:09.000000000 +0300
2040 #include <mysql_com.h>
2043 +// Uses the THD to update the global stats by user name and client IP
2044 +void update_global_user_stats(THD* thd, bool create_user, time_t now);
2047 A result class used to send cursor rows using the binary protocol.
2049 @@ -2103,8 +2106,32 @@
2050 /* First of all clear possible warnings from the previous command */
2051 mysql_reset_thd_for_next_command(thd);
2053 + int start_time_error = 0;
2054 + int end_time_error = 0;
2055 + struct timeval start_time, end_time;
2056 + double start_usecs = 0;
2057 + double end_usecs = 0;
2059 + int cputime_error = 0;
2060 + struct timespec tp;
2061 + double start_cpu_nsecs = 0;
2062 + double end_cpu_nsecs = 0;
2064 + if (opt_userstat_running) {
2065 +#ifdef HAVE_CLOCK_GETTIME
2066 + /* get start cputime */
2067 + if (!(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2068 + start_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
2071 + // Gets the start time, in order to measure how long this command takes.
2072 + if (!(start_time_error = gettimeofday(&start_time, NULL))) {
2073 + start_usecs = start_time.tv_sec * 1000000.0 + start_time.tv_usec;
2077 if (! (stmt= new Prepared_statement(thd)))
2078 - DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */
2079 + goto end; /* out of memory: error is set in Sql_alloc */
2081 if (thd->stmt_map.insert(thd, stmt))
2083 @@ -2112,7 +2139,7 @@
2084 The error is set in the insert. The statement itself
2085 will be also deleted there (this is how the hash works).
2091 /* Reset warnings from previous command */
2092 @@ -2139,6 +2166,44 @@
2093 thd->protocol= save_protocol;
2095 /* check_prepared_statemnt sends the metadata packet in case of success */
2097 + if (opt_userstat_running) {
2098 + // Gets the end time.
2099 + 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) {
2105 + thd->busy_time = (end_usecs - start_usecs) / 1000000;
2106 + // In case there are bad values, 2629743 is the #seconds in a month.
2107 + if (thd->busy_time > 2629743) {
2108 + thd->busy_time = 0;
2111 + // end time went back in time, or gettimeofday() failed.
2112 + thd->busy_time = 0;
2115 +#ifdef HAVE_CLOCK_GETTIME
2116 + /* get end cputime */
2117 + if (!cputime_error &&
2118 + !(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2119 + end_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
2121 + if (start_cpu_nsecs && !cputime_error) {
2122 + thd->cpu_time = (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
2123 + // In case there are bad values, 2629743 is the #seconds in a month.
2124 + if (thd->cpu_time > 2629743) {
2125 + thd->cpu_time = 0;
2128 + thd->cpu_time = 0;
2130 + // Updates THD stats and the global user stats.
2131 + thd->update_stats(true);
2132 + update_global_user_stats(thd, true, time(NULL));
2137 @@ -2489,12 +2554,36 @@
2138 /* First of all clear possible warnings from the previous command */
2139 mysql_reset_thd_for_next_command(thd);
2141 + int start_time_error = 0;
2142 + int end_time_error = 0;
2143 + struct timeval start_time, end_time;
2144 + double start_usecs = 0;
2145 + double end_usecs = 0;
2147 + int cputime_error = 0;
2148 + struct timespec tp;
2149 + double start_cpu_nsecs = 0;
2150 + double end_cpu_nsecs = 0;
2152 + if (opt_userstat_running) {
2153 +#ifdef HAVE_CLOCK_GETTIME
2154 + /* get start cputime */
2155 + if (!(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2156 + start_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
2159 + // Gets the start time, in order to measure how long this command takes.
2160 + if (!(start_time_error = gettimeofday(&start_time, NULL))) {
2161 + start_usecs = start_time.tv_sec * 1000000.0 + start_time.tv_usec;
2165 if (!(stmt= find_prepared_statement(thd, stmt_id)))
2168 my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), sizeof(llbuf),
2169 llstr(stmt_id, llbuf), "mysqld_stmt_execute");
2174 #if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
2175 @@ -2515,6 +2604,44 @@
2176 /* Close connection socket; for use with client testing (Bug#43560). */
2177 DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio););
2180 + if (opt_userstat_running) {
2181 + // Gets the end time.
2182 + if (!(end_time_error = gettimeofday(&end_time, NULL))) {
2183 + end_usecs = end_time.tv_sec * 1000000.0 + end_time.tv_usec;
2186 + // Calculates the difference between the end and start times.
2187 + if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error) {
2188 + thd->busy_time = (end_usecs - start_usecs) / 1000000;
2189 + // In case there are bad values, 2629743 is the #seconds in a month.
2190 + if (thd->busy_time > 2629743) {
2191 + thd->busy_time = 0;
2194 + // end time went back in time, or gettimeofday() failed.
2195 + thd->busy_time = 0;
2198 +#ifdef HAVE_CLOCK_GETTIME
2199 + /* get end cputime */
2200 + if (!cputime_error &&
2201 + !(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2202 + end_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
2204 + if (start_cpu_nsecs && !cputime_error) {
2205 + thd->cpu_time = (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
2206 + // In case there are bad values, 2629743 is the #seconds in a month.
2207 + if (thd->cpu_time > 2629743) {
2208 + thd->cpu_time = 0;
2211 + thd->cpu_time = 0;
2213 + // Updates THD stats and the global user stats.
2214 + thd->update_stats(true);
2215 + update_global_user_stats(thd, true, time(NULL));
2220 @@ -2588,20 +2715,45 @@
2222 /* First of all clear possible warnings from the previous command */
2223 mysql_reset_thd_for_next_command(thd);
2225 + int start_time_error = 0;
2226 + int end_time_error = 0;
2227 + struct timeval start_time, end_time;
2228 + double start_usecs = 0;
2229 + double end_usecs = 0;
2231 + int cputime_error = 0;
2232 + struct timespec tp;
2233 + double start_cpu_nsecs = 0;
2234 + double end_cpu_nsecs = 0;
2236 + if (opt_userstat_running) {
2237 +#ifdef HAVE_CLOCK_GETTIME
2238 + /* get start cputime */
2239 + if (!(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2240 + start_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
2243 + // Gets the start time, in order to measure how long this command takes.
2244 + if (!(start_time_error = gettimeofday(&start_time, NULL))) {
2245 + start_usecs = start_time.tv_sec * 1000000.0 + start_time.tv_usec;
2249 status_var_increment(thd->status_var.com_stmt_fetch);
2250 if (!(stmt= find_prepared_statement(thd, stmt_id)))
2253 my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), sizeof(llbuf),
2254 llstr(stmt_id, llbuf), "mysqld_stmt_fetch");
2259 cursor= stmt->cursor;
2262 my_error(ER_STMT_HAS_NO_OPEN_CURSOR, MYF(0), stmt_id);
2267 thd->stmt_arena= stmt;
2268 @@ -2625,6 +2777,44 @@
2269 thd->restore_backup_statement(stmt, &stmt_backup);
2270 thd->stmt_arena= thd;
2273 + if (opt_userstat_running) {
2274 + // Gets the end time.
2275 + if (!(end_time_error = gettimeofday(&end_time, NULL))) {
2276 + end_usecs = end_time.tv_sec * 1000000.0 + end_time.tv_usec;
2279 + // Calculates the difference between the end and start times.
2280 + if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error) {
2281 + thd->busy_time = (end_usecs - start_usecs) / 1000000;
2282 + // In case there are bad values, 2629743 is the #seconds in a month.
2283 + if (thd->busy_time > 2629743) {
2284 + thd->busy_time = 0;
2287 + // end time went back in time, or gettimeofday() failed.
2288 + thd->busy_time = 0;
2291 +#ifdef HAVE_CLOCK_GETTIME
2292 + /* get end cputime */
2293 + if (!cputime_error &&
2294 + !(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2295 + end_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
2297 + if (start_cpu_nsecs && !cputime_error) {
2298 + thd->cpu_time = (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
2299 + // In case there are bad values, 2629743 is the #seconds in a month.
2300 + if (thd->cpu_time > 2629743) {
2301 + thd->cpu_time = 0;
2304 + thd->cpu_time = 0;
2306 + // Updates THD stats and the global user stats.
2307 + thd->update_stats(true);
2308 + update_global_user_stats(thd, true, time(NULL));
2313 @@ -2655,13 +2845,37 @@
2314 /* First of all clear possible warnings from the previous command */
2315 mysql_reset_thd_for_next_command(thd);
2317 + int start_time_error = 0;
2318 + int end_time_error = 0;
2319 + struct timeval start_time, end_time;
2320 + double start_usecs = 0;
2321 + double end_usecs = 0;
2323 + int cputime_error = 0;
2324 + struct timespec tp;
2325 + double start_cpu_nsecs = 0;
2326 + double end_cpu_nsecs = 0;
2328 + if (opt_userstat_running) {
2329 +#ifdef HAVE_CLOCK_GETTIME
2330 + /* get start cputime */
2331 + if (!(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2332 + start_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
2335 + // Gets the start time, in order to measure how long this command takes.
2336 + if (!(start_time_error = gettimeofday(&start_time, NULL))) {
2337 + start_usecs = start_time.tv_sec * 1000000.0 + start_time.tv_usec;
2341 status_var_increment(thd->status_var.com_stmt_reset);
2342 if (!(stmt= find_prepared_statement(thd, stmt_id)))
2345 my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), sizeof(llbuf),
2346 llstr(stmt_id, llbuf), "mysqld_stmt_reset");
2351 stmt->close_cursor();
2352 @@ -2678,6 +2892,44 @@
2357 + if (opt_userstat_running) {
2358 + // Gets the end time.
2359 + if (!(end_time_error = gettimeofday(&end_time, NULL))) {
2360 + end_usecs = end_time.tv_sec * 1000000.0 + end_time.tv_usec;
2363 + // Calculates the difference between the end and start times.
2364 + if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error) {
2365 + thd->busy_time = (end_usecs - start_usecs) / 1000000;
2366 + // In case there are bad values, 2629743 is the #seconds in a month.
2367 + if (thd->busy_time > 2629743) {
2368 + thd->busy_time = 0;
2371 + // end time went back in time, or gettimeofday() failed.
2372 + thd->busy_time = 0;
2375 +#ifdef HAVE_CLOCK_GETTIME
2376 + /* get end cputime */
2377 + if (!cputime_error &&
2378 + !(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2379 + end_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
2381 + if (start_cpu_nsecs && !cputime_error) {
2382 + thd->cpu_time = (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
2383 + // In case there are bad values, 2629743 is the #seconds in a month.
2384 + if (thd->cpu_time > 2629743) {
2385 + thd->cpu_time = 0;
2388 + thd->cpu_time = 0;
2390 + // Updates THD stats and the global user stats.
2391 + thd->update_stats(true);
2392 + update_global_user_stats(thd, true, time(NULL));
2397 diff -ruN a/sql/sql_show.cc b/sql/sql_show.cc
2398 --- a/sql/sql_show.cc 2010-11-24 17:24:52.000000000 +0300
2399 +++ b/sql/sql_show.cc 2010-11-24 17:31:33.000000000 +0300
2402 static COND * make_cond_for_info_schema(COND *cond, TABLE_LIST *table);
2405 + * Solaris 10 does not have strsep().
2407 + * based on getToken from http://www.winehq.org/pipermail/wine-patches/2001-November/001322.html
2411 +#ifndef HAVE_STRSEP
2412 +static char* strsep(char** str, const char* delims)
2416 + if (*str == NULL) {
2417 + /* No more tokens */
2422 + while (**str != '\0') {
2423 + 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 @@ -2386,6 +2421,279 @@
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) {
2469 + restore_record(table, s->default_values);
2470 + USER_STATS *user_stats = (USER_STATS*)hash_element(all_user_stats, i);
2471 + table->field[0]->store(user_stats->user, strlen(user_stats->user), system_charset_info);
2472 + table->field[1]->store((longlong)user_stats->total_connections);
2473 + table->field[2]->store((longlong)user_stats->concurrent_connections);
2474 + table->field[3]->store((longlong)user_stats->connected_time);
2475 + table->field[4]->store((longlong)user_stats->busy_time);
2476 + table->field[5]->store((longlong)user_stats->cpu_time);
2477 + table->field[6]->store((longlong)user_stats->bytes_received);
2478 + table->field[7]->store((longlong)user_stats->bytes_sent);
2479 + table->field[8]->store((longlong)user_stats->binlog_bytes_written);
2480 + table->field[9]->store((longlong)user_stats->rows_fetched);
2481 + table->field[10]->store((longlong)user_stats->rows_updated);
2482 + table->field[11]->store((longlong)user_stats->rows_read);
2483 + table->field[12]->store((longlong)user_stats->select_commands);
2484 + table->field[13]->store((longlong)user_stats->update_commands);
2485 + table->field[14]->store((longlong)user_stats->other_commands);
2486 + table->field[15]->store((longlong)user_stats->commit_trans);
2487 + table->field[16]->store((longlong)user_stats->rollback_trans);
2488 + table->field[17]->store((longlong)user_stats->denied_connections);
2489 + table->field[18]->store((longlong)user_stats->lost_connections);
2490 + table->field[19]->store((longlong)user_stats->access_denied_errors);
2491 + table->field[20]->store((longlong)user_stats->empty_queries);
2492 + if (schema_table_store_record(thd, table))
2494 + DBUG_PRINT("error", ("store record error"));
2501 +int send_thread_stats(THD* thd, HASH *all_thread_stats, TABLE *table)
2503 + DBUG_ENTER("send_thread_stats");
2504 + for (uint i = 0; i < all_thread_stats->records; ++i) {
2505 + restore_record(table, s->default_values);
2506 + THREAD_STATS *user_stats = (THREAD_STATS*)hash_element(all_thread_stats, i);
2507 + table->field[0]->store((longlong)user_stats->id);
2508 + table->field[1]->store((longlong)user_stats->total_connections);
2509 + table->field[2]->store((longlong)user_stats->concurrent_connections);
2510 + table->field[3]->store((longlong)user_stats->connected_time);
2511 + table->field[4]->store((longlong)user_stats->busy_time);
2512 + table->field[5]->store((longlong)user_stats->cpu_time);
2513 + table->field[6]->store((longlong)user_stats->bytes_received);
2514 + table->field[7]->store((longlong)user_stats->bytes_sent);
2515 + table->field[8]->store((longlong)user_stats->binlog_bytes_written);
2516 + table->field[9]->store((longlong)user_stats->rows_fetched);
2517 + table->field[10]->store((longlong)user_stats->rows_updated);
2518 + table->field[11]->store((longlong)user_stats->rows_read);
2519 + table->field[12]->store((longlong)user_stats->select_commands);
2520 + table->field[13]->store((longlong)user_stats->update_commands);
2521 + table->field[14]->store((longlong)user_stats->other_commands);
2522 + table->field[15]->store((longlong)user_stats->commit_trans);
2523 + table->field[16]->store((longlong)user_stats->rollback_trans);
2524 + table->field[17]->store((longlong)user_stats->denied_connections);
2525 + table->field[18]->store((longlong)user_stats->lost_connections);
2526 + table->field[19]->store((longlong)user_stats->access_denied_errors);
2527 + table->field[20]->store((longlong)user_stats->empty_queries);
2528 + if (schema_table_store_record(thd, table))
2530 + DBUG_PRINT("error", ("store record error"));
2538 + Process SHOW USER_STATISTICS
2541 + mysqld_show_user_stats
2542 + thd - current thread
2543 + wild - limit results to the entry for this user
2544 + with_roles - when true, display role for mapped users
2552 +int fill_schema_user_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2554 + TABLE *table= tables->table;
2555 + DBUG_ENTER("fill_schema_user_stats");
2557 + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
2560 + // Iterates through all the global stats and sends them to the client.
2561 + // Pattern matching on the client IP is supported.
2563 + pthread_mutex_lock(&LOCK_global_user_client_stats);
2564 + int result= send_user_stats(thd, &global_user_stats, table);
2565 + pthread_mutex_unlock(&LOCK_global_user_client_stats);
2569 + DBUG_PRINT("exit", ("fill_schema_user_stats result is 0"));
2573 + DBUG_PRINT("exit", ("fill_schema_user_stats result is 1"));
2578 + Process SHOW CLIENT_STATISTICS
2581 + mysqld_show_client_stats
2582 + thd - current thread
2583 + wild - limit results to the entry for this client
2591 +int fill_schema_client_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2593 + TABLE *table= tables->table;
2594 + DBUG_ENTER("fill_schema_client_stats");
2596 + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
2599 + // Iterates through all the global stats and sends them to the client.
2600 + // Pattern matching on the client IP is supported.
2602 + pthread_mutex_lock(&LOCK_global_user_client_stats);
2603 + int result= send_user_stats(thd, &global_client_stats, table);
2604 + pthread_mutex_unlock(&LOCK_global_user_client_stats);
2608 + DBUG_PRINT("exit", ("mysqld_show_client_stats result is 0"));
2612 + DBUG_PRINT("exit", ("mysqld_show_client_stats result is 1"));
2616 +int fill_schema_thread_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2618 + TABLE *table= tables->table;
2619 + DBUG_ENTER("fill_schema_thread_stats");
2621 + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
2624 + // Iterates through all the global stats and sends them to the client.
2625 + // Pattern matching on the client IP is supported.
2627 + pthread_mutex_lock(&LOCK_global_user_client_stats);
2628 + int result= send_thread_stats(thd, &global_thread_stats, table);
2629 + pthread_mutex_unlock(&LOCK_global_user_client_stats);
2633 + DBUG_PRINT("exit", ("mysqld_show_thread_stats result is 0"));
2637 + DBUG_PRINT("exit", ("mysqld_show_thread_stats result is 1"));
2641 +// Sends the global table stats back to the client.
2642 +int fill_schema_table_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2644 + TABLE *table= tables->table;
2645 + DBUG_ENTER("fill_schema_table_stats");
2646 + char *table_full_name, *table_schema;
2648 + pthread_mutex_lock(&LOCK_global_table_stats);
2649 + for (uint i = 0; i < global_table_stats.records; ++i) {
2650 + restore_record(table, s->default_values);
2651 + TABLE_STATS *table_stats =
2652 + (TABLE_STATS*)hash_element(&global_table_stats, i);
2654 + table_full_name= thd->strdup(table_stats->table);
2655 + table_schema= strsep(&table_full_name, ".");
2657 + TABLE_LIST tmp_table;
2658 + bzero((char*) &tmp_table,sizeof(tmp_table));
2659 + tmp_table.table_name= table_full_name;
2660 + tmp_table.db= table_schema;
2661 + tmp_table.grant.privilege= 0;
2662 + if (check_access(thd, SELECT_ACL | EXTRA_ACL, tmp_table.db,
2663 + &tmp_table.grant.privilege, 0, 0,
2664 + is_schema_db(table_schema)) ||
2665 + check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, 1))
2668 + table->field[0]->store(table_schema, strlen(table_schema), system_charset_info);
2669 + table->field[1]->store(table_full_name, strlen(table_full_name), system_charset_info);
2670 + table->field[2]->store((longlong)table_stats->rows_read, TRUE);
2671 + table->field[3]->store((longlong)table_stats->rows_changed, TRUE);
2672 + table->field[4]->store((longlong)table_stats->rows_changed_x_indexes, TRUE);
2674 + if (schema_table_store_record(thd, table))
2676 + VOID(pthread_mutex_unlock(&LOCK_global_table_stats));
2680 + pthread_mutex_unlock(&LOCK_global_table_stats);
2684 +// Sends the global index stats back to the client.
2685 +int fill_schema_index_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2687 + TABLE *table= tables->table;
2688 + DBUG_ENTER("fill_schema_index_stats");
2689 + char *index_full_name, *table_schema, *table_name;
2691 + pthread_mutex_lock(&LOCK_global_index_stats);
2692 + for (uint i = 0; i < global_index_stats.records; ++i) {
2693 + restore_record(table, s->default_values);
2694 + INDEX_STATS *index_stats =
2695 + (INDEX_STATS*)hash_element(&global_index_stats, i);
2697 + index_full_name= thd->strdup(index_stats->index);
2698 + table_schema= strsep(&index_full_name, ".");
2699 + table_name= strsep(&index_full_name, ".");
2701 + TABLE_LIST tmp_table;
2702 + bzero((char*) &tmp_table,sizeof(tmp_table));
2703 + tmp_table.table_name= table_name;
2704 + tmp_table.db= table_schema;
2705 + tmp_table.grant.privilege= 0;
2706 + if (check_access(thd, SELECT_ACL | EXTRA_ACL, tmp_table.db,
2707 + &tmp_table.grant.privilege, 0, 0,
2708 + is_schema_db(table_schema)) ||
2709 + check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, 1))
2712 + table->field[0]->store(table_schema, strlen(table_schema), system_charset_info);
2713 + table->field[1]->store(table_name, strlen(table_name), system_charset_info);
2714 + table->field[2]->store(index_full_name, strlen(index_full_name), system_charset_info);
2715 + table->field[3]->store((longlong)index_stats->rows_read, TRUE);
2717 + if (schema_table_store_record(thd, table))
2719 + VOID(pthread_mutex_unlock(&LOCK_global_index_stats));
2723 + pthread_mutex_unlock(&LOCK_global_index_stats);
2727 /* collect status for all running threads */
2729 @@ -6688,6 +6996,104 @@
2733 +ST_FIELD_INFO user_stats_fields_info[]=
2735 + {"USER", USERNAME_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User", SKIP_OPEN_TABLE},
2736 + {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections", SKIP_OPEN_TABLE},
2737 + {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections", SKIP_OPEN_TABLE},
2738 + {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time", SKIP_OPEN_TABLE},
2739 + {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Busy_time", SKIP_OPEN_TABLE},
2740 + {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Cpu_time", SKIP_OPEN_TABLE},
2741 + {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_received", SKIP_OPEN_TABLE},
2742 + {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_sent", SKIP_OPEN_TABLE},
2743 + {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Binlog_bytes_written", SKIP_OPEN_TABLE},
2744 + {"ROWS_FETCHED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_fetched", SKIP_OPEN_TABLE},
2745 + {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated", SKIP_OPEN_TABLE},
2746 + {"TABLE_ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Table_rows_read", SKIP_OPEN_TABLE},
2747 + {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Select_commands", SKIP_OPEN_TABLE},
2748 + {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Update_commands", SKIP_OPEN_TABLE},
2749 + {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Other_commands", SKIP_OPEN_TABLE},
2750 + {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Commit_transactions", SKIP_OPEN_TABLE},
2751 + {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rollback_transactions", SKIP_OPEN_TABLE},
2752 + {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Denied_connections", SKIP_OPEN_TABLE},
2753 + {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Lost_connections", SKIP_OPEN_TABLE},
2754 + {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Access_denied", SKIP_OPEN_TABLE},
2755 + {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Empty_queries", SKIP_OPEN_TABLE},
2756 + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2759 +ST_FIELD_INFO client_stats_fields_info[]=
2761 + {"CLIENT", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Client", SKIP_OPEN_TABLE},
2762 + {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections", SKIP_OPEN_TABLE},
2763 + {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections", SKIP_OPEN_TABLE},
2764 + {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time", SKIP_OPEN_TABLE},
2765 + {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Busy_time", SKIP_OPEN_TABLE},
2766 + {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Cpu_time", SKIP_OPEN_TABLE},
2767 + {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_received", SKIP_OPEN_TABLE},
2768 + {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_sent", SKIP_OPEN_TABLE},
2769 + {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Binlog_bytes_written", SKIP_OPEN_TABLE},
2770 + {"ROWS_FETCHED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_fetched", SKIP_OPEN_TABLE},
2771 + {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated", SKIP_OPEN_TABLE},
2772 + {"TABLE_ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Table_rows_read", SKIP_OPEN_TABLE},
2773 + {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Select_commands", SKIP_OPEN_TABLE},
2774 + {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Update_commands", SKIP_OPEN_TABLE},
2775 + {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Other_commands", SKIP_OPEN_TABLE},
2776 + {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Commit_transactions", SKIP_OPEN_TABLE},
2777 + {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rollback_transactions", SKIP_OPEN_TABLE},
2778 + {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Denied_connections", SKIP_OPEN_TABLE},
2779 + {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Lost_connections", SKIP_OPEN_TABLE},
2780 + {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Access_denied", SKIP_OPEN_TABLE},
2781 + {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Empty_queries", SKIP_OPEN_TABLE},
2782 + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2785 +ST_FIELD_INFO thread_stats_fields_info[]=
2787 + {"THREAD_ID", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Thread_id", SKIP_OPEN_TABLE},
2788 + {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections", SKIP_OPEN_TABLE},
2789 + {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections", SKIP_OPEN_TABLE},
2790 + {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time", SKIP_OPEN_TABLE},
2791 + {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Busy_time", SKIP_OPEN_TABLE},
2792 + {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Cpu_time", SKIP_OPEN_TABLE},
2793 + {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_received", SKIP_OPEN_TABLE},
2794 + {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_sent", SKIP_OPEN_TABLE},
2795 + {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Binlog_bytes_written", SKIP_OPEN_TABLE},
2796 + {"ROWS_FETCHED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_fetched", SKIP_OPEN_TABLE},
2797 + {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated", SKIP_OPEN_TABLE},
2798 + {"TABLE_ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Table_rows_read", SKIP_OPEN_TABLE},
2799 + {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Select_commands", SKIP_OPEN_TABLE},
2800 + {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Update_commands", SKIP_OPEN_TABLE},
2801 + {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Other_commands", SKIP_OPEN_TABLE},
2802 + {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Commit_transactions", SKIP_OPEN_TABLE},
2803 + {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rollback_transactions", SKIP_OPEN_TABLE},
2804 + {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Denied_connections", SKIP_OPEN_TABLE},
2805 + {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Lost_connections", SKIP_OPEN_TABLE},
2806 + {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Access_denied", SKIP_OPEN_TABLE},
2807 + {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Empty_queries", SKIP_OPEN_TABLE},
2808 + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2811 +ST_FIELD_INFO table_stats_fields_info[]=
2813 + {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_schema", SKIP_OPEN_TABLE},
2814 + {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name", SKIP_OPEN_TABLE},
2815 + {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read", SKIP_OPEN_TABLE},
2816 + {"ROWS_CHANGED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_changed", SKIP_OPEN_TABLE},
2817 + {"ROWS_CHANGED_X_INDEXES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_changed_x_#indexes", SKIP_OPEN_TABLE},
2818 + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2821 +ST_FIELD_INFO index_stats_fields_info[]=
2823 + {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_schema", SKIP_OPEN_TABLE},
2824 + {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name", SKIP_OPEN_TABLE},
2825 + {"INDEX_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Index_name", SKIP_OPEN_TABLE},
2826 + {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read", SKIP_OPEN_TABLE},
2827 + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2831 ST_FIELD_INFO processlist_fields_info[]=
2833 {"ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Id", SKIP_OPEN_TABLE},
2834 @@ -6823,6 +7229,8 @@
2836 {"CHARACTER_SETS", charsets_fields_info, create_schema_table,
2837 fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0, 0},
2838 + {"CLIENT_STATISTICS", client_stats_fields_info, create_schema_table,
2839 + fill_schema_client_stats, make_old_format, 0, -1, -1, 0, 0},
2840 {"COLLATIONS", collation_fields_info, create_schema_table,
2841 fill_schema_collation, make_old_format, 0, -1, -1, 0, 0},
2842 {"COLLATION_CHARACTER_SET_APPLICABILITY", coll_charset_app_fields_info,
2843 @@ -6832,6 +7240,8 @@
2844 OPTIMIZE_I_S_TABLE|OPEN_VIEW_FULL},
2845 {"COLUMN_PRIVILEGES", column_privileges_fields_info, create_schema_table,
2846 fill_schema_column_privileges, 0, 0, -1, -1, 0, 0},
2847 + {"INDEX_STATISTICS", index_stats_fields_info, create_schema_table,
2848 + fill_schema_index_stats, make_old_format, 0, -1, -1, 0, 0},
2849 {"ENGINES", engines_fields_info, create_schema_table,
2850 fill_schema_engines, make_old_format, 0, -1, -1, 0, 0},
2851 #ifdef HAVE_EVENT_SCHEDULER
2852 @@ -6888,11 +7298,17 @@
2853 get_all_tables, make_table_names_old_format, 0, 1, 2, 1, 0},
2854 {"TABLE_PRIVILEGES", table_privileges_fields_info, create_schema_table,
2855 fill_schema_table_privileges, 0, 0, -1, -1, 0, 0},
2856 + {"TABLE_STATISTICS", table_stats_fields_info, create_schema_table,
2857 + fill_schema_table_stats, make_old_format, 0, -1, -1, 0, 0},
2858 + {"THREAD_STATISTICS", thread_stats_fields_info, create_schema_table,
2859 + fill_schema_thread_stats, make_old_format, 0, -1, -1, 0, 0},
2860 {"TRIGGERS", triggers_fields_info, create_schema_table,
2861 get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0,
2863 {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table,
2864 fill_schema_user_privileges, 0, 0, -1, -1, 0, 0},
2865 + {"USER_STATISTICS", user_stats_fields_info, create_schema_table,
2866 + fill_schema_user_stats, make_old_format, 0, -1, -1, 0, 0},
2867 {"VARIABLES", variables_fields_info, create_schema_table, fill_variables,
2868 make_old_format, 0, 0, -1, 1, 0},
2869 {"VIEWS", view_fields_info, create_schema_table,
2870 diff -ruN a/sql/sql_update.cc b/sql/sql_update.cc
2871 --- a/sql/sql_update.cc 2010-10-12 00:34:16.000000000 +0400
2872 +++ b/sql/sql_update.cc 2010-11-24 17:24:52.000000000 +0300
2874 thd->row_count_func=
2875 (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
2876 my_ok(thd, (ulong) thd->row_count_func, id, buff);
2877 + thd->updated_row_count += thd->row_count_func;
2878 DBUG_PRINT("info",("%ld records updated", (long) updated));
2880 thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */
2881 @@ -2176,5 +2177,6 @@
2882 thd->row_count_func=
2883 (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
2884 ::my_ok(thd, (ulong) thd->row_count_func, id, buff);
2885 + thd->updated_row_count += thd->row_count_func;
2888 diff -ruN a/sql/sql_yacc.yy b/sql/sql_yacc.yy
2889 --- a/sql/sql_yacc.yy 2010-11-24 17:24:51.000000000 +0300
2890 +++ b/sql/sql_yacc.yy 2010-11-24 17:31:33.000000000 +0300
2892 %token CHECK_SYM /* SQL-2003-R */
2895 +%token CLIENT_STATS_SYM
2896 %token CLOSE_SYM /* SQL-2003-R */
2897 %token COALESCE /* SQL-2003-N */
2903 +%token INDEX_STATS_SYM
2905 %token INITIAL_SIZE_SYM
2906 %token INNER_SYM /* SQL-2003-R */
2907 @@ -1144,6 +1146,7 @@
2909 %token SIMPLE_SYM /* SQL-2003-N */
2912 %token SMALLINT /* SQL-2003-R */
2915 @@ -1189,6 +1192,7 @@
2917 %token TABLE_REF_PRIORITY
2918 %token TABLE_SYM /* SQL-2003-R */
2919 +%token TABLE_STATS_SYM
2920 %token TABLE_CHECKSUM_SYM
2921 %token TEMPORARY /* SQL-2003-N */
2922 %token TEMPTABLE_SYM
2923 @@ -1197,6 +1201,7 @@
2926 %token THEN_SYM /* SQL-2003-R */
2927 +%token THREAD_STATS_SYM
2928 %token TIMESTAMP /* SQL-2003-R */
2929 %token TIMESTAMP_ADD
2930 %token TIMESTAMP_DIFF
2931 @@ -1234,6 +1239,7 @@
2933 %token USAGE /* SQL-2003-N */
2934 %token USER /* SQL-2003-R */
2935 +%token USER_STATS_SYM
2938 %token USING /* SQL-2003-R */
2939 @@ -10346,6 +10352,41 @@
2941 Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
2943 + | CLIENT_STATS_SYM wild_and_where
2946 + Lex->sql_command = SQLCOM_SELECT;
2947 + if (prepare_schema_table(YYTHD, lex, 0, SCH_CLIENT_STATS))
2950 + | USER_STATS_SYM wild_and_where
2953 + lex->sql_command = SQLCOM_SELECT;
2954 + if (prepare_schema_table(YYTHD, lex, 0, SCH_USER_STATS))
2957 + | THREAD_STATS_SYM wild_and_where
2960 + Lex->sql_command = SQLCOM_SELECT;
2961 + if (prepare_schema_table(YYTHD, lex, 0, SCH_THREAD_STATS))
2964 + | TABLE_STATS_SYM wild_and_where
2967 + lex->sql_command= SQLCOM_SELECT;
2968 + if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_STATS))
2971 + | INDEX_STATS_SYM wild_and_where
2974 + lex->sql_command= SQLCOM_SELECT;
2975 + if (prepare_schema_table(YYTHD, lex, 0, SCH_INDEX_STATS))
2978 | CREATE PROCEDURE sp_name
2981 @@ -10554,6 +10595,18 @@
2982 { Lex->type|= REFRESH_STATUS; }
2984 { Lex->type|= REFRESH_SLAVE; }
2985 + | SLOW_SYM QUERY_SYM LOGS_SYM
2986 + { Lex->type |= REFRESH_SLOW_QUERY_LOG; }
2987 + | CLIENT_STATS_SYM
2988 + { Lex->type|= REFRESH_CLIENT_STATS; }
2990 + { Lex->type|= REFRESH_USER_STATS; }
2991 + | THREAD_STATS_SYM
2992 + { Lex->type|= REFRESH_THREAD_STATS; }
2994 + { Lex->type|= REFRESH_TABLE_STATS; }
2996 + { Lex->type|= REFRESH_INDEX_STATS; }
2998 { Lex->type|= REFRESH_MASTER; }
3000 @@ -11677,6 +11730,7 @@
3004 + | CLIENT_STATS_SYM {}
3008 @@ -11738,6 +11792,7 @@
3012 + | INDEX_STATS_SYM {}
3016 @@ -11862,6 +11917,7 @@
3024 @@ -11881,6 +11937,7 @@
3028 + | TABLE_STATS_SYM {}
3030 | TABLE_CHECKSUM_SYM {}
3032 @@ -11888,6 +11945,7 @@
3036 + | THREAD_STATS_SYM {}
3037 | TRANSACTION_SYM {}
3040 @@ -11905,6 +11963,7 @@
3044 + | USER_STATS_SYM {}
3048 diff -ruN a/sql/structs.h b/sql/structs.h
3049 --- a/sql/structs.h 2010-10-12 00:34:34.000000000 +0400
3050 +++ b/sql/structs.h 2010-11-24 17:24:52.000000000 +0300
3051 @@ -237,6 +237,171 @@
3052 USER_RESOURCES user_resources;
3055 +typedef struct st_user_stats {
3056 + char user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1];
3057 + // Account name the user is mapped to when this is a user from mapped_user.
3058 + // Otherwise, the same value as user.
3059 + char priv_user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1];
3060 + uint total_connections;
3061 + uint concurrent_connections;
3062 + time_t connected_time; // in seconds
3063 + double busy_time; // in seconds
3064 + double cpu_time; // in seconds
3065 + ulonglong bytes_received;
3066 + ulonglong bytes_sent;
3067 + ulonglong binlog_bytes_written;
3068 + ha_rows rows_fetched, rows_updated, rows_read;
3069 + ulonglong select_commands, update_commands, other_commands;
3070 + ulonglong commit_trans, rollback_trans;
3071 + ulonglong denied_connections, lost_connections;
3072 + ulonglong access_denied_errors;
3073 + ulonglong empty_queries;
3076 +/* Lookup function for hash tables with USER_STATS entries */
3077 +extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
3078 + my_bool not_used __attribute__((unused)));
3080 +/* Free all memory for a hash table with USER_STATS entries */
3081 +extern void free_user_stats(USER_STATS* user_stats);
3083 +/* Intialize an instance of USER_STATS */
3085 +init_user_stats(USER_STATS *user_stats,
3087 + const char *priv_user,
3088 + uint total_connections,
3089 + uint concurrent_connections,
3090 + time_t connected_time,
3093 + ulonglong bytes_received,
3094 + ulonglong bytes_sent,
3095 + ulonglong binlog_bytes_written,
3096 + ha_rows rows_fetched,
3097 + ha_rows rows_updated,
3098 + ha_rows rows_read,
3099 + ulonglong select_commands,
3100 + ulonglong update_commands,
3101 + ulonglong other_commands,
3102 + ulonglong commit_trans,
3103 + ulonglong rollback_trans,
3104 + ulonglong denied_connections,
3105 + ulonglong lost_connections,
3106 + ulonglong access_denied_errors,
3107 + ulonglong empty_queries);
3109 +/* Increment values of an instance of USER_STATS */
3111 +add_user_stats(USER_STATS *user_stats,
3112 + uint total_connections,
3113 + uint concurrent_connections,
3114 + time_t connected_time,
3117 + ulonglong bytes_received,
3118 + ulonglong bytes_sent,
3119 + ulonglong binlog_bytes_written,
3120 + ha_rows rows_fetched,
3121 + ha_rows rows_updated,
3122 + ha_rows rows_read,
3123 + ulonglong select_commands,
3124 + ulonglong update_commands,
3125 + ulonglong other_commands,
3126 + ulonglong commit_trans,
3127 + ulonglong rollback_trans,
3128 + ulonglong denied_connections,
3129 + ulonglong lost_connections,
3130 + ulonglong access_denied_errors,
3131 + ulonglong empty_queries);
3133 +typedef struct st_thread_stats {
3135 + uint total_connections;
3136 + uint concurrent_connections;
3137 + time_t connected_time; // in seconds
3138 + double busy_time; // in seconds
3139 + double cpu_time; // in seconds
3140 + ulonglong bytes_received;
3141 + ulonglong bytes_sent;
3142 + ulonglong binlog_bytes_written;
3143 + ha_rows rows_fetched, rows_updated, rows_read;
3144 + ulonglong select_commands, update_commands, other_commands;
3145 + ulonglong commit_trans, rollback_trans;
3146 + ulonglong denied_connections, lost_connections;
3147 + ulonglong access_denied_errors;
3148 + ulonglong empty_queries;
3151 +/* Lookup function for hash tables with THREAD_STATS entries */
3152 +extern "C" uchar *get_key_thread_stats(THREAD_STATS *thread_stats, size_t *length,
3153 + my_bool not_used __attribute__((unused)));
3155 +/* Free all memory for a hash table with THREAD_STATS entries */
3156 +extern void free_thread_stats(THREAD_STATS* thread_stats);
3158 +/* Intialize an instance of THREAD_STATS */
3160 +init_thread_stats(THREAD_STATS *thread_stats,
3162 + uint total_connections,
3163 + uint concurrent_connections,
3164 + time_t connected_time,
3167 + ulonglong bytes_received,
3168 + ulonglong bytes_sent,
3169 + ulonglong binlog_bytes_written,
3170 + ha_rows rows_fetched,
3171 + ha_rows rows_updated,
3172 + ha_rows rows_read,
3173 + ulonglong select_commands,
3174 + ulonglong update_commands,
3175 + ulonglong other_commands,
3176 + ulonglong commit_trans,
3177 + ulonglong rollback_trans,
3178 + ulonglong denied_connections,
3179 + ulonglong lost_connections,
3180 + ulonglong access_denied_errors,
3181 + ulonglong empty_queries);
3183 +/* Increment values of an instance of THREAD_STATS */
3185 +add_thread_stats(THREAD_STATS *thread_stats,
3186 + uint total_connections,
3187 + uint concurrent_connections,
3188 + time_t connected_time,
3191 + ulonglong bytes_received,
3192 + ulonglong bytes_sent,
3193 + ulonglong binlog_bytes_written,
3194 + ha_rows rows_fetched,
3195 + ha_rows rows_updated,
3196 + ha_rows rows_read,
3197 + ulonglong select_commands,
3198 + ulonglong update_commands,
3199 + ulonglong other_commands,
3200 + ulonglong commit_trans,
3201 + ulonglong rollback_trans,
3202 + ulonglong denied_connections,
3203 + ulonglong lost_connections,
3204 + ulonglong access_denied_errors,
3205 + ulonglong empty_queries);
3207 +typedef struct st_table_stats {
3208 + char table[NAME_LEN * 2 + 2]; // [db] + '.' + [table] + '\0'
3209 + ulonglong rows_read, rows_changed;
3210 + ulonglong rows_changed_x_indexes;
3211 + /* Stores enum db_type, but forward declarations cannot be done */
3215 +typedef struct st_index_stats {
3216 + char index[NAME_LEN * 3 + 3]; // [db] + '.' + [table] + '.' + [index] + '\0'
3217 + ulonglong rows_read;
3220 /* Bits in form->update */
3221 #define REG_MAKE_DUPP 1 /* Make a copy of record when read */
3222 #define REG_NEW_RECORD 2 /* Write a new record if not found */
3223 diff -ruN a/sql/table.h b/sql/table.h
3224 --- a/sql/table.h 2010-10-12 00:34:16.000000000 +0400
3225 +++ b/sql/table.h 2010-11-24 17:31:33.000000000 +0300
3226 @@ -944,10 +944,12 @@
3227 enum enum_schema_tables
3232 SCH_COLLATION_CHARACTER_SET_APPLICABILITY,
3234 SCH_COLUMN_PRIVILEGES,
3239 @@ -971,8 +973,11 @@
3240 SCH_TABLE_CONSTRAINTS,
3242 SCH_TABLE_PRIVILEGES,
3246 SCH_USER_PRIVILEGES,
3251 diff -ruN a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
3252 --- a/storage/innobase/handler/ha_innodb.cc 2010-10-12 00:34:15.000000000 +0400
3253 +++ b/storage/innobase/handler/ha_innodb.cc 2010-11-24 17:24:52.000000000 +0300
3254 @@ -4055,6 +4055,8 @@
3256 error = row_insert_for_mysql((byte*) record, prebuilt);
3258 + if (error == DB_SUCCESS) rows_changed++;
3260 /* Handle duplicate key errors */
3261 if (auto_inc_used) {
3263 @@ -4392,6 +4394,8 @@
3267 + if (error == DB_SUCCESS) rows_changed++;
3269 innodb_srv_conc_exit_innodb(trx);
3271 error = convert_error_code_to_mysql(error, user_thd);
3272 @@ -4444,6 +4448,8 @@
3274 error = row_update_for_mysql((byte*) record, prebuilt);
3276 + if (error == DB_SUCCESS) rows_changed++;
3278 innodb_srv_conc_exit_innodb(trx);
3280 error = convert_error_code_to_mysql(error, user_thd);
3281 @@ -4923,6 +4929,9 @@
3282 if (ret == DB_SUCCESS) {
3286 + if (active_index >= 0 && active_index < MAX_KEY)
3287 + index_rows_read[active_index]++;
3289 } else if (ret == DB_RECORD_NOT_FOUND) {
3290 error = HA_ERR_END_OF_FILE;
3291 diff -ruN a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc
3292 --- a/storage/myisam/ha_myisam.cc 2010-10-12 00:34:25.000000000 +0400
3293 +++ b/storage/myisam/ha_myisam.cc 2010-11-24 17:24:52.000000000 +0300
3296 int ha_myisam::write_row(uchar *buf)
3299 ha_statistic_increment(&SSV::ha_write_count);
3301 /* If we have a timestamp column, update it to the current time */
3302 @@ -773,11 +774,12 @@
3304 if (table->next_number_field && buf == table->record[0])
3307 if ((error= update_auto_increment()))
3310 - return mi_write(file,buf);
3311 + error=mi_write(file,buf);
3312 + if (!error) rows_changed++;
3316 int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
3317 @@ -1638,16 +1640,22 @@
3319 int ha_myisam::update_row(const uchar *old_data, uchar *new_data)
3322 ha_statistic_increment(&SSV::ha_update_count);
3323 if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
3324 table->timestamp_field->set_time();
3325 - return mi_update(file,old_data,new_data);
3326 + error=mi_update(file,old_data,new_data);
3327 + if (!error) rows_changed++;
3331 int ha_myisam::delete_row(const uchar *buf)
3334 ha_statistic_increment(&SSV::ha_delete_count);
3335 - return mi_delete(file,buf);
3336 + error=mi_delete(file,buf);
3337 + if (!error) rows_changed++;
3341 int ha_myisam::index_read_map(uchar *buf, const uchar *key,
3342 @@ -1658,6 +1666,13 @@
3343 ha_statistic_increment(&SSV::ha_read_key_count);
3344 int error=mi_rkey(file, buf, active_index, key, keypart_map, find_flag);
3345 table->status=error ? STATUS_NOT_FOUND: 0;
3349 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3350 + if (inx >= 0 && inx < MAX_KEY)
3351 + index_rows_read[inx]++;
3356 @@ -1668,6 +1683,13 @@
3357 ha_statistic_increment(&SSV::ha_read_key_count);
3358 int error=mi_rkey(file, buf, index, key, keypart_map, find_flag);
3359 table->status=error ? STATUS_NOT_FOUND: 0;
3364 + if (inx >= 0 && inx < MAX_KEY)
3365 + index_rows_read[inx]++;
3370 @@ -1680,6 +1702,13 @@
3371 int error=mi_rkey(file, buf, active_index, key, keypart_map,
3372 HA_READ_PREFIX_LAST);
3373 table->status=error ? STATUS_NOT_FOUND: 0;
3377 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3378 + if (inx >= 0 && inx < MAX_KEY)
3379 + index_rows_read[inx]++;
3384 @@ -1689,6 +1718,13 @@
3385 ha_statistic_increment(&SSV::ha_read_next_count);
3386 int error=mi_rnext(file,buf,active_index);
3387 table->status=error ? STATUS_NOT_FOUND: 0;
3391 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3392 + if (inx >= 0 && inx < MAX_KEY)
3393 + index_rows_read[inx]++;
3398 @@ -1698,6 +1734,13 @@
3399 ha_statistic_increment(&SSV::ha_read_prev_count);
3400 int error=mi_rprev(file,buf, active_index);
3401 table->status=error ? STATUS_NOT_FOUND: 0;
3405 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3406 + if (inx >= 0 && inx < MAX_KEY)
3407 + index_rows_read[inx]++;
3412 @@ -1707,6 +1750,13 @@
3413 ha_statistic_increment(&SSV::ha_read_first_count);
3414 int error=mi_rfirst(file, buf, active_index);
3415 table->status=error ? STATUS_NOT_FOUND: 0;
3419 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3420 + if (inx >= 0 && inx < MAX_KEY)
3421 + index_rows_read[inx]++;
3426 @@ -1716,6 +1766,13 @@
3427 ha_statistic_increment(&SSV::ha_read_last_count);
3428 int error=mi_rlast(file, buf, active_index);
3429 table->status=error ? STATUS_NOT_FOUND: 0;
3433 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3434 + if (inx >= 0 && inx < MAX_KEY)
3435 + index_rows_read[inx]++;
3440 @@ -1731,6 +1788,13 @@
3441 error= mi_rnext_same(file,buf);
3442 } while (error == HA_ERR_RECORD_DELETED);
3443 table->status=error ? STATUS_NOT_FOUND: 0;
3447 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3448 + if (inx >= 0 && inx < MAX_KEY)
3449 + index_rows_read[inx]++;
3454 @@ -1747,6 +1811,7 @@
3455 ha_statistic_increment(&SSV::ha_read_rnd_next_count);
3456 int error=mi_scan(file, buf);
3457 table->status=error ? STATUS_NOT_FOUND: 0;
3458 + if (!error) rows_read++;
3462 @@ -1760,6 +1825,7 @@
3463 ha_statistic_increment(&SSV::ha_read_rnd_count);
3464 int error=mi_rrnd(file, buf, my_get_ptr(pos,ref_length));
3465 table->status=error ? STATUS_NOT_FOUND: 0;
3466 + if (!error) rows_read++;