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