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