]> git.pld-linux.org Git - packages/mysql.git/blob - userstat.patch
- updated to 5.5.32
[packages/mysql.git] / userstat.patch
1 # name       : userstat.patch
2 # introduced : 11 or before
3 # maintainer : Oleg
4 #
5 #!!! notice !!!
6 # Any small change to this file in the main branch
7 # should be done or reviewed by the maintainer!
8 --- a/include/mysql/plugin.h
9 +++ b/include/mysql/plugin.h
10 @@ -556,6 +556,9 @@
11  unsigned long thd_log_slow_verbosity(const MYSQL_THD thd);
12  int thd_opt_slow_log();
13  #define EXTENDED_SLOWLOG
14 +
15 +#define EXTENDED_FOR_USERSTAT
16 +
17  /**
18    Create a temporary file.
19  
20 --- a/include/mysql_com.h
21 +++ b/include/mysql_com.h
22 @@ -31,6 +31,7 @@
23  
24  #define SERVER_VERSION_LENGTH 60
25  #define SQLSTATE_LENGTH 5
26 +#define LIST_PROCESS_HOST_LEN 64
27  
28  /*
29    Maximum length of comments
30 @@ -146,6 +147,11 @@
31  #define REFRESH_DES_KEY_FILE        0x40000L
32  #define REFRESH_USER_RESOURCES      0x80000L
33  #define REFRESH_QUERY_RESPONSE_TIME 0x100000L /* response time distibution */
34 +#define REFRESH_TABLE_STATS    0x200000L /* Refresh table stats my_hash table */
35 +#define REFRESH_INDEX_STATS    0x400000L /* Refresh index stats my_hash table */
36 +#define REFRESH_USER_STATS     0x800000L /* Refresh user stats my_hash table */
37 +#define REFRESH_CLIENT_STATS   0x1000000L /* Refresh client stats my_hash table */
38 +#define REFRESH_THREAD_STATS   0x2000000L /* Refresh thread stats my_hash table */
39  
40  #define CLIENT_LONG_PASSWORD   1       /* new more secure passwords */
41  #define CLIENT_FOUND_ROWS      2       /* Found instead of affected rows */
42 --- /dev/null
43 +++ b/patch_info/userstats.patch
44 @@ -0,0 +1,17 @@
45 +File=userstats.patch
46 +Name=SHOW USER/TABLE/INDEX statistics
47 +Version=V2
48 +Author=Google
49 +License=GPL
50 +Comment=Added INFORMATION_SCHEMA.*_STATISTICS
51 +2008-12-01
52 +YK: fix behavior for prepared statements
53 +
54 +2008-11-26
55 +YK: add switch variable "userstat" to control INFORMATION_SCHEMA.*_STATISTICS (default:OFF)
56 +2010-12-31
57 +Ported to 5.5.8
58 +2011-1-5
59 +Fix porting
60 +2011-02
61 +Rename variable USERSTAT_RUNNING => USERSTAT
62 --- a/sql/handler.cc
63 +++ b/sql/handler.cc
64 @@ -1245,6 +1245,8 @@
65          goto end;
66        }
67      DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
68 +    if (is_real_trans)
69 +      thd->diff_commit_trans++;
70      RUN_HOOK(transaction, after_commit, (thd, FALSE));
71  end:
72      if (rw_trans && mdl_request.ticket)
73 @@ -1399,6 +1401,8 @@
74    /* Always cleanup. Even if nht==0. There may be savepoints. */
75    if (is_real_trans)
76      thd->transaction.cleanup();
77 +
78 +  thd->diff_rollback_trans++;
79    if (all)
80      thd->transaction_rollback_request= FALSE;
81  
82 @@ -1803,6 +1807,7 @@
83      ha_info->reset(); /* keep it conveniently zero-filled */
84    }
85    trans->ha_list= sv->ha_list;
86 +  thd->diff_rollback_trans++;
87    DBUG_RETURN(error);
88  }
89  
90 @@ -2179,6 +2184,8 @@
91        dup_ref=ref+ALIGN_SIZE(ref_length);
92      cached_table_flags= table_flags();
93    }
94 +  rows_read= rows_changed= 0;
95 +  memset(index_rows_read, 0, sizeof(index_rows_read));
96    DBUG_RETURN(error);
97  }
98  
99 @@ -3644,6 +3651,127 @@
100    return;
101  }
102  
103 +// Updates the global table stats with the TABLE this handler represents.
104 +void handler::update_global_table_stats()
105 +{
106 +  if (!opt_userstat)
107 +  {
108 +    rows_read= rows_changed= 0;
109 +    return;
110 +  }
111 +
112 +  if (!rows_read && !rows_changed)
113 +    return;  // Nothing to update.
114 +  // table_cache_key is db_name + '\0' + table_name + '\0'.
115 +  if (!table->s || !table->s->table_cache_key.str || !table->s->table_name.str)
116 +    return;
117 +
118 +  TABLE_STATS* table_stats;
119 +  char key[NAME_LEN * 2 + 2];
120 +  // [db] + '.' + [table]
121 +  sprintf(key, "%s.%s", table->s->table_cache_key.str, table->s->table_name.str);
122 +
123 +  mysql_mutex_lock(&LOCK_global_table_stats);
124 +  // Gets the global table stats, creating one if necessary.
125 +  if (!(table_stats = (TABLE_STATS *) my_hash_search(&global_table_stats,
126 +                                                     (uchar*)key,
127 +                                                     strlen(key))))
128 +  {
129 +    if (!(table_stats = ((TABLE_STATS *)
130 +                         my_malloc(sizeof(TABLE_STATS), MYF(MY_WME | MY_ZEROFILL)))))
131 +    {
132 +      // Out of memory.
133 +      sql_print_error("Allocating table stats failed.");
134 +      goto end;
135 +    }
136 +    strncpy(table_stats->table, key, sizeof(table_stats->table));
137 +    table_stats->rows_read=              0;
138 +    table_stats->rows_changed=           0;
139 +    table_stats->rows_changed_x_indexes= 0;
140 +    table_stats->engine_type=            (int) ht->db_type;
141 +
142 +    if (my_hash_insert(&global_table_stats, (uchar *) table_stats))
143 +    {
144 +      // Out of memory.
145 +      sql_print_error("Inserting table stats failed.");
146 +      my_free((char *) table_stats);
147 +      goto end;
148 +    }
149 +  }
150 +  // Updates the global table stats.
151 +  table_stats->rows_read+=              rows_read;
152 +  table_stats->rows_changed+=           rows_changed;
153 +  table_stats->rows_changed_x_indexes+=
154 +    rows_changed * (table->s->keys ? table->s->keys : 1);
155 +  current_thd->diff_total_read_rows+=   rows_read;
156 +  rows_read= rows_changed=              0;
157 +end:
158 +  mysql_mutex_unlock(&LOCK_global_table_stats);
159 +}
160 +
161 +// Updates the global index stats with this handler's accumulated index reads.
162 +void handler::update_global_index_stats()
163 +{
164 +  // table_cache_key is db_name + '\0' + table_name + '\0'.
165 +  if (!table->s || !table->s->table_cache_key.str || !table->s->table_name.str)
166 +    return;
167 +
168 +  if (!opt_userstat)
169 +  {
170 +    for (uint x= 0; x < table->s->keys; ++x)
171 +    {
172 +      index_rows_read[x]= 0;
173 +    }
174 +    return;
175 +  }
176 +
177 +  for (uint x = 0; x < table->s->keys; ++x)
178 +  {
179 +    if (index_rows_read[x])
180 +    {
181 +      // Rows were read using this index.
182 +      KEY* key_info = &table->key_info[x];
183 +
184 +      if (!key_info->name) continue;
185 +
186 +      INDEX_STATS* index_stats;
187 +      char key[NAME_LEN * 3 + 3];
188 +      // [db] + '.' + [table] + '.' + [index]
189 +      sprintf(key, "%s.%s.%s",  table->s->table_cache_key.str,
190 +              table->s->table_name.str, key_info->name);
191 +
192 +      mysql_mutex_lock(&LOCK_global_index_stats);
193 +      // Gets the global index stats, creating one if necessary.
194 +      if (!(index_stats = (INDEX_STATS *) my_hash_search(&global_index_stats,
195 +                                                         (uchar *) key,
196 +                                                         strlen(key))))
197 +      {
198 +        if (!(index_stats = ((INDEX_STATS *)
199 +                             my_malloc(sizeof(INDEX_STATS), MYF(MY_WME | MY_ZEROFILL)))))
200 +        {
201 +          // Out of memory.
202 +          sql_print_error("Allocating index stats failed.");
203 +          goto end;
204 +        }
205 +        strncpy(index_stats->index, key, sizeof(index_stats->index));
206 +        index_stats->rows_read= 0;
207 +
208 +        if (my_hash_insert(&global_index_stats, (uchar *) index_stats))
209 +        {
210 +          // Out of memory.
211 +          sql_print_error("Inserting index stats failed.");
212 +          my_free((char *) index_stats);
213 +          goto end;
214 +        }
215 +      }
216 +      // Updates the global index stats.
217 +      index_stats->rows_read+= index_rows_read[x];
218 +      index_rows_read[x]=      0;
219 +  end:
220 +      mysql_mutex_unlock(&LOCK_global_index_stats);
221 +    }
222 +  }
223 +}
224  
225  /****************************************************************************
226  ** Some general functions that isn't in the handler class
227 --- a/sql/handler.h
228 +++ b/sql/handler.h
229 @@ -36,6 +36,10 @@
230  #include <ft_global.h>
231  #include <keycache.h>
232  
233 +#if MAX_KEY > 128
234 +#error MAX_KEY is too large.  Values up to 128 are supported.
235 +#endif
236 +
237  // the following is for checking tables
238  
239  #define HA_ADMIN_ALREADY_DONE    1
240 @@ -562,10 +566,12 @@
241  enum enum_schema_tables
242  {
243    SCH_CHARSETS= 0,
244 +  SCH_CLIENT_STATS,
245    SCH_COLLATIONS,
246    SCH_COLLATION_CHARACTER_SET_APPLICABILITY,
247    SCH_COLUMNS,
248    SCH_COLUMN_PRIVILEGES,
249 +  SCH_INDEX_STATS,
250    SCH_ENGINES,
251    SCH_EVENTS,
252    SCH_FILES,
253 @@ -593,9 +599,12 @@
254    SCH_TABLE_CONSTRAINTS,
255    SCH_TABLE_NAMES,
256    SCH_TABLE_PRIVILEGES,
257 +  SCH_TABLE_STATS,
258    SCH_TEMPORARY_TABLES,
259 +  SCH_THREAD_STATS,
260    SCH_TRIGGERS,
261    SCH_USER_PRIVILEGES,
262 +  SCH_USER_STATS,
263    SCH_VARIABLES,
264    SCH_VIEWS
265  };
266 @@ -1233,6 +1242,9 @@
267    bool locked;
268    bool implicit_emptied;                /* Can be !=0 only if HEAP */
269    const COND *pushed_cond;
270 +  ulonglong rows_read;
271 +  ulonglong rows_changed;
272 +  ulonglong index_rows_read[MAX_KEY];
273    /**
274      next_insert_id is the next value which should be inserted into the
275      auto_increment column: in a inserting-multi-row statement (like INSERT
276 @@ -1284,10 +1296,12 @@
277      ref_length(sizeof(my_off_t)),
278      ft_handler(0), inited(NONE),
279      locked(FALSE), implicit_emptied(0),
280 -    pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
281 +    pushed_cond(0), rows_read(0), rows_changed(0), next_insert_id(0), insert_id_for_cur_row(0),
282      auto_inc_intervals_count(0),
283      m_psi(NULL)
284 -    {}
285 +    {
286 +      memset(index_rows_read, 0, sizeof(index_rows_read));
287 +    }
288    virtual ~handler(void)
289    {
290      DBUG_ASSERT(locked == FALSE);
291 @@ -1410,6 +1424,8 @@
292    {
293      table= table_arg;
294      table_share= share;
295 +    rows_read = rows_changed= 0;
296 +    memset(index_rows_read, 0, sizeof(index_rows_read));
297    }
298    virtual double scan_time()
299    { return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; }
300 @@ -1805,6 +1821,8 @@
301    virtual bool is_crashed() const  { return 0; }
302    virtual bool auto_repair() const { return 0; }
303  
304 +  void update_global_table_stats();
305 +  void update_global_index_stats();
306  
307  #define CHF_CREATE_FLAG 0
308  #define CHF_DELETE_FLAG 1
309 --- a/sql/lex.h
310 +++ b/sql/lex.h
311 @@ -111,6 +111,7 @@
312    { "CIPHER",          SYM(CIPHER_SYM)},
313    { "CLASS_ORIGIN",     SYM(CLASS_ORIGIN_SYM)},
314    { "CLIENT",          SYM(CLIENT_SYM)},
315 +  { "CLIENT_STATISTICS",       SYM(CLIENT_STATS_SYM)},
316    { "CLOSE",           SYM(CLOSE_SYM)},
317    { "COALESCE",                SYM(COALESCE)},
318    { "CODE",             SYM(CODE_SYM)},
319 @@ -257,6 +258,7 @@
320    { "IN",              SYM(IN_SYM)},
321    { "INDEX",           SYM(INDEX_SYM)},
322    { "INDEXES",         SYM(INDEXES)},
323 +  { "INDEX_STATISTICS",        SYM(INDEX_STATS_SYM)},
324    { "INFILE",          SYM(INFILE)},
325    { "INITIAL_SIZE",    SYM(INITIAL_SIZE_SYM)},
326    { "INNER",           SYM(INNER_SYM)},
327 @@ -550,12 +552,14 @@
328    { "TABLES",          SYM(TABLES)},
329    { "TABLESPACE",              SYM(TABLESPACE)},
330    { "TABLE_CHECKSUM",  SYM(TABLE_CHECKSUM_SYM)},
331 +  { "TABLE_STATISTICS",        SYM(TABLE_STATS_SYM)},
332    { "TEMPORARY",       SYM(TEMPORARY)},
333    { "TEMPTABLE",       SYM(TEMPTABLE_SYM)},
334    { "TERMINATED",      SYM(TERMINATED)},
335    { "TEXT",            SYM(TEXT_SYM)},
336    { "THAN",             SYM(THAN_SYM)},
337    { "THEN",            SYM(THEN_SYM)},
338 +  { "THREAD_STATISTICS",       SYM(THREAD_STATS_SYM)},
339    { "TIME",            SYM(TIME_SYM)},
340    { "TIMESTAMP",       SYM(TIMESTAMP)},
341    { "TIMESTAMPADD",     SYM(TIMESTAMP_ADD)},
342 @@ -591,6 +595,7 @@
343    { "USE",             SYM(USE_SYM)},
344    { "USER",            SYM(USER)},
345    { "USER_RESOURCES",  SYM(RESOURCES)},
346 +  { "USER_STATISTICS", SYM(USER_STATS_SYM)},
347    { "USE_FRM",         SYM(USE_FRM)},
348    { "USING",           SYM(USING)},
349    { "UTC_DATE",         SYM(UTC_DATE_SYM)},
350 --- a/sql/log.cc
351 +++ b/sql/log.cc
352 @@ -1007,6 +1007,13 @@
353      mysql_slow_log.reopen_file();
354  }
355  
356 +void Log_to_file_event_handler::flush_slow_log()
357 +{
358 +  /* reopen slow log file */
359 +  if (opt_slow_log)
360 +    mysql_slow_log.reopen_file();
361 +}
362 +
363  /*
364    Log error with all enabled log event handlers
365  
366 @@ -5062,6 +5069,8 @@
367                               thd->first_successful_insert_id_in_prev_stmt_for_binlog);
368            if (e.write(file))
369              goto err;
370 +          if (file == &log_file)
371 +            thd->binlog_bytes_written+= e.data_written;
372          }
373          if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
374          {
375 @@ -5073,12 +5082,16 @@
376                               minimum());
377            if (e.write(file))
378              goto err;
379 +          if (file == &log_file)
380 +            thd->binlog_bytes_written+= e.data_written;
381          }
382          if (thd->rand_used)
383          {
384            Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2);
385            if (e.write(file))
386              goto err;
387 +          if (file == &log_file)
388 +            thd->binlog_bytes_written+= e.data_written;
389          }
390          if (thd->user_var_events.elements)
391          {
392 @@ -5101,6 +5114,8 @@
393                                   flags);
394              if (e.write(file))
395                goto err;
396 +            if (file == &log_file)
397 +              thd->binlog_bytes_written+= e.data_written;
398            }
399          }
400        }
401 @@ -5112,6 +5127,8 @@
402      if (event_info->write(file) ||
403          DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0))
404        goto err;
405 +    if (file == &log_file)
406 +      thd->binlog_bytes_written+= event_info->data_written;
407  
408      error= 0;
409  err:
410 @@ -5346,7 +5363,8 @@
411      be reset as a READ_CACHE to be able to read the contents from it.
412   */
413  
414 -int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
415 +int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache,
416 +                               bool lock_log, bool sync_log)
417  {
418    Mutex_sentry sentry(lock_log ? &LOCK_log : NULL);
419  
420 @@ -5393,6 +5411,7 @@
421        /* write the first half of the split header */
422        if (my_b_write(&log_file, header, carry))
423          return ER_ERROR_ON_WRITE;
424 +      thd->binlog_bytes_written+= carry;
425  
426        /*
427          copy fixed second half of header to cache so the correct
428 @@ -5461,6 +5480,7 @@
429      /* Write data to the binary log file */
430      if (my_b_write(&log_file, cache->read_pos, length))
431        return ER_ERROR_ON_WRITE;
432 +    thd->binlog_bytes_written+= length;
433      cache->read_pos=cache->read_end;           // Mark buffer used up
434    } while ((length= my_b_fill(cache)));
435  
436 @@ -5584,20 +5604,23 @@
437        Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), TRUE, FALSE, TRUE, 0);
438        if (qinfo.write(&log_file))
439          goto err;
440 +      thd->binlog_bytes_written+= qinfo.data_written;
441        DBUG_EXECUTE_IF("crash_before_writing_xid",
442                        {
443 -                        if ((write_error= write_cache(cache, false, true)))
444 +                        if ((write_error= write_cache(thd, cache, false, true)))
445                            DBUG_PRINT("info", ("error writing binlog cache: %d",
446                                                 write_error));
447                          DBUG_PRINT("info", ("crashing before writing xid"));
448                          DBUG_SUICIDE();
449                        });
450  
451 -      if ((write_error= write_cache(cache, false, false)))
452 +      if ((write_error= write_cache(thd, cache, false, false)))
453          goto err;
454  
455        if (commit_event && commit_event->write(&log_file))
456          goto err;
457 +      if (commit_event)
458 +        thd->binlog_bytes_written+= commit_event->data_written;
459  
460        if (incident && write_incident(thd, FALSE))
461          goto err;
462 --- a/sql/log.h
463 +++ b/sql/log.h
464 @@ -437,7 +437,8 @@
465    bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event, bool incident);
466    bool write_incident(THD *thd, bool lock);
467  
468 -  int  write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync);
469 +  int  write_cache(THD *thd, IO_CACHE *cache,
470 +                   bool lock_log, bool flush_and_sync);
471    void set_write_error(THD *thd, bool is_transactional);
472    bool check_write_error(THD *thd);
473  
474 @@ -591,6 +592,7 @@
475                             const char *sql_text, uint sql_text_len,
476                             CHARSET_INFO *client_cs);
477    void flush();
478 +  void flush_slow_log();
479    void init_pthread_objects();
480    MYSQL_QUERY_LOG *get_mysql_slow_log() { return &mysql_slow_log; }
481    MYSQL_QUERY_LOG *get_mysql_log() { return &mysql_log; }
482 --- a/sql/mysqld.cc
483 +++ b/sql/mysqld.cc
484 @@ -445,6 +445,7 @@
485  MYSQL_PLUGIN_IMPORT uint    opt_debug_sync_timeout= 0;
486  #endif /* defined(ENABLED_DEBUG_SYNC) */
487  my_bool opt_old_style_user_limits= 0, trust_function_creators= 0;
488 +my_bool opt_userstat= 0, opt_thread_statistics= 0;
489  my_bool opt_optimizer_fix= 0;
490  /*
491    True if there is at least one per-hour limit for some user, so we should
492 @@ -496,6 +497,7 @@
493  ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
494  ulong binlog_stmt_cache_use= 0, binlog_stmt_cache_disk_use= 0;
495  ulong max_connections, max_connect_errors;
496 +ulonglong denied_connections= 0;
497  
498  /* flashcache */
499  int cachedev_fd;
500 @@ -645,7 +647,9 @@
501    LOCK_crypt,
502    LOCK_global_system_variables,
503    LOCK_user_conn, LOCK_slave_list, LOCK_active_mi,
504 -  LOCK_connection_count, LOCK_error_messages;
505 +  LOCK_connection_count, LOCK_error_messages,
506 +  LOCK_stats, LOCK_global_user_client_stats,
507 +  LOCK_global_table_stats, LOCK_global_index_stats;
508  /**
509    The below lock protects access to two global server variables:
510    max_prepared_stmt_count and prepared_stmt_count. These variables
511 @@ -1509,6 +1513,11 @@
512  #ifdef HAVE_RESPONSE_TIME_DISTRIBUTION
513    query_response_time_free();
514  #endif // HAVE_RESPONSE_TIME_DISTRIBUTION
515 +  free_global_user_stats();
516 +  free_global_client_stats();
517 +  free_global_thread_stats();
518 +  free_global_table_stats();
519 +  free_global_index_stats();
520  #ifdef HAVE_REPLICATION
521    end_slave_list();
522  #endif
523 @@ -1612,6 +1621,10 @@
524    mysql_cond_destroy(&COND_thread_cache);
525    mysql_cond_destroy(&COND_flush_thread_cache);
526    mysql_cond_destroy(&COND_manager);
527 +  mysql_mutex_destroy(&LOCK_stats);
528 +  mysql_mutex_destroy(&LOCK_global_user_client_stats);
529 +  mysql_mutex_destroy(&LOCK_global_table_stats);
530 +  mysql_mutex_destroy(&LOCK_global_index_stats);
531  }
532  #endif /*EMBEDDED_LIBRARY*/
533  
534 @@ -2938,6 +2951,7 @@
535    {"show_binlog_events",   (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOG_EVENTS]), SHOW_LONG_STATUS},
536    {"show_binlogs",         (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOGS]), SHOW_LONG_STATUS},
537    {"show_charsets",        (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CHARSETS]), SHOW_LONG_STATUS},
538 +  {"show_client_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CLIENT_STATS]), SHOW_LONG_STATUS},
539    {"show_collations",      (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLLATIONS]), SHOW_LONG_STATUS},
540    {"show_contributors",    (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CONTRIBUTORS]), SHOW_LONG_STATUS},
541    {"show_create_db",       (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_DB]), SHOW_LONG_STATUS},
542 @@ -2958,6 +2972,7 @@
543  #endif
544    {"show_function_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS_FUNC]), SHOW_LONG_STATUS},
545    {"show_grants",          (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS]), SHOW_LONG_STATUS},
546 +  {"show_index_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_INDEX_STATS]), SHOW_LONG_STATUS},
547    {"show_keys",            (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS},
548    {"show_master_status",   (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_MASTER_STAT]), SHOW_LONG_STATUS},
549    {"show_open_tables",     (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_OPEN_TABLES]), SHOW_LONG_STATUS},
550 @@ -2976,10 +2991,13 @@
551    {"show_slave_status_nolock", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_NOLOCK_STAT]), SHOW_LONG_STATUS},
552    {"show_status",          (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS]), SHOW_LONG_STATUS},
553    {"show_storage_engines", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STORAGE_ENGINES]), SHOW_LONG_STATUS},
554 +  {"show_table_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATS]), SHOW_LONG_STATUS},
555    {"show_table_status",    (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATUS]), SHOW_LONG_STATUS},
556    {"show_tables",          (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLES]), SHOW_LONG_STATUS},
557    {"show_temporary_tables",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TEMPORARY_TABLES]), SHOW_LONG_STATUS},
558 +  {"show_thread_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_THREAD_STATS]), SHOW_LONG_STATUS},
559    {"show_triggers",        (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TRIGGERS]), SHOW_LONG_STATUS},
560 +  {"show_user_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_USER_STATS]), SHOW_LONG_STATUS},
561    {"show_variables",       (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_VARIABLES]), SHOW_LONG_STATUS},
562    {"show_warnings",        (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_WARNS]), SHOW_LONG_STATUS},
563    {"slave_start",          (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS},
564 @@ -3517,6 +3535,13 @@
565    mysql_mutex_init(key_LOCK_server_started,
566                     &LOCK_server_started, MY_MUTEX_INIT_FAST);
567    mysql_cond_init(key_COND_server_started, &COND_server_started, NULL);
568 +  mysql_mutex_init(key_LOCK_stats, &LOCK_stats, MY_MUTEX_INIT_FAST);
569 +  mysql_mutex_init(key_LOCK_global_user_client_stats,
570 +    &LOCK_global_user_client_stats, MY_MUTEX_INIT_FAST);
571 +  mysql_mutex_init(key_LOCK_global_table_stats,
572 +    &LOCK_global_table_stats, MY_MUTEX_INIT_FAST);
573 +  mysql_mutex_init(key_LOCK_global_index_stats,
574 +    &LOCK_global_index_stats, MY_MUTEX_INIT_FAST);
575    sp_cache_init();
576  #ifdef HAVE_EVENT_SCHEDULER
577    Events::init_mutexes();
578 @@ -3886,6 +3911,9 @@
579    query_response_time_init();
580  #endif // HAVE_RESPONSE_TIME_DISTRIBUTION
581    /* We have to initialize the storage engines before CSV logging */
582 +  init_global_table_stats();
583 +  init_global_index_stats();
584 +
585    if (ha_init())
586    {
587      sql_print_error("Can't init databases");
588 @@ -4022,6 +4050,9 @@
589  
590    init_max_user_conn();
591    init_update_queries();
592 +  init_global_user_stats();
593 +  init_global_client_stats();
594 +  init_global_thread_stats();
595    DBUG_RETURN(0);
596  }
597  
598 @@ -5087,6 +5118,7 @@
599      {
600        sql_print_warning("%s", ER_DEFAULT(ER_CON_COUNT_ERROR));
601      }
602 +    statistic_increment(denied_connections, &LOCK_status);
603      delete thd;
604      DBUG_VOID_RETURN;
605    }
606 @@ -7825,6 +7857,8 @@
607    key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
608    key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
609    key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
610 +  key_LOCK_stats, key_LOCK_global_user_client_stats,
611 +  key_LOCK_global_table_stats, key_LOCK_global_index_stats,
612    key_LOCK_gdl, key_LOCK_global_system_variables,
613    key_LOCK_manager,
614    key_LOCK_prepared_stmt_count,
615 @@ -7864,6 +7898,13 @@
616    { &key_LOCK_delayed_insert, "LOCK_delayed_insert", PSI_FLAG_GLOBAL},
617    { &key_LOCK_delayed_status, "LOCK_delayed_status", PSI_FLAG_GLOBAL},
618    { &key_LOCK_error_log, "LOCK_error_log", PSI_FLAG_GLOBAL},
619 +  { &key_LOCK_stats, "LOCK_stats", PSI_FLAG_GLOBAL},
620 +  { &key_LOCK_global_user_client_stats,
621 +    "LOCK_global_user_client_stats", PSI_FLAG_GLOBAL},
622 +  { &key_LOCK_global_table_stats,
623 +     "LOCK_global_table_stats", PSI_FLAG_GLOBAL},
624 +  { &key_LOCK_global_index_stats,
625 +    "LOCK_global_index_stats", PSI_FLAG_GLOBAL},
626    { &key_LOCK_gdl, "LOCK_gdl", PSI_FLAG_GLOBAL},
627    { &key_LOCK_global_system_variables, "LOCK_global_system_variables", PSI_FLAG_GLOBAL},
628    { &key_LOCK_manager, "LOCK_manager", PSI_FLAG_GLOBAL},
629 --- a/sql/mysqld.h
630 +++ b/sql/mysqld.h
631 @@ -23,6 +23,7 @@
632  #include "my_atomic.h"                     /* my_atomic_rwlock_t */
633  #include "mysql/psi/mysql_file.h"          /* MYSQL_FILE */
634  #include "sql_list.h"                      /* I_List */
635 +#include "hash.h"
636  
637  class THD;
638  struct handlerton;
639 @@ -114,6 +115,7 @@
640  extern ulonglong slave_type_conversions_options;
641  extern my_bool read_only, opt_readonly;
642  extern my_bool lower_case_file_system;
643 +extern my_bool opt_userstat, opt_thread_statistics;
644  extern my_bool opt_optimizer_fix;
645  extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
646  extern my_bool opt_secure_auth;
647 @@ -183,6 +185,7 @@
648  extern ulong slave_trans_retries;
649  extern uint  slave_net_timeout;
650  extern uint max_user_connections;
651 +extern ulonglong denied_connections;
652  extern ulong what_to_log,flush_time;
653  extern ulong max_prepared_stmt_count, prepared_stmt_count;
654  extern ulong open_files_limit;
655 @@ -210,6 +213,11 @@
656  extern struct system_variables max_system_variables;
657  extern struct system_status_var global_status_var;
658  extern struct rand_struct sql_rand;
659 +extern HASH global_user_stats;
660 +extern HASH global_client_stats;
661 +extern HASH global_thread_stats;
662 +extern HASH global_table_stats;
663 +extern HASH global_index_stats;
664  extern const char *opt_date_time_formats[];
665  extern handlerton *partition_hton;
666  extern handlerton *myisam_hton;
667 @@ -252,6 +260,8 @@
668    key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
669    key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
670    key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
671 +  key_LOCK_stats, key_LOCK_global_user_client_stats,
672 +  key_LOCK_global_table_stats, key_LOCK_global_index_stats,
673    key_LOCK_gdl, key_LOCK_global_system_variables,
674    key_LOCK_logger, key_LOCK_manager,
675    key_LOCK_prepared_stmt_count,
676 @@ -351,7 +361,9 @@
677         LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
678         LOCK_slave_list, LOCK_active_mi, LOCK_manager,
679         LOCK_global_system_variables, LOCK_user_conn,
680 -       LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count;
681 +       LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count,
682 +       LOCK_stats, LOCK_global_user_client_stats,
683 +       LOCK_global_table_stats, LOCK_global_index_stats;
684  extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thread_count;
685  #ifdef HAVE_OPENSSL
686  extern mysql_mutex_t LOCK_des_key_file;
687 @@ -463,6 +475,16 @@
688    return id;
689  }
690  
691 +void init_global_user_stats(void);
692 +void init_global_table_stats(void);
693 +void init_global_index_stats(void);
694 +void init_global_client_stats(void);
695 +void init_global_thread_stats(void);
696 +void free_global_user_stats(void);
697 +void free_global_table_stats(void);
698 +void free_global_index_stats(void);
699 +void free_global_client_stats(void);
700 +void free_global_thread_stats(void);
701  
702  /*
703    TODO: Replace this with an inline function.
704 --- a/sql/sql_base.cc
705 +++ b/sql/sql_base.cc
706 @@ -1587,6 +1587,11 @@
707    table->mdl_ticket= NULL;
708  
709    mysql_mutex_lock(&thd->LOCK_thd_data);
710 +  if(table->file)
711 +  {
712 +    table->file->update_global_table_stats();
713 +    table->file->update_global_index_stats();
714 +  }
715    *table_ptr=table->next;
716    mysql_mutex_unlock(&thd->LOCK_thd_data);
717  
718 @@ -2225,6 +2230,8 @@
719    DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
720                            table->s->db.str, table->s->table_name.str));
721  
722 +  table->file->update_global_table_stats();
723 +  table->file->update_global_index_stats();
724    free_io_cache(table);
725    closefrm(table, 0);
726    if (delete_table)
727 --- a/sql/sql_class.cc
728 +++ b/sql/sql_class.cc
729 @@ -978,6 +978,13 @@
730    mysys_var=0;
731    binlog_evt_union.do_union= FALSE;
732    enable_slow_log= 0;
733 +  busy_time=            0;
734 +  cpu_time=             0;
735 +  bytes_received=       0;
736 +  bytes_sent=           0;
737 +  binlog_bytes_written= 0;
738 +  updated_row_count=    0;
739 +  sent_row_count_2=     0;
740  #ifndef DBUG_OFF
741    dbug_sentry=THD_SENTRY_MAGIC;
742  #endif
743 @@ -1357,6 +1364,7 @@
744      variables.option_bits|= OPTION_BIN_LOG;
745    else
746      variables.option_bits&= ~OPTION_BIN_LOG;
747 +  reset_stats();
748  
749  #if defined(ENABLED_DEBUG_SYNC)
750    /* Initialize the Debug Sync Facility. See debug_sync.cc. */
751 @@ -1366,6 +1374,94 @@
752    clear_slow_extended();
753  }
754  
755 +// Resets stats in a THD.
756 +void THD::reset_stats(void)
757 +{
758 +  current_connect_time=    time(NULL);
759 +  last_global_update_time= current_connect_time;
760 +  reset_diff_stats();
761 +}
762 +
763 +// Resets the 'diff' stats, which are used to update global stats.
764 +void THD::reset_diff_stats(void)
765 +{
766 +  diff_total_busy_time=            0;
767 +  diff_total_cpu_time=             0;
768 +  diff_total_bytes_received=       0;
769 +  diff_total_bytes_sent=           0;
770 +  diff_total_binlog_bytes_written= 0;
771 +  diff_total_sent_rows=            0;
772 +  diff_total_updated_rows=         0;
773 +  diff_total_read_rows=            0;
774 +  diff_select_commands=            0;
775 +  diff_update_commands=            0;
776 +  diff_other_commands=             0;
777 +  diff_commit_trans=               0;
778 +  diff_rollback_trans=             0;
779 +  diff_denied_connections=         0;
780 +  diff_lost_connections=           0;
781 +  diff_access_denied_errors=       0;
782 +  diff_empty_queries=              0;
783 +}
784 +
785 +// Updates 'diff' stats of a THD.
786 +void THD::update_stats(bool ran_command)
787 +{
788 +  if (opt_userstat)
789 +  {
790 +  diff_total_busy_time+=            busy_time;
791 +  diff_total_cpu_time+=             cpu_time;
792 +  diff_total_bytes_received+=       bytes_received;
793 +  diff_total_bytes_sent+=           bytes_sent;
794 +  diff_total_binlog_bytes_written+= binlog_bytes_written;
795 +  diff_total_sent_rows+=            sent_row_count_2;
796 +  diff_total_updated_rows+=         updated_row_count;
797 +  // diff_total_read_rows is updated in handler.cc.
798 +
799 +  if (ran_command)
800 +  {
801 +    // The replication thread has the COM_CONNECT command.
802 +    if ((old_command == COM_QUERY || command == COM_CONNECT) &&
803 +        (lex->sql_command >= 0 && lex->sql_command < SQLCOM_END))
804 +    {
805 +      // A SQL query.
806 +      if (lex->sql_command == SQLCOM_SELECT)
807 +      {
808 +        diff_select_commands++;
809 +        if (!sent_row_count_2)
810 +          diff_empty_queries++;
811 +      }
812 +      else if (!sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND)
813 +      {
814 +        // 'SHOW ' commands become SQLCOM_SELECT.
815 +        diff_other_commands++;
816 +        // 'SHOW ' commands shouldn't inflate total sent row count.
817 +        diff_total_sent_rows-= sent_row_count_2;
818 +      } else if (is_update_query(lex->sql_command)) {
819 +        diff_update_commands++;
820 +      } else {
821 +        diff_other_commands++;
822 +      }
823 +    }
824 +  }
825 +  // diff_commit_trans is updated in handler.cc.
826 +  // diff_rollback_trans is updated in handler.cc.
827 +  // diff_denied_connections is updated in sql_parse.cc.
828 +  // diff_lost_connections is updated in sql_parse.cc.
829 +  // diff_access_denied_errors is updated in sql_parse.cc.
830 +
831 +  /* reset counters to zero to avoid double-counting since values
832 +     are already store in diff_total_*.
833 +  */
834 +  }
835 +  busy_time=            0;
836 +  cpu_time=             0;
837 +  bytes_received=       0;
838 +  bytes_sent=           0;
839 +  binlog_bytes_written= 0;
840 +  updated_row_count=    0;
841 +  sent_row_count_2=     0;
842 +}
843  
844  /*
845    Init THD for query processing.
846 @@ -2125,6 +2221,32 @@
847  }
848  #endif
849  
850 +char *THD::get_client_host_port(THD *client)
851 +{
852 +  Security_context *client_sctx= client->security_ctx;
853 +  char *client_host= NULL;
854 +
855 +  if (client->peer_port && (client_sctx->host || client_sctx->ip) &&
856 +      security_ctx->host_or_ip[0])
857 +  {
858 +    if ((client_host= (char *) this->alloc(LIST_PROCESS_HOST_LEN+1)))
859 +      my_snprintf((char *) client_host, LIST_PROCESS_HOST_LEN,
860 +                  "%s:%u", client_sctx->host_or_ip, client->peer_port);
861 +  }
862 +  else
863 +    client_host= this->strdup(client_sctx->host_or_ip[0] ?
864 +                              client_sctx->host_or_ip :
865 +                              client_sctx->host ? client_sctx->host : "");
866 +
867 +  return client_host;
868 +}
869 +
870 +const char *get_client_host(THD *client)
871 +{
872 +  return client->security_ctx->host_or_ip[0] ?
873 +      client->security_ctx->host_or_ip :
874 +      client->security_ctx->host ? client->security_ctx->host : "";
875 +}
876  
877  struct Item_change_record: public ilink
878  {
879 @@ -2301,6 +2423,7 @@
880    }
881  
882    thd->sent_row_count++;
883 +  thd->sent_row_count_2++;
884  
885    if (thd->vio_ok())
886      DBUG_RETURN(protocol->write());
887 @@ -2393,6 +2516,7 @@
888  select_export::~select_export()
889  {
890    thd->sent_row_count=row_count;
891 +  thd->sent_row_count_2= row_count;
892  }
893  
894  
895 @@ -3416,6 +3540,7 @@
896    if (likely(thd != 0))
897    { /* current_thd==0 when close_connection() calls net_send_error() */
898      thd->status_var.bytes_sent+= length;
899 +    thd->bytes_sent+= length;
900    }
901  }
902  
903 @@ -3423,6 +3548,7 @@
904  void thd_increment_bytes_received(ulong length)
905  {
906    current_thd->status_var.bytes_received+= length;
907 +  current_thd->bytes_received+= length;
908  }
909  
910  
911 --- a/sql/sql_class.h
912 +++ b/sql/sql_class.h
913 @@ -1705,6 +1705,8 @@
914    */
915    enum enum_server_command command;
916    uint32     server_id;
917 +  // Used to save the command, before it is set to COM_SLEEP.
918 +  enum enum_server_command old_command;
919    uint32     file_id;                  // for LOAD DATA INFILE
920    /* remote (peer) port */
921    uint16 peer_port;
922 @@ -2214,6 +2216,8 @@
923    */
924    enum_tx_isolation tx_isolation;
925    enum_check_fields count_cuted_fields;
926 +  ha_rows    updated_row_count;
927 +  ha_rows    sent_row_count_2; /* for userstat */
928  
929    DYNAMIC_ARRAY user_var_events;        /* For user variables replication */
930    MEM_ROOT      *user_var_events_alloc; /* Allocate above array elements here */
931 @@ -2308,6 +2312,49 @@
932    */
933    LOG_INFO*  current_linfo;
934    NET*       slave_net;                        // network connection from slave -> m.
935 +
936 +  /*
937 +    Used to update global user stats.  The global user stats are updated
938 +    occasionally with the 'diff' variables.  After the update, the 'diff'
939 +    variables are reset to 0.
940 +  */
941 +  // Time when the current thread connected to MySQL.
942 +  time_t current_connect_time;
943 +  // Last time when THD stats were updated in global_user_stats.
944 +  time_t last_global_update_time;
945 +  // Busy (non-idle) time for just one command.
946 +  double busy_time;
947 +  // Busy time not updated in global_user_stats yet.
948 +  double diff_total_busy_time;
949 +  // Cpu (non-idle) time for just one thread.
950 +  double cpu_time;
951 +  // Cpu time not updated in global_user_stats yet.
952 +  double diff_total_cpu_time;
953 +  /* bytes counting */
954 +  ulonglong bytes_received;
955 +  ulonglong diff_total_bytes_received;
956 +  ulonglong bytes_sent;
957 +  ulonglong diff_total_bytes_sent;
958 +  ulonglong binlog_bytes_written;
959 +  ulonglong diff_total_binlog_bytes_written;
960 +
961 +  // Number of rows not reflected in global_user_stats yet.
962 +  ha_rows diff_total_sent_rows, diff_total_updated_rows, diff_total_read_rows;
963 +  // Number of commands not reflected in global_user_stats yet.
964 +  ulonglong diff_select_commands, diff_update_commands, diff_other_commands;
965 +  // Number of transactions not reflected in global_user_stats yet.
966 +  ulonglong diff_commit_trans, diff_rollback_trans;
967 +  // Number of connection errors not reflected in global_user_stats yet.
968 +  ulonglong diff_denied_connections, diff_lost_connections;
969 +  // Number of db access denied, not reflected in global_user_stats yet.
970 +  ulonglong diff_access_denied_errors;
971 +  // Number of queries that return 0 rows
972 +  ulonglong diff_empty_queries;
973 +
974 +  // Per account query delay in miliseconds. When not 0, sleep this number of
975 +  // milliseconds before every SQL command.
976 +  ulonglong query_delay_millis;
977 +
978    /* Used by the sys_var class to store temporary values */
979    union
980    {
981 @@ -2388,6 +2435,11 @@
982      alloc_root. 
983    */
984    void init_for_queries();
985 +  void reset_stats(void);
986 +  void reset_diff_stats(void);
987 +  // ran_command is true when this is called immediately after a
988 +  // command has been run.
989 +  void update_stats(bool ran_command);
990    void change_user(void);
991    void cleanup(void);
992    void cleanup_after_query();
993 @@ -2860,6 +2912,15 @@
994    }
995    thd_scheduler scheduler;
996  
997 +  /* Returns string as 'IP:port' for the client-side
998 +     of the connnection represented
999 +     by 'client' as displayed by SHOW PROCESSLIST.
1000 +     Allocates memory from the heap of
1001 +     this THD and that is not reclaimed
1002 +     immediately, so use sparingly. May return NULL.
1003 +  */
1004 +  char *get_client_host_port(THD *client);
1005 +
1006  public:
1007    inline Internal_error_handler *get_internal_handler()
1008    { return m_internal_handler; }
1009 @@ -3060,6 +3121,10 @@
1010    LEX_STRING invoker_host;
1011  };
1012  
1013 +/* Returns string as 'IP' for the client-side of the connection represented by
1014 +   'client'. Does not allocate memory. May return "".
1015 +*/
1016 +const char *get_client_host(THD *client);
1017  
1018  /** A short cut for thd->stmt_da->set_ok_status(). */
1019  
1020 --- a/sql/sql_connect.cc
1021 +++ b/sql/sql_connect.cc
1022 @@ -56,6 +56,24 @@
1023  #define MIN_HANDSHAKE_SIZE      6
1024  #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
1025  
1026 +// Increments connection count for user.
1027 +static int increment_connection_count(THD* thd, bool use_lock);
1028 +
1029 +// Uses the THD to update the global stats by user name and client IP
1030 +void update_global_user_stats(THD* thd, bool create_user, time_t now);
1031 +
1032 +HASH global_user_stats;
1033 +HASH global_client_stats;
1034 +HASH global_thread_stats;
1035 +// Protects global_user_stats and global_client_stats
1036 +extern mysql_mutex_t LOCK_global_user_client_stats;
1037 +
1038 +HASH global_table_stats;
1039 +extern mysql_mutex_t LOCK_global_table_stats;
1040 +
1041 +HASH global_index_stats;
1042 +extern mysql_mutex_t LOCK_global_index_stats;
1043 +
1044  /*
1045    Get structure for logging connection data for the current user
1046  */
1047 @@ -113,6 +131,586 @@
1048  
1049  }
1050  
1051 +extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
1052 +                         my_bool not_used __attribute__((unused)))
1053 +{
1054 +  *length= strlen(user_stats->user);
1055 +  return (uchar*) user_stats->user;
1056 +}
1057 +
1058 +extern "C" uchar *get_key_thread_stats(THREAD_STATS *thread_stats, size_t *length,
1059 +                         my_bool not_used __attribute__((unused)))
1060 +{
1061 +  *length= sizeof(my_thread_id);
1062 +  return (uchar *) &(thread_stats->id);
1063 +}
1064 +
1065 +void free_user_stats(USER_STATS* user_stats)
1066 +{
1067 +  my_free((char *) user_stats);
1068 +}
1069 +
1070 +void free_thread_stats(THREAD_STATS* thread_stats)
1071 +{
1072 +  my_free((char *) thread_stats);
1073 +}
1074 +
1075 +void init_user_stats(USER_STATS *user_stats,
1076 +                     const char *user,
1077 +                     const char *priv_user,
1078 +                     uint total_connections,
1079 +                     uint concurrent_connections,
1080 +                     time_t connected_time,
1081 +                     double busy_time,
1082 +                     double cpu_time,
1083 +                     ulonglong bytes_received,
1084 +                     ulonglong bytes_sent,
1085 +                     ulonglong binlog_bytes_written,
1086 +                     ha_rows rows_fetched,
1087 +                     ha_rows rows_updated,
1088 +                     ha_rows rows_read,
1089 +                     ulonglong select_commands,
1090 +                     ulonglong update_commands,
1091 +                     ulonglong other_commands,
1092 +                     ulonglong commit_trans,
1093 +                     ulonglong rollback_trans,
1094 +                     ulonglong denied_connections,
1095 +                     ulonglong lost_connections,
1096 +                     ulonglong access_denied_errors,
1097 +                     ulonglong empty_queries)
1098 +{
1099 +  DBUG_ENTER("init_user_stats");
1100 +  DBUG_PRINT("info",
1101 +             ("Add user_stats entry for user %s - priv_user %s",
1102 +              user, priv_user));
1103 +  strncpy(user_stats->user, user, sizeof(user_stats->user));
1104 +  strncpy(user_stats->priv_user, priv_user, sizeof(user_stats->priv_user));
1105 +
1106 +  user_stats->total_connections=      total_connections;
1107 +  user_stats->concurrent_connections= concurrent_connections;
1108 +  user_stats->connected_time=         connected_time;
1109 +  user_stats->busy_time=              busy_time;
1110 +  user_stats->cpu_time=               cpu_time;
1111 +  user_stats->bytes_received=         bytes_received;
1112 +  user_stats->bytes_sent=             bytes_sent;
1113 +  user_stats->binlog_bytes_written=   binlog_bytes_written;
1114 +  user_stats->rows_fetched=           rows_fetched;
1115 +  user_stats->rows_updated=           rows_updated;
1116 +  user_stats->rows_read=              rows_read;
1117 +  user_stats->select_commands=        select_commands;
1118 +  user_stats->update_commands=        update_commands;
1119 +  user_stats->other_commands=         other_commands;
1120 +  user_stats->commit_trans=           commit_trans;
1121 +  user_stats->rollback_trans=         rollback_trans;
1122 +  user_stats->denied_connections=     denied_connections;
1123 +  user_stats->lost_connections=       lost_connections;
1124 +  user_stats->access_denied_errors=   access_denied_errors;
1125 +  user_stats->empty_queries=          empty_queries;
1126 +  DBUG_VOID_RETURN;
1127 +}
1128 +
1129 +void init_thread_stats(THREAD_STATS *thread_stats,
1130 +                     my_thread_id id,
1131 +                     uint total_connections,
1132 +                     uint concurrent_connections,
1133 +                     time_t connected_time,
1134 +                     double busy_time,
1135 +                     double cpu_time,
1136 +                     ulonglong bytes_received,
1137 +                     ulonglong bytes_sent,
1138 +                     ulonglong binlog_bytes_written,
1139 +                     ha_rows rows_fetched,
1140 +                     ha_rows rows_updated,
1141 +                     ha_rows rows_read,
1142 +                     ulonglong select_commands,
1143 +                     ulonglong update_commands,
1144 +                     ulonglong other_commands,
1145 +                     ulonglong commit_trans,
1146 +                     ulonglong rollback_trans,
1147 +                     ulonglong denied_connections,
1148 +                     ulonglong lost_connections,
1149 +                     ulonglong access_denied_errors,
1150 +                     ulonglong empty_queries)
1151 +{
1152 +  DBUG_ENTER("init_thread_stats");
1153 +  DBUG_PRINT("info",
1154 +             ("Add thread_stats entry for thread %lu",
1155 +              id));
1156 +  thread_stats->id= id;
1157 +
1158 +  thread_stats->total_connections=      total_connections;
1159 +  thread_stats->concurrent_connections= concurrent_connections;
1160 +  thread_stats->connected_time=         connected_time;
1161 +  thread_stats->busy_time=              busy_time;
1162 +  thread_stats->cpu_time=               cpu_time;
1163 +  thread_stats->bytes_received=         bytes_received;
1164 +  thread_stats->bytes_sent=             bytes_sent;
1165 +  thread_stats->binlog_bytes_written=   binlog_bytes_written;
1166 +  thread_stats->rows_fetched=           rows_fetched;
1167 +  thread_stats->rows_updated=           rows_updated;
1168 +  thread_stats->rows_read=              rows_read;
1169 +  thread_stats->select_commands=        select_commands;
1170 +  thread_stats->update_commands=        update_commands;
1171 +  thread_stats->other_commands=         other_commands;
1172 +  thread_stats->commit_trans=           commit_trans;
1173 +  thread_stats->rollback_trans=         rollback_trans;
1174 +  thread_stats->denied_connections=     denied_connections;
1175 +  thread_stats->lost_connections=       lost_connections;
1176 +  thread_stats->access_denied_errors=   access_denied_errors;
1177 +  thread_stats->empty_queries=          empty_queries;
1178 +  DBUG_VOID_RETURN;
1179 +}
1180 +
1181 +void add_user_stats(USER_STATS *user_stats,
1182 +                    uint total_connections,
1183 +                    uint concurrent_connections,
1184 +                    time_t connected_time,
1185 +                    double busy_time,
1186 +                    double cpu_time,
1187 +                    ulonglong bytes_received,
1188 +                    ulonglong bytes_sent,
1189 +                    ulonglong binlog_bytes_written,
1190 +                    ha_rows rows_fetched,
1191 +                    ha_rows rows_updated,
1192 +                    ha_rows rows_read,
1193 +                    ulonglong select_commands,
1194 +                    ulonglong update_commands,
1195 +                    ulonglong other_commands,
1196 +                    ulonglong commit_trans,
1197 +                    ulonglong rollback_trans,
1198 +                    ulonglong denied_connections,
1199 +                    ulonglong lost_connections,
1200 +                    ulonglong access_denied_errors,
1201 +                    ulonglong empty_queries)
1202 +{
1203 +  user_stats->total_connections+=      total_connections;
1204 +  user_stats->concurrent_connections+= concurrent_connections;
1205 +  user_stats->connected_time+=         connected_time;
1206 +  user_stats->busy_time+=              busy_time;
1207 +  user_stats->cpu_time+=               cpu_time;
1208 +  user_stats->bytes_received+=         bytes_received;
1209 +  user_stats->bytes_sent+=             bytes_sent;
1210 +  user_stats->binlog_bytes_written+=   binlog_bytes_written;
1211 +  user_stats->rows_fetched+=           rows_fetched;
1212 +  user_stats->rows_updated+=           rows_updated;
1213 +  user_stats->rows_read+=              rows_read;
1214 +  user_stats->select_commands+=        select_commands;
1215 +  user_stats->update_commands+=        update_commands;
1216 +  user_stats->other_commands+=         other_commands;
1217 +  user_stats->commit_trans+=           commit_trans;
1218 +  user_stats->rollback_trans+=         rollback_trans;
1219 +  user_stats->denied_connections+=     denied_connections;
1220 +  user_stats->lost_connections+=       lost_connections;
1221 +  user_stats->access_denied_errors+=   access_denied_errors;
1222 +  user_stats->empty_queries+=          empty_queries;
1223 +}
1224 +
1225 +void add_thread_stats(THREAD_STATS *thread_stats,
1226 +                    uint total_connections,
1227 +                    uint concurrent_connections,
1228 +                    time_t connected_time,
1229 +                    double busy_time,
1230 +                    double cpu_time,
1231 +                    ulonglong bytes_received,
1232 +                    ulonglong bytes_sent,
1233 +                    ulonglong binlog_bytes_written,
1234 +                    ha_rows rows_fetched,
1235 +                    ha_rows rows_updated,
1236 +                    ha_rows rows_read,
1237 +                    ulonglong select_commands,
1238 +                    ulonglong update_commands,
1239 +                    ulonglong other_commands,
1240 +                    ulonglong commit_trans,
1241 +                    ulonglong rollback_trans,
1242 +                    ulonglong denied_connections,
1243 +                    ulonglong lost_connections,
1244 +                    ulonglong access_denied_errors,
1245 +                    ulonglong empty_queries)
1246 +{
1247 +  thread_stats->total_connections+=      total_connections;
1248 +  thread_stats->concurrent_connections+= concurrent_connections;
1249 +  thread_stats->connected_time+=         connected_time;
1250 +  thread_stats->busy_time+=              busy_time;
1251 +  thread_stats->cpu_time+=               cpu_time;
1252 +  thread_stats->bytes_received+=         bytes_received;
1253 +  thread_stats->bytes_sent+=             bytes_sent;
1254 +  thread_stats->binlog_bytes_written+=   binlog_bytes_written;
1255 +  thread_stats->rows_fetched+=           rows_fetched;
1256 +  thread_stats->rows_updated+=           rows_updated;
1257 +  thread_stats->rows_read+=              rows_read;
1258 +  thread_stats->select_commands+=        select_commands;
1259 +  thread_stats->update_commands+=        update_commands;
1260 +  thread_stats->other_commands+=         other_commands;
1261 +  thread_stats->commit_trans+=           commit_trans;
1262 +  thread_stats->rollback_trans+=         rollback_trans;
1263 +  thread_stats->denied_connections+=     denied_connections;
1264 +  thread_stats->lost_connections+=       lost_connections;
1265 +  thread_stats->access_denied_errors+=   access_denied_errors;
1266 +  thread_stats->empty_queries+=          empty_queries;
1267 +}
1268 +
1269 +void init_global_user_stats(void)
1270 +{
1271 +  if (my_hash_init(&global_user_stats, system_charset_info, max_connections,
1272 +                0, 0, (my_hash_get_key)get_key_user_stats,
1273 +                (my_hash_free_key)free_user_stats, 0)) {
1274 +    sql_print_error("Initializing global_user_stats failed.");
1275 +    exit(1);
1276 +  }
1277 +}
1278 +
1279 +void init_global_client_stats(void)
1280 +{
1281 +  if (my_hash_init(&global_client_stats, system_charset_info, max_connections,
1282 +                0, 0, (my_hash_get_key)get_key_user_stats,
1283 +                (my_hash_free_key)free_user_stats, 0)) {
1284 +    sql_print_error("Initializing global_client_stats failed.");
1285 +    exit(1);
1286 +  }
1287 +}
1288 +
1289 +void init_global_thread_stats(void)
1290 +{
1291 +  if (my_hash_init(&global_thread_stats, &my_charset_bin, max_connections,
1292 +                0, 0, (my_hash_get_key) get_key_thread_stats,
1293 +                (my_hash_free_key) free_thread_stats, 0))
1294 +  {
1295 +    sql_print_error("Initializing global_client_stats failed.");
1296 +    exit(1);
1297 +  }
1298 +}
1299 +
1300 +extern "C" uchar *get_key_table_stats(TABLE_STATS *table_stats, size_t *length,
1301 +                                     my_bool not_used __attribute__((unused)))
1302 +{
1303 +  *length= strlen(table_stats->table);
1304 +  return (uchar*) table_stats->table;
1305 +}
1306 +
1307 +extern "C" void free_table_stats(TABLE_STATS* table_stats)
1308 +{
1309 +  my_free((char*) table_stats);
1310 +}
1311 +
1312 +void init_global_table_stats(void)
1313 +{
1314 +  if (my_hash_init(&global_table_stats, system_charset_info, max_connections,
1315 +                0, 0, (my_hash_get_key)get_key_table_stats,
1316 +                (my_hash_free_key)free_table_stats, 0)) {
1317 +    sql_print_error("Initializing global_table_stats failed.");
1318 +    exit(1);
1319 +  }
1320 +}
1321 +
1322 +extern "C" uchar *get_key_index_stats(INDEX_STATS *index_stats, size_t *length,
1323 +                                     my_bool not_used __attribute__((unused)))
1324 +{
1325 +  *length= strlen(index_stats->index);
1326 +  return (uchar*) index_stats->index;
1327 +}
1328 +
1329 +extern "C" void free_index_stats(INDEX_STATS* index_stats)
1330 +{
1331 +  my_free((char*) index_stats);
1332 +}
1333 +
1334 +void init_global_index_stats(void)
1335 +{
1336 +  if (my_hash_init(&global_index_stats, system_charset_info, max_connections,
1337 +                0, 0, (my_hash_get_key)get_key_index_stats,
1338 +                (my_hash_free_key)free_index_stats, 0)) {
1339 +    sql_print_error("Initializing global_index_stats failed.");
1340 +    exit(1);
1341 +  }
1342 +}
1343 +
1344 +void free_global_user_stats(void)
1345 +{
1346 +  my_hash_free(&global_user_stats);
1347 +}
1348 +
1349 +void free_global_thread_stats(void)
1350 +{
1351 +  my_hash_free(&global_thread_stats);
1352 +}
1353 +
1354 +void free_global_table_stats(void)
1355 +{
1356 +  my_hash_free(&global_table_stats);
1357 +}
1358 +
1359 +void free_global_index_stats(void)
1360 +{
1361 +  my_hash_free(&global_index_stats);
1362 +}
1363 +
1364 +void free_global_client_stats(void)
1365 +{
1366 +  my_hash_free(&global_client_stats);
1367 +}
1368 +
1369 +// 'mysql_system_user' is used for when the user is not defined for a THD.
1370 +static char mysql_system_user[] = "#mysql_system#";
1371 +
1372 +// Returns 'user' if it's not NULL.  Returns 'mysql_system_user' otherwise.
1373 +static char* get_valid_user_string(char* user) {
1374 +  return user ? user : mysql_system_user;
1375 +}
1376 +
1377 +// Increments the global stats connection count for an entry from
1378 +// global_client_stats or global_user_stats. Returns 0 on success
1379 +// and 1 on error.
1380 +static int increment_count_by_name(const char *name, const char *role_name,
1381 +                                   HASH *users_or_clients, THD *thd)
1382 +{
1383 +  USER_STATS* user_stats;
1384 +
1385 +  if (!(user_stats = (USER_STATS *) my_hash_search(users_or_clients,
1386 +                                                   (uchar*) name,
1387 +                                                   strlen(name))))
1388 +  {
1389 +    // First connection for this user or client
1390 +    if (!(user_stats = ((USER_STATS *)
1391 +                        my_malloc(sizeof(USER_STATS), MYF(MY_WME | MY_ZEROFILL)))))
1392 +    {
1393 +      return 1; // Out of memory
1394 +    }
1395 +
1396 +    init_user_stats(user_stats, name, role_name,
1397 +                    0, 0,      // connections
1398 +                    0, 0, 0,   // time
1399 +                    0, 0, 0,   // bytes sent, received and written
1400 +                    0, 0, 0,   // rows fetched, updated and read
1401 +                    0, 0, 0,   // select, update and other commands
1402 +                    0, 0,      // commit and rollback trans
1403 +                    thd->diff_denied_connections,
1404 +                    0,         // lost connections
1405 +                    0,         // access denied errors
1406 +                    0);        // empty queries
1407 +
1408 +    if (my_hash_insert(users_or_clients, (uchar *) user_stats))
1409 +    {
1410 +      my_free((char *) user_stats);
1411 +      return 1; // Out of memory
1412 +    }
1413 +  }
1414 +  user_stats->total_connections++;
1415 +  return 0;
1416 +}
1417 +
1418 +static int increment_count_by_id(my_thread_id id,
1419 +                                 HASH *users_or_clients, THD *thd)
1420 +{
1421 +  THREAD_STATS* thread_stats;
1422 +
1423 +  if (!(thread_stats = (THREAD_STATS *) my_hash_search(users_or_clients,
1424 +                                                       (uchar*) &id,
1425 +                                                       sizeof(my_thread_id))))
1426 +  {
1427 +    // First connection for this user or client
1428 +    if (!(thread_stats = ((THREAD_STATS *)
1429 +                        my_malloc(sizeof(THREAD_STATS), MYF(MY_WME | MY_ZEROFILL)))))
1430 +    {
1431 +      return 1; // Out of memory
1432 +    }
1433 +
1434 +    init_thread_stats(thread_stats, id,
1435 +                    0, 0,      // connections
1436 +                    0, 0, 0,   // time
1437 +                    0, 0, 0,   // bytes sent, received and written
1438 +                    0, 0, 0,   // rows fetched, updated and read
1439 +                    0, 0, 0,   // select, update and other commands
1440 +                    0, 0,      // commit and rollback trans
1441 +                    thd->diff_denied_connections,
1442 +                    0,         // lost connections
1443 +                    0,         // access denied errors
1444 +                    0);        // empty queries
1445 +
1446 +    if (my_hash_insert(users_or_clients, (uchar *) thread_stats))
1447 +    {
1448 +      my_free((char *) thread_stats);
1449 +      return 1; // Out of memory
1450 +    }
1451 +  }
1452 +  thread_stats->total_connections++;
1453 +  return 0;
1454 +}
1455 +
1456 +/* Increments the global user and client stats connection count.  If 'use_lock'
1457 +   is true, LOCK_global_user_client_stats will be locked/unlocked.  Returns
1458 +   0 on success, 1 on error.
1459 +*/
1460 +static int increment_connection_count(THD* thd, bool use_lock)
1461 +{
1462 +  char* user_string=         get_valid_user_string(thd->main_security_ctx.user);
1463 +  const char* client_string= get_client_host(thd);
1464 +  int return_value=          0;
1465 +
1466 +  if (!opt_userstat)
1467 +    return return_value;
1468 +
1469 +  if (use_lock)
1470 +    mysql_mutex_lock(&LOCK_global_user_client_stats);
1471 +
1472 +  if (increment_count_by_name(user_string, user_string,
1473 +                              &global_user_stats, thd))
1474 +  {
1475 +    return_value= 1;
1476 +    goto end;
1477 +  }
1478 +  if (increment_count_by_name(client_string,
1479 +                              user_string,
1480 +                              &global_client_stats, thd))
1481 +  {
1482 +    return_value= 1;
1483 +    goto end;
1484 +  }
1485 +  if (opt_thread_statistics)
1486 +  {
1487 +    if (increment_count_by_id(thd->thread_id, &global_thread_stats, thd))
1488 +    {
1489 +      return_value= 1;
1490 +      goto end;
1491 +    }
1492 + }
1493 +
1494 +end:
1495 +  if (use_lock)
1496 +    mysql_mutex_unlock(&LOCK_global_user_client_stats);
1497 +  return return_value;
1498 +}
1499 +
1500 +// Used to update the global user and client stats.
1501 +static void update_global_user_stats_with_user(THD* thd,
1502 +                                               USER_STATS* user_stats,
1503 +                                               time_t now)
1504 +{
1505 +  user_stats->connected_time+=       now - thd->last_global_update_time;
1506 +//thd->last_global_update_time=      now;
1507 +  user_stats->busy_time+=            thd->diff_total_busy_time;
1508 +  user_stats->cpu_time+=             thd->diff_total_cpu_time;
1509 +  user_stats->bytes_received+=       thd->diff_total_bytes_received;
1510 +  user_stats->bytes_sent+=           thd->diff_total_bytes_sent;
1511 +  user_stats->binlog_bytes_written+= thd->diff_total_binlog_bytes_written;
1512 +  user_stats->rows_fetched+=         thd->diff_total_sent_rows;
1513 +  user_stats->rows_updated+=         thd->diff_total_updated_rows;
1514 +  user_stats->rows_read+=            thd->diff_total_read_rows;
1515 +  user_stats->select_commands+=      thd->diff_select_commands;
1516 +  user_stats->update_commands+=      thd->diff_update_commands;
1517 +  user_stats->other_commands+=       thd->diff_other_commands;
1518 +  user_stats->commit_trans+=         thd->diff_commit_trans;
1519 +  user_stats->rollback_trans+=       thd->diff_rollback_trans;
1520 +  user_stats->denied_connections+=   thd->diff_denied_connections;
1521 +  user_stats->lost_connections+=     thd->diff_lost_connections;
1522 +  user_stats->access_denied_errors+= thd->diff_access_denied_errors;
1523 +  user_stats->empty_queries+=        thd->diff_empty_queries;
1524 +}
1525 +
1526 +static void update_global_thread_stats_with_thread(THD* thd,
1527 +                                               THREAD_STATS* thread_stats,
1528 +                                               time_t now)
1529 +{
1530 +  thread_stats->connected_time+=       now - thd->last_global_update_time;
1531 +//thd->last_global_update_time=        now;
1532 +  thread_stats->busy_time+=            thd->diff_total_busy_time;
1533 +  thread_stats->cpu_time+=             thd->diff_total_cpu_time;
1534 +  thread_stats->bytes_received+=       thd->diff_total_bytes_received;
1535 +  thread_stats->bytes_sent+=           thd->diff_total_bytes_sent;
1536 +  thread_stats->binlog_bytes_written+= thd->diff_total_binlog_bytes_written;
1537 +  thread_stats->rows_fetched+=         thd->diff_total_sent_rows;
1538 +  thread_stats->rows_updated+=         thd->diff_total_updated_rows;
1539 +  thread_stats->rows_read+=            thd->diff_total_read_rows;
1540 +  thread_stats->select_commands+=      thd->diff_select_commands;
1541 +  thread_stats->update_commands+=      thd->diff_update_commands;
1542 +  thread_stats->other_commands+=       thd->diff_other_commands;
1543 +  thread_stats->commit_trans+=         thd->diff_commit_trans;
1544 +  thread_stats->rollback_trans+=       thd->diff_rollback_trans;
1545 +  thread_stats->denied_connections+=   thd->diff_denied_connections;
1546 +  thread_stats->lost_connections+=     thd->diff_lost_connections;
1547 +  thread_stats->access_denied_errors+= thd->diff_access_denied_errors;
1548 +  thread_stats->empty_queries+=        thd->diff_empty_queries;
1549 +}
1550 +
1551 +// Updates the global stats of a user or client
1552 +void update_global_user_stats(THD* thd, bool create_user, time_t now)
1553 +{
1554 +  if (opt_userstat)
1555 +  {
1556 +    char* user_string=         get_valid_user_string(thd->main_security_ctx.user);
1557 +    const char* client_string= get_client_host(thd);
1558 +
1559 +    USER_STATS* user_stats;
1560 +    THREAD_STATS* thread_stats;
1561 +    mysql_mutex_lock(&LOCK_global_user_client_stats);
1562 +
1563 +    // Update by user name
1564 +    if ((user_stats = (USER_STATS *) my_hash_search(&global_user_stats,
1565 +                                                    (uchar *) user_string,
1566 +                                                    strlen(user_string))))
1567 +    {
1568 +      // Found user.
1569 +      update_global_user_stats_with_user(thd, user_stats, now);
1570 +    }
1571 +    else
1572 +    {
1573 +      // Create the entry
1574 +      if (create_user)
1575 +      {
1576 +        increment_count_by_name(user_string, user_string,
1577 +                                &global_user_stats, thd);
1578 +      }
1579 +    }
1580 +
1581 +    // Update by client IP
1582 +    if ((user_stats = (USER_STATS *) my_hash_search(&global_client_stats,
1583 +                                                    (uchar *) client_string,
1584 +                                                    strlen(client_string))))
1585 +    {
1586 +      // Found by client IP
1587 +      update_global_user_stats_with_user(thd, user_stats, now);
1588 +    }
1589 +    else
1590 +    {
1591 +      // Create the entry
1592 +      if (create_user)
1593 +      {
1594 +        increment_count_by_name(client_string,
1595 +                                user_string,
1596 +                                &global_client_stats, thd);
1597 +      }
1598 +    }
1599 +
1600 +    if (opt_thread_statistics)
1601 +    {
1602 +      // Update by thread ID
1603 +      if ((thread_stats = (THREAD_STATS *) my_hash_search(&global_thread_stats,
1604 +                                                          (uchar *) &(thd->thread_id),
1605 +                                                          sizeof(my_thread_id))))
1606 +      {
1607 +        // Found by thread ID
1608 +        update_global_thread_stats_with_thread(thd, thread_stats, now);
1609 +      }
1610 +      else
1611 +      {
1612 +        // Create the entry
1613 +        if (create_user)
1614 +        {
1615 +          increment_count_by_id(thd->thread_id,
1616 +                                &global_thread_stats, thd);
1617 +        }
1618 +      }
1619 +    }
1620 +
1621 +    thd->last_global_update_time = now;
1622 +    thd->reset_diff_stats();
1623 +
1624 +    mysql_mutex_unlock(&LOCK_global_user_client_stats);
1625 +  }
1626 +  else
1627 +  {
1628 +    thd->reset_diff_stats();
1629 +  }
1630 +}
1631  
1632  /*
1633    check if user has already too many connections
1634 @@ -170,6 +768,7 @@
1635    if (error)
1636    {
1637      uc->connections--; // no need for decrease_user_connections() here
1638 +    statistic_increment(denied_connections, &LOCK_status);
1639      /*
1640        The thread may returned back to the pool and assigned to a user
1641        that doesn't have a limit. Ensure the user is not using resources
1642 @@ -589,11 +1188,18 @@
1643        my_sleep(1000);                          /* must wait after eof() */
1644  #endif
1645      statistic_increment(aborted_connects,&LOCK_status);
1646 +    thd->diff_denied_connections++;
1647      DBUG_RETURN(1);
1648    }
1649    /* Connect completed, set read/write timeouts back to default */
1650    my_net_set_read_timeout(net, thd->variables.net_read_timeout);
1651    my_net_set_write_timeout(net, thd->variables.net_write_timeout);
1652 +
1653 +  thd->reset_stats();
1654 +  // Updates global user connection stats.
1655 +  if (increment_connection_count(thd, true))
1656 +    DBUG_RETURN(1);
1657 +
1658    DBUG_RETURN(0);
1659  }
1660  
1661 @@ -623,6 +1229,7 @@
1662    if (thd->killed || (net->error && net->vio != 0))
1663    {
1664      statistic_increment(aborted_threads,&LOCK_status);
1665 +    thd->diff_lost_connections++;
1666    }
1667  
1668    if (net->error && net->vio != 0)
1669 @@ -787,10 +1394,14 @@
1670    for (;;)
1671    {
1672      bool rc;
1673 +    bool create_user= TRUE;
1674  
1675      rc= thd_prepare_connection(thd);
1676      if (rc)
1677 +    {
1678 +      create_user= FALSE;
1679        goto end_thread;
1680 +    }
1681  
1682      while (thd_is_connection_alive(thd))
1683      {
1684 @@ -802,6 +1413,8 @@
1685     
1686  end_thread:
1687      close_connection(thd);
1688 +    thd->update_stats(false);
1689 +    update_global_user_stats(thd, create_user, time(NULL));
1690      if (MYSQL_CALLBACK_ELSE(thread_scheduler, end_thread, (thd, 1), 0))
1691        return;                                 // Probably no-threads
1692  
1693 --- a/sql/sql_delete.cc
1694 +++ b/sql/sql_delete.cc
1695 @@ -411,6 +411,7 @@
1696      my_ok(thd, deleted);
1697      DBUG_PRINT("info",("%ld records deleted",(long) deleted));
1698    }
1699 +  thd->updated_row_count+= deleted;
1700    DBUG_RETURN(error >= 0 || thd->is_error());
1701  }
1702  
1703 @@ -1005,6 +1006,7 @@
1704    {
1705      ::my_ok(thd, deleted);
1706    }
1707 +  thd->updated_row_count+= deleted;
1708    return 0;
1709  }
1710  
1711 --- a/sql/sql_insert.cc
1712 +++ b/sql/sql_insert.cc
1713 @@ -1072,13 +1072,14 @@
1714  
1715    if (error)
1716      goto abort;
1717 +  ha_rows row_count;
1718    if (values_list.elements == 1 && (!(thd->variables.option_bits & OPTION_WARNINGS) ||
1719                                     !thd->cuted_fields))
1720    {
1721 -    my_ok(thd, info.copied + info.deleted +
1722 +    row_count= info.copied + info.deleted +
1723                 ((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
1724 -                info.touched : info.updated),
1725 -          id);
1726 +                info.touched : info.updated);
1727 +    my_ok(thd, row_count, id);
1728    }
1729    else
1730    {
1731 @@ -1094,8 +1095,10 @@
1732        sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
1733               (ulong) (info.deleted + updated),
1734                (ulong) thd->warning_info->statement_warn_count());
1735 -    ::my_ok(thd, info.copied + info.deleted + updated, id, buff);
1736 +    row_count= info.copied + info.deleted + updated;
1737 +    ::my_ok(thd, row_count, id, buff);
1738    }
1739 +  thd->updated_row_count+= row_count;
1740    thd->abort_on_warning= 0;
1741    DBUG_RETURN(FALSE);
1742  
1743 @@ -3540,6 +3543,7 @@
1744       thd->first_successful_insert_id_in_prev_stmt :
1745       (info.copied ? autoinc_value_of_last_inserted_row : 0));
1746    ::my_ok(thd, row_count, id, buff);
1747 +  thd->updated_row_count+= row_count;
1748    DBUG_RETURN(0);
1749  }
1750  
1751 --- a/sql/sql_lex.h
1752 +++ b/sql/sql_lex.h
1753 @@ -197,6 +197,9 @@
1754      in "struct show_var_st status_vars[]= {" ...
1755    */
1756    SQLCOM_SHOW_TEMPORARY_TABLES,
1757 +  // TODO(mcallaghan): update status_vars in mysqld to export these
1758 +  SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS,
1759 +  SQLCOM_SHOW_CLIENT_STATS, SQLCOM_SHOW_THREAD_STATS,
1760    /* This should be the last !!! */
1761    SQLCOM_END
1762  };
1763 --- a/sql/sql_parse.cc
1764 +++ b/sql/sql_parse.cc
1765 @@ -117,6 +117,9 @@
1766  static void sql_kill(THD *thd, ulong id, bool only_kill_query);
1767  static inline ulonglong get_query_exec_time(THD *thd, ulonglong cur_utime);
1768  
1769 +// Uses the THD to update the global stats by user name and client IP
1770 +void update_global_user_stats(THD* thd, bool create_user, time_t now);
1771 +
1772  const char *any_db="*any*";    // Special symbol for check_access
1773  
1774  const LEX_STRING command_name[]={
1775 @@ -703,6 +706,12 @@
1776    */
1777    thd->clear_error();                          // Clear error message
1778    thd->stmt_da->reset_diagnostics_area();
1779 +  thd->updated_row_count=    0;
1780 +  thd->busy_time=            0;
1781 +  thd->cpu_time=             0;
1782 +  thd->bytes_received=       0;
1783 +  thd->bytes_sent=           0;
1784 +  thd->binlog_bytes_written= 0;
1785  
1786    net_new_transaction(net);
1787  
1788 @@ -888,6 +897,10 @@
1789                        (char *) thd->security_ctx->host_or_ip);
1790    
1791    thd->command=command;
1792 +  /* To increment the corrent command counter for user stats, 'command' must
1793 +     be saved because it is set to COM_SLEEP at the end of this function.
1794 +  */
1795 +  thd->old_command= command;
1796    /*
1797      Commands which always take a long time are logged into
1798      the slow log only if opt_log_slow_admin_statements is set.
1799 @@ -1683,6 +1696,13 @@
1800      thd->profiling.discard_current_query();
1801  #endif
1802      break;
1803 +  case SCH_USER_STATS:
1804 +  case SCH_CLIENT_STATS:
1805 +  case SCH_THREAD_STATS:
1806 +    if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
1807 +      DBUG_RETURN(1);
1808 +  case SCH_TABLE_STATS:
1809 +  case SCH_INDEX_STATS:
1810    case SCH_OPEN_TABLES:
1811    case SCH_VARIABLES:
1812    case SCH_STATUS:
1813 @@ -1857,6 +1877,7 @@
1814                         thd->security_ctx->priv_host)) &&
1815          check_global_access(thd, SUPER_ACL))
1816      {
1817 +      thd->diff_access_denied_errors++;
1818        my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
1819        DBUG_RETURN(TRUE);
1820      }
1821 @@ -4892,6 +4913,7 @@
1822        case ACL_INTERNAL_ACCESS_DENIED:
1823          if (! no_errors)
1824          {
1825 +          thd->diff_access_denied_errors++;
1826            my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
1827                     sctx->priv_user, sctx->priv_host, db);
1828          }
1829 @@ -4942,6 +4964,7 @@
1830      DBUG_PRINT("error",("No possible access"));
1831      if (!no_errors)
1832      {
1833 +      thd->diff_access_denied_errors++;
1834        if (thd->password == 2)
1835          my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
1836                   sctx->priv_user,
1837 @@ -5058,6 +5081,7 @@
1838  
1839      if (!thd->col_access && check_grant_db(thd, dst_db_name))
1840      {
1841 +      thd->diff_access_denied_errors++;
1842        my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
1843                 thd->security_ctx->priv_user,
1844                 thd->security_ctx->priv_host,
1845 @@ -5328,6 +5352,7 @@
1846    if ((thd->security_ctx->master_access & want_access))
1847      return 0;
1848    get_privilege_desc(command, sizeof(command), want_access);
1849 +  thd->diff_access_denied_errors++;
1850    my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
1851    return 1;
1852  #else
1853 @@ -5695,6 +5720,32 @@
1854    lex_start(thd);
1855    mysql_reset_thd_for_next_command(thd);
1856  
1857 +  int start_time_error=   0;
1858 +  int end_time_error=     0;
1859 +  struct timeval start_time, end_time;
1860 +  double start_usecs=     0;
1861 +  double end_usecs=       0;
1862 +  /* cpu time */
1863 +  int cputime_error=      0;
1864 +  struct timespec tp;
1865 +  double start_cpu_nsecs= 0;
1866 +  double end_cpu_nsecs=   0;
1867 +
1868 +  if (opt_userstat)
1869 +  {
1870 +#ifdef HAVE_CLOCK_GETTIME
1871 +    /* get start cputime */
1872 +    if (!(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
1873 +      start_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
1874 +#endif
1875 +
1876 +    // Gets the start time, in order to measure how long this command takes.
1877 +    if (!(start_time_error = gettimeofday(&start_time, NULL)))
1878 +    {
1879 +      start_usecs = start_time.tv_sec * 1000000.0 + start_time.tv_usec;
1880 +    }
1881 +  }
1882 +
1883    if (query_cache_send_result_to_client(thd, rawbuf, length) <= 0)
1884    {
1885      LEX *lex= thd->lex;
1886 @@ -5763,6 +5814,52 @@
1887      DBUG_ASSERT(thd->change_list.is_empty());
1888    }
1889  
1890 +  if (opt_userstat)
1891 +  {
1892 +    // Gets the end time.
1893 +    if (!(end_time_error= gettimeofday(&end_time, NULL)))
1894 +    {
1895 +      end_usecs= end_time.tv_sec * 1000000.0 + end_time.tv_usec;
1896 +    }
1897 +
1898 +    // Calculates the difference between the end and start times.
1899 +    if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error)
1900 +    {
1901 +      thd->busy_time= (end_usecs - start_usecs) / 1000000;
1902 +      // In case there are bad values, 2629743 is the #seconds in a month.
1903 +      if (thd->busy_time > 2629743)
1904 +      {
1905 +        thd->busy_time= 0;
1906 +      }
1907 +    }
1908 +    else
1909 +    {
1910 +      // end time went back in time, or gettimeofday() failed.
1911 +      thd->busy_time= 0;
1912 +    }
1913 +
1914 +#ifdef HAVE_CLOCK_GETTIME
1915 +    /* get end cputime */
1916 +    if (!cputime_error &&
1917 +        !(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
1918 +      end_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
1919 +#endif
1920 +    if (start_cpu_nsecs && !cputime_error)
1921 +    {
1922 +      thd->cpu_time = (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
1923 +      // In case there are bad values, 2629743 is the #seconds in a month.
1924 +      if (thd->cpu_time > 2629743)
1925 +      {
1926 +        thd->cpu_time = 0;
1927 +      }
1928 +    }
1929 +    else
1930 +      thd->cpu_time = 0;
1931 +  }
1932 +  // Updates THD stats and the global user stats.
1933 +  thd->update_stats(true);
1934 +  update_global_user_stats(thd, true, time(NULL));
1935 +
1936    DBUG_VOID_RETURN;
1937  }
1938  
1939 --- a/sql/sql_prepare.cc
1940 +++ b/sql/sql_prepare.cc
1941 @@ -114,6 +114,9 @@
1942  #endif
1943  #include "lock.h"                               // MYSQL_OPEN_FORCE_SHARED_MDL
1944  
1945 +// Uses the THD to update the global stats by user name and client IP
1946 +void update_global_user_stats(THD* thd, bool create_user, time_t now);
1947 +
1948  /**
1949    A result class used to send cursor rows using the binary protocol.
1950  */
1951 @@ -2173,8 +2176,34 @@
1952    /* First of all clear possible warnings from the previous command */
1953    mysql_reset_thd_for_next_command(thd);
1954  
1955 +  int start_time_error=   0;
1956 +  int end_time_error=     0;
1957 +  struct timeval start_time, end_time;
1958 +  double start_usecs=     0;
1959 +  double end_usecs=       0;
1960 +  /* cpu time */
1961 +  int cputime_error=      0;
1962 +  struct timespec tp;
1963 +  double start_cpu_nsecs= 0;
1964 +  double end_cpu_nsecs=   0;
1965 +
1966 +  if (opt_userstat)
1967 +  {
1968 +#ifdef HAVE_CLOCK_GETTIME
1969 +    /* get start cputime */
1970 +    if (!(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
1971 +      start_cpu_nsecs= tp.tv_sec * 1000000000.0 + tp.tv_nsec;
1972 +#endif
1973 +
1974 +    // Gets the start time, in order to measure how long this command takes.
1975 +    if (!(start_time_error= gettimeofday(&start_time, NULL)))
1976 +    {
1977 +      start_usecs= start_time.tv_sec * 1000000.0 + start_time.tv_usec;
1978 +    }
1979 +  }
1980 +
1981    if (! (stmt= new Prepared_statement(thd)))
1982 -    DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */
1983 +    goto end; /* out of memory: error is set in Sql_alloc */
1984  
1985    if (thd->stmt_map.insert(thd, stmt))
1986    {
1987 @@ -2182,7 +2211,7 @@
1988        The error is set in the insert. The statement itself
1989        will be also deleted there (this is how the hash works).
1990      */
1991 -    DBUG_VOID_RETURN;
1992 +    goto end;
1993    }
1994  
1995    thd->protocol= &thd->protocol_binary;
1996 @@ -2196,6 +2225,53 @@
1997    thd->protocol= save_protocol;
1998  
1999    /* check_prepared_statemnt sends the metadata packet in case of success */
2000 +end:
2001 +  if (opt_userstat)
2002 +  {
2003 +    // Gets the end time.
2004 +    if (!(end_time_error= gettimeofday(&end_time, NULL)))
2005 +    {
2006 +      end_usecs= end_time.tv_sec * 1000000.0 + end_time.tv_usec;
2007 +    }
2008 +
2009 +    // Calculates the difference between the end and start times.
2010 +    if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error)
2011 +    {
2012 +      thd->busy_time= (end_usecs - start_usecs) / 1000000;
2013 +      // In case there are bad values, 2629743 is the #seconds in a month.
2014 +      if (thd->busy_time > 2629743)
2015 +      {
2016 +        thd->busy_time= 0;
2017 +      }
2018 +    }
2019 +    else
2020 +    {
2021 +      // end time went back in time, or gettimeofday() failed.
2022 +      thd->busy_time= 0;
2023 +    }
2024 +
2025 +#ifdef HAVE_CLOCK_GETTIME
2026 +    /* get end cputime */
2027 +    if (!cputime_error &&
2028 +        !(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2029 +      end_cpu_nsecs= tp.tv_sec*1000000000.0+tp.tv_nsec;
2030 +#endif
2031 +    if (start_cpu_nsecs && !cputime_error)
2032 +    {
2033 +      thd->cpu_time= (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
2034 +      // In case there are bad values, 2629743 is the #seconds in a month.
2035 +      if (thd->cpu_time > 2629743)
2036 +      {
2037 +        thd->cpu_time= 0;
2038 +      }
2039 +    }
2040 +    else
2041 +      thd->cpu_time = 0;
2042 +  }
2043 +  // Updates THD stats and the global user stats.
2044 +  thd->update_stats(true);
2045 +  update_global_user_stats(thd, true, time(NULL));
2046 +
2047    DBUG_VOID_RETURN;
2048  }
2049  
2050 @@ -2540,12 +2616,38 @@
2051    /* First of all clear possible warnings from the previous command */
2052    mysql_reset_thd_for_next_command(thd);
2053  
2054 +  int start_time_error=   0;
2055 +  int end_time_error=     0;
2056 +  struct timeval start_time, end_time;
2057 +  double start_usecs=     0;
2058 +  double end_usecs=       0;
2059 +  /* cpu time */
2060 +  int cputime_error=      0;
2061 +  struct timespec tp;
2062 +  double start_cpu_nsecs= 0;
2063 +  double end_cpu_nsecs=   0;
2064 +
2065 +  if (opt_userstat)
2066 +  {
2067 +#ifdef HAVE_CLOCK_GETTIME
2068 +    /* get start cputime */
2069 +    if (!(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2070 +      start_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
2071 +#endif
2072 +
2073 +    // Gets the start time, in order to measure how long this command takes.
2074 +    if (!(start_time_error = gettimeofday(&start_time, NULL)))
2075 +    {
2076 +      start_usecs = start_time.tv_sec * 1000000.0 + start_time.tv_usec;
2077 +    }
2078 +  }
2079 +
2080    if (!(stmt= find_prepared_statement(thd, stmt_id)))
2081    {
2082      char llbuf[22];
2083      my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(sizeof(llbuf)),
2084               llstr(stmt_id, llbuf), "mysqld_stmt_execute");
2085 -    DBUG_VOID_RETURN;
2086 +    goto end;
2087    }
2088  
2089  #if defined(ENABLED_PROFILING)
2090 @@ -2563,6 +2665,53 @@
2091    /* Close connection socket; for use with client testing (Bug#43560). */
2092    DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio););
2093  
2094 +end:
2095 +  if (opt_userstat)
2096 +  {
2097 +    // Gets the end time.
2098 +    if (!(end_time_error= gettimeofday(&end_time, NULL)))
2099 +    {
2100 +      end_usecs= end_time.tv_sec * 1000000.0 + end_time.tv_usec;
2101 +    }
2102 +
2103 +    // Calculates the difference between the end and start times.
2104 +    if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error)
2105 +    {
2106 +      thd->busy_time= (end_usecs - start_usecs) / 1000000;
2107 +      // In case there are bad values, 2629743 is the #seconds in a month.
2108 +      if (thd->busy_time > 2629743)
2109 +      {
2110 +        thd->busy_time= 0;
2111 +      }
2112 +    }
2113 +    else
2114 +    {
2115 +      // end time went back in time, or gettimeofday() failed.
2116 +      thd->busy_time= 0;
2117 +    }
2118 +
2119 +#ifdef HAVE_CLOCK_GETTIME
2120 +    /* get end cputime */
2121 +    if (!cputime_error &&
2122 +        !(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2123 +      end_cpu_nsecs= tp.tv_sec*1000000000.0+tp.tv_nsec;
2124 +#endif
2125 +    if (start_cpu_nsecs && !cputime_error)
2126 +    {
2127 +      thd->cpu_time= (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
2128 +      // In case there are bad values, 2629743 is the #seconds in a month.
2129 +      if (thd->cpu_time > 2629743)
2130 +      {
2131 +        thd->cpu_time= 0;
2132 +      }
2133 +    }
2134 +    else
2135 +      thd->cpu_time = 0;
2136 +  }
2137 +  // Updates THD stats and the global user stats.
2138 +  thd->update_stats(true);
2139 +  update_global_user_stats(thd, true, time(NULL));
2140 +
2141    DBUG_VOID_RETURN;
2142  }
2143  
2144 @@ -2635,20 +2784,47 @@
2145  
2146    /* First of all clear possible warnings from the previous command */
2147    mysql_reset_thd_for_next_command(thd);
2148 +
2149 +  int start_time_error=   0;
2150 +  int end_time_error=     0;
2151 +  struct timeval start_time, end_time;
2152 +  double start_usecs=     0;
2153 +  double end_usecs=       0;
2154 +  /* cpu time */
2155 +  int cputime_error=      0;
2156 +  struct timespec tp;
2157 +  double start_cpu_nsecs= 0;
2158 +  double end_cpu_nsecs=   0;
2159 +
2160 +  if (opt_userstat)
2161 +  {
2162 +#ifdef HAVE_CLOCK_GETTIME
2163 +    /* get start cputime */
2164 +    if (!(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2165 +      start_cpu_nsecs= tp.tv_sec*1000000000.0+tp.tv_nsec;
2166 +#endif
2167 +
2168 +    // Gets the start time, in order to measure how long this command takes.
2169 +    if (!(start_time_error= gettimeofday(&start_time, NULL)))
2170 +    {
2171 +      start_usecs= start_time.tv_sec * 1000000.0 + start_time.tv_usec;
2172 +    }
2173 +  }
2174 +
2175    status_var_increment(thd->status_var.com_stmt_fetch);
2176    if (!(stmt= find_prepared_statement(thd, stmt_id)))
2177    {
2178      char llbuf[22];
2179      my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(sizeof(llbuf)),
2180               llstr(stmt_id, llbuf), "mysqld_stmt_fetch");
2181 -    DBUG_VOID_RETURN;
2182 +    goto end;
2183    }
2184  
2185    cursor= stmt->cursor;
2186    if (!cursor)
2187    {
2188      my_error(ER_STMT_HAS_NO_OPEN_CURSOR, MYF(0), stmt_id);
2189 -    DBUG_VOID_RETURN;
2190 +    goto end;
2191    }
2192  
2193    thd->stmt_arena= stmt;
2194 @@ -2665,6 +2841,52 @@
2195    thd->restore_backup_statement(stmt, &stmt_backup);
2196    thd->stmt_arena= thd;
2197  
2198 +end:
2199 +  if (opt_userstat)
2200 +  {
2201 +    // Gets the end time.
2202 +    if (!(end_time_error = gettimeofday(&end_time, NULL)))
2203 +    {
2204 +      end_usecs = end_time.tv_sec * 1000000.0 + end_time.tv_usec;
2205 +    }
2206 +
2207 +    // Calculates the difference between the end and start times.
2208 +    if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error)
2209 +    {
2210 +      thd->busy_time= (end_usecs - start_usecs) / 1000000;
2211 +      // In case there are bad values, 2629743 is the #seconds in a month.
2212 +      if (thd->busy_time > 2629743)
2213 +      {
2214 +        thd->busy_time= 0;
2215 +      }
2216 +    }
2217 +    else
2218 +    {
2219 +      // end time went back in time, or gettimeofday() failed.
2220 +      thd->busy_time= 0;
2221 +    }
2222 +
2223 +#ifdef HAVE_CLOCK_GETTIME
2224 +    /* get end cputime */
2225 +    if (!cputime_error &&
2226 +        !(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2227 +      end_cpu_nsecs= tp.tv_sec*1000000000.0+tp.tv_nsec;
2228 +#endif
2229 +    if (start_cpu_nsecs && !cputime_error)
2230 +    {
2231 +      thd->cpu_time= (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
2232 +      // In case there are bad values, 2629743 is the #seconds in a month.
2233 +      if (thd->cpu_time > 2629743)
2234 +      {
2235 +        thd->cpu_time= 0;
2236 +      }
2237 +    } else
2238 +      thd->cpu_time= 0;
2239 +  }
2240 +  // Updates THD stats and the global user stats.
2241 +  thd->update_stats(true);
2242 +  update_global_user_stats(thd, true, time(NULL));
2243 +
2244    DBUG_VOID_RETURN;
2245  }
2246  
2247 @@ -2695,13 +2917,39 @@
2248    /* First of all clear possible warnings from the previous command */
2249    mysql_reset_thd_for_next_command(thd);
2250  
2251 +  int start_time_error=   0;
2252 +  int end_time_error=     0;
2253 +  struct timeval start_time, end_time;
2254 +  double start_usecs=     0;
2255 +  double end_usecs=       0;
2256 +  /* cpu time */
2257 +  int cputime_error=      0;
2258 +  struct timespec tp;
2259 +  double start_cpu_nsecs= 0;
2260 +  double end_cpu_nsecs=   0;
2261 +
2262 +  if (opt_userstat)
2263 +  {
2264 +#ifdef HAVE_CLOCK_GETTIME
2265 +    /* get start cputime */
2266 +    if (!(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2267 +      start_cpu_nsecs= tp.tv_sec * 1000000000.0+tp.tv_nsec;
2268 +#endif
2269 +
2270 +    // Gets the start time, in order to measure how long this command takes.
2271 +    if (!(start_time_error= gettimeofday(&start_time, NULL)))
2272 +    {
2273 +      start_usecs= start_time.tv_sec * 1000000.0 + start_time.tv_usec;
2274 +    }
2275 +  }
2276 +
2277    status_var_increment(thd->status_var.com_stmt_reset);
2278    if (!(stmt= find_prepared_statement(thd, stmt_id)))
2279    {
2280      char llbuf[22];
2281      my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(sizeof(llbuf)),
2282               llstr(stmt_id, llbuf), "mysqld_stmt_reset");
2283 -    DBUG_VOID_RETURN;
2284 +    goto end;
2285    }
2286  
2287    stmt->close_cursor();
2288 @@ -2718,6 +2966,53 @@
2289  
2290    my_ok(thd);
2291  
2292 +end:
2293 +  if (opt_userstat)
2294 +  {
2295 +    // Gets the end time.
2296 +    if (!(end_time_error = gettimeofday(&end_time, NULL)))
2297 +    {
2298 +      end_usecs = end_time.tv_sec * 1000000.0 + end_time.tv_usec;
2299 +    }
2300 +
2301 +    // Calculates the difference between the end and start times.
2302 +    if (start_usecs && end_usecs >= start_usecs && !start_time_error && !end_time_error)
2303 +    {
2304 +      thd->busy_time= (end_usecs - start_usecs) / 1000000;
2305 +      // In case there are bad values, 2629743 is the #seconds in a month.
2306 +      if (thd->busy_time > 2629743)
2307 +      {
2308 +        thd->busy_time= 0;
2309 +      }
2310 +    }
2311 +    else
2312 +    {
2313 +      // end time went back in time, or gettimeofday() failed.
2314 +      thd->busy_time= 0;
2315 +    }
2316 +
2317 +#ifdef HAVE_CLOCK_GETTIME
2318 +    /* get end cputime */
2319 +    if (!cputime_error &&
2320 +        !(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
2321 +      end_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
2322 +#endif
2323 +    if (start_cpu_nsecs && !cputime_error)
2324 +    {
2325 +      thd->cpu_time = (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
2326 +      // In case there are bad values, 2629743 is the #seconds in a month.
2327 +      if (thd->cpu_time > 2629743)
2328 +      {
2329 +        thd->cpu_time= 0;
2330 +      }
2331 +    }
2332 +    else
2333 +      thd->cpu_time= 0;
2334 +  }
2335 +  // Updates THD stats and the global user stats.
2336 +  thd->update_stats(true);
2337 +  update_global_user_stats(thd, true, time(NULL));
2338 +
2339    DBUG_VOID_RETURN;
2340  }
2341  
2342 --- a/sql/sql_reload.cc
2343 +++ b/sql/sql_reload.cc
2344 @@ -320,14 +320,48 @@
2345     mysql_mutex_unlock(&LOCK_active_mi);
2346   }
2347  #endif
2348 - if (options & REFRESH_USER_RESOURCES)
2349 -   reset_mqh((LEX_USER *) NULL, 0);             /* purecov: inspected */
2350  #ifdef HAVE_RESPONSE_TIME_DISTRIBUTION
2351   if (options & REFRESH_QUERY_RESPONSE_TIME)
2352   {
2353     query_response_time_flush();
2354   }
2355  #endif // HAVE_RESPONSE_TIME_DISTRIBUTION
2356 +  if (options & REFRESH_USER_RESOURCES)
2357 +    reset_mqh((LEX_USER *) NULL, 0);             /* purecov: inspected */
2358 +  if (options & REFRESH_TABLE_STATS)
2359 +  {
2360 +    mysql_mutex_lock(&LOCK_global_table_stats);
2361 +    free_global_table_stats();
2362 +    init_global_table_stats();
2363 +    mysql_mutex_unlock(&LOCK_global_table_stats);
2364 +  }
2365 +  if (options & REFRESH_INDEX_STATS)
2366 +  {
2367 +    mysql_mutex_lock(&LOCK_global_index_stats);
2368 +    free_global_index_stats();
2369 +    init_global_index_stats();
2370 +    mysql_mutex_unlock(&LOCK_global_index_stats);
2371 +  }
2372 +  if (options & (REFRESH_USER_STATS | REFRESH_CLIENT_STATS | REFRESH_THREAD_STATS))
2373 +  {
2374 +    mysql_mutex_lock(&LOCK_global_user_client_stats);
2375 +    if (options & REFRESH_USER_STATS)
2376 +    {
2377 +      free_global_user_stats();
2378 +      init_global_user_stats();
2379 +    }
2380 +    if (options & REFRESH_CLIENT_STATS)
2381 +    {
2382 +      free_global_client_stats();
2383 +      init_global_client_stats();
2384 +    }
2385 +    if (options & REFRESH_THREAD_STATS)
2386 +    {
2387 +      free_global_thread_stats();
2388 +      init_global_thread_stats();
2389 +    }
2390 +    mysql_mutex_unlock(&LOCK_global_user_client_stats);
2391 +  }
2392   if (*write_to_binlog != -1)
2393     *write_to_binlog= tmp_write_to_binlog;
2394   /*
2395 --- a/sql/sql_show.cc
2396 +++ b/sql/sql_show.cc
2397 @@ -114,6 +114,43 @@
2398  
2399  static COND * make_cond_for_info_schema(COND *cond, TABLE_LIST *table);
2400  
2401 +/*
2402 + * Solaris 10 does not have strsep().
2403 + *
2404 + * based on getToken from http://www.winehq.org/pipermail/wine-patches/2001-November/001322.html
2405 + *
2406 +*/
2407 +
2408 +#ifndef HAVE_STRSEP
2409 +static char* strsep(char** str, const char* delims)
2410 +{
2411 +  char *token;
2412 +
2413 +  if (*str == NULL)
2414 +  {
2415 +    /* No more tokens */
2416 +    return NULL;
2417 +  }
2418 +
2419 +  token= *str;
2420 +  while (**str != '\0')
2421 +  {
2422 +    if (strchr(delims, **str) != NULL)
2423 +    {
2424 +      **str= '\0';
2425 +      (*str)++;
2426 +      return token;
2427 +    }
2428 +    (*str)++;
2429 +  }
2430 +
2431 +  /* There is not another token */
2432 +  *str= NULL;
2433 +
2434 +  return token;
2435 +}
2436 +#endif
2437 +
2438  /***************************************************************************
2439  ** List all table types supported
2440  ***************************************************************************/
2441 @@ -800,6 +837,7 @@
2442                 sctx->master_access);
2443    if (!(db_access & DB_ACLS) && check_grant_db(thd,dbname))
2444    {
2445 +    thd->diff_access_denied_errors++;
2446      my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
2447               sctx->priv_user, sctx->host_or_ip, dbname);
2448      general_log_print(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
2449 @@ -2359,6 +2397,284 @@
2450    DBUG_RETURN(res);
2451  }
2452  
2453 +/*
2454 +   Write result to network for SHOW USER_STATISTICS
2455 +
2456 +   SYNOPSIS
2457 +     send_user_stats
2458 +       all_user_stats - values to return
2459 +       table - I_S table
2460 +
2461 +   RETURN
2462 +     0 - OK
2463 +     1 - error
2464 +*/
2465 +int send_user_stats(THD* thd, HASH *all_user_stats, TABLE *table)
2466 +{
2467 +  DBUG_ENTER("send_user_stats");
2468 +  for (uint i = 0; i < all_user_stats->records; ++i)
2469 +  {
2470 +    restore_record(table, s->default_values);
2471 +    USER_STATS *user_stats = (USER_STATS *) my_hash_element(all_user_stats, i);
2472 +      table->field[0]->store(user_stats->user, strlen(user_stats->user), system_charset_info);
2473 +      table->field[1]->store((longlong)user_stats->total_connections);
2474 +      table->field[2]->store((longlong)user_stats->concurrent_connections);
2475 +      table->field[3]->store((longlong)user_stats->connected_time);
2476 +      table->field[4]->store((longlong)user_stats->busy_time);
2477 +      table->field[5]->store((longlong)user_stats->cpu_time);
2478 +      table->field[6]->store((longlong)user_stats->bytes_received);
2479 +      table->field[7]->store((longlong)user_stats->bytes_sent);
2480 +      table->field[8]->store((longlong)user_stats->binlog_bytes_written);
2481 +      table->field[9]->store((longlong)user_stats->rows_fetched);
2482 +      table->field[10]->store((longlong)user_stats->rows_updated);
2483 +      table->field[11]->store((longlong)user_stats->rows_read);
2484 +      table->field[12]->store((longlong)user_stats->select_commands);
2485 +      table->field[13]->store((longlong)user_stats->update_commands);
2486 +      table->field[14]->store((longlong)user_stats->other_commands);
2487 +      table->field[15]->store((longlong)user_stats->commit_trans);
2488 +      table->field[16]->store((longlong)user_stats->rollback_trans);
2489 +      table->field[17]->store((longlong)user_stats->denied_connections);
2490 +      table->field[18]->store((longlong)user_stats->lost_connections);
2491 +      table->field[19]->store((longlong)user_stats->access_denied_errors);
2492 +      table->field[20]->store((longlong)user_stats->empty_queries);
2493 +      if (schema_table_store_record(thd, table))
2494 +      {
2495 +             DBUG_PRINT("error", ("store record error"));
2496 +             DBUG_RETURN(1);
2497 +      }
2498 +  }
2499 +  DBUG_RETURN(0);
2500 +}
2501 +
2502 +int send_thread_stats(THD* thd, HASH *all_thread_stats, TABLE *table)
2503 +{
2504 +  DBUG_ENTER("send_thread_stats");
2505 +  for (uint i = 0; i < all_thread_stats->records; ++i)
2506 +  {
2507 +    restore_record(table, s->default_values);
2508 +    THREAD_STATS *user_stats = (THREAD_STATS *) my_hash_element(all_thread_stats, i);
2509 +      table->field[0]->store((longlong)user_stats->id);
2510 +      table->field[1]->store((longlong)user_stats->total_connections);
2511 +      table->field[2]->store((longlong)user_stats->concurrent_connections);
2512 +      table->field[3]->store((longlong)user_stats->connected_time);
2513 +      table->field[4]->store((longlong)user_stats->busy_time);
2514 +      table->field[5]->store((longlong)user_stats->cpu_time);
2515 +      table->field[6]->store((longlong)user_stats->bytes_received);
2516 +      table->field[7]->store((longlong)user_stats->bytes_sent);
2517 +      table->field[8]->store((longlong)user_stats->binlog_bytes_written);
2518 +      table->field[9]->store((longlong)user_stats->rows_fetched);
2519 +      table->field[10]->store((longlong)user_stats->rows_updated);
2520 +      table->field[11]->store((longlong)user_stats->rows_read);
2521 +      table->field[12]->store((longlong)user_stats->select_commands);
2522 +      table->field[13]->store((longlong)user_stats->update_commands);
2523 +      table->field[14]->store((longlong)user_stats->other_commands);
2524 +      table->field[15]->store((longlong)user_stats->commit_trans);
2525 +      table->field[16]->store((longlong)user_stats->rollback_trans);
2526 +      table->field[17]->store((longlong)user_stats->denied_connections);
2527 +      table->field[18]->store((longlong)user_stats->lost_connections);
2528 +      table->field[19]->store((longlong)user_stats->access_denied_errors);
2529 +      table->field[20]->store((longlong)user_stats->empty_queries);
2530 +      if (schema_table_store_record(thd, table))
2531 +      {
2532 +              DBUG_PRINT("error", ("store record error"));
2533 +              DBUG_RETURN(1);
2534 +      }
2535 +  }
2536 +  DBUG_RETURN(0);
2537 +}
2538 +
2539 +/*
2540 +   Process SHOW USER_STATISTICS
2541 +
2542 +   SYNOPSIS
2543 +     mysqld_show_user_stats
2544 +       thd - current thread
2545 +       wild - limit results to the entry for this user
2546 +       with_roles - when true, display role for mapped users
2547 +
2548 +   RETURN
2549 +     0 - OK
2550 +     1 - error
2551 +*/
2552 +
2553 +
2554 +int fill_schema_user_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2555 +{
2556 +  TABLE *table= tables->table;
2557 +  DBUG_ENTER("fill_schema_user_stats");
2558 +
2559 +  if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
2560 +          DBUG_RETURN(1);
2561 +
2562 +  // Iterates through all the global stats and sends them to the client.
2563 +  // Pattern matching on the client IP is supported.
2564 +
2565 +  mysql_mutex_lock(&LOCK_global_user_client_stats);
2566 +  int result= send_user_stats(thd, &global_user_stats, table);
2567 +  mysql_mutex_unlock(&LOCK_global_user_client_stats);
2568 +  if (result)
2569 +    goto err;
2570 +
2571 +  DBUG_PRINT("exit", ("fill_schema_user_stats result is 0"));
2572 +  DBUG_RETURN(0);
2573 +
2574 + err:
2575 +  DBUG_PRINT("exit", ("fill_schema_user_stats result is 1"));
2576 +  DBUG_RETURN(1);
2577 +}
2578 +
2579 +/*
2580 +   Process SHOW CLIENT_STATISTICS
2581 +
2582 +   SYNOPSIS
2583 +     mysqld_show_client_stats
2584 +       thd - current thread
2585 +       wild - limit results to the entry for this client
2586 +
2587 +   RETURN
2588 +     0 - OK
2589 +     1 - error
2590 +*/
2591 +
2592 +
2593 +int fill_schema_client_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2594 +{
2595 +  TABLE *table= tables->table;
2596 +  DBUG_ENTER("fill_schema_client_stats");
2597 +
2598 +  if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
2599 +          DBUG_RETURN(1);
2600 +
2601 +  // Iterates through all the global stats and sends them to the client.
2602 +  // Pattern matching on the client IP is supported.
2603 +
2604 +  mysql_mutex_lock(&LOCK_global_user_client_stats);
2605 +  int result= send_user_stats(thd, &global_client_stats, table);
2606 +  mysql_mutex_unlock(&LOCK_global_user_client_stats);
2607 +  if (result)
2608 +    goto err;
2609 +
2610 +  DBUG_PRINT("exit", ("mysqld_show_client_stats result is 0"));
2611 +  DBUG_RETURN(0);
2612 +
2613 + err:
2614 +  DBUG_PRINT("exit", ("mysqld_show_client_stats result is 1"));
2615 +  DBUG_RETURN(1);
2616 +}
2617 +
2618 +int fill_schema_thread_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2619 +{
2620 +  TABLE *table= tables->table;
2621 +  DBUG_ENTER("fill_schema_thread_stats");
2622 +
2623 +  if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
2624 +          DBUG_RETURN(1);
2625 +
2626 +  // Iterates through all the global stats and sends them to the client.
2627 +  // Pattern matching on the client IP is supported.
2628 +
2629 +  mysql_mutex_lock(&LOCK_global_user_client_stats);
2630 +  int result= send_thread_stats(thd, &global_thread_stats, table);
2631 +  mysql_mutex_unlock(&LOCK_global_user_client_stats);
2632 +  if (result)
2633 +    goto err;
2634 +
2635 +  DBUG_PRINT("exit", ("mysqld_show_thread_stats result is 0"));
2636 +  DBUG_RETURN(0);
2637 +
2638 + err:
2639 +  DBUG_PRINT("exit", ("mysqld_show_thread_stats result is 1"));
2640 +  DBUG_RETURN(1);
2641 +}
2642 +
2643 +// Sends the global table stats back to the client.
2644 +int fill_schema_table_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2645 +{
2646 +  TABLE *table= tables->table;
2647 +  DBUG_ENTER("fill_schema_table_stats");
2648 +  char *table_full_name, *table_schema;
2649 +
2650 +  mysql_mutex_lock(&LOCK_global_table_stats);
2651 +  for (uint i = 0; i < global_table_stats.records; ++i)
2652 +  {
2653 +    restore_record(table, s->default_values);
2654 +    TABLE_STATS *table_stats =
2655 +      (TABLE_STATS *) my_hash_element(&global_table_stats, i);
2656 +
2657 +    table_full_name= thd->strdup(table_stats->table);
2658 +    table_schema= strsep(&table_full_name, ".");
2659 +
2660 +    TABLE_LIST tmp_table;
2661 +    bzero((char *) &tmp_table,sizeof(tmp_table));
2662 +    tmp_table.table_name= table_full_name;
2663 +    tmp_table.db= table_schema;
2664 +    tmp_table.grant.privilege= 0;
2665 +    if (check_access(thd, SELECT_ACL, tmp_table.db,
2666 +                      &tmp_table.grant.privilege, 0, 0,
2667 +                      is_infoschema_db(table_schema)) ||
2668 +         check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, 1))
2669 +        continue;
2670 +
2671 +    table->field[0]->store(table_schema, strlen(table_schema), system_charset_info);
2672 +    table->field[1]->store(table_full_name, strlen(table_full_name), system_charset_info);
2673 +    table->field[2]->store((longlong)table_stats->rows_read, TRUE);
2674 +    table->field[3]->store((longlong)table_stats->rows_changed, TRUE);
2675 +    table->field[4]->store((longlong)table_stats->rows_changed_x_indexes, TRUE);
2676 +
2677 +    if (schema_table_store_record(thd, table))
2678 +    {
2679 +      mysql_mutex_unlock(&LOCK_global_table_stats);
2680 +      DBUG_RETURN(1);
2681 +    }
2682 +  }
2683 +  mysql_mutex_unlock(&LOCK_global_table_stats);
2684 +  DBUG_RETURN(0);
2685 +}
2686 +
2687 +// Sends the global index stats back to the client.
2688 +int fill_schema_index_stats(THD* thd, TABLE_LIST* tables, COND* cond)
2689 +{
2690 +  TABLE *table= tables->table;
2691 +  DBUG_ENTER("fill_schema_index_stats");
2692 +  char *index_full_name, *table_schema, *table_name;
2693 +
2694 +  mysql_mutex_lock(&LOCK_global_index_stats);
2695 +  for (uint i = 0; i < global_index_stats.records; ++i)
2696 +  {
2697 +    restore_record(table, s->default_values);
2698 +    INDEX_STATS *index_stats =
2699 +      (INDEX_STATS *) my_hash_element(&global_index_stats, i);
2700 +
2701 +    index_full_name= thd->strdup(index_stats->index);
2702 +    table_schema= strsep(&index_full_name, ".");
2703 +    table_name= strsep(&index_full_name, ".");
2704 +
2705 +    TABLE_LIST tmp_table;
2706 +    bzero((char *) &tmp_table,sizeof(tmp_table));
2707 +    tmp_table.table_name= table_name;
2708 +    tmp_table.db= table_schema;
2709 +    tmp_table.grant.privilege= 0;
2710 +    if (check_access(thd, SELECT_ACL, tmp_table.db,
2711 +                      &tmp_table.grant.privilege, 0, 0,
2712 +                      is_infoschema_db(table_schema)) ||
2713 +         check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, 1))
2714 +        continue;
2715 +
2716 +    table->field[0]->store(table_schema, strlen(table_schema), system_charset_info);
2717 +    table->field[1]->store(table_name, strlen(table_name), system_charset_info);
2718 +    table->field[2]->store(index_full_name, strlen(index_full_name), system_charset_info);
2719 +    table->field[3]->store((longlong)index_stats->rows_read, TRUE);
2720 +
2721 +    if (schema_table_store_record(thd, table))
2722 +    {
2723 +      mysql_mutex_unlock(&LOCK_global_index_stats);
2724 +      DBUG_RETURN(1);
2725 +    }
2726 +  }
2727 +  mysql_mutex_unlock(&LOCK_global_index_stats);
2728 +  DBUG_RETURN(0);
2729 +}
2730 +
2731  
2732  /* collect status for all running threads */
2733  
2734 @@ -7712,6 +8028,104 @@
2735  };
2736  
2737  
2738 +ST_FIELD_INFO user_stats_fields_info[]=
2739 +{
2740 +  {"USER", USERNAME_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User", SKIP_OPEN_TABLE},
2741 +  {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections", SKIP_OPEN_TABLE},
2742 +  {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections", SKIP_OPEN_TABLE},
2743 +  {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time", SKIP_OPEN_TABLE},
2744 +  {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Busy_time", SKIP_OPEN_TABLE},
2745 +  {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Cpu_time", SKIP_OPEN_TABLE},
2746 +  {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_received", SKIP_OPEN_TABLE},
2747 +  {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_sent", SKIP_OPEN_TABLE},
2748 +  {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Binlog_bytes_written", SKIP_OPEN_TABLE},
2749 +  {"ROWS_FETCHED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_fetched", SKIP_OPEN_TABLE},
2750 +  {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated", SKIP_OPEN_TABLE},
2751 +  {"TABLE_ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Table_rows_read", SKIP_OPEN_TABLE},
2752 +  {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Select_commands", SKIP_OPEN_TABLE},
2753 +  {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Update_commands", SKIP_OPEN_TABLE},
2754 +  {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Other_commands", SKIP_OPEN_TABLE},
2755 +  {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Commit_transactions", SKIP_OPEN_TABLE},
2756 +  {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rollback_transactions", SKIP_OPEN_TABLE},
2757 +  {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Denied_connections", SKIP_OPEN_TABLE},
2758 +  {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Lost_connections", SKIP_OPEN_TABLE},
2759 +  {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Access_denied", SKIP_OPEN_TABLE},
2760 +  {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Empty_queries", SKIP_OPEN_TABLE},
2761 +  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2762 +};
2763 +
2764 +ST_FIELD_INFO client_stats_fields_info[]=
2765 +{
2766 +  {"CLIENT", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Client", SKIP_OPEN_TABLE},
2767 +  {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections", SKIP_OPEN_TABLE},
2768 +  {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections", SKIP_OPEN_TABLE},
2769 +  {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time", SKIP_OPEN_TABLE},
2770 +  {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Busy_time", SKIP_OPEN_TABLE},
2771 +  {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Cpu_time", SKIP_OPEN_TABLE},
2772 +  {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_received", SKIP_OPEN_TABLE},
2773 +  {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_sent", SKIP_OPEN_TABLE},
2774 +  {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Binlog_bytes_written", SKIP_OPEN_TABLE},
2775 +  {"ROWS_FETCHED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_fetched", SKIP_OPEN_TABLE},
2776 +  {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated", SKIP_OPEN_TABLE},
2777 +  {"TABLE_ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Table_rows_read", SKIP_OPEN_TABLE},
2778 +  {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Select_commands", SKIP_OPEN_TABLE},
2779 +  {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Update_commands", SKIP_OPEN_TABLE},
2780 +  {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Other_commands", SKIP_OPEN_TABLE},
2781 +  {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Commit_transactions", SKIP_OPEN_TABLE},
2782 +  {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rollback_transactions", SKIP_OPEN_TABLE},
2783 +  {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Denied_connections", SKIP_OPEN_TABLE},
2784 +  {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Lost_connections", SKIP_OPEN_TABLE},
2785 +  {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Access_denied", SKIP_OPEN_TABLE},
2786 +  {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Empty_queries", SKIP_OPEN_TABLE},
2787 +  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2788 +};
2789 +
2790 +ST_FIELD_INFO thread_stats_fields_info[]=
2791 +{
2792 +  {"THREAD_ID", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Thread_id", SKIP_OPEN_TABLE},
2793 +  {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections", SKIP_OPEN_TABLE},
2794 +  {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections", SKIP_OPEN_TABLE},
2795 +  {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time", SKIP_OPEN_TABLE},
2796 +  {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Busy_time", SKIP_OPEN_TABLE},
2797 +  {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Cpu_time", SKIP_OPEN_TABLE},
2798 +  {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_received", SKIP_OPEN_TABLE},
2799 +  {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_sent", SKIP_OPEN_TABLE},
2800 +  {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Binlog_bytes_written", SKIP_OPEN_TABLE},
2801 +  {"ROWS_FETCHED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_fetched", SKIP_OPEN_TABLE},
2802 +  {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated", SKIP_OPEN_TABLE},
2803 +  {"TABLE_ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Table_rows_read", SKIP_OPEN_TABLE},
2804 +  {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Select_commands", SKIP_OPEN_TABLE},
2805 +  {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Update_commands", SKIP_OPEN_TABLE},
2806 +  {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Other_commands", SKIP_OPEN_TABLE},
2807 +  {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Commit_transactions", SKIP_OPEN_TABLE},
2808 +  {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rollback_transactions", SKIP_OPEN_TABLE},
2809 +  {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Denied_connections", SKIP_OPEN_TABLE},
2810 +  {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Lost_connections", SKIP_OPEN_TABLE},
2811 +  {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Access_denied", SKIP_OPEN_TABLE},
2812 +  {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Empty_queries", SKIP_OPEN_TABLE},
2813 +  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2814 +};
2815 +
2816 +ST_FIELD_INFO table_stats_fields_info[]=
2817 +{
2818 +  {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_schema", SKIP_OPEN_TABLE},
2819 +  {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name", SKIP_OPEN_TABLE},
2820 +  {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read", SKIP_OPEN_TABLE},
2821 +  {"ROWS_CHANGED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_changed", SKIP_OPEN_TABLE},
2822 +  {"ROWS_CHANGED_X_INDEXES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_changed_x_#indexes", SKIP_OPEN_TABLE},
2823 +  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2824 +};
2825 +
2826 +ST_FIELD_INFO index_stats_fields_info[]=
2827 +{
2828 +  {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_schema", SKIP_OPEN_TABLE},
2829 +  {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name", SKIP_OPEN_TABLE},
2830 +  {"INDEX_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Index_name", SKIP_OPEN_TABLE},
2831 +  {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read", SKIP_OPEN_TABLE},
2832 +  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
2833 +};
2834 +
2835 +
2836  ST_FIELD_INFO processlist_fields_info[]=
2837  {
2838    {"ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Id", SKIP_OPEN_TABLE},
2839 @@ -7901,6 +8315,8 @@
2840  {
2841    {"CHARACTER_SETS", charsets_fields_info, create_schema_table, 
2842     fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0, 0},
2843 +  {"CLIENT_STATISTICS", client_stats_fields_info, create_schema_table,
2844 +    fill_schema_client_stats, make_old_format, 0, -1, -1, 0, 0},
2845    {"COLLATIONS", collation_fields_info, create_schema_table, 
2846     fill_schema_collation, make_old_format, 0, -1, -1, 0, 0},
2847    {"COLLATION_CHARACTER_SET_APPLICABILITY", coll_charset_app_fields_info,
2848 @@ -7910,6 +8326,8 @@
2849     OPTIMIZE_I_S_TABLE|OPEN_VIEW_FULL},
2850    {"COLUMN_PRIVILEGES", column_privileges_fields_info, create_schema_table,
2851     fill_schema_column_privileges, 0, 0, -1, -1, 0, 0},
2852 +  {"INDEX_STATISTICS", index_stats_fields_info, create_schema_table,
2853 +   fill_schema_index_stats, make_old_format, 0, -1, -1, 0, 0},
2854    {"ENGINES", engines_fields_info, create_schema_table,
2855     fill_schema_engines, make_old_format, 0, -1, -1, 0, 0},
2856  #ifdef HAVE_EVENT_SCHEDULER
2857 @@ -7982,14 +8400,20 @@
2858     get_all_tables, make_table_names_old_format, 0, 1, 2, 1, 0},
2859    {"TABLE_PRIVILEGES", table_privileges_fields_info, create_schema_table,
2860     fill_schema_table_privileges, 0, 0, -1, -1, 0, 0},
2861 +  {"TABLE_STATISTICS", table_stats_fields_info, create_schema_table,
2862 +    fill_schema_table_stats, make_old_format, 0, -1, -1, 0, 0},
2863    {"TEMPORARY_TABLES", temporary_table_fields_info, create_schema_table,
2864     fill_temporary_tables, make_temporary_tables_old_format, 0, 2, 3, 0,
2865     OPEN_TABLE_ONLY|OPTIMIZE_I_S_TABLE},
2866 +  {"THREAD_STATISTICS", thread_stats_fields_info, create_schema_table,
2867 +    fill_schema_thread_stats, make_old_format, 0, -1, -1, 0, 0},
2868    {"TRIGGERS", triggers_fields_info, create_schema_table,
2869     get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0,
2870     OPEN_TRIGGER_ONLY|OPTIMIZE_I_S_TABLE},
2871    {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table, 
2872     fill_schema_user_privileges, 0, 0, -1, -1, 0, 0},
2873 +  {"USER_STATISTICS", user_stats_fields_info, create_schema_table,
2874 +    fill_schema_user_stats, make_old_format, 0, -1, -1, 0, 0},
2875    {"VARIABLES", variables_fields_info, create_schema_table, fill_variables,
2876     make_old_format, 0, 0, -1, 1, 0},
2877    {"VIEWS", view_fields_info, create_schema_table, 
2878 --- a/sql/sql_update.cc
2879 +++ b/sql/sql_update.cc
2880 @@ -900,8 +900,10 @@
2881      my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), (ulong) found,
2882                  (ulong) updated,
2883                  (ulong) thd->warning_info->statement_warn_count());
2884 -    my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
2885 -          id, buff);
2886 +    ha_rows row_count=
2887 +      (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
2888 +    my_ok(thd, row_count, id, buff);
2889 +    thd->updated_row_count += row_count;
2890      DBUG_PRINT("info",("%ld records updated", (long) updated));
2891    }
2892    thd->count_cuted_fields= CHECK_FIELD_IGNORE;         /* calc cuted fields */
2893 @@ -2252,7 +2254,9 @@
2894      thd->first_successful_insert_id_in_prev_stmt : 0;
2895    my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO),
2896                (ulong) found, (ulong) updated, (ulong) thd->cuted_fields);
2897 -  ::my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
2898 -          id, buff);
2899 +  ha_rows row_count=
2900 +    (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
2901 +  ::my_ok(thd, row_count, id, buff);
2902 +  thd->updated_row_count+= row_count;
2903    DBUG_RETURN(FALSE);
2904  }
2905 --- a/sql/sql_yacc.yy
2906 +++ b/sql/sql_yacc.yy
2907 @@ -865,6 +865,7 @@
2908  %token  CIPHER_SYM
2909  %token  CLASS_ORIGIN_SYM              /* SQL-2003-N */
2910  %token  CLIENT_SYM
2911 +%token  CLIENT_STATS_SYM
2912  %token  CLOSE_SYM                     /* SQL-2003-R */
2913  %token  COALESCE                      /* SQL-2003-N */
2914  %token  CODE_SYM
2915 @@ -1018,6 +1019,7 @@
2916  %token  IMPORT
2917  %token  INDEXES
2918  %token  INDEX_SYM
2919 +%token  INDEX_STATS_SYM
2920  %token  INFILE
2921  %token  INITIAL_SIZE_SYM
2922  %token  INNER_SYM                     /* SQL-2003-R */
2923 @@ -1316,6 +1318,7 @@
2924  %token  TABLESPACE
2925  %token  TABLE_REF_PRIORITY
2926  %token  TABLE_SYM                     /* SQL-2003-R */
2927 +%token  TABLE_STATS_SYM
2928  %token  TABLE_CHECKSUM_SYM
2929  %token  TABLE_NAME_SYM                /* SQL-2003-N */
2930  %token  TEMPORARY                     /* SQL-2003-N */
2931 @@ -1325,6 +1328,7 @@
2932  %token  TEXT_SYM
2933  %token  THAN_SYM
2934  %token  THEN_SYM                      /* SQL-2003-R */
2935 +%token  THREAD_STATS_SYM
2936  %token  TIMESTAMP                     /* SQL-2003-R */
2937  %token  TIMESTAMP_ADD
2938  %token  TIMESTAMP_DIFF
2939 @@ -1362,6 +1366,7 @@
2940  %token  UPGRADE_SYM
2941  %token  USAGE                         /* SQL-2003-N */
2942  %token  USER                          /* SQL-2003-R */
2943 +%token  USER_STATS_SYM
2944  %token  USE_FRM
2945  %token  USE_SYM
2946  %token  USING                         /* SQL-2003-R */
2947 @@ -11126,6 +11131,41 @@
2948               MYSQL_YYABORT;
2949  #endif // HAVE_RESPONSE_TIME_DISTRIBUTION
2950           }
2951 +        | CLIENT_STATS_SYM wild_and_where
2952 +          {
2953 +           LEX *lex= Lex;
2954 +           Lex->sql_command= SQLCOM_SELECT;
2955 +           if (prepare_schema_table(YYTHD, lex, 0, SCH_CLIENT_STATS))
2956 +             MYSQL_YYABORT;
2957 +          }
2958 +        | USER_STATS_SYM wild_and_where
2959 +          {
2960 +           LEX *lex= Lex;
2961 +           lex->sql_command= SQLCOM_SELECT;
2962 +           if (prepare_schema_table(YYTHD, lex, 0, SCH_USER_STATS))
2963 +             MYSQL_YYABORT;
2964 +          }
2965 +        | THREAD_STATS_SYM wild_and_where
2966 +          {
2967 +           LEX *lex= Lex;
2968 +           Lex->sql_command= SQLCOM_SELECT;
2969 +           if (prepare_schema_table(YYTHD, lex, 0, SCH_THREAD_STATS))
2970 +             MYSQL_YYABORT;
2971 +          }
2972 +        | TABLE_STATS_SYM wild_and_where
2973 +          {
2974 +           LEX *lex= Lex;
2975 +           lex->sql_command= SQLCOM_SELECT;
2976 +           if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_STATS))
2977 +             MYSQL_YYABORT;
2978 +          }
2979 +        | INDEX_STATS_SYM wild_and_where
2980 +          {
2981 +           LEX *lex= Lex;
2982 +           lex->sql_command= SQLCOM_SELECT;
2983 +           if (prepare_schema_table(YYTHD, lex, 0, SCH_INDEX_STATS))
2984 +             MYSQL_YYABORT;
2985 +          }
2986          | CREATE PROCEDURE_SYM sp_name
2987            {
2988              LEX *lex= Lex;
2989 @@ -11371,6 +11411,16 @@
2990              Lex->type|= REFRESH_QUERY_RESPONSE_TIME;
2991  #endif // HAVE_RESPONSE_TIME_DISTRIBUTION
2992            }
2993 +        | CLIENT_STATS_SYM
2994 +          { Lex->type|= REFRESH_CLIENT_STATS; }
2995 +        | USER_STATS_SYM
2996 +          { Lex->type|= REFRESH_USER_STATS; }
2997 +        | THREAD_STATS_SYM
2998 +          { Lex->type|= REFRESH_THREAD_STATS; }
2999 +        | TABLE_STATS_SYM
3000 +          { Lex->type|= REFRESH_TABLE_STATS; }
3001 +        | INDEX_STATS_SYM
3002 +          { Lex->type|= REFRESH_INDEX_STATS; }
3003          | MASTER_SYM
3004            { Lex->type|= REFRESH_MASTER; }
3005          | DES_KEY_FILE
3006 @@ -12515,6 +12565,7 @@
3007          | CHAIN_SYM                {}
3008          | CHANGED                  {}
3009          | CIPHER_SYM               {}
3010 +        | CLIENT_STATS_SYM         {}
3011          | CLIENT_SYM               {}
3012          | CLASS_ORIGIN_SYM         {}
3013          | COALESCE                 {}
3014 @@ -12583,6 +12634,7 @@
3015          | HOSTS_SYM                {}
3016          | HOUR_SYM                 {}
3017          | IDENTIFIED_SYM           {}
3018 +        | INDEX_STATS_SYM          {}
3019          | IGNORE_SERVER_IDS_SYM    {}
3020          | INVOKER_SYM              {}
3021          | IMPORT                   {}
3022 @@ -12734,6 +12786,7 @@
3023          | SUSPEND_SYM              {}
3024          | SWAPS_SYM                {}
3025          | SWITCHES_SYM             {}
3026 +        | TABLE_STATS_SYM          {}
3027          | TABLE_NAME_SYM           {}
3028          | TABLES                   {}
3029          | TABLE_CHECKSUM_SYM       {}
3030 @@ -12759,6 +12812,7 @@
3031          | UNKNOWN_SYM              {}
3032          | UNTIL_SYM                {}
3033          | USER                     {}
3034 +        | USER_STATS_SYM           {}
3035          | USE_FRM                  {}
3036          | VARIABLES                {}
3037          | VIEW_SYM                 {}
3038 --- a/sql/structs.h
3039 +++ b/sql/structs.h
3040 @@ -25,6 +25,7 @@
3041  #include "my_time.h"                   /* enum_mysql_timestamp_type */
3042  #include "thr_lock.h"                  /* thr_lock_type */
3043  #include "my_base.h"                   /* ha_rows, ha_key_alg */
3044 +#include "mysql_com.h"
3045  
3046  struct TABLE;
3047  class Field;
3048 @@ -218,6 +219,171 @@
3049    USER_RESOURCES user_resources;
3050  } USER_CONN;
3051  
3052 +typedef struct st_user_stats {
3053 +  char user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1];
3054 +  // Account name the user is mapped to when this is a user from mapped_user.
3055 +  // Otherwise, the same value as user.
3056 +  char priv_user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1];
3057 +  uint total_connections;
3058 +  uint concurrent_connections;
3059 +  time_t connected_time;  // in seconds
3060 +  double busy_time;       // in seconds
3061 +  double cpu_time;        // in seconds
3062 +  ulonglong bytes_received;
3063 +  ulonglong bytes_sent;
3064 +  ulonglong binlog_bytes_written;
3065 +  ha_rows rows_fetched, rows_updated, rows_read;
3066 +  ulonglong select_commands, update_commands, other_commands;
3067 +  ulonglong commit_trans, rollback_trans;
3068 +  ulonglong denied_connections, lost_connections;
3069 +  ulonglong access_denied_errors;
3070 +  ulonglong empty_queries;
3071 +} USER_STATS;
3072 +
3073 +/* Lookup function for my_hash tables with USER_STATS entries */
3074 +extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
3075 +                                my_bool not_used __attribute__((unused)));
3076 +
3077 +/* Free all memory for a my_hash table with USER_STATS entries */
3078 +extern void free_user_stats(USER_STATS* user_stats);
3079 +
3080 +/* Intialize an instance of USER_STATS */
3081 +extern void
3082 +init_user_stats(USER_STATS *user_stats,
3083 +                const char *user,
3084 +                const char *priv_user,
3085 +                uint total_connections,
3086 +                uint concurrent_connections,
3087 +                time_t connected_time,
3088 +                double busy_time,
3089 +                double cpu_time,
3090 +                ulonglong bytes_received,
3091 +                ulonglong bytes_sent,
3092 +                ulonglong binlog_bytes_written,
3093 +                ha_rows rows_fetched,
3094 +                ha_rows rows_updated,
3095 +                ha_rows rows_read,
3096 +                ulonglong select_commands,
3097 +                ulonglong update_commands,
3098 +                ulonglong other_commands,
3099 +                ulonglong commit_trans,
3100 +                ulonglong rollback_trans,
3101 +                ulonglong denied_connections,
3102 +                ulonglong lost_connections,
3103 +                ulonglong access_denied_errors,
3104 +                ulonglong empty_queries);
3105 +
3106 +/* Increment values of an instance of USER_STATS */
3107 +extern void
3108 +add_user_stats(USER_STATS *user_stats,
3109 +               uint total_connections,
3110 +               uint concurrent_connections,
3111 +               time_t connected_time,
3112 +               double busy_time,
3113 +               double cpu_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);
3129 +
3130 +typedef struct st_thread_stats {
3131 +  my_thread_id id;
3132 +  uint total_connections;
3133 +  uint concurrent_connections;
3134 +  time_t connected_time;  // in seconds
3135 +  double busy_time;       // in seconds
3136 +  double cpu_time;        // in seconds
3137 +  ulonglong bytes_received;
3138 +  ulonglong bytes_sent;
3139 +  ulonglong binlog_bytes_written;
3140 +  ha_rows rows_fetched, rows_updated, rows_read;
3141 +  ulonglong select_commands, update_commands, other_commands;
3142 +  ulonglong commit_trans, rollback_trans;
3143 +  ulonglong denied_connections, lost_connections;
3144 +  ulonglong access_denied_errors;
3145 +  ulonglong empty_queries;
3146 +} THREAD_STATS;
3147 +
3148 +/* Lookup function for my_hash tables with THREAD_STATS entries */
3149 +extern "C" uchar *get_key_thread_stats(THREAD_STATS *thread_stats, size_t *length,
3150 +                                my_bool not_used __attribute__((unused)));
3151 +
3152 +/* Free all memory for a my_hash table with THREAD_STATS entries */
3153 +extern void free_thread_stats(THREAD_STATS* thread_stats);
3154 +
3155 +/* Intialize an instance of THREAD_STATS */
3156 +extern void
3157 +init_thread_stats(THREAD_STATS *thread_stats,
3158 +                my_thread_id id,
3159 +                uint total_connections,
3160 +                uint concurrent_connections,
3161 +                time_t connected_time,
3162 +                double busy_time,
3163 +                double cpu_time,
3164 +                ulonglong bytes_received,
3165 +                ulonglong bytes_sent,
3166 +                ulonglong binlog_bytes_written,
3167 +                ha_rows rows_fetched,
3168 +                ha_rows rows_updated,
3169 +                ha_rows rows_read,
3170 +                ulonglong select_commands,
3171 +                ulonglong update_commands,
3172 +                ulonglong other_commands,
3173 +                ulonglong commit_trans,
3174 +                ulonglong rollback_trans,
3175 +                ulonglong denied_connections,
3176 +                ulonglong lost_connections,
3177 +                ulonglong access_denied_errors,
3178 +                ulonglong empty_queries);
3179 +
3180 +/* Increment values of an instance of THREAD_STATS */
3181 +extern void
3182 +add_thread_stats(THREAD_STATS *thread_stats,
3183 +               uint total_connections,
3184 +               uint concurrent_connections,
3185 +               time_t connected_time,
3186 +               double busy_time,
3187 +               double cpu_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);
3203 +
3204 +typedef struct st_table_stats {
3205 +  char table[NAME_LEN * 2 + 2];  // [db] + '.' + [table] + '\0'
3206 +  ulonglong rows_read, rows_changed;
3207 +  ulonglong rows_changed_x_indexes;
3208 +  /* Stores enum db_type, but forward declarations cannot be done */
3209 +  int engine_type;
3210 +} TABLE_STATS;
3211 +
3212 +typedef struct st_index_stats {
3213 +  char index[NAME_LEN * 3 + 3];  // [db] + '.' + [table] + '.' + [index] + '\0'
3214 +  ulonglong rows_read;
3215 +} INDEX_STATS;
3216 +
3217         /* Bits in form->update */
3218  #define REG_MAKE_DUPP          1       /* Make a copy of record when read */
3219  #define REG_NEW_RECORD         2       /* Write a new record if not found */
3220 --- a/sql/sys_vars.cc
3221 +++ b/sql/sys_vars.cc
3222 @@ -1711,6 +1711,17 @@
3223         NO_MUTEX_GUARD, NOT_IN_BINLOG,
3224         ON_CHECK(check_read_only), ON_UPDATE(fix_read_only));
3225  
3226 +static Sys_var_mybool Sys_userstat(
3227 +       "userstat",
3228 +       "Control USER_STATISTICS, CLIENT_STATISTICS, THREAD_STATISTICS, "
3229 +       "INDEX_STATISTICS and TABLE_STATISTICS running",
3230 +       GLOBAL_VAR(opt_userstat), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
3231 +
3232 +static Sys_var_mybool Sys_thread_statistics(
3233 +       "thread_statistics",
3234 +       "Control TABLE_STATISTICS running, when userstat is enabled",
3235 +       GLOBAL_VAR(opt_thread_statistics), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
3236 +
3237  // Small lower limit to be able to test MRR
3238  static Sys_var_ulong Sys_read_rnd_buff_size(
3239         "read_rnd_buffer_size",
3240 --- a/storage/myisam/ha_myisam.cc
3241 +++ b/storage/myisam/ha_myisam.cc
3242 @@ -770,6 +770,7 @@
3243  
3244  int ha_myisam::write_row(uchar *buf)
3245  {
3246 +  int error;
3247    ha_statistic_increment(&SSV::ha_write_count);
3248  
3249    /* If we have a timestamp column, update it to the current time */
3250 @@ -782,11 +783,13 @@
3251    */
3252    if (table->next_number_field && buf == table->record[0])
3253    {
3254 -    int error;
3255      if ((error= update_auto_increment()))
3256        return error;
3257    }
3258 -  return mi_write(file,buf);
3259 +  error=mi_write(file,buf);
3260 +  if (!error)
3261 +    rows_changed++;
3262 +  return error;
3263  }
3264  
3265  int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
3266 @@ -1553,16 +1556,24 @@
3267  
3268  int ha_myisam::update_row(const uchar *old_data, uchar *new_data)
3269  {
3270 +  int error;
3271    ha_statistic_increment(&SSV::ha_update_count);
3272    if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
3273      table->timestamp_field->set_time();
3274 -  return mi_update(file,old_data,new_data);
3275 +  error=mi_update(file,old_data,new_data);
3276 +  if (!error)
3277 +    rows_changed++;
3278 +  return error;
3279  }
3280  
3281  int ha_myisam::delete_row(const uchar *buf)
3282  {
3283 +  int error;
3284    ha_statistic_increment(&SSV::ha_delete_count);
3285 -  return mi_delete(file,buf);
3286 +  error=mi_delete(file,buf);
3287 +  if (!error)
3288 +    rows_changed++;
3289 +  return error;
3290  }
3291  
3292  int ha_myisam::index_read_map(uchar *buf, const uchar *key,
3293 @@ -1574,6 +1585,14 @@
3294    ha_statistic_increment(&SSV::ha_read_key_count);
3295    int error=mi_rkey(file, buf, active_index, key, keypart_map, find_flag);
3296    table->status=error ? STATUS_NOT_FOUND: 0;
3297 +  if (!error)
3298 +  {
3299 +    rows_read++;
3300 +
3301 +    int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3302 +    if (inx >= 0 && inx < MAX_KEY)
3303 +      index_rows_read[inx]++;
3304 +  }
3305    MYSQL_INDEX_READ_ROW_DONE(error);
3306    return error;
3307  }
3308 @@ -1586,6 +1605,14 @@
3309    ha_statistic_increment(&SSV::ha_read_key_count);
3310    int error=mi_rkey(file, buf, index, key, keypart_map, find_flag);
3311    table->status=error ? STATUS_NOT_FOUND: 0;
3312 +  if (!error)
3313 +  {
3314 +    rows_read++;
3315 +
3316 +    int inx = index;
3317 +    if (inx >= 0 && inx < MAX_KEY)
3318 +      index_rows_read[inx]++;
3319 +  }
3320    MYSQL_INDEX_READ_ROW_DONE(error);
3321    return error;
3322  }
3323 @@ -1600,6 +1627,14 @@
3324    int error=mi_rkey(file, buf, active_index, key, keypart_map,
3325                      HA_READ_PREFIX_LAST);
3326    table->status=error ? STATUS_NOT_FOUND: 0;
3327 +  if (!error)
3328 +  {
3329 +    rows_read++;
3330 +
3331 +    int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3332 +    if (inx >= 0 && inx < MAX_KEY)
3333 +      index_rows_read[inx]++;
3334 +  }
3335    MYSQL_INDEX_READ_ROW_DONE(error);
3336    DBUG_RETURN(error);
3337  }
3338 @@ -1611,6 +1646,13 @@
3339    ha_statistic_increment(&SSV::ha_read_next_count);
3340    int error=mi_rnext(file,buf,active_index);
3341    table->status=error ? STATUS_NOT_FOUND: 0;
3342 +  if (!error) {
3343 +    rows_read++;
3344 +
3345 +    int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3346 +    if (inx >= 0 && inx < MAX_KEY)
3347 +      index_rows_read[inx]++;
3348 +  }
3349    MYSQL_INDEX_READ_ROW_DONE(error);
3350    return error;
3351  }
3352 @@ -1622,6 +1664,13 @@
3353    ha_statistic_increment(&SSV::ha_read_prev_count);
3354    int error=mi_rprev(file,buf, active_index);
3355    table->status=error ? STATUS_NOT_FOUND: 0;
3356 +  if (!error) {
3357 +    rows_read++;
3358 +
3359 +    int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3360 +    if (inx >= 0 && inx < MAX_KEY)
3361 +      index_rows_read[inx]++;
3362 +  }
3363    MYSQL_INDEX_READ_ROW_DONE(error);
3364    return error;
3365  }
3366 @@ -1633,6 +1682,14 @@
3367    ha_statistic_increment(&SSV::ha_read_first_count);
3368    int error=mi_rfirst(file, buf, active_index);
3369    table->status=error ? STATUS_NOT_FOUND: 0;
3370 +  if (!error)
3371 +  {
3372 +    rows_read++;
3373 +
3374 +    int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3375 +    if (inx >= 0 && inx < MAX_KEY)
3376 +      index_rows_read[inx]++;
3377 +  }
3378    MYSQL_INDEX_READ_ROW_DONE(error);
3379    return error;
3380  }
3381 @@ -1644,6 +1701,14 @@
3382    ha_statistic_increment(&SSV::ha_read_last_count);
3383    int error=mi_rlast(file, buf, active_index);
3384    table->status=error ? STATUS_NOT_FOUND: 0;
3385 +  if (!error)
3386 +  {
3387 +    rows_read++;
3388 +
3389 +    int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3390 +    if (inx >= 0 && inx < MAX_KEY)
3391 +      index_rows_read[inx]++;
3392 +  }
3393    MYSQL_INDEX_READ_ROW_DONE(error);
3394    return error;
3395  }
3396 @@ -1661,6 +1726,14 @@
3397      error= mi_rnext_same(file,buf);
3398    } while (error == HA_ERR_RECORD_DELETED);
3399    table->status=error ? STATUS_NOT_FOUND: 0;
3400 +  if (!error)
3401 +  {
3402 +    rows_read++;
3403 +
3404 +    int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
3405 +    if (inx >= 0 && inx < MAX_KEY)
3406 +      index_rows_read[inx]++;
3407 +  }
3408    MYSQL_INDEX_READ_ROW_DONE(error);
3409    return error;
3410  }
3411 @@ -1680,6 +1753,8 @@
3412    ha_statistic_increment(&SSV::ha_read_rnd_next_count);
3413    int error=mi_scan(file, buf);
3414    table->status=error ? STATUS_NOT_FOUND: 0;
3415 +  if (!error)
3416 +    rows_read++;
3417    MYSQL_READ_ROW_DONE(error);
3418    return error;
3419  }
3420 @@ -1696,6 +1771,8 @@
3421    ha_statistic_increment(&SSV::ha_read_rnd_count);
3422    int error=mi_rrnd(file, buf, my_get_ptr(pos,ref_length));
3423    table->status=error ? STATUS_NOT_FOUND: 0;
3424 +  if (!error)
3425 +    rows_read++;
3426    MYSQL_READ_ROW_DONE(error);
3427    return error;
3428  }
3429 --- /dev/null
3430 +++ b/mysql-test/r/userstat_bug602047.result
3431 @@ -0,0 +1,15 @@
3432 +DROP TABLE IF EXISTS t1;
3433 +SET GLOBAL userstat=ON;
3434 +CREATE TABLE t1 ( id int(10), PRIMARY KEY (id)) ENGINE=InnoDB;
3435 +INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
3436 +SELECT COUNT(*) FROM t1;
3437 +COUNT(*)
3438 +10
3439 +SELECT ROWS_READ FROM information_schema.table_statistics WHERE TABLE_NAME='t1';
3440 +ROWS_READ
3441 +10
3442 +SELECT ROWS_READ FROM information_schema.index_statistics WHERE TABLE_NAME='t1';
3443 +ROWS_READ
3444 +10
3445 +SET GLOBAL userstat=OFF;
3446 +DROP TABLE t1;
3447 --- /dev/null
3448 +++ b/mysql-test/t/userstat_bug602047.test
3449 @@ -0,0 +1,11 @@
3450 +--disable_warnings
3451 +DROP TABLE IF EXISTS t1; 
3452 +--enable_warnings
3453 +SET GLOBAL userstat=ON;
3454 +CREATE TABLE t1 ( id int(10), PRIMARY KEY (id)) ENGINE=InnoDB;
3455 +INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
3456 +SELECT COUNT(*) FROM t1; 
3457 +SELECT ROWS_READ FROM information_schema.table_statistics WHERE TABLE_NAME='t1';
3458 +SELECT ROWS_READ FROM information_schema.index_statistics WHERE TABLE_NAME='t1';
3459 +SET GLOBAL userstat=OFF;
3460 +DROP TABLE t1;
3461 \ No newline at end of file
3462 --- a/mysql-test/r/mysqld--help-notwin.result
3463 +++ b/mysql-test/r/mysqld--help-notwin.result
3464 @@ -745,6 +745,8 @@
3465   Define threads usage for handling queries, one of
3466   one-thread-per-connection, no-threads, loaded-dynamically
3467   --thread-stack=#    The stack size for each thread
3468 + --thread-statistics Control TABLE_STATISTICS running, when userstat is
3469 + enabled
3470   --time-format=name  The TIME format (ignored)
3471   --timed-mutexes     Specify whether to time mutexes (only InnoDB mutexes are
3472   currently supported)
3473 @@ -770,6 +772,9 @@
3474   of the underlying table and the query uses a LIMIT clause
3475   (usually get from GUI tools)
3476   -u, --user=name     Run mysqld daemon as user.
3477 + --userstat          Control USER_STATISTICS, CLIENT_STATISTICS,
3478 + THREAD_STATISTICS, INDEX_STATISTICS and TABLE_STATISTICS
3479 + running
3480   -v, --verbose       Used with --help option for detailed help.
3481   -V, --version       Output version information and exit.
3482   --wait-timeout=#    The number of seconds the server waits for activity on a
3483 @@ -1005,6 +1010,7 @@
3484  thread-cache-size 0
3485  thread-handling one-thread-per-connection
3486  thread-stack 262144
3487 +thread-statistics FALSE
3488  time-format %H:%i:%s
3489  timed-mutexes FALSE
3490  tmp-table-size 16777216
3491 @@ -1012,6 +1018,7 @@
3492  transaction-isolation REPEATABLE-READ
3493  transaction-prealloc-size 4096
3494  updatable-views-with-limit YES
3495 +userstat FALSE
3496  verbose TRUE
3497  wait-timeout 28800
3498  xtradb-admin-command ON
This page took 0.295355 seconds and 3 git commands to generate.