1 # name : userstat.patch
2 # introduced : 11 or before
6 # Any small change to this file in the main branch
7 # should be done or reviewed by the maintainer!
8 diff -ruN a/include/mysql/plugin.h b/include/mysql/plugin.h
9 --- a/include/mysql/plugin.h 2010-12-03 20:58:24.000000000 +0300
10 +++ b/include/mysql/plugin.h 2010-12-31 06:06:43.000000000 +0300
12 unsigned long thd_log_slow_verbosity(const MYSQL_THD thd);
13 int thd_opt_slow_log();
14 #define EXTENDED_SLOWLOG
16 +#define EXTENDED_FOR_USERSTAT
19 Create a temporary file.
21 diff -ruN a/include/mysql_com.h b/include/mysql_com.h
22 --- a/include/mysql_com.h 2010-12-03 20:58:24.000000000 +0300
23 +++ b/include/mysql_com.h 2010-12-31 06:12:05.000000000 +0300
26 #define SERVER_VERSION_LENGTH 60
27 #define SQLSTATE_LENGTH 5
28 +#define LIST_PROCESS_HOST_LEN 64
31 Maximum length of comments
33 #define REFRESH_DES_KEY_FILE 0x40000L
34 #define REFRESH_USER_RESOURCES 0x80000L
35 #define REFRESH_QUERY_RESPONSE_TIME 0x100000L /* response time distibution */
36 +#define REFRESH_TABLE_STATS 0x200000L /* Refresh table stats my_hash table */
37 +#define REFRESH_INDEX_STATS 0x400000L /* Refresh index stats my_hash table */
38 +#define REFRESH_USER_STATS 0x800000L /* Refresh user stats my_hash table */
39 +#define REFRESH_CLIENT_STATS 0x1000000L /* Refresh client stats my_hash table */
40 +#define REFRESH_THREAD_STATS 0x2000000L /* Refresh thread stats my_hash table */
42 #define CLIENT_LONG_PASSWORD 1 /* new more secure passwords */
43 #define CLIENT_FOUND_ROWS 2 /* Found instead of affected rows */
44 diff -ruN a/patch_info/userstats.patch b/patch_info/userstats.patch
45 --- a/patch_info/userstats.patch 1970-01-01 03:00:00.000000000 +0300
46 +++ b/patch_info/userstats.patch 2010-12-30 00:45:46.000000000 +0300
49 +Name=SHOW USER/TABLE/INDEX statistics
53 +Comment=Added INFORMATION_SCHEMA.*_STATISTICS
55 +YK: fix behavior for prepared statements
58 +YK: add switch variable "userstat_running" to control INFORMATION_SCHEMA.*_STATISTICS (default:OFF)
63 diff -ruN a/sql/handler.cc b/sql/handler.cc
64 --- a/sql/handler.cc 2010-12-03 20:58:26.000000000 +0300
65 +++ b/sql/handler.cc 2010-12-30 00:59:23.000000000 +0300
69 DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
71 + thd->diff_commit_trans++;
72 RUN_HOOK(transaction, after_commit, (thd, FALSE));
74 if (rw_trans && mdl_request.ticket)
76 /* Always cleanup. Even if nht==0. There may be savepoints. */
78 thd->transaction.cleanup();
80 + thd->diff_rollback_trans++;
82 thd->transaction_rollback_request= FALSE;
85 ha_info->reset(); /* keep it conveniently zero-filled */
87 trans->ha_list= sv->ha_list;
88 + thd->diff_rollback_trans++;
93 dup_ref=ref+ALIGN_SIZE(ref_length);
94 cached_table_flags= table_flags();
96 + rows_read= rows_changed= 0;
97 + memset(index_rows_read, 0, sizeof(index_rows_read));
101 @@ -3614,6 +3621,127 @@
105 +// Updates the global table stats with the TABLE this handler represents.
106 +void handler::update_global_table_stats()
108 + if (!opt_userstat_running)
110 + rows_read= rows_changed= 0;
114 + if (!rows_read && !rows_changed)
115 + return; // Nothing to update.
116 + // table_cache_key is db_name + '\0' + table_name + '\0'.
117 + if (!table->s || !table->s->table_cache_key.str || !table->s->table_name.str)
120 + TABLE_STATS* table_stats;
121 + char key[NAME_LEN * 2 + 2];
122 + // [db] + '.' + [table]
123 + sprintf(key, "%s.%s", table->s->table_cache_key.str, table->s->table_name.str);
125 + mysql_mutex_lock(&LOCK_global_table_stats);
126 + // Gets the global table stats, creating one if necessary.
127 + if (!(table_stats = (TABLE_STATS *) my_hash_search(&global_table_stats,
131 + if (!(table_stats = ((TABLE_STATS *)
132 + my_malloc(sizeof(TABLE_STATS), MYF(MY_WME | MY_ZEROFILL)))))
135 + sql_print_error("Allocating table stats failed.");
138 + strncpy(table_stats->table, key, sizeof(table_stats->table));
139 + table_stats->rows_read= 0;
140 + table_stats->rows_changed= 0;
141 + table_stats->rows_changed_x_indexes= 0;
142 + table_stats->engine_type= (int) ht->db_type;
144 + if (my_hash_insert(&global_table_stats, (uchar *) table_stats))
147 + sql_print_error("Inserting table stats failed.");
148 + my_free((char *) table_stats);
152 + // Updates the global table stats.
153 + table_stats->rows_read+= rows_read;
154 + table_stats->rows_changed+= rows_changed;
155 + table_stats->rows_changed_x_indexes+=
156 + rows_changed * (table->s->keys ? table->s->keys : 1);
157 + current_thd->diff_total_read_rows+= rows_read;
158 + rows_read= rows_changed= 0;
160 + mysql_mutex_unlock(&LOCK_global_table_stats);
163 +// Updates the global index stats with this handler's accumulated index reads.
164 +void handler::update_global_index_stats()
166 + // table_cache_key is db_name + '\0' + table_name + '\0'.
167 + if (!table->s || !table->s->table_cache_key.str || !table->s->table_name.str)
170 + if (!opt_userstat_running)
172 + for (uint x= 0; x < table->s->keys; ++x)
174 + index_rows_read[x]= 0;
179 + for (uint x = 0; x < table->s->keys; ++x)
181 + if (index_rows_read[x])
183 + // Rows were read using this index.
184 + KEY* key_info = &table->key_info[x];
186 + if (!key_info->name) continue;
188 + INDEX_STATS* index_stats;
189 + char key[NAME_LEN * 3 + 3];
190 + // [db] + '.' + [table] + '.' + [index]
191 + sprintf(key, "%s.%s.%s", table->s->table_cache_key.str,
192 + table->s->table_name.str, key_info->name);
194 + mysql_mutex_lock(&LOCK_global_index_stats);
195 + // Gets the global index stats, creating one if necessary.
196 + if (!(index_stats = (INDEX_STATS *) my_hash_search(&global_index_stats,
200 + if (!(index_stats = ((INDEX_STATS *)
201 + my_malloc(sizeof(INDEX_STATS), MYF(MY_WME | MY_ZEROFILL)))))
204 + sql_print_error("Allocating index stats failed.");
207 + strncpy(index_stats->index, key, sizeof(index_stats->index));
208 + index_stats->rows_read= 0;
210 + if (my_hash_insert(&global_index_stats, (uchar *) index_stats))
213 + sql_print_error("Inserting index stats failed.");
214 + my_free((char *) index_stats);
218 + // Updates the global index stats.
219 + index_stats->rows_read+= index_rows_read[x];
220 + index_rows_read[x]= 0;
222 + mysql_mutex_unlock(&LOCK_global_index_stats);
227 /****************************************************************************
228 ** Some general functions that isn't in the handler class
229 diff -ruN a/sql/handler.h b/sql/handler.h
230 --- a/sql/handler.h 2010-12-03 20:58:26.000000000 +0300
231 +++ b/sql/handler.h 2010-12-31 05:10:00.000000000 +0300
233 #include <ft_global.h>
234 #include <keycache.h>
237 +#error MAX_KEY is too large. Values up to 128 are supported.
240 // the following is for checking tables
242 #define HA_ADMIN_ALREADY_DONE 1
243 @@ -561,10 +565,12 @@
244 enum enum_schema_tables
249 SCH_COLLATION_CHARACTER_SET_APPLICABILITY,
251 SCH_COLUMN_PRIVILEGES,
257 SCH_TABLE_CONSTRAINTS,
259 SCH_TABLE_PRIVILEGES,
261 SCH_TEMPORARY_TABLES,
269 @@ -1209,6 +1218,9 @@
271 bool implicit_emptied; /* Can be !=0 only if HEAP */
272 const COND *pushed_cond;
273 + ulonglong rows_read;
274 + ulonglong rows_changed;
275 + ulonglong index_rows_read[MAX_KEY];
277 next_insert_id is the next value which should be inserted into the
278 auto_increment column: in a inserting-multi-row statement (like INSERT
279 @@ -1260,10 +1272,12 @@
280 ref_length(sizeof(my_off_t)),
281 ft_handler(0), inited(NONE),
282 locked(FALSE), implicit_emptied(0),
283 - pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
284 + pushed_cond(0), rows_read(0), rows_changed(0), next_insert_id(0), insert_id_for_cur_row(0),
285 auto_inc_intervals_count(0),
289 + memset(index_rows_read, 0, sizeof(index_rows_read));
291 virtual ~handler(void)
293 DBUG_ASSERT(locked == FALSE);
294 @@ -1386,6 +1400,8 @@
298 + rows_read = rows_changed= 0;
299 + memset(index_rows_read, 0, sizeof(index_rows_read));
301 virtual double scan_time()
302 { return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; }
303 @@ -1753,6 +1769,8 @@
304 virtual bool is_crashed() const { return 0; }
305 virtual bool auto_repair() const { return 0; }
307 + void update_global_table_stats();
308 + void update_global_index_stats();
310 #define CHF_CREATE_FLAG 0
311 #define CHF_DELETE_FLAG 1
312 diff -ruN a/sql/lex.h b/sql/lex.h
313 --- a/sql/lex.h 2010-12-03 20:58:26.000000000 +0300
314 +++ b/sql/lex.h 2010-12-30 01:25:40.000000000 +0300
316 { "CIPHER", SYM(CIPHER_SYM)},
317 { "CLASS_ORIGIN", SYM(CLASS_ORIGIN_SYM)},
318 { "CLIENT", SYM(CLIENT_SYM)},
319 + { "CLIENT_STATISTICS", SYM(CLIENT_STATS_SYM)},
320 { "CLOSE", SYM(CLOSE_SYM)},
321 { "COALESCE", SYM(COALESCE)},
322 { "CODE", SYM(CODE_SYM)},
324 { "IN", SYM(IN_SYM)},
325 { "INDEX", SYM(INDEX_SYM)},
326 { "INDEXES", SYM(INDEXES)},
327 + { "INDEX_STATISTICS", SYM(INDEX_STATS_SYM)},
328 { "INFILE", SYM(INFILE)},
329 { "INITIAL_SIZE", SYM(INITIAL_SIZE_SYM)},
330 { "INNER", SYM(INNER_SYM)},
331 @@ -550,12 +552,14 @@
332 { "TABLES", SYM(TABLES)},
333 { "TABLESPACE", SYM(TABLESPACE)},
334 { "TABLE_CHECKSUM", SYM(TABLE_CHECKSUM_SYM)},
335 + { "TABLE_STATISTICS", SYM(TABLE_STATS_SYM)},
336 { "TEMPORARY", SYM(TEMPORARY)},
337 { "TEMPTABLE", SYM(TEMPTABLE_SYM)},
338 { "TERMINATED", SYM(TERMINATED)},
339 { "TEXT", SYM(TEXT_SYM)},
340 { "THAN", SYM(THAN_SYM)},
341 { "THEN", SYM(THEN_SYM)},
342 + { "THREAD_STATISTICS", SYM(THREAD_STATS_SYM)},
343 { "TIME", SYM(TIME_SYM)},
344 { "TIMESTAMP", SYM(TIMESTAMP)},
345 { "TIMESTAMPADD", SYM(TIMESTAMP_ADD)},
347 { "USE", SYM(USE_SYM)},
348 { "USER", SYM(USER)},
349 { "USER_RESOURCES", SYM(RESOURCES)},
350 + { "USER_STATISTICS", SYM(USER_STATS_SYM)},
351 { "USE_FRM", SYM(USE_FRM)},
352 { "USING", SYM(USING)},
353 { "UTC_DATE", SYM(UTC_DATE_SYM)},
354 diff -ruN a/sql/log.cc b/sql/log.cc
355 --- a/sql/log.cc 2010-12-03 20:58:26.000000000 +0300
356 +++ b/sql/log.cc 2010-12-30 01:55:35.000000000 +0300
357 @@ -1007,6 +1007,13 @@
358 mysql_slow_log.reopen_file();
361 +void Log_to_file_event_handler::flush_slow_log()
363 + /* reopen slow log file */
365 + mysql_slow_log.reopen_file();
369 Log error with all enabled log event handlers
371 @@ -5041,6 +5048,8 @@
372 thd->first_successful_insert_id_in_prev_stmt_for_binlog);
375 + if (file == &log_file)
376 + thd->binlog_bytes_written+= e.data_written;
378 if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
380 @@ -5052,12 +5061,16 @@
384 + if (file == &log_file)
385 + thd->binlog_bytes_written+= e.data_written;
389 Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2);
392 + if (file == &log_file)
393 + thd->binlog_bytes_written+= e.data_written;
395 if (thd->user_var_events.elements)
397 @@ -5080,6 +5093,8 @@
401 + if (file == &log_file)
402 + thd->binlog_bytes_written+= e.data_written;
406 @@ -5091,6 +5106,8 @@
407 if (event_info->write(file) ||
408 DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0))
410 + if (file == &log_file)
411 + thd->binlog_bytes_written+= event_info->data_written;
415 @@ -5276,7 +5293,8 @@
416 be reset as a READ_CACHE to be able to read the contents from it.
419 -int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
420 +int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache,
421 + bool lock_log, bool sync_log)
423 Mutex_sentry sentry(lock_log ? &LOCK_log : NULL);
425 @@ -5323,6 +5341,7 @@
426 /* write the first half of the split header */
427 if (my_b_write(&log_file, header, carry))
428 return ER_ERROR_ON_WRITE;
429 + thd->binlog_bytes_written+= carry;
432 copy fixed second half of header to cache so the correct
433 @@ -5391,6 +5410,7 @@
434 /* Write data to the binary log file */
435 if (my_b_write(&log_file, cache->read_pos, length))
436 return ER_ERROR_ON_WRITE;
437 + thd->binlog_bytes_written+= length;
438 cache->read_pos=cache->read_end; // Mark buffer used up
439 } while ((length= my_b_fill(cache)));
441 @@ -5505,20 +5525,23 @@
442 Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), TRUE, FALSE, TRUE, 0);
443 if (qinfo.write(&log_file))
445 + thd->binlog_bytes_written+= qinfo.data_written;
446 DBUG_EXECUTE_IF("crash_before_writing_xid",
448 - if ((write_error= write_cache(cache, false, true)))
449 + if ((write_error= write_cache(thd, cache, false, true)))
450 DBUG_PRINT("info", ("error writing binlog cache: %d",
452 DBUG_PRINT("info", ("crashing before writing xid"));
456 - if ((write_error= write_cache(cache, false, false)))
457 + if ((write_error= write_cache(thd, cache, false, false)))
460 if (commit_event && commit_event->write(&log_file))
463 + thd->binlog_bytes_written+= commit_event->data_written;
465 if (incident && write_incident(thd, FALSE))
467 diff -ruN a/sql/log.h b/sql/log.h
468 --- a/sql/log.h 2010-12-03 20:58:26.000000000 +0300
469 +++ b/sql/log.h 2010-12-30 01:56:04.000000000 +0300
471 bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event, bool incident);
472 bool write_incident(THD *thd, bool lock);
474 - int write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync);
475 + int write_cache(THD *thd, IO_CACHE *cache,
476 + bool lock_log, bool flush_and_sync);
477 void set_write_error(THD *thd, bool is_transactional);
478 bool check_write_error(THD *thd);
481 const char *sql_text, uint sql_text_len,
482 CHARSET_INFO *client_cs);
484 + void flush_slow_log();
485 void init_pthread_objects();
486 MYSQL_QUERY_LOG *get_mysql_slow_log() { return &mysql_slow_log; }
487 MYSQL_QUERY_LOG *get_mysql_log() { return &mysql_log; }
488 diff -ruN a/sql/mysqld.cc b/sql/mysqld.cc
489 --- a/sql/mysqld.cc 2010-12-03 20:58:26.000000000 +0300
490 +++ b/sql/mysqld.cc 2010-12-30 02:04:50.000000000 +0300
492 uint opt_debug_sync_timeout= 0;
493 #endif /* defined(ENABLED_DEBUG_SYNC) */
494 my_bool opt_old_style_user_limits= 0, trust_function_creators= 0;
495 +my_bool opt_userstat_running= 0, opt_thread_statistics= 0;
496 my_bool opt_optimizer_fix= 0;
498 True if there is at least one per-hour limit for some user, so we should
500 ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
501 ulong binlog_stmt_cache_use= 0, binlog_stmt_cache_disk_use= 0;
502 ulong max_connections, max_connect_errors;
503 +ulonglong denied_connections= 0;
509 LOCK_global_system_variables,
510 LOCK_user_conn, LOCK_slave_list, LOCK_active_mi,
511 - LOCK_connection_count, LOCK_error_messages;
512 + LOCK_connection_count, LOCK_error_messages,
513 + LOCK_stats, LOCK_global_user_client_stats,
514 + LOCK_global_table_stats, LOCK_global_index_stats;
516 The below lock protects access to two global server variables:
517 max_prepared_stmt_count and prepared_stmt_count. These variables
518 @@ -1493,6 +1497,11 @@
519 #ifdef HAVE_RESPONSE_TIME_DISTRIBUTION
520 query_response_time_free();
521 #endif // HAVE_RESPONSE_TIME_DISTRIBUTION
522 + free_global_user_stats();
523 + free_global_client_stats();
524 + free_global_thread_stats();
525 + free_global_table_stats();
526 + free_global_index_stats();
527 #ifdef HAVE_REPLICATION
530 @@ -1596,6 +1605,10 @@
531 mysql_cond_destroy(&COND_thread_cache);
532 mysql_cond_destroy(&COND_flush_thread_cache);
533 mysql_cond_destroy(&COND_manager);
534 + mysql_mutex_destroy(&LOCK_stats);
535 + mysql_mutex_destroy(&LOCK_global_user_client_stats);
536 + mysql_mutex_destroy(&LOCK_global_table_stats);
537 + mysql_mutex_destroy(&LOCK_global_index_stats);
539 #endif /*EMBEDDED_LIBRARY*/
541 @@ -3019,6 +3032,7 @@
542 {"show_binlog_events", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOG_EVENTS]), SHOW_LONG_STATUS},
543 {"show_binlogs", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOGS]), SHOW_LONG_STATUS},
544 {"show_charsets", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CHARSETS]), SHOW_LONG_STATUS},
545 + {"show_client_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CLIENT_STATS]), SHOW_LONG_STATUS},
546 {"show_collations", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLLATIONS]), SHOW_LONG_STATUS},
547 {"show_contributors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CONTRIBUTORS]), SHOW_LONG_STATUS},
548 {"show_create_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_DB]), SHOW_LONG_STATUS},
549 @@ -3039,6 +3053,7 @@
551 {"show_function_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS_FUNC]), SHOW_LONG_STATUS},
552 {"show_grants", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS]), SHOW_LONG_STATUS},
553 + {"show_index_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_INDEX_STATS]), SHOW_LONG_STATUS},
554 {"show_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS},
555 {"show_master_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_MASTER_STAT]), SHOW_LONG_STATUS},
556 {"show_new_master", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_NEW_MASTER]), SHOW_LONG_STATUS},
557 @@ -3058,10 +3073,13 @@
558 {"show_slave_status_nolock", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_NOLOCK_STAT]), SHOW_LONG_STATUS},
559 {"show_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS]), SHOW_LONG_STATUS},
560 {"show_storage_engines", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STORAGE_ENGINES]), SHOW_LONG_STATUS},
561 + {"show_table_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATS]), SHOW_LONG_STATUS},
562 {"show_table_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATUS]), SHOW_LONG_STATUS},
563 {"show_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLES]), SHOW_LONG_STATUS},
564 {"show_temporary_tables",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TEMPORARY_TABLES]), SHOW_LONG_STATUS},
565 + {"show_thread_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_THREAD_STATS]), SHOW_LONG_STATUS},
566 {"show_triggers", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TRIGGERS]), SHOW_LONG_STATUS},
567 + {"show_user_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_USER_STATS]), SHOW_LONG_STATUS},
568 {"show_variables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_VARIABLES]), SHOW_LONG_STATUS},
569 {"show_warnings", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_WARNS]), SHOW_LONG_STATUS},
570 {"slave_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS},
571 @@ -3582,6 +3600,13 @@
572 mysql_mutex_init(key_LOCK_server_started,
573 &LOCK_server_started, MY_MUTEX_INIT_FAST);
574 mysql_cond_init(key_COND_server_started, &COND_server_started, NULL);
575 + mysql_mutex_init(key_LOCK_stats, &LOCK_stats, MY_MUTEX_INIT_FAST);
576 + mysql_mutex_init(key_LOCK_global_user_client_stats,
577 + &LOCK_global_user_client_stats, MY_MUTEX_INIT_FAST);
578 + mysql_mutex_init(key_LOCK_global_table_stats,
579 + &LOCK_global_table_stats, MY_MUTEX_INIT_FAST);
580 + mysql_mutex_init(key_LOCK_global_index_stats,
581 + &LOCK_global_index_stats, MY_MUTEX_INIT_FAST);
583 #ifdef HAVE_EVENT_SCHEDULER
584 Events::init_mutexes();
585 @@ -3951,6 +3976,9 @@
586 query_response_time_init();
587 #endif // HAVE_RESPONSE_TIME_DISTRIBUTION
588 /* We have to initialize the storage engines before CSV logging */
589 + init_global_table_stats();
590 + init_global_index_stats();
594 sql_print_error("Can't init databases");
595 @@ -4087,6 +4115,9 @@
597 init_max_user_conn();
598 init_update_queries();
599 + init_global_user_stats();
600 + init_global_client_stats();
601 + init_global_thread_stats();
605 @@ -5118,6 +5149,7 @@
607 sql_print_warning("%s", ER_DEFAULT(ER_CON_COUNT_ERROR));
609 + statistic_increment(denied_connections, &LOCK_status);
613 @@ -7817,6 +7849,8 @@
614 key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
615 key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
616 key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
617 + key_LOCK_stats, key_LOCK_global_user_client_stats,
618 + key_LOCK_global_table_stats, key_LOCK_global_index_stats,
619 key_LOCK_gdl, key_LOCK_global_system_variables,
621 key_LOCK_prepared_stmt_count,
622 @@ -7854,6 +7888,13 @@
623 { &key_LOCK_delayed_insert, "LOCK_delayed_insert", PSI_FLAG_GLOBAL},
624 { &key_LOCK_delayed_status, "LOCK_delayed_status", PSI_FLAG_GLOBAL},
625 { &key_LOCK_error_log, "LOCK_error_log", PSI_FLAG_GLOBAL},
626 + { &key_LOCK_stats, "LOCK_stats", PSI_FLAG_GLOBAL},
627 + { &key_LOCK_global_user_client_stats,
628 + "LOCK_global_user_client_stats", PSI_FLAG_GLOBAL},
629 + { &key_LOCK_global_table_stats,
630 + "LOCK_global_table_stats", PSI_FLAG_GLOBAL},
631 + { &key_LOCK_global_index_stats,
632 + "LOCK_global_index_stats", PSI_FLAG_GLOBAL},
633 { &key_LOCK_gdl, "LOCK_gdl", PSI_FLAG_GLOBAL},
634 { &key_LOCK_global_system_variables, "LOCK_global_system_variables", PSI_FLAG_GLOBAL},
635 { &key_LOCK_manager, "LOCK_manager", PSI_FLAG_GLOBAL},
636 diff -ruN a/sql/mysqld.h b/sql/mysqld.h
637 --- a/sql/mysqld.h 2010-12-03 20:58:26.000000000 +0300
638 +++ b/sql/mysqld.h 2010-12-31 06:04:59.000000000 +0300
640 #include "my_atomic.h" /* my_atomic_rwlock_t */
641 #include "mysql/psi/mysql_file.h" /* MYSQL_FILE */
642 #include "sql_list.h" /* I_List */
648 extern ulonglong slave_type_conversions_options;
649 extern my_bool read_only, opt_readonly;
650 extern my_bool lower_case_file_system;
651 +extern my_bool opt_userstat_running, opt_thread_statistics;
652 extern my_bool opt_optimizer_fix;
653 extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
654 extern my_bool opt_secure_auth;
656 extern ulong slave_trans_retries;
657 extern uint slave_net_timeout;
658 extern uint max_user_connections;
659 +extern ulonglong denied_connections;
660 extern ulong what_to_log,flush_time;
661 extern ulong max_prepared_stmt_count, prepared_stmt_count;
662 extern ulong open_files_limit;
664 extern struct system_variables max_system_variables;
665 extern struct system_status_var global_status_var;
666 extern struct rand_struct sql_rand;
667 +extern HASH global_user_stats;
668 +extern HASH global_client_stats;
669 +extern HASH global_thread_stats;
670 +extern HASH global_table_stats;
671 +extern HASH global_index_stats;
672 extern const char *opt_date_time_formats[];
673 extern handlerton *partition_hton;
674 extern handlerton *myisam_hton;
676 key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
677 key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
678 key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
679 + key_LOCK_stats, key_LOCK_global_user_client_stats,
680 + key_LOCK_global_table_stats, key_LOCK_global_index_stats,
681 key_LOCK_gdl, key_LOCK_global_system_variables,
682 key_LOCK_logger, key_LOCK_manager,
683 key_LOCK_prepared_stmt_count,
685 LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
686 LOCK_slave_list, LOCK_active_mi, LOCK_manager,
687 LOCK_global_system_variables, LOCK_user_conn,
688 - LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count;
689 + LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count,
690 + LOCK_stats, LOCK_global_user_client_stats,
691 + LOCK_global_table_stats, LOCK_global_index_stats;
692 extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thread_count;
694 extern mysql_mutex_t LOCK_des_key_file;
699 +void init_global_user_stats(void);
700 +void init_global_table_stats(void);
701 +void init_global_index_stats(void);
702 +void init_global_client_stats(void);
703 +void init_global_thread_stats(void);
704 +void free_global_user_stats(void);
705 +void free_global_table_stats(void);
706 +void free_global_index_stats(void);
707 +void free_global_client_stats(void);
708 +void free_global_thread_stats(void);
711 TODO: Replace this with an inline function.
712 diff -ruN a/sql/sql_base.cc b/sql/sql_base.cc
713 --- a/sql/sql_base.cc 2010-12-03 20:58:26.000000000 +0300
714 +++ b/sql/sql_base.cc 2010-12-30 02:33:17.000000000 +0300
715 @@ -1524,6 +1524,11 @@
716 table->mdl_ticket= NULL;
718 mysql_mutex_lock(&thd->LOCK_thd_data);
721 + table->file->update_global_table_stats();
722 + table->file->update_global_index_stats();
724 *table_ptr=table->next;
725 mysql_mutex_unlock(&thd->LOCK_thd_data);
727 @@ -2149,6 +2154,8 @@
728 DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
729 table->s->db.str, table->s->table_name.str));
731 + table->file->update_global_table_stats();
732 + table->file->update_global_index_stats();
733 free_io_cache(table);
736 diff -ruN a/sql/sql_class.cc b/sql/sql_class.cc
737 --- a/sql/sql_class.cc 2010-12-03 20:58:26.000000000 +0300
738 +++ b/sql/sql_class.cc 2010-12-30 02:41:40.000000000 +0300
741 binlog_evt_union.do_union= FALSE;
747 + binlog_bytes_written= 0;
748 + updated_row_count= 0;
749 + sent_row_count_2= 0;
751 dbug_sentry=THD_SENTRY_MAGIC;
754 variables.option_bits|= OPTION_BIN_LOG;
756 variables.option_bits&= ~OPTION_BIN_LOG;
759 #if defined(ENABLED_DEBUG_SYNC)
760 /* Initialize the Debug Sync Facility. See debug_sync.cc. */
762 #endif /* defined(ENABLED_DEBUG_SYNC) */
765 +// Resets stats in a THD.
766 +void THD::reset_stats(void)
768 + current_connect_time= time(NULL);
769 + last_global_update_time= current_connect_time;
770 + reset_diff_stats();
773 +// Resets the 'diff' stats, which are used to update global stats.
774 +void THD::reset_diff_stats(void)
776 + diff_total_busy_time= 0;
777 + diff_total_cpu_time= 0;
778 + diff_total_bytes_received= 0;
779 + diff_total_bytes_sent= 0;
780 + diff_total_binlog_bytes_written= 0;
781 + diff_total_sent_rows= 0;
782 + diff_total_updated_rows= 0;
783 + diff_total_read_rows= 0;
784 + diff_select_commands= 0;
785 + diff_update_commands= 0;
786 + diff_other_commands= 0;
787 + diff_commit_trans= 0;
788 + diff_rollback_trans= 0;
789 + diff_denied_connections= 0;
790 + diff_lost_connections= 0;
791 + diff_access_denied_errors= 0;
792 + diff_empty_queries= 0;
795 +// Updates 'diff' stats of a THD.
796 +void THD::update_stats(bool ran_command)
798 + if (opt_userstat_running)
800 + diff_total_busy_time+= busy_time;
801 + diff_total_cpu_time+= cpu_time;
802 + diff_total_bytes_received+= bytes_received;
803 + diff_total_bytes_sent+= bytes_sent;
804 + diff_total_binlog_bytes_written+= binlog_bytes_written;
805 + diff_total_sent_rows+= sent_row_count_2;
806 + diff_total_updated_rows+= updated_row_count;
807 + // diff_total_read_rows is updated in handler.cc.
811 + // The replication thread has the COM_CONNECT command.
812 + if ((old_command == COM_QUERY || command == COM_CONNECT) &&
813 + (lex->sql_command >= 0 && lex->sql_command < SQLCOM_END))
816 + if (lex->sql_command == SQLCOM_SELECT)
818 + diff_select_commands++;
819 + if (!sent_row_count_2)
820 + diff_empty_queries++;
822 + else if (!sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND)
824 + // 'SHOW ' commands become SQLCOM_SELECT.
825 + diff_other_commands++;
826 + // 'SHOW ' commands shouldn't inflate total sent row count.
827 + diff_total_sent_rows-= sent_row_count_2;
828 + } else if (is_update_query(lex->sql_command)) {
829 + diff_update_commands++;
831 + diff_other_commands++;
835 + // diff_commit_trans is updated in handler.cc.
836 + // diff_rollback_trans is updated in handler.cc.
837 + // diff_denied_connections is updated in sql_parse.cc.
838 + // diff_lost_connections is updated in sql_parse.cc.
839 + // diff_access_denied_errors is updated in sql_parse.cc.
841 + /* reset counters to zero to avoid double-counting since values
842 + are already store in diff_total_*.
849 + binlog_bytes_written= 0;
850 + updated_row_count= 0;
851 + sent_row_count_2= 0;
855 Init THD for query processing.
856 @@ -1722,6 +1818,32 @@
860 +char *THD::get_client_host_port(THD *client)
862 + Security_context *client_sctx= client->security_ctx;
863 + char *client_host= NULL;
865 + if (client->peer_port && (client_sctx->host || client_sctx->ip) &&
866 + security_ctx->host_or_ip[0])
868 + if ((client_host= (char *) this->alloc(LIST_PROCESS_HOST_LEN+1)))
869 + my_snprintf((char *) client_host, LIST_PROCESS_HOST_LEN,
870 + "%s:%u", client_sctx->host_or_ip, client->peer_port);
873 + client_host= this->strdup(client_sctx->host_or_ip[0] ?
874 + client_sctx->host_or_ip :
875 + client_sctx->host ? client_sctx->host : "");
877 + return client_host;
880 +const char *get_client_host(THD *client)
882 + return client->security_ctx->host_or_ip[0] ?
883 + client->security_ctx->host_or_ip :
884 + client->security_ctx->host ? client->security_ctx->host : "";
887 struct Item_change_record: public ilink
889 @@ -1898,6 +2020,7 @@
892 thd->sent_row_count++;
893 + thd->sent_row_count_2++;
896 DBUG_RETURN(protocol->write());
897 @@ -1990,6 +2113,7 @@
898 select_export::~select_export()
900 thd->sent_row_count=row_count;
901 + thd->sent_row_count_2= row_count;
905 @@ -3013,6 +3137,7 @@
906 if (likely(thd != 0))
907 { /* current_thd==0 when close_connection() calls net_send_error() */
908 thd->status_var.bytes_sent+= length;
909 + thd->bytes_sent+= length;
913 @@ -3020,6 +3145,7 @@
914 void thd_increment_bytes_received(ulong length)
916 current_thd->status_var.bytes_received+= length;
917 + current_thd->bytes_received+= length;
921 diff -ruN a/sql/sql_class.h b/sql/sql_class.h
922 --- a/sql/sql_class.h 2010-12-03 20:58:26.000000000 +0300
923 +++ b/sql/sql_class.h 2010-12-31 05:15:57.000000000 +0300
924 @@ -1610,6 +1610,8 @@
926 enum enum_server_command command;
928 + // Used to save the command, before it is set to COM_SLEEP.
929 + enum enum_server_command old_command;
930 uint32 file_id; // for LOAD DATA INFILE
931 /* remote (peer) port */
933 @@ -2081,6 +2083,8 @@
935 enum_tx_isolation tx_isolation;
936 enum_check_fields count_cuted_fields;
937 + ha_rows updated_row_count;
938 + ha_rows sent_row_count_2; /* for userstat */
940 DYNAMIC_ARRAY user_var_events; /* For user variables replication */
941 MEM_ROOT *user_var_events_alloc; /* Allocate above array elements here */
942 @@ -2176,6 +2180,49 @@
944 LOG_INFO* current_linfo;
945 NET* slave_net; // network connection from slave -> m.
948 + Used to update global user stats. The global user stats are updated
949 + occasionally with the 'diff' variables. After the update, the 'diff'
950 + variables are reset to 0.
952 + // Time when the current thread connected to MySQL.
953 + time_t current_connect_time;
954 + // Last time when THD stats were updated in global_user_stats.
955 + time_t last_global_update_time;
956 + // Busy (non-idle) time for just one command.
958 + // Busy time not updated in global_user_stats yet.
959 + double diff_total_busy_time;
960 + // Cpu (non-idle) time for just one thread.
962 + // Cpu time not updated in global_user_stats yet.
963 + double diff_total_cpu_time;
964 + /* bytes counting */
965 + ulonglong bytes_received;
966 + ulonglong diff_total_bytes_received;
967 + ulonglong bytes_sent;
968 + ulonglong diff_total_bytes_sent;
969 + ulonglong binlog_bytes_written;
970 + ulonglong diff_total_binlog_bytes_written;
972 + // Number of rows not reflected in global_user_stats yet.
973 + ha_rows diff_total_sent_rows, diff_total_updated_rows, diff_total_read_rows;
974 + // Number of commands not reflected in global_user_stats yet.
975 + ulonglong diff_select_commands, diff_update_commands, diff_other_commands;
976 + // Number of transactions not reflected in global_user_stats yet.
977 + ulonglong diff_commit_trans, diff_rollback_trans;
978 + // Number of connection errors not reflected in global_user_stats yet.
979 + ulonglong diff_denied_connections, diff_lost_connections;
980 + // Number of db access denied, not reflected in global_user_stats yet.
981 + ulonglong diff_access_denied_errors;
982 + // Number of queries that return 0 rows
983 + ulonglong diff_empty_queries;
985 + // Per account query delay in miliseconds. When not 0, sleep this number of
986 + // milliseconds before every SQL command.
987 + ulonglong query_delay_millis;
989 /* Used by the sys_var class to store temporary values */
992 @@ -2256,6 +2303,11 @@
995 void init_for_queries();
996 + void reset_stats(void);
997 + void reset_diff_stats(void);
998 + // ran_command is true when this is called immediately after a
999 + // command has been run.
1000 + void update_stats(bool ran_command);
1001 void change_user(void);
1003 void cleanup_after_query();
1004 @@ -2727,6 +2779,15 @@
1006 thd_scheduler scheduler;
1008 + /* Returns string as 'IP:port' for the client-side
1009 + of the connnection represented
1010 + by 'client' as displayed by SHOW PROCESSLIST.
1011 + Allocates memory from the heap of
1012 + this THD and that is not reclaimed
1013 + immediately, so use sparingly. May return NULL.
1015 + char *get_client_host_port(THD *client);
1018 inline Internal_error_handler *get_internal_handler()
1019 { return m_internal_handler; }
1020 @@ -2914,6 +2975,10 @@
1021 LEX_STRING invoker_host;
1024 +/* Returns string as 'IP' for the client-side of the connection represented by
1025 + 'client'. Does not allocate memory. May return "".
1027 +const char *get_client_host(THD *client);
1029 /** A short cut for thd->stmt_da->set_ok_status(). */
1031 diff -ruN a/sql/sql_connect.cc b/sql/sql_connect.cc
1032 --- a/sql/sql_connect.cc 2010-12-03 20:58:26.000000000 +0300
1033 +++ b/sql/sql_connect.cc 2010-12-31 03:53:28.000000000 +0300
1035 #define MIN_HANDSHAKE_SIZE 6
1036 #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
1038 +// Increments connection count for user.
1039 +static int increment_connection_count(THD* thd, bool use_lock);
1041 +// Uses the THD to update the global stats by user name and client IP
1042 +void update_global_user_stats(THD* thd, bool create_user, time_t now);
1044 +HASH global_user_stats;
1045 +HASH global_client_stats;
1046 +HASH global_thread_stats;
1047 +// Protects global_user_stats and global_client_stats
1048 +extern mysql_mutex_t LOCK_global_user_client_stats;
1050 +HASH global_table_stats;
1051 +extern mysql_mutex_t LOCK_global_table_stats;
1053 +HASH global_index_stats;
1054 +extern mysql_mutex_t LOCK_global_index_stats;
1057 Get structure for logging connection data for the current user
1059 @@ -112,6 +130,586 @@
1063 +extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
1064 + my_bool not_used __attribute__((unused)))
1066 + *length= strlen(user_stats->user);
1067 + return (uchar*) user_stats->user;
1070 +extern "C" uchar *get_key_thread_stats(THREAD_STATS *thread_stats, size_t *length,
1071 + my_bool not_used __attribute__((unused)))
1073 + *length= sizeof(my_thread_id);
1074 + return (uchar *) &(thread_stats->id);
1077 +void free_user_stats(USER_STATS* user_stats)
1079 + my_free((char *) user_stats);
1082 +void free_thread_stats(THREAD_STATS* thread_stats)
1084 + my_free((char *) thread_stats);
1087 +void init_user_stats(USER_STATS *user_stats,
1089 + const char *priv_user,
1090 + uint total_connections,
1091 + uint concurrent_connections,
1092 + time_t connected_time,
1095 + ulonglong bytes_received,
1096 + ulonglong bytes_sent,
1097 + ulonglong binlog_bytes_written,
1098 + ha_rows rows_fetched,
1099 + ha_rows rows_updated,
1100 + ha_rows rows_read,
1101 + ulonglong select_commands,
1102 + ulonglong update_commands,
1103 + ulonglong other_commands,
1104 + ulonglong commit_trans,
1105 + ulonglong rollback_trans,
1106 + ulonglong denied_connections,
1107 + ulonglong lost_connections,
1108 + ulonglong access_denied_errors,
1109 + ulonglong empty_queries)
1111 + DBUG_ENTER("init_user_stats");
1112 + DBUG_PRINT("info",
1113 + ("Add user_stats entry for user %s - priv_user %s",
1114 + user, priv_user));
1115 + strncpy(user_stats->user, user, sizeof(user_stats->user));
1116 + strncpy(user_stats->priv_user, priv_user, sizeof(user_stats->priv_user));
1118 + user_stats->total_connections= total_connections;
1119 + user_stats->concurrent_connections= concurrent_connections;
1120 + user_stats->connected_time= connected_time;
1121 + user_stats->busy_time= busy_time;
1122 + user_stats->cpu_time= cpu_time;
1123 + user_stats->bytes_received= bytes_received;
1124 + user_stats->bytes_sent= bytes_sent;
1125 + user_stats->binlog_bytes_written= binlog_bytes_written;
1126 + user_stats->rows_fetched= rows_fetched;
1127 + user_stats->rows_updated= rows_updated;
1128 + user_stats->rows_read= rows_read;
1129 + user_stats->select_commands= select_commands;
1130 + user_stats->update_commands= update_commands;
1131 + user_stats->other_commands= other_commands;
1132 + user_stats->commit_trans= commit_trans;
1133 + user_stats->rollback_trans= rollback_trans;
1134 + user_stats->denied_connections= denied_connections;
1135 + user_stats->lost_connections= lost_connections;
1136 + user_stats->access_denied_errors= access_denied_errors;
1137 + user_stats->empty_queries= empty_queries;
1141 +void init_thread_stats(THREAD_STATS *thread_stats,
1143 + uint total_connections,
1144 + uint concurrent_connections,
1145 + time_t connected_time,
1148 + ulonglong bytes_received,
1149 + ulonglong bytes_sent,
1150 + ulonglong binlog_bytes_written,
1151 + ha_rows rows_fetched,
1152 + ha_rows rows_updated,
1153 + ha_rows rows_read,
1154 + ulonglong select_commands,
1155 + ulonglong update_commands,
1156 + ulonglong other_commands,
1157 + ulonglong commit_trans,
1158 + ulonglong rollback_trans,
1159 + ulonglong denied_connections,
1160 + ulonglong lost_connections,
1161 + ulonglong access_denied_errors,
1162 + ulonglong empty_queries)
1164 + DBUG_ENTER("init_thread_stats");
1165 + DBUG_PRINT("info",
1166 + ("Add thread_stats entry for thread %lu",
1168 + thread_stats->id= id;
1170 + thread_stats->total_connections= total_connections;
1171 + thread_stats->concurrent_connections= concurrent_connections;
1172 + thread_stats->connected_time= connected_time;
1173 + thread_stats->busy_time= busy_time;
1174 + thread_stats->cpu_time= cpu_time;
1175 + thread_stats->bytes_received= bytes_received;
1176 + thread_stats->bytes_sent= bytes_sent;
1177 + thread_stats->binlog_bytes_written= binlog_bytes_written;
1178 + thread_stats->rows_fetched= rows_fetched;
1179 + thread_stats->rows_updated= rows_updated;
1180 + thread_stats->rows_read= rows_read;
1181 + thread_stats->select_commands= select_commands;
1182 + thread_stats->update_commands= update_commands;
1183 + thread_stats->other_commands= other_commands;
1184 + thread_stats->commit_trans= commit_trans;
1185 + thread_stats->rollback_trans= rollback_trans;
1186 + thread_stats->denied_connections= denied_connections;
1187 + thread_stats->lost_connections= lost_connections;
1188 + thread_stats->access_denied_errors= access_denied_errors;
1189 + thread_stats->empty_queries= empty_queries;
1193 +void add_user_stats(USER_STATS *user_stats,
1194 + uint total_connections,
1195 + uint concurrent_connections,
1196 + time_t connected_time,
1199 + ulonglong bytes_received,
1200 + ulonglong bytes_sent,
1201 + ulonglong binlog_bytes_written,
1202 + ha_rows rows_fetched,
1203 + ha_rows rows_updated,
1204 + ha_rows rows_read,
1205 + ulonglong select_commands,
1206 + ulonglong update_commands,
1207 + ulonglong other_commands,
1208 + ulonglong commit_trans,
1209 + ulonglong rollback_trans,
1210 + ulonglong denied_connections,
1211 + ulonglong lost_connections,
1212 + ulonglong access_denied_errors,
1213 + ulonglong empty_queries)
1215 + user_stats->total_connections+= total_connections;
1216 + user_stats->concurrent_connections+= concurrent_connections;
1217 + user_stats->connected_time+= connected_time;
1218 + user_stats->busy_time+= busy_time;
1219 + user_stats->cpu_time+= cpu_time;
1220 + user_stats->bytes_received+= bytes_received;
1221 + user_stats->bytes_sent+= bytes_sent;
1222 + user_stats->binlog_bytes_written+= binlog_bytes_written;
1223 + user_stats->rows_fetched+= rows_fetched;
1224 + user_stats->rows_updated+= rows_updated;
1225 + user_stats->rows_read+= rows_read;
1226 + user_stats->select_commands+= select_commands;
1227 + user_stats->update_commands+= update_commands;
1228 + user_stats->other_commands+= other_commands;
1229 + user_stats->commit_trans+= commit_trans;
1230 + user_stats->rollback_trans+= rollback_trans;
1231 + user_stats->denied_connections+= denied_connections;
1232 + user_stats->lost_connections+= lost_connections;
1233 + user_stats->access_denied_errors+= access_denied_errors;
1234 + user_stats->empty_queries+= empty_queries;
1237 +void add_thread_stats(THREAD_STATS *thread_stats,
1238 + uint total_connections,
1239 + uint concurrent_connections,
1240 + time_t connected_time,
1243 + ulonglong bytes_received,
1244 + ulonglong bytes_sent,
1245 + ulonglong binlog_bytes_written,
1246 + ha_rows rows_fetched,
1247 + ha_rows rows_updated,
1248 + ha_rows rows_read,
1249 + ulonglong select_commands,
1250 + ulonglong update_commands,
1251 + ulonglong other_commands,
1252 + ulonglong commit_trans,
1253 + ulonglong rollback_trans,
1254 + ulonglong denied_connections,
1255 + ulonglong lost_connections,
1256 + ulonglong access_denied_errors,
1257 + ulonglong empty_queries)
1259 + thread_stats->total_connections+= total_connections;
1260 + thread_stats->concurrent_connections+= concurrent_connections;
1261 + thread_stats->connected_time+= connected_time;
1262 + thread_stats->busy_time+= busy_time;
1263 + thread_stats->cpu_time+= cpu_time;
1264 + thread_stats->bytes_received+= bytes_received;
1265 + thread_stats->bytes_sent+= bytes_sent;
1266 + thread_stats->binlog_bytes_written+= binlog_bytes_written;
1267 + thread_stats->rows_fetched+= rows_fetched;
1268 + thread_stats->rows_updated+= rows_updated;
1269 + thread_stats->rows_read+= rows_read;
1270 + thread_stats->select_commands+= select_commands;
1271 + thread_stats->update_commands+= update_commands;
1272 + thread_stats->other_commands+= other_commands;
1273 + thread_stats->commit_trans+= commit_trans;
1274 + thread_stats->rollback_trans+= rollback_trans;
1275 + thread_stats->denied_connections+= denied_connections;
1276 + thread_stats->lost_connections+= lost_connections;
1277 + thread_stats->access_denied_errors+= access_denied_errors;
1278 + thread_stats->empty_queries+= empty_queries;
1281 +void init_global_user_stats(void)
1283 + if (my_hash_init(&global_user_stats, system_charset_info, max_connections,
1284 + 0, 0, (my_hash_get_key)get_key_user_stats,
1285 + (my_hash_free_key)free_user_stats, 0)) {
1286 + sql_print_error("Initializing global_user_stats failed.");
1291 +void init_global_client_stats(void)
1293 + if (my_hash_init(&global_client_stats, system_charset_info, max_connections,
1294 + 0, 0, (my_hash_get_key)get_key_user_stats,
1295 + (my_hash_free_key)free_user_stats, 0)) {
1296 + sql_print_error("Initializing global_client_stats failed.");
1301 +void init_global_thread_stats(void)
1303 + if (my_hash_init(&global_thread_stats, &my_charset_bin, max_connections,
1304 + 0, 0, (my_hash_get_key) get_key_thread_stats,
1305 + (my_hash_free_key) free_thread_stats, 0))
1307 + sql_print_error("Initializing global_client_stats failed.");
1312 +extern "C" uchar *get_key_table_stats(TABLE_STATS *table_stats, size_t *length,
1313 + my_bool not_used __attribute__((unused)))
1315 + *length= strlen(table_stats->table);
1316 + return (uchar*) table_stats->table;
1319 +extern "C" void free_table_stats(TABLE_STATS* table_stats)
1321 + my_free((char*) table_stats);
1324 +void init_global_table_stats(void)
1326 + if (my_hash_init(&global_table_stats, system_charset_info, max_connections,
1327 + 0, 0, (my_hash_get_key)get_key_table_stats,
1328 + (my_hash_free_key)free_table_stats, 0)) {
1329 + sql_print_error("Initializing global_table_stats failed.");
1334 +extern "C" uchar *get_key_index_stats(INDEX_STATS *index_stats, size_t *length,
1335 + my_bool not_used __attribute__((unused)))
1337 + *length= strlen(index_stats->index);
1338 + return (uchar*) index_stats->index;
1341 +extern "C" void free_index_stats(INDEX_STATS* index_stats)
1343 + my_free((char*) index_stats);
1346 +void init_global_index_stats(void)
1348 + if (my_hash_init(&global_index_stats, system_charset_info, max_connections,
1349 + 0, 0, (my_hash_get_key)get_key_index_stats,
1350 + (my_hash_free_key)free_index_stats, 0)) {
1351 + sql_print_error("Initializing global_index_stats failed.");
1356 +void free_global_user_stats(void)
1358 + my_hash_free(&global_user_stats);
1361 +void free_global_thread_stats(void)
1363 + my_hash_free(&global_thread_stats);
1366 +void free_global_table_stats(void)
1368 + my_hash_free(&global_table_stats);
1371 +void free_global_index_stats(void)
1373 + my_hash_free(&global_index_stats);
1376 +void free_global_client_stats(void)
1378 + my_hash_free(&global_client_stats);
1381 +// 'mysql_system_user' is used for when the user is not defined for a THD.
1382 +static char mysql_system_user[] = "#mysql_system#";
1384 +// Returns 'user' if it's not NULL. Returns 'mysql_system_user' otherwise.
1385 +static char* get_valid_user_string(char* user) {
1386 + return user ? user : mysql_system_user;
1389 +// Increments the global stats connection count for an entry from
1390 +// global_client_stats or global_user_stats. Returns 0 on success
1392 +static int increment_count_by_name(const char *name, const char *role_name,
1393 + HASH *users_or_clients, THD *thd)
1395 + USER_STATS* user_stats;
1397 + if (!(user_stats = (USER_STATS *) my_hash_search(users_or_clients,
1401 + // First connection for this user or client
1402 + if (!(user_stats = ((USER_STATS *)
1403 + my_malloc(sizeof(USER_STATS), MYF(MY_WME | MY_ZEROFILL)))))
1405 + return 1; // Out of memory
1408 + init_user_stats(user_stats, name, role_name,
1409 + 0, 0, // connections
1411 + 0, 0, 0, // bytes sent, received and written
1412 + 0, 0, 0, // rows fetched, updated and read
1413 + 0, 0, 0, // select, update and other commands
1414 + 0, 0, // commit and rollback trans
1415 + thd->diff_denied_connections,
1416 + 0, // lost connections
1417 + 0, // access denied errors
1418 + 0); // empty queries
1420 + if (my_hash_insert(users_or_clients, (uchar *) user_stats))
1422 + my_free((char *) user_stats);
1423 + return 1; // Out of memory
1426 + user_stats->total_connections++;
1430 +static int increment_count_by_id(my_thread_id id,
1431 + HASH *users_or_clients, THD *thd)
1433 + THREAD_STATS* thread_stats;
1435 + if (!(thread_stats = (THREAD_STATS *) my_hash_search(users_or_clients,
1437 + sizeof(my_thread_id))))
1439 + // First connection for this user or client
1440 + if (!(thread_stats = ((THREAD_STATS *)
1441 + my_malloc(sizeof(THREAD_STATS), MYF(MY_WME | MY_ZEROFILL)))))
1443 + return 1; // Out of memory
1446 + init_thread_stats(thread_stats, id,
1447 + 0, 0, // connections
1449 + 0, 0, 0, // bytes sent, received and written
1450 + 0, 0, 0, // rows fetched, updated and read
1451 + 0, 0, 0, // select, update and other commands
1452 + 0, 0, // commit and rollback trans
1453 + thd->diff_denied_connections,
1454 + 0, // lost connections
1455 + 0, // access denied errors
1456 + 0); // empty queries
1458 + if (my_hash_insert(users_or_clients, (uchar *) thread_stats))
1460 + my_free((char *) thread_stats);
1461 + return 1; // Out of memory
1464 + thread_stats->total_connections++;
1468 +/* Increments the global user and client stats connection count. If 'use_lock'
1469 + is true, LOCK_global_user_client_stats will be locked/unlocked. Returns
1470 + 0 on success, 1 on error.
1472 +static int increment_connection_count(THD* thd, bool use_lock)
1474 + char* user_string= get_valid_user_string(thd->main_security_ctx.user);
1475 + const char* client_string= get_client_host(thd);
1476 + int return_value= 0;
1478 + if (!opt_userstat_running)
1479 + return return_value;
1482 + mysql_mutex_lock(&LOCK_global_user_client_stats);
1484 + if (increment_count_by_name(user_string, user_string,
1485 + &global_user_stats, thd))
1490 + if (increment_count_by_name(client_string,
1492 + &global_client_stats, thd))
1497 + if (opt_thread_statistics)
1499 + if (increment_count_by_id(thd->thread_id, &global_thread_stats, thd))
1508 + mysql_mutex_unlock(&LOCK_global_user_client_stats);
1509 + return return_value;
1512 +// Used to update the global user and client stats.
1513 +static void update_global_user_stats_with_user(THD* thd,
1514 + USER_STATS* user_stats,
1517 + user_stats->connected_time+= now - thd->last_global_update_time;
1518 +//thd->last_global_update_time= now;
1519 + user_stats->busy_time+= thd->diff_total_busy_time;
1520 + user_stats->cpu_time+= thd->diff_total_cpu_time;
1521 + user_stats->bytes_received+= thd->diff_total_bytes_received;
1522 + user_stats->bytes_sent+= thd->diff_total_bytes_sent;
1523 + user_stats->binlog_bytes_written+= thd->diff_total_binlog_bytes_written;
1524 + user_stats->rows_fetched+= thd->diff_total_sent_rows;
1525 + user_stats->rows_updated+= thd->diff_total_updated_rows;
1526 + user_stats->rows_read+= thd->diff_total_read_rows;
1527 + user_stats->select_commands+= thd->diff_select_commands;
1528 + user_stats->update_commands+= thd->diff_update_commands;
1529 + user_stats->other_commands+= thd->diff_other_commands;
1530 + user_stats->commit_trans+= thd->diff_commit_trans;
1531 + user_stats->rollback_trans+= thd->diff_rollback_trans;
1532 + user_stats->denied_connections+= thd->diff_denied_connections;
1533 + user_stats->lost_connections+= thd->diff_lost_connections;
1534 + user_stats->access_denied_errors+= thd->diff_access_denied_errors;
1535 + user_stats->empty_queries+= thd->diff_empty_queries;
1538 +static void update_global_thread_stats_with_thread(THD* thd,
1539 + THREAD_STATS* thread_stats,
1542 + thread_stats->connected_time+= now - thd->last_global_update_time;
1543 +//thd->last_global_update_time= now;
1544 + thread_stats->busy_time+= thd->diff_total_busy_time;
1545 + thread_stats->cpu_time+= thd->diff_total_cpu_time;
1546 + thread_stats->bytes_received+= thd->diff_total_bytes_received;
1547 + thread_stats->bytes_sent+= thd->diff_total_bytes_sent;
1548 + thread_stats->binlog_bytes_written+= thd->diff_total_binlog_bytes_written;
1549 + thread_stats->rows_fetched+= thd->diff_total_sent_rows;
1550 + thread_stats->rows_updated+= thd->diff_total_updated_rows;
1551 + thread_stats->rows_read+= thd->diff_total_read_rows;
1552 + thread_stats->select_commands+= thd->diff_select_commands;
1553 + thread_stats->update_commands+= thd->diff_update_commands;
1554 + thread_stats->other_commands+= thd->diff_other_commands;
1555 + thread_stats->commit_trans+= thd->diff_commit_trans;
1556 + thread_stats->rollback_trans+= thd->diff_rollback_trans;
1557 + thread_stats->denied_connections+= thd->diff_denied_connections;
1558 + thread_stats->lost_connections+= thd->diff_lost_connections;
1559 + thread_stats->access_denied_errors+= thd->diff_access_denied_errors;
1560 + thread_stats->empty_queries+= thd->diff_empty_queries;
1563 +// Updates the global stats of a user or client
1564 +void update_global_user_stats(THD* thd, bool create_user, time_t now)
1566 + if (opt_userstat_running)
1568 + char* user_string= get_valid_user_string(thd->main_security_ctx.user);
1569 + const char* client_string= get_client_host(thd);
1571 + USER_STATS* user_stats;
1572 + THREAD_STATS* thread_stats;
1573 + mysql_mutex_lock(&LOCK_global_user_client_stats);
1575 + // Update by user name
1576 + if ((user_stats = (USER_STATS *) my_hash_search(&global_user_stats,
1577 + (uchar *) user_string,
1578 + strlen(user_string))))
1581 + update_global_user_stats_with_user(thd, user_stats, now);
1585 + // Create the entry
1588 + increment_count_by_name(user_string, user_string,
1589 + &global_user_stats, thd);
1593 + // Update by client IP
1594 + if ((user_stats = (USER_STATS *) my_hash_search(&global_client_stats,
1595 + (uchar *) client_string,
1596 + strlen(client_string))))
1598 + // Found by client IP
1599 + update_global_user_stats_with_user(thd, user_stats, now);
1603 + // Create the entry
1606 + increment_count_by_name(client_string,
1608 + &global_client_stats, thd);
1612 + if (opt_thread_statistics)
1614 + // Update by thread ID
1615 + if ((thread_stats = (THREAD_STATS *) my_hash_search(&global_thread_stats,
1616 + (uchar *) &(thd->thread_id),
1617 + sizeof(my_thread_id))))
1619 + // Found by thread ID
1620 + update_global_thread_stats_with_thread(thd, thread_stats, now);
1624 + // Create the entry
1627 + increment_count_by_id(thd->thread_id,
1628 + &global_thread_stats, thd);
1633 + thd->last_global_update_time = now;
1634 + thd->reset_diff_stats();
1636 + mysql_mutex_unlock(&LOCK_global_user_client_stats);
1640 + thd->reset_diff_stats();
1645 check if user has already too many connections
1649 uc->connections--; // no need for decrease_user_connections() here
1650 + statistic_increment(denied_connections, &LOCK_status);
1652 The thread may returned back to the pool and assigned to a user
1653 that doesn't have a limit. Ensure the user is not using resources
1654 @@ -565,11 +1164,18 @@
1655 my_sleep(1000); /* must wait after eof() */
1657 statistic_increment(aborted_connects,&LOCK_status);
1658 + thd->diff_denied_connections++;
1661 /* Connect completed, set read/write timeouts back to default */
1662 my_net_set_read_timeout(net, thd->variables.net_read_timeout);
1663 my_net_set_write_timeout(net, thd->variables.net_write_timeout);
1665 + thd->reset_stats();
1666 + // Updates global user connection stats.
1667 + if (increment_connection_count(thd, true))
1673 @@ -599,6 +1205,7 @@
1674 if (thd->killed || (net->error && net->vio != 0))
1676 statistic_increment(aborted_threads,&LOCK_status);
1677 + thd->diff_lost_connections++;
1680 if (net->error && net->vio != 0)
1681 @@ -729,12 +1336,16 @@
1683 NET *net= &thd->net;
1685 + bool create_user= TRUE;
1688 rc= login_connection(thd);
1689 MYSQL_AUDIT_NOTIFY_CONNECTION_CONNECT(thd);
1692 + create_user= FALSE;
1696 MYSQL_CONNECTION_START(thd->thread_id, &thd->security_ctx->priv_user[0],
1697 (char *) thd->security_ctx->host_or_ip);
1698 @@ -761,6 +1372,8 @@
1701 close_connection(thd);
1702 + thd->update_stats(false);
1703 + update_global_user_stats(thd, create_user, time(NULL));
1704 if (MYSQL_CALLBACK_ELSE(thread_scheduler, end_thread, (thd, 1), 0))
1705 return; // Probably no-threads
1707 diff -ruN a/sql/sql_delete.cc b/sql/sql_delete.cc
1708 --- a/sql/sql_delete.cc 2010-12-03 20:58:26.000000000 +0300
1709 +++ b/sql/sql_delete.cc 2010-12-31 03:58:22.000000000 +0300
1711 my_ok(thd, deleted);
1712 DBUG_PRINT("info",("%ld records deleted",(long) deleted));
1714 + thd->updated_row_count+= deleted;
1715 DBUG_RETURN(error >= 0 || thd->is_error());
1718 @@ -1005,6 +1006,7 @@
1720 ::my_ok(thd, deleted);
1722 + thd->updated_row_count+= deleted;
1726 diff -ruN a/sql/sql_insert.cc b/sql/sql_insert.cc
1727 --- a/sql/sql_insert.cc 2010-12-03 20:58:26.000000000 +0300
1728 +++ b/sql/sql_insert.cc 2010-12-31 04:12:35.000000000 +0300
1729 @@ -1069,13 +1069,14 @@
1733 + ha_rows row_count;
1734 if (values_list.elements == 1 && (!(thd->variables.option_bits & OPTION_WARNINGS) ||
1735 !thd->cuted_fields))
1737 - my_ok(thd, info.copied + info.deleted +
1738 + row_count= info.copied + info.deleted +
1739 ((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
1740 - info.touched : info.updated),
1742 + info.touched : info.updated);
1743 + my_ok(thd, row_count, id);
1747 @@ -1091,8 +1092,10 @@
1748 sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
1749 (ulong) (info.deleted + updated),
1750 (ulong) thd->warning_info->statement_warn_count());
1751 - ::my_ok(thd, info.copied + info.deleted + updated, id, buff);
1752 + row_count= info.copied + info.deleted + updated;
1753 + ::my_ok(thd, row_count, id, buff);
1755 + thd->updated_row_count+= row_count;
1756 thd->abort_on_warning= 0;
1759 @@ -3539,6 +3542,7 @@
1760 thd->first_successful_insert_id_in_prev_stmt :
1761 (info.copied ? autoinc_value_of_last_inserted_row : 0));
1762 ::my_ok(thd, row_count, id, buff);
1763 + thd->updated_row_count+= row_count;
1767 diff -ruN a/sql/sql_lex.h b/sql/sql_lex.h
1768 --- a/sql/sql_lex.h 2010-12-03 20:58:26.000000000 +0300
1769 +++ b/sql/sql_lex.h 2010-12-31 05:07:18.000000000 +0300
1771 When a command is added here, be sure it's also added in mysqld.cc
1772 in "struct show_var_st status_vars[]= {" ...
1774 + // TODO(mcallaghan): update status_vars in mysqld to export these
1775 + SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS,
1776 + SQLCOM_SHOW_CLIENT_STATS, SQLCOM_SHOW_THREAD_STATS,
1777 /* This should be the last !!! */
1780 diff -ruN a/sql/sql_parse.cc b/sql/sql_parse.cc
1781 --- a/sql/sql_parse.cc 2010-12-03 20:58:26.000000000 +0300
1782 +++ b/sql/sql_parse.cc 2010-12-31 04:57:45.000000000 +0300
1784 static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
1785 static void sql_kill(THD *thd, ulong id, bool only_kill_query);
1787 +// Uses the THD to update the global stats by user name and client IP
1788 +void update_global_user_stats(THD* thd, bool create_user, time_t now);
1790 const char *any_db="*any*"; // Special symbol for check_access
1792 const LEX_STRING command_name[]={
1793 @@ -701,6 +704,12 @@
1795 thd->clear_error(); // Clear error message
1796 thd->stmt_da->reset_diagnostics_area();
1797 + thd->updated_row_count= 0;
1798 + thd->busy_time= 0;
1800 + thd->bytes_received= 0;
1801 + thd->bytes_sent= 0;
1802 + thd->binlog_bytes_written= 0;
1804 net_new_transaction(net);
1806 @@ -886,6 +895,10 @@
1807 (char *) thd->security_ctx->host_or_ip);
1809 thd->command=command;
1810 + /* To increment the corrent command counter for user stats, 'command' must
1811 + be saved because it is set to COM_SLEEP at the end of this function.
1813 + thd->old_command= command;
1815 Commands which always take a long time are logged into
1816 the slow log only if opt_log_slow_admin_statements is set.
1817 @@ -1626,6 +1639,13 @@
1818 thd->profiling.discard_current_query();
1821 + case SCH_USER_STATS:
1822 + case SCH_CLIENT_STATS:
1823 + case SCH_THREAD_STATS:
1824 + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
1826 + case SCH_TABLE_STATS:
1827 + case SCH_INDEX_STATS:
1828 case SCH_OPEN_TABLES:
1831 @@ -1783,6 +1803,7 @@
1832 thd->security_ctx->priv_host)) &&
1833 check_global_access(thd, SUPER_ACL))
1835 + thd->diff_access_denied_errors++;
1836 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
1839 @@ -4725,6 +4746,7 @@
1840 case ACL_INTERNAL_ACCESS_DENIED:
1843 + thd->diff_access_denied_errors++;
1844 my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
1845 sctx->priv_user, sctx->priv_host, db);
1847 @@ -4775,6 +4797,7 @@
1848 DBUG_PRINT("error",("No possible access"));
1851 + thd->diff_access_denied_errors++;
1852 if (thd->password == 2)
1853 my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
1855 @@ -4891,6 +4914,7 @@
1857 if (!thd->col_access && check_grant_db(thd, dst_db_name))
1859 + thd->diff_access_denied_errors++;
1860 my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
1861 thd->security_ctx->priv_user,
1862 thd->security_ctx->priv_host,
1863 @@ -5161,6 +5185,7 @@
1864 if ((thd->security_ctx->master_access & want_access))
1866 get_privilege_desc(command, sizeof(command), want_access);
1867 + thd->diff_access_denied_errors++;
1868 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
1871 @@ -5542,6 +5567,32 @@
1873 mysql_reset_thd_for_next_command(thd);
1875 + int start_time_error= 0;
1876 + int end_time_error= 0;
1877 + struct timeval start_time, end_time;
1878 + double start_usecs= 0;
1879 + double end_usecs= 0;
1881 + int cputime_error= 0;
1882 + struct timespec tp;
1883 + double start_cpu_nsecs= 0;
1884 + double end_cpu_nsecs= 0;
1886 + if (opt_userstat_running)
1888 +#ifdef HAVE_CLOCK_GETTIME
1889 + /* get start cputime */
1890 + if (!(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
1891 + start_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
1894 + // Gets the start time, in order to measure how long this command takes.
1895 + if (!(start_time_error = gettimeofday(&start_time, NULL)))
1897 + start_usecs = start_time.tv_sec * 1000000.0 + start_time.tv_usec;
1901 if (query_cache_send_result_to_client(thd, rawbuf, length) <= 0)
1904 @@ -5610,6 +5661,52 @@
1905 DBUG_ASSERT(thd->change_list.is_empty());
1908 + if (opt_userstat_running)
1910 + // Gets the end time.
1911 + if (!(end_time_error= gettimeofday(&end_time, NULL)))
1913 + end_usecs= end_time.tv_sec * 1000000.0 + end_time.tv_usec;
1916 + // Calculates the difference between the end and start times.
1917 + if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error)
1919 + thd->busy_time= (end_usecs - start_usecs) / 1000000;
1920 + // In case there are bad values, 2629743 is the #seconds in a month.
1921 + if (thd->busy_time > 2629743)
1923 + thd->busy_time= 0;
1928 + // end time went back in time, or gettimeofday() failed.
1929 + thd->busy_time= 0;
1932 +#ifdef HAVE_CLOCK_GETTIME
1933 + /* get end cputime */
1934 + if (!cputime_error &&
1935 + !(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
1936 + end_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
1938 + if (start_cpu_nsecs && !cputime_error)
1940 + thd->cpu_time = (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
1941 + // In case there are bad values, 2629743 is the #seconds in a month.
1942 + if (thd->cpu_time > 2629743)
1944 + thd->cpu_time = 0;
1948 + thd->cpu_time = 0;
1950 + // Updates THD stats and the global user stats.
1951 + thd->update_stats(true);
1952 + update_global_user_stats(thd, true, time(NULL));
1957 diff -ruN a/sql/sql_prepare.cc b/sql/sql_prepare.cc
1958 --- a/sql/sql_prepare.cc 2010-12-03 20:58:26.000000000 +0300
1959 +++ b/sql/sql_prepare.cc 2010-12-31 04:25:04.000000000 +0300
1962 #include "lock.h" // MYSQL_OPEN_FORCE_SHARED_MDL
1964 +// Uses the THD to update the global stats by user name and client IP
1965 +void update_global_user_stats(THD* thd, bool create_user, time_t now);
1968 A result class used to send cursor rows using the binary protocol.
1970 @@ -2173,8 +2176,34 @@
1971 /* First of all clear possible warnings from the previous command */
1972 mysql_reset_thd_for_next_command(thd);
1974 + int start_time_error= 0;
1975 + int end_time_error= 0;
1976 + struct timeval start_time, end_time;
1977 + double start_usecs= 0;
1978 + double end_usecs= 0;
1980 + int cputime_error= 0;
1981 + struct timespec tp;
1982 + double start_cpu_nsecs= 0;
1983 + double end_cpu_nsecs= 0;
1985 + if (opt_userstat_running)
1987 +#ifdef HAVE_CLOCK_GETTIME
1988 + /* get start cputime */
1989 + if (!(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
1990 + start_cpu_nsecs= tp.tv_sec * 1000000000.0 + tp.tv_nsec;
1993 + // Gets the start time, in order to measure how long this command takes.
1994 + if (!(start_time_error= gettimeofday(&start_time, NULL)))
1996 + start_usecs= start_time.tv_sec * 1000000.0 + start_time.tv_usec;
2000 if (! (stmt= new Prepared_statement(thd)))
2001 - DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */
2002 + goto end; /* out of memory: error is set in Sql_alloc */
2004 if (thd->stmt_map.insert(thd, stmt))
2006 @@ -2182,7 +2211,7 @@
2007 The error is set in the insert. The statement itself
2008 will be also deleted there (this is how the hash works).
2014 thd->protocol= &thd->protocol_binary;
2015 @@ -2196,6 +2225,53 @@
2016 thd->protocol= save_protocol;
2018 /* check_prepared_statemnt sends the metadata packet in case of success */
2020 + if (opt_userstat_running)
2022 + // Gets the end time.
2023 + if (!(end_time_error= gettimeofday(&end_time, NULL)))
2025 + end_usecs= end_time.tv_sec * 1000000.0 + end_time.tv_usec;
2028 + // Calculates the difference between the end and start times.
2029 + if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error)
2031 + thd->busy_time= (end_usecs - start_usecs) / 1000000;
2032 + // In case there are bad values, 2629743 is the #seconds in a month.
2033 + if (thd->busy_time > 2629743)
2035 + thd->busy_time= 0;
2040 + // end time went back in time, or gettimeofday() failed.
2041 + thd->busy_time= 0;
2044 +#ifdef HAVE_CLOCK_GETTIME
2045 + /* get end cputime */
2046 + if (!cputime_error &&
2047 + !(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2048 + end_cpu_nsecs= tp.tv_sec*1000000000.0+tp.tv_nsec;
2050 + if (start_cpu_nsecs && !cputime_error)
2052 + thd->cpu_time= (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
2053 + // In case there are bad values, 2629743 is the #seconds in a month.
2054 + if (thd->cpu_time > 2629743)
2060 + thd->cpu_time = 0;
2062 + // Updates THD stats and the global user stats.
2063 + thd->update_stats(true);
2064 + update_global_user_stats(thd, true, time(NULL));
2069 @@ -2540,12 +2616,38 @@
2070 /* First of all clear possible warnings from the previous command */
2071 mysql_reset_thd_for_next_command(thd);
2073 + int start_time_error= 0;
2074 + int end_time_error= 0;
2075 + struct timeval start_time, end_time;
2076 + double start_usecs= 0;
2077 + double end_usecs= 0;
2079 + int cputime_error= 0;
2080 + struct timespec tp;
2081 + double start_cpu_nsecs= 0;
2082 + double end_cpu_nsecs= 0;
2084 + if (opt_userstat_running)
2086 +#ifdef HAVE_CLOCK_GETTIME
2087 + /* get start cputime */
2088 + if (!(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2089 + start_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
2092 + // Gets the start time, in order to measure how long this command takes.
2093 + if (!(start_time_error = gettimeofday(&start_time, NULL)))
2095 + start_usecs = start_time.tv_sec * 1000000.0 + start_time.tv_usec;
2099 if (!(stmt= find_prepared_statement(thd, stmt_id)))
2102 my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), sizeof(llbuf),
2103 llstr(stmt_id, llbuf), "mysqld_stmt_execute");
2108 #if defined(ENABLED_PROFILING)
2109 @@ -2563,6 +2665,53 @@
2110 /* Close connection socket; for use with client testing (Bug#43560). */
2111 DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio););
2114 + if (opt_userstat_running)
2116 + // Gets the end time.
2117 + if (!(end_time_error= gettimeofday(&end_time, NULL)))
2119 + end_usecs= end_time.tv_sec * 1000000.0 + end_time.tv_usec;
2122 + // Calculates the difference between the end and start times.
2123 + if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error)
2125 + thd->busy_time= (end_usecs - start_usecs) / 1000000;
2126 + // In case there are bad values, 2629743 is the #seconds in a month.
2127 + if (thd->busy_time > 2629743)
2129 + thd->busy_time= 0;
2134 + // end time went back in time, or gettimeofday() failed.
2135 + thd->busy_time= 0;
2138 +#ifdef HAVE_CLOCK_GETTIME
2139 + /* get end cputime */
2140 + if (!cputime_error &&
2141 + !(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2142 + end_cpu_nsecs= tp.tv_sec*1000000000.0+tp.tv_nsec;
2144 + if (start_cpu_nsecs && !cputime_error)
2146 + thd->cpu_time= (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
2147 + // In case there are bad values, 2629743 is the #seconds in a month.
2148 + if (thd->cpu_time > 2629743)
2154 + thd->cpu_time = 0;
2156 + // Updates THD stats and the global user stats.
2157 + thd->update_stats(true);
2158 + update_global_user_stats(thd, true, time(NULL));
2163 @@ -2635,20 +2784,47 @@
2165 /* First of all clear possible warnings from the previous command */
2166 mysql_reset_thd_for_next_command(thd);
2168 + int start_time_error= 0;
2169 + int end_time_error= 0;
2170 + struct timeval start_time, end_time;
2171 + double start_usecs= 0;
2172 + double end_usecs= 0;
2174 + int cputime_error= 0;
2175 + struct timespec tp;
2176 + double start_cpu_nsecs= 0;
2177 + double end_cpu_nsecs= 0;
2179 + if (opt_userstat_running)
2181 +#ifdef HAVE_CLOCK_GETTIME
2182 + /* get start cputime */
2183 + if (!(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2184 + start_cpu_nsecs= tp.tv_sec*1000000000.0+tp.tv_nsec;
2187 + // Gets the start time, in order to measure how long this command takes.
2188 + if (!(start_time_error= gettimeofday(&start_time, NULL)))
2190 + start_usecs= start_time.tv_sec * 1000000.0 + start_time.tv_usec;
2194 status_var_increment(thd->status_var.com_stmt_fetch);
2195 if (!(stmt= find_prepared_statement(thd, stmt_id)))
2198 my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), sizeof(llbuf),
2199 llstr(stmt_id, llbuf), "mysqld_stmt_fetch");
2204 cursor= stmt->cursor;
2207 my_error(ER_STMT_HAS_NO_OPEN_CURSOR, MYF(0), stmt_id);
2212 thd->stmt_arena= stmt;
2213 @@ -2665,6 +2841,52 @@
2214 thd->restore_backup_statement(stmt, &stmt_backup);
2215 thd->stmt_arena= thd;
2218 + if (opt_userstat_running)
2220 + // Gets the end time.
2221 + if (!(end_time_error = gettimeofday(&end_time, NULL)))
2223 + end_usecs = end_time.tv_sec * 1000000.0 + end_time.tv_usec;
2226 + // Calculates the difference between the end and start times.
2227 + if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error)
2229 + thd->busy_time= (end_usecs - start_usecs) / 1000000;
2230 + // In case there are bad values, 2629743 is the #seconds in a month.
2231 + if (thd->busy_time > 2629743)
2233 + thd->busy_time= 0;
2238 + // end time went back in time, or gettimeofday() failed.
2239 + thd->busy_time= 0;
2242 +#ifdef HAVE_CLOCK_GETTIME
2243 + /* get end cputime */
2244 + if (!cputime_error &&
2245 + !(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2246 + end_cpu_nsecs= tp.tv_sec*1000000000.0+tp.tv_nsec;
2248 + if (start_cpu_nsecs && !cputime_error)
2250 + thd->cpu_time= (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
2251 + // In case there are bad values, 2629743 is the #seconds in a month.
2252 + if (thd->cpu_time > 2629743)
2259 + // Updates THD stats and the global user stats.
2260 + thd->update_stats(true);
2261 + update_global_user_stats(thd, true, time(NULL));
2266 @@ -2695,13 +2917,39 @@
2267 /* First of all clear possible warnings from the previous command */
2268 mysql_reset_thd_for_next_command(thd);
2270 + int start_time_error= 0;
2271 + int end_time_error= 0;
2272 + struct timeval start_time, end_time;
2273 + double start_usecs= 0;
2274 + double end_usecs= 0;
2276 + int cputime_error= 0;
2277 + struct timespec tp;
2278 + double start_cpu_nsecs= 0;
2279 + double end_cpu_nsecs= 0;
2281 + if (opt_userstat_running)
2283 +#ifdef HAVE_CLOCK_GETTIME
2284 + /* get start cputime */
2285 + if (!(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2286 + start_cpu_nsecs= tp.tv_sec * 1000000000.0+tp.tv_nsec;
2289 + // Gets the start time, in order to measure how long this command takes.
2290 + if (!(start_time_error= gettimeofday(&start_time, NULL)))
2292 + start_usecs= start_time.tv_sec * 1000000.0 + start_time.tv_usec;
2296 status_var_increment(thd->status_var.com_stmt_reset);
2297 if (!(stmt= find_prepared_statement(thd, stmt_id)))
2300 my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), sizeof(llbuf),
2301 llstr(stmt_id, llbuf), "mysqld_stmt_reset");
2306 stmt->close_cursor();
2307 @@ -2718,6 +2966,53 @@
2312 + if (opt_userstat_running)
2314 + // Gets the end time.
2315 + if (!(end_time_error = gettimeofday(&end_time, NULL)))
2317 + end_usecs = end_time.tv_sec * 1000000.0 + end_time.tv_usec;
2320 + // Calculates the difference between the end and start times.
2321 + if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error)
2323 + thd->busy_time= (end_usecs - start_usecs) / 1000000;
2324 + // In case there are bad values, 2629743 is the #seconds in a month.
2325 + if (thd->busy_time > 2629743)
2327 + thd->busy_time= 0;
2332 + // end time went back in time, or gettimeofday() failed.
2333 + thd->busy_time= 0;
2336 +#ifdef HAVE_CLOCK_GETTIME
2337 + /* get end cputime */
2338 + if (!cputime_error &&
2339 + !(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2340 + end_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
2342 + if (start_cpu_nsecs && !cputime_error)
2344 + thd->cpu_time = (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
2345 + // In case there are bad values, 2629743 is the #seconds in a month.
2346 + if (thd->cpu_time > 2629743)
2354 + // Updates THD stats and the global user stats.
2355 + thd->update_stats(true);
2356 + update_global_user_stats(thd, true, time(NULL));
2361 diff -ruN a/sql/sql_reload.cc b/sql/sql_reload.cc
2362 --- a/sql/sql_reload.cc 2010-12-03 20:58:26.000000000 +0300
2363 +++ b/sql/sql_reload.cc 2010-12-31 05:00:59.000000000 +0300
2364 @@ -280,14 +280,48 @@
2365 mysql_mutex_unlock(&LOCK_active_mi);
2368 - if (options & REFRESH_USER_RESOURCES)
2369 - reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
2370 #ifdef HAVE_RESPONSE_TIME_DISTRIBUTION
2371 if (options & REFRESH_QUERY_RESPONSE_TIME)
2373 query_response_time_flush();
2375 #endif // HAVE_RESPONSE_TIME_DISTRIBUTION
2376 + if (options & REFRESH_USER_RESOURCES)
2377 + reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
2378 + if (options & REFRESH_TABLE_STATS)
2380 + mysql_mutex_lock(&LOCK_global_table_stats);
2381 + free_global_table_stats();
2382 + init_global_table_stats();
2383 + mysql_mutex_unlock(&LOCK_global_table_stats);
2385 + if (options & REFRESH_INDEX_STATS)
2387 + mysql_mutex_lock(&LOCK_global_index_stats);
2388 + free_global_index_stats();
2389 + init_global_index_stats();
2390 + mysql_mutex_unlock(&LOCK_global_index_stats);
2392 + if (options & (REFRESH_USER_STATS | REFRESH_CLIENT_STATS | REFRESH_THREAD_STATS))
2394 + mysql_mutex_lock(&LOCK_global_user_client_stats);
2395 + if (options & REFRESH_USER_STATS)
2397 + free_global_user_stats();
2398 + init_global_user_stats();
2400 + if (options & REFRESH_CLIENT_STATS)
2402 + free_global_client_stats();
2403 + init_global_client_stats();
2405 + if (options & REFRESH_THREAD_STATS)
2407 + free_global_thread_stats();
2408 + init_global_thread_stats();
2410 + mysql_mutex_unlock(&LOCK_global_user_client_stats);
2412 if (*write_to_binlog != -1)
2413 *write_to_binlog= tmp_write_to_binlog;
2415 diff -ruN a/sql/sql_show.cc b/sql/sql_show.cc
2416 --- a/sql/sql_show.cc 2010-12-03 20:58:26.000000000 +0300
2417 +++ b/sql/sql_show.cc 2010-12-31 04:39:23.000000000 +0300
2418 @@ -114,6 +114,43 @@
2420 static COND * make_cond_for_info_schema(COND *cond, TABLE_LIST *table);
2423 + * Solaris 10 does not have strsep().
2425 + * based on getToken from http://www.winehq.org/pipermail/wine-patches/2001-November/001322.html
2429 +#ifndef HAVE_STRSEP
2430 +static char* strsep(char** str, const char* delims)
2436 + /* No more tokens */
2441 + while (**str != '\0')
2443 + if (strchr(delims, **str) != NULL)
2452 + /* There is not another token */
2459 /***************************************************************************
2460 ** List all table types supported
2461 ***************************************************************************/
2463 sctx->master_access);
2464 if (!(db_access & DB_ACLS) && check_grant_db(thd,dbname))
2466 + thd->diff_access_denied_errors++;
2467 my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
2468 sctx->priv_user, sctx->host_or_ip, dbname);
2469 general_log_print(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
2470 @@ -2351,6 +2389,284 @@
2475 + Write result to network for SHOW USER_STATISTICS
2479 + all_user_stats - values to return
2486 +int send_user_stats(THD* thd, HASH *all_user_stats, TABLE *table)
2488 + DBUG_ENTER("send_user_stats");
2489 + for (uint i = 0; i < all_user_stats->records; ++i)
2491 + restore_record(table, s->default_values);
2492 + USER_STATS *user_stats = (USER_STATS *) my_hash_element(all_user_stats, i);
2493 + table->field[0]->store(user_stats->user, strlen(user_stats->user), system_charset_info);
2494 + table->field[1]->store((longlong)user_stats->total_connections);
2495 + table->field[2]->store((longlong)user_stats->concurrent_connections);
2496 + table->field[3]->store((longlong)user_stats->connected_time);
2497 + table->field[4]->store((longlong)user_stats->busy_time);
2498 + table->field[5]->store((longlong)user_stats->cpu_time);
2499 + table->field[6]->store((longlong)user_stats->bytes_received);
2500 + table->field[7]->store((longlong)user_stats->bytes_sent);
2501 + table->field[8]->store((longlong)user_stats->binlog_bytes_written);
2502 + table->field[9]->store((longlong)user_stats->rows_fetched);
2503 + table->field[10]->store((longlong)user_stats->rows_updated);
2504 + table->field[11]->store((longlong)user_stats->rows_read);
2505 + table->field[12]->store((longlong)user_stats->select_commands);
2506 + table->field[13]->store((longlong)user_stats->update_commands);
2507 + table->field[14]->store((longlong)user_stats->other_commands);
2508 + table->field[15]->store((longlong)user_stats->commit_trans);
2509 + table->field[16]->store((longlong)user_stats->rollback_trans);
2510 + table->field[17]->store((longlong)user_stats->denied_connections);
2511 + table->field[18]->store((longlong)user_stats->lost_connections);
2512 + table->field[19]->store((longlong)user_stats->access_denied_errors);
2513 + table->field[20]->store((longlong)user_stats->empty_queries);
2514 + if (schema_table_store_record(thd, table))
2516 + DBUG_PRINT("error", ("store record error"));
2523 +int send_thread_stats(THD* thd, HASH *all_thread_stats, TABLE *table)
2525 + DBUG_ENTER("send_thread_stats");
2526 + for (uint i = 0; i < all_thread_stats->records; ++i)
2528 + restore_record(table, s->default_values);
2529 + THREAD_STATS *user_stats = (THREAD_STATS *) my_hash_element(all_thread_stats, i);
2530 + table->field[0]->store((longlong)user_stats->id);
2531 + table->field[1]->store((longlong)user_stats->total_connections);
2532 + table->field[2]->store((longlong)user_stats->concurrent_connections);
2533 + table->field[3]->store((longlong)user_stats->connected_time);
2534 + table->field[4]->store((longlong)user_stats->busy_time);
2535 + table->field[5]->store((longlong)user_stats->cpu_time);
2536 + table->field[6]->store((longlong)user_stats->bytes_received);
2537 + table->field[7]->store((longlong)user_stats->bytes_sent);
2538 + table->field[8]->store((longlong)user_stats->binlog_bytes_written);
2539 + table->field[9]->store((longlong)user_stats->rows_fetched);
2540 + table->field[10]->store((longlong)user_stats->rows_updated);
2541 + table->field[11]->store((longlong)user_stats->rows_read);
2542 + table->field[12]->store((longlong)user_stats->select_commands);
2543 + table->field[13]->store((longlong)user_stats->update_commands);
2544 + table->field[14]->store((longlong)user_stats->other_commands);
2545 + table->field[15]->store((longlong)user_stats->commit_trans);
2546 + table->field[16]->store((longlong)user_stats->rollback_trans);
2547 + table->field[17]->store((longlong)user_stats->denied_connections);
2548 + table->field[18]->store((longlong)user_stats->lost_connections);
2549 + table->field[19]->store((longlong)user_stats->access_denied_errors);
2550 + table->field[20]->store((longlong)user_stats->empty_queries);
2551 + if (schema_table_store_record(thd, table))
2553 + DBUG_PRINT("error", ("store record error"));
2561 + Process SHOW USER_STATISTICS
2564 + mysqld_show_user_stats
2565 + thd - current thread
2566 + wild - limit results to the entry for this user
2567 + with_roles - when true, display role for mapped users
2575 +int fill_schema_user_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2577 + TABLE *table= tables->table;
2578 + DBUG_ENTER("fill_schema_user_stats");
2580 + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
2583 + // Iterates through all the global stats and sends them to the client.
2584 + // Pattern matching on the client IP is supported.
2586 + mysql_mutex_lock(&LOCK_global_user_client_stats);
2587 + int result= send_user_stats(thd, &global_user_stats, table);
2588 + mysql_mutex_unlock(&LOCK_global_user_client_stats);
2592 + DBUG_PRINT("exit", ("fill_schema_user_stats result is 0"));
2596 + DBUG_PRINT("exit", ("fill_schema_user_stats result is 1"));
2601 + Process SHOW CLIENT_STATISTICS
2604 + mysqld_show_client_stats
2605 + thd - current thread
2606 + wild - limit results to the entry for this client
2614 +int fill_schema_client_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2616 + TABLE *table= tables->table;
2617 + DBUG_ENTER("fill_schema_client_stats");
2619 + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
2622 + // Iterates through all the global stats and sends them to the client.
2623 + // Pattern matching on the client IP is supported.
2625 + mysql_mutex_lock(&LOCK_global_user_client_stats);
2626 + int result= send_user_stats(thd, &global_client_stats, table);
2627 + mysql_mutex_unlock(&LOCK_global_user_client_stats);
2631 + DBUG_PRINT("exit", ("mysqld_show_client_stats result is 0"));
2635 + DBUG_PRINT("exit", ("mysqld_show_client_stats result is 1"));
2639 +int fill_schema_thread_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2641 + TABLE *table= tables->table;
2642 + DBUG_ENTER("fill_schema_thread_stats");
2644 + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
2647 + // Iterates through all the global stats and sends them to the client.
2648 + // Pattern matching on the client IP is supported.
2650 + mysql_mutex_lock(&LOCK_global_user_client_stats);
2651 + int result= send_thread_stats(thd, &global_thread_stats, table);
2652 + mysql_mutex_unlock(&LOCK_global_user_client_stats);
2656 + DBUG_PRINT("exit", ("mysqld_show_thread_stats result is 0"));
2660 + DBUG_PRINT("exit", ("mysqld_show_thread_stats result is 1"));
2664 +// Sends the global table stats back to the client.
2665 +int fill_schema_table_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2667 + TABLE *table= tables->table;
2668 + DBUG_ENTER("fill_schema_table_stats");
2669 + char *table_full_name, *table_schema;
2671 + mysql_mutex_lock(&LOCK_global_table_stats);
2672 + for (uint i = 0; i < global_table_stats.records; ++i)
2674 + restore_record(table, s->default_values);
2675 + TABLE_STATS *table_stats =
2676 + (TABLE_STATS *) my_hash_element(&global_table_stats, i);
2678 + table_full_name= thd->strdup(table_stats->table);
2679 + table_schema= strsep(&table_full_name, ".");
2681 + TABLE_LIST tmp_table;
2682 + bzero((char *) &tmp_table,sizeof(tmp_table));
2683 + tmp_table.table_name= table_full_name;
2684 + tmp_table.db= table_schema;
2685 + tmp_table.grant.privilege= 0;
2686 + if (check_access(thd, SELECT_ACL, tmp_table.db,
2687 + &tmp_table.grant.privilege, 0, 0,
2688 + is_infoschema_db(table_schema)) ||
2689 + check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, 1))
2692 + table->field[0]->store(table_schema, strlen(table_schema), system_charset_info);
2693 + table->field[1]->store(table_full_name, strlen(table_full_name), system_charset_info);
2694 + table->field[2]->store((longlong)table_stats->rows_read, TRUE);
2695 + table->field[3]->store((longlong)table_stats->rows_changed, TRUE);
2696 + table->field[4]->store((longlong)table_stats->rows_changed_x_indexes, TRUE);
2698 + if (schema_table_store_record(thd, table))
2700 + mysql_mutex_unlock(&LOCK_global_table_stats);
2704 + mysql_mutex_unlock(&LOCK_global_table_stats);
2708 +// Sends the global index stats back to the client.
2709 +int fill_schema_index_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2711 + TABLE *table= tables->table;
2712 + DBUG_ENTER("fill_schema_index_stats");
2713 + char *index_full_name, *table_schema, *table_name;
2715 + mysql_mutex_lock(&LOCK_global_index_stats);
2716 + for (uint i = 0; i < global_index_stats.records; ++i)
2718 + restore_record(table, s->default_values);
2719 + INDEX_STATS *index_stats =
2720 + (INDEX_STATS *) my_hash_element(&global_index_stats, i);
2722 + index_full_name= thd->strdup(index_stats->index);
2723 + table_schema= strsep(&index_full_name, ".");
2724 + table_name= strsep(&index_full_name, ".");
2726 + TABLE_LIST tmp_table;
2727 + bzero((char *) &tmp_table,sizeof(tmp_table));
2728 + tmp_table.table_name= table_name;
2729 + tmp_table.db= table_schema;
2730 + tmp_table.grant.privilege= 0;
2731 + if (check_access(thd, SELECT_ACL, tmp_table.db,
2732 + &tmp_table.grant.privilege, 0, 0,
2733 + is_infoschema_db(table_schema)) ||
2734 + check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, 1))
2737 + table->field[0]->store(table_schema, strlen(table_schema), system_charset_info);
2738 + table->field[1]->store(table_name, strlen(table_name), system_charset_info);
2739 + table->field[2]->store(index_full_name, strlen(index_full_name), system_charset_info);
2740 + table->field[3]->store((longlong)index_stats->rows_read, TRUE);
2742 + if (schema_table_store_record(thd, table))
2744 + mysql_mutex_unlock(&LOCK_global_index_stats);
2748 + mysql_mutex_unlock(&LOCK_global_index_stats);
2753 /* collect status for all running threads */
2755 @@ -7512,6 +7828,104 @@
2759 +ST_FIELD_INFO user_stats_fields_info[]=
2761 + {"USER", USERNAME_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User", 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 client_stats_fields_info[]=
2787 + {"CLIENT", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Client", 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 thread_stats_fields_info[]=
2813 + {"THREAD_ID", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Thread_id", SKIP_OPEN_TABLE},
2814 + {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections", SKIP_OPEN_TABLE},
2815 + {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections", SKIP_OPEN_TABLE},
2816 + {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time", SKIP_OPEN_TABLE},
2817 + {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Busy_time", SKIP_OPEN_TABLE},
2818 + {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Cpu_time", SKIP_OPEN_TABLE},
2819 + {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_received", SKIP_OPEN_TABLE},
2820 + {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_sent", SKIP_OPEN_TABLE},
2821 + {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Binlog_bytes_written", SKIP_OPEN_TABLE},
2822 + {"ROWS_FETCHED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_fetched", SKIP_OPEN_TABLE},
2823 + {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated", SKIP_OPEN_TABLE},
2824 + {"TABLE_ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Table_rows_read", SKIP_OPEN_TABLE},
2825 + {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Select_commands", SKIP_OPEN_TABLE},
2826 + {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Update_commands", SKIP_OPEN_TABLE},
2827 + {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Other_commands", SKIP_OPEN_TABLE},
2828 + {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Commit_transactions", SKIP_OPEN_TABLE},
2829 + {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rollback_transactions", SKIP_OPEN_TABLE},
2830 + {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Denied_connections", SKIP_OPEN_TABLE},
2831 + {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Lost_connections", SKIP_OPEN_TABLE},
2832 + {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Access_denied", SKIP_OPEN_TABLE},
2833 + {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Empty_queries", SKIP_OPEN_TABLE},
2834 + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2837 +ST_FIELD_INFO table_stats_fields_info[]=
2839 + {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_schema", SKIP_OPEN_TABLE},
2840 + {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name", SKIP_OPEN_TABLE},
2841 + {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read", SKIP_OPEN_TABLE},
2842 + {"ROWS_CHANGED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_changed", SKIP_OPEN_TABLE},
2843 + {"ROWS_CHANGED_X_INDEXES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_changed_x_#indexes", SKIP_OPEN_TABLE},
2844 + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2847 +ST_FIELD_INFO index_stats_fields_info[]=
2849 + {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_schema", SKIP_OPEN_TABLE},
2850 + {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name", SKIP_OPEN_TABLE},
2851 + {"INDEX_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Index_name", SKIP_OPEN_TABLE},
2852 + {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read", SKIP_OPEN_TABLE},
2853 + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2857 ST_FIELD_INFO processlist_fields_info[]=
2859 {"ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Id", SKIP_OPEN_TABLE},
2860 @@ -7701,6 +8115,8 @@
2862 {"CHARACTER_SETS", charsets_fields_info, create_schema_table,
2863 fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0, 0},
2864 + {"CLIENT_STATISTICS", client_stats_fields_info, create_schema_table,
2865 + fill_schema_client_stats, make_old_format, 0, -1, -1, 0, 0},
2866 {"COLLATIONS", collation_fields_info, create_schema_table,
2867 fill_schema_collation, make_old_format, 0, -1, -1, 0, 0},
2868 {"COLLATION_CHARACTER_SET_APPLICABILITY", coll_charset_app_fields_info,
2869 @@ -7710,6 +8126,8 @@
2870 OPTIMIZE_I_S_TABLE|OPEN_VIEW_FULL},
2871 {"COLUMN_PRIVILEGES", column_privileges_fields_info, create_schema_table,
2872 fill_schema_column_privileges, 0, 0, -1, -1, 0, 0},
2873 + {"INDEX_STATISTICS", index_stats_fields_info, create_schema_table,
2874 + fill_schema_index_stats, make_old_format, 0, -1, -1, 0, 0},
2875 {"ENGINES", engines_fields_info, create_schema_table,
2876 fill_schema_engines, make_old_format, 0, -1, -1, 0, 0},
2877 #ifdef HAVE_EVENT_SCHEDULER
2878 @@ -7782,14 +8200,20 @@
2879 get_all_tables, make_table_names_old_format, 0, 1, 2, 1, 0},
2880 {"TABLE_PRIVILEGES", table_privileges_fields_info, create_schema_table,
2881 fill_schema_table_privileges, 0, 0, -1, -1, 0, 0},
2882 + {"TABLE_STATISTICS", table_stats_fields_info, create_schema_table,
2883 + fill_schema_table_stats, make_old_format, 0, -1, -1, 0, 0},
2884 {"TEMPORARY_TABLES", temporary_table_fields_info, create_schema_table,
2885 fill_temporary_tables, make_temporary_tables_old_format, 0, 2, 3, 0,
2886 OPEN_TABLE_ONLY|OPTIMIZE_I_S_TABLE},
2887 + {"THREAD_STATISTICS", thread_stats_fields_info, create_schema_table,
2888 + fill_schema_thread_stats, make_old_format, 0, -1, -1, 0, 0},
2889 {"TRIGGERS", triggers_fields_info, create_schema_table,
2890 get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0,
2891 OPEN_TRIGGER_ONLY|OPTIMIZE_I_S_TABLE},
2892 {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table,
2893 fill_schema_user_privileges, 0, 0, -1, -1, 0, 0},
2894 + {"USER_STATISTICS", user_stats_fields_info, create_schema_table,
2895 + fill_schema_user_stats, make_old_format, 0, -1, -1, 0, 0},
2896 {"VARIABLES", variables_fields_info, create_schema_table, fill_variables,
2897 make_old_format, 0, 0, -1, 1, 0},
2898 {"VIEWS", view_fields_info, create_schema_table,
2899 diff -ruN a/sql/sql_update.cc b/sql/sql_update.cc
2900 --- a/sql/sql_update.cc 2010-12-03 20:58:26.000000000 +0300
2901 +++ b/sql/sql_update.cc 2010-12-31 04:08:17.000000000 +0300
2902 @@ -900,8 +900,10 @@
2903 my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), (ulong) found,
2905 (ulong) thd->warning_info->statement_warn_count());
2906 - my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
2908 + ha_rows row_count=
2909 + (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
2910 + my_ok(thd, row_count, id, buff);
2911 + thd->updated_row_count += row_count;
2912 DBUG_PRINT("info",("%ld records updated", (long) updated));
2914 thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */
2915 @@ -2146,7 +2148,9 @@
2916 thd->first_successful_insert_id_in_prev_stmt : 0;
2917 my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO),
2918 (ulong) found, (ulong) updated, (ulong) thd->cuted_fields);
2919 - ::my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
2921 + ha_rows row_count=
2922 + (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
2923 + ::my_ok(thd, row_count, id, buff);
2924 + thd->updated_row_count+= row_count;
2927 diff -ruN a/sql/sql_yacc.yy b/sql/sql_yacc.yy
2928 --- a/sql/sql_yacc.yy 2010-12-03 20:58:26.000000000 +0300
2929 +++ b/sql/sql_yacc.yy 2010-12-31 05:06:16.000000000 +0300
2932 %token CLASS_ORIGIN_SYM /* SQL-2003-N */
2934 +%token CLIENT_STATS_SYM
2935 %token CLOSE_SYM /* SQL-2003-R */
2936 %token COALESCE /* SQL-2003-N */
2938 @@ -1017,6 +1018,7 @@
2942 +%token INDEX_STATS_SYM
2944 %token INITIAL_SIZE_SYM
2945 %token INNER_SYM /* SQL-2003-R */
2946 @@ -1315,6 +1317,7 @@
2948 %token TABLE_REF_PRIORITY
2949 %token TABLE_SYM /* SQL-2003-R */
2950 +%token TABLE_STATS_SYM
2951 %token TABLE_CHECKSUM_SYM
2952 %token TABLE_NAME_SYM /* SQL-2003-N */
2953 %token TEMPORARY /* SQL-2003-N */
2954 @@ -1324,6 +1327,7 @@
2957 %token THEN_SYM /* SQL-2003-R */
2958 +%token THREAD_STATS_SYM
2959 %token TIMESTAMP /* SQL-2003-R */
2960 %token TIMESTAMP_ADD
2961 %token TIMESTAMP_DIFF
2962 @@ -1361,6 +1365,7 @@
2964 %token USAGE /* SQL-2003-N */
2965 %token USER /* SQL-2003-R */
2966 +%token USER_STATS_SYM
2969 %token USING /* SQL-2003-R */
2970 @@ -11109,6 +11114,41 @@
2972 #endif // HAVE_RESPONSE_TIME_DISTRIBUTION
2974 + | CLIENT_STATS_SYM wild_and_where
2977 + Lex->sql_command= SQLCOM_SELECT;
2978 + if (prepare_schema_table(YYTHD, lex, 0, SCH_CLIENT_STATS))
2981 + | USER_STATS_SYM wild_and_where
2984 + lex->sql_command= SQLCOM_SELECT;
2985 + if (prepare_schema_table(YYTHD, lex, 0, SCH_USER_STATS))
2988 + | THREAD_STATS_SYM wild_and_where
2991 + Lex->sql_command= SQLCOM_SELECT;
2992 + if (prepare_schema_table(YYTHD, lex, 0, SCH_THREAD_STATS))
2995 + | TABLE_STATS_SYM wild_and_where
2998 + lex->sql_command= SQLCOM_SELECT;
2999 + if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_STATS))
3002 + | INDEX_STATS_SYM wild_and_where
3005 + lex->sql_command= SQLCOM_SELECT;
3006 + if (prepare_schema_table(YYTHD, lex, 0, SCH_INDEX_STATS))
3009 | CREATE PROCEDURE_SYM sp_name
3012 @@ -11351,6 +11391,16 @@
3013 Lex->type|= REFRESH_QUERY_RESPONSE_TIME;
3014 #endif // HAVE_RESPONSE_TIME_DISTRIBUTION
3016 + | CLIENT_STATS_SYM
3017 + { Lex->type|= REFRESH_CLIENT_STATS; }
3019 + { Lex->type|= REFRESH_USER_STATS; }
3020 + | THREAD_STATS_SYM
3021 + { Lex->type|= REFRESH_THREAD_STATS; }
3023 + { Lex->type|= REFRESH_TABLE_STATS; }
3025 + { Lex->type|= REFRESH_INDEX_STATS; }
3027 { Lex->type|= REFRESH_MASTER; }
3029 @@ -12473,6 +12523,7 @@
3033 + | CLIENT_STATS_SYM {}
3035 | CLASS_ORIGIN_SYM {}
3037 @@ -12541,6 +12592,7 @@
3041 + | INDEX_STATS_SYM {}
3042 | IGNORE_SERVER_IDS_SYM {}
3045 @@ -12692,6 +12744,7 @@
3049 + | TABLE_STATS_SYM {}
3052 | TABLE_CHECKSUM_SYM {}
3053 @@ -12717,6 +12770,7 @@
3057 + | USER_STATS_SYM {}
3061 diff -ruN a/sql/structs.h b/sql/structs.h
3062 --- a/sql/structs.h 2010-12-03 20:58:26.000000000 +0300
3063 +++ b/sql/structs.h 2010-12-31 05:12:04.000000000 +0300
3065 #include "my_time.h" /* enum_mysql_timestamp_type */
3066 #include "thr_lock.h" /* thr_lock_type */
3067 #include "my_base.h" /* ha_rows, ha_key_alg */
3068 +#include "mysql_com.h"
3072 @@ -218,6 +219,171 @@
3073 USER_RESOURCES user_resources;
3076 +typedef struct st_user_stats {
3077 + char user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1];
3078 + // Account name the user is mapped to when this is a user from mapped_user.
3079 + // Otherwise, the same value as user.
3080 + char priv_user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1];
3081 + uint total_connections;
3082 + uint concurrent_connections;
3083 + time_t connected_time; // in seconds
3084 + double busy_time; // in seconds
3085 + double cpu_time; // in seconds
3086 + ulonglong bytes_received;
3087 + ulonglong bytes_sent;
3088 + ulonglong binlog_bytes_written;
3089 + ha_rows rows_fetched, rows_updated, rows_read;
3090 + ulonglong select_commands, update_commands, other_commands;
3091 + ulonglong commit_trans, rollback_trans;
3092 + ulonglong denied_connections, lost_connections;
3093 + ulonglong access_denied_errors;
3094 + ulonglong empty_queries;
3097 +/* Lookup function for my_hash tables with USER_STATS entries */
3098 +extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
3099 + my_bool not_used __attribute__((unused)));
3101 +/* Free all memory for a my_hash table with USER_STATS entries */
3102 +extern void free_user_stats(USER_STATS* user_stats);
3104 +/* Intialize an instance of USER_STATS */
3106 +init_user_stats(USER_STATS *user_stats,
3108 + const char *priv_user,
3109 + uint total_connections,
3110 + uint concurrent_connections,
3111 + time_t connected_time,
3114 + ulonglong bytes_received,
3115 + ulonglong bytes_sent,
3116 + ulonglong binlog_bytes_written,
3117 + ha_rows rows_fetched,
3118 + ha_rows rows_updated,
3119 + ha_rows rows_read,
3120 + ulonglong select_commands,
3121 + ulonglong update_commands,
3122 + ulonglong other_commands,
3123 + ulonglong commit_trans,
3124 + ulonglong rollback_trans,
3125 + ulonglong denied_connections,
3126 + ulonglong lost_connections,
3127 + ulonglong access_denied_errors,
3128 + ulonglong empty_queries);
3130 +/* Increment values of an instance of USER_STATS */
3132 +add_user_stats(USER_STATS *user_stats,
3133 + uint total_connections,
3134 + uint concurrent_connections,
3135 + time_t connected_time,
3138 + ulonglong bytes_received,
3139 + ulonglong bytes_sent,
3140 + ulonglong binlog_bytes_written,
3141 + ha_rows rows_fetched,
3142 + ha_rows rows_updated,
3143 + ha_rows rows_read,
3144 + ulonglong select_commands,
3145 + ulonglong update_commands,
3146 + ulonglong other_commands,
3147 + ulonglong commit_trans,
3148 + ulonglong rollback_trans,
3149 + ulonglong denied_connections,
3150 + ulonglong lost_connections,
3151 + ulonglong access_denied_errors,
3152 + ulonglong empty_queries);
3154 +typedef struct st_thread_stats {
3156 + uint total_connections;
3157 + uint concurrent_connections;
3158 + time_t connected_time; // in seconds
3159 + double busy_time; // in seconds
3160 + double cpu_time; // in seconds
3161 + ulonglong bytes_received;
3162 + ulonglong bytes_sent;
3163 + ulonglong binlog_bytes_written;
3164 + ha_rows rows_fetched, rows_updated, rows_read;
3165 + ulonglong select_commands, update_commands, other_commands;
3166 + ulonglong commit_trans, rollback_trans;
3167 + ulonglong denied_connections, lost_connections;
3168 + ulonglong access_denied_errors;
3169 + ulonglong empty_queries;
3172 +/* Lookup function for my_hash tables with THREAD_STATS entries */
3173 +extern "C" uchar *get_key_thread_stats(THREAD_STATS *thread_stats, size_t *length,
3174 + my_bool not_used __attribute__((unused)));
3176 +/* Free all memory for a my_hash table with THREAD_STATS entries */
3177 +extern void free_thread_stats(THREAD_STATS* thread_stats);
3179 +/* Intialize an instance of THREAD_STATS */
3181 +init_thread_stats(THREAD_STATS *thread_stats,
3183 + uint total_connections,
3184 + uint concurrent_connections,
3185 + time_t connected_time,
3188 + ulonglong bytes_received,
3189 + ulonglong bytes_sent,
3190 + ulonglong binlog_bytes_written,
3191 + ha_rows rows_fetched,
3192 + ha_rows rows_updated,
3193 + ha_rows rows_read,
3194 + ulonglong select_commands,
3195 + ulonglong update_commands,
3196 + ulonglong other_commands,
3197 + ulonglong commit_trans,
3198 + ulonglong rollback_trans,
3199 + ulonglong denied_connections,
3200 + ulonglong lost_connections,
3201 + ulonglong access_denied_errors,
3202 + ulonglong empty_queries);
3204 +/* Increment values of an instance of THREAD_STATS */
3206 +add_thread_stats(THREAD_STATS *thread_stats,
3207 + uint total_connections,
3208 + uint concurrent_connections,
3209 + time_t connected_time,
3212 + ulonglong bytes_received,
3213 + ulonglong bytes_sent,
3214 + ulonglong binlog_bytes_written,
3215 + ha_rows rows_fetched,
3216 + ha_rows rows_updated,
3217 + ha_rows rows_read,
3218 + ulonglong select_commands,
3219 + ulonglong update_commands,
3220 + ulonglong other_commands,
3221 + ulonglong commit_trans,
3222 + ulonglong rollback_trans,
3223 + ulonglong denied_connections,
3224 + ulonglong lost_connections,
3225 + ulonglong access_denied_errors,
3226 + ulonglong empty_queries);
3228 +typedef struct st_table_stats {
3229 + char table[NAME_LEN * 2 + 2]; // [db] + '.' + [table] + '\0'
3230 + ulonglong rows_read, rows_changed;
3231 + ulonglong rows_changed_x_indexes;
3232 + /* Stores enum db_type, but forward declarations cannot be done */
3236 +typedef struct st_index_stats {
3237 + char index[NAME_LEN * 3 + 3]; // [db] + '.' + [table] + '.' + [index] + '\0'
3238 + ulonglong rows_read;
3241 /* Bits in form->update */
3242 #define REG_MAKE_DUPP 1 /* Make a copy of record when read */
3243 #define REG_NEW_RECORD 2 /* Write a new record if not found */
3244 diff -ruN a/sql/sys_vars.cc b/sql/sys_vars.cc
3245 --- a/sql/sys_vars.cc 2010-12-03 20:58:26.000000000 +0300
3246 +++ b/sql/sys_vars.cc 2010-12-30 02:22:25.000000000 +0300
3247 @@ -1563,6 +1563,17 @@
3248 NO_MUTEX_GUARD, NOT_IN_BINLOG,
3249 ON_CHECK(check_read_only), ON_UPDATE(fix_read_only));
3251 +static Sys_var_mybool Sys_userstat_running(
3252 + "userstat_running",
3253 + "Control USER_STATISTICS, CLIENT_STATISTICS, THREAD_STATISTICS, "
3254 + "INDEX_STATISTICS and TABLE_STATISTICS running",
3255 + GLOBAL_VAR(opt_userstat_running), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
3257 +static Sys_var_mybool Sys_thread_statistics(
3258 + "thread_statistics",
3259 + "Control TABLE_STATISTICS running, when userstat_running is enabled",
3260 + GLOBAL_VAR(opt_thread_statistics), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
3262 // Small lower limit to be able to test MRR
3263 static Sys_var_ulong Sys_read_rnd_buff_size(
3264 "read_rnd_buffer_size",
3265 diff -ruN a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc
3266 --- a/storage/myisam/ha_myisam.cc 2010-12-03 20:58:26.000000000 +0300
3267 +++ b/storage/myisam/ha_myisam.cc 2010-12-31 05:58:01.000000000 +0300
3270 int ha_myisam::write_row(uchar *buf)
3273 ha_statistic_increment(&SSV::ha_write_count);
3275 /* If we have a timestamp column, update it to the current time */
3276 @@ -780,11 +781,13 @@
3278 if (table->next_number_field && buf == table->record[0])
3281 if ((error= update_auto_increment()))
3284 - return mi_write(file,buf);
3285 + error=mi_write(file,buf);
3291 int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
3292 @@ -1535,16 +1538,24 @@
3294 int ha_myisam::update_row(const uchar *old_data, uchar *new_data)
3297 ha_statistic_increment(&SSV::ha_update_count);
3298 if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
3299 table->timestamp_field->set_time();
3300 - return mi_update(file,old_data,new_data);
3301 + error=mi_update(file,old_data,new_data);
3307 int ha_myisam::delete_row(const uchar *buf)
3310 ha_statistic_increment(&SSV::ha_delete_count);
3311 - return mi_delete(file,buf);
3312 + error=mi_delete(file,buf);
3318 int ha_myisam::index_read_map(uchar *buf, const uchar *key,
3319 @@ -1556,6 +1567,14 @@
3320 ha_statistic_increment(&SSV::ha_read_key_count);
3321 int error=mi_rkey(file, buf, active_index, key, keypart_map, find_flag);
3322 table->status=error ? STATUS_NOT_FOUND: 0;
3327 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3328 + if (inx >= 0 && inx < MAX_KEY)
3329 + index_rows_read[inx]++;
3331 MYSQL_INDEX_READ_ROW_DONE(error);
3334 @@ -1568,6 +1587,14 @@
3335 ha_statistic_increment(&SSV::ha_read_key_count);
3336 int error=mi_rkey(file, buf, index, key, keypart_map, find_flag);
3337 table->status=error ? STATUS_NOT_FOUND: 0;
3343 + if (inx >= 0 && inx < MAX_KEY)
3344 + index_rows_read[inx]++;
3346 MYSQL_INDEX_READ_ROW_DONE(error);
3349 @@ -1582,6 +1609,14 @@
3350 int error=mi_rkey(file, buf, active_index, key, keypart_map,
3351 HA_READ_PREFIX_LAST);
3352 table->status=error ? STATUS_NOT_FOUND: 0;
3357 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3358 + if (inx >= 0 && inx < MAX_KEY)
3359 + index_rows_read[inx]++;
3361 MYSQL_INDEX_READ_ROW_DONE(error);
3364 @@ -1593,6 +1628,13 @@
3365 ha_statistic_increment(&SSV::ha_read_next_count);
3366 int error=mi_rnext(file,buf,active_index);
3367 table->status=error ? STATUS_NOT_FOUND: 0;
3371 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3372 + if (inx >= 0 && inx < MAX_KEY)
3373 + index_rows_read[inx]++;
3375 MYSQL_INDEX_READ_ROW_DONE(error);
3378 @@ -1604,6 +1646,13 @@
3379 ha_statistic_increment(&SSV::ha_read_prev_count);
3380 int error=mi_rprev(file,buf, active_index);
3381 table->status=error ? STATUS_NOT_FOUND: 0;
3385 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3386 + if (inx >= 0 && inx < MAX_KEY)
3387 + index_rows_read[inx]++;
3389 MYSQL_INDEX_READ_ROW_DONE(error);
3392 @@ -1615,6 +1664,14 @@
3393 ha_statistic_increment(&SSV::ha_read_first_count);
3394 int error=mi_rfirst(file, buf, active_index);
3395 table->status=error ? STATUS_NOT_FOUND: 0;
3400 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3401 + if (inx >= 0 && inx < MAX_KEY)
3402 + index_rows_read[inx]++;
3404 MYSQL_INDEX_READ_ROW_DONE(error);
3407 @@ -1626,6 +1683,14 @@
3408 ha_statistic_increment(&SSV::ha_read_last_count);
3409 int error=mi_rlast(file, buf, active_index);
3410 table->status=error ? STATUS_NOT_FOUND: 0;
3415 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3416 + if (inx >= 0 && inx < MAX_KEY)
3417 + index_rows_read[inx]++;
3419 MYSQL_INDEX_READ_ROW_DONE(error);
3422 @@ -1643,6 +1708,14 @@
3423 error= mi_rnext_same(file,buf);
3424 } while (error == HA_ERR_RECORD_DELETED);
3425 table->status=error ? STATUS_NOT_FOUND: 0;
3430 + int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3431 + if (inx >= 0 && inx < MAX_KEY)
3432 + index_rows_read[inx]++;
3434 MYSQL_INDEX_READ_ROW_DONE(error);
3437 @@ -1662,6 +1735,8 @@
3438 ha_statistic_increment(&SSV::ha_read_rnd_next_count);
3439 int error=mi_scan(file, buf);
3440 table->status=error ? STATUS_NOT_FOUND: 0;
3443 MYSQL_READ_ROW_DONE(error);
3446 @@ -1678,6 +1753,8 @@
3447 ha_statistic_increment(&SSV::ha_read_rnd_count);
3448 int error=mi_rrnd(file, buf, my_get_ptr(pos,ref_length));
3449 table->status=error ? STATUS_NOT_FOUND: 0;
3452 MYSQL_READ_ROW_DONE(error);