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