]> git.pld-linux.org Git - packages/percona-server.git/commitdiff
- oops, restore
authorElan Ruusamäe <glen@pld-linux.org>
Wed, 26 Aug 2009 09:42:51 +0000 (09:42 +0000)
committercvs2git <feedback@pld-linux.org>
Sun, 24 Jun 2012 12:13:13 +0000 (12:13 +0000)
Changed files:
    mysql-acc-pslist.patch -> 1.7
    mysql-bug-43594.patch -> 1.3
    mysql-config.patch -> 1.3
    mysql-libwrap.patch -> 1.4
    mysql-microslow.patch -> 1.4
    mysql-upgrade.patch -> 1.10
    mysql-userstats.patch -> 1.7

mysql-acc-pslist.patch [new file with mode: 0644]
mysql-bug-43594.patch [new file with mode: 0644]
mysql-config.patch [new file with mode: 0644]
mysql-libwrap.patch [new file with mode: 0644]
mysql-microslow.patch [new file with mode: 0644]
mysql-upgrade.patch [new file with mode: 0644]
mysql-userstats.patch [new file with mode: 0644]

diff --git a/mysql-acc-pslist.patch b/mysql-acc-pslist.patch
new file mode 100644 (file)
index 0000000..f447f4f
--- /dev/null
@@ -0,0 +1,80 @@
+diff -r 1a59073d658d mysql-test/r/create.result
+--- a/mysql-test/r/create.result       Sat Sep 13 17:31:30 2008 -0700
++++ b/mysql-test/r/create.result       Sat Sep 13 17:31:40 2008 -0700
+@@ -1720,7 +1720,8 @@
+   `COMMAND` varchar(16) NOT NULL DEFAULT '',
+   `TIME` bigint(7) NOT NULL DEFAULT '0',
+   `STATE` varchar(64) DEFAULT NULL,
+-  `INFO` longtext
++  `INFO` longtext,
++  `TIME_MS` decimal(22,3) NOT NULL DEFAULT '0.000'
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8
+ drop table t1;
+ create temporary table t1 like information_schema.processlist;
+@@ -1734,7 +1735,8 @@
+   `COMMAND` varchar(16) NOT NULL DEFAULT '',
+   `TIME` bigint(7) NOT NULL DEFAULT '0',
+   `STATE` varchar(64) DEFAULT NULL,
+-  `INFO` longtext
++  `INFO` longtext,
++  `TIME_MS` decimal(22,3) NOT NULL DEFAULT '0.000'
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8
+ drop table t1;
+ create table t1 like information_schema.character_sets;
+diff -r 1a59073d658d sql/sql_show.cc
+--- a/sql/sql_show.cc  Sat Sep 13 17:31:30 2008 -0700
++++ b/sql/sql_show.cc  Sat Sep 13 17:31:40 2008 -0700
+@@ -1803,7 +1803,7 @@
+   TABLE *table= tables->table;
+   CHARSET_INFO *cs= system_charset_info;
+   char *user;
+-  time_t now= my_time(0);
++  ulonglong unow= my_micro_time();
+   DBUG_ENTER("fill_process_list");
+   user= thd->security_ctx->master_access & PROCESS_ACL ?
+@@ -1873,8 +1873,8 @@
+         table->field[4]->store(command_name[tmp->command].str,
+                                command_name[tmp->command].length, cs);
+       /* MYSQL_TIME */
+-      table->field[5]->store((longlong)(tmp->start_time ?
+-                                      now - tmp->start_time : 0), FALSE);
++      const ulonglong utime= tmp->start_utime ? unow - tmp->start_utime : 0;
++      table->field[5]->store(utime / 1000000, TRUE);
+       /* STATE */
+ #ifndef EMBEDDED_LIBRARY
+       val= (char*) (tmp->locked ? "Locked" :
+@@ -1896,11 +1896,15 @@
+         table->field[7]->set_notnull();
+       }
++      /* TIME_MS */
++      table->field[8]->store((double)(utime / 1000.0));
++
+       if (schema_table_store_record(thd, table))
+       {
+         VOID(pthread_mutex_unlock(&LOCK_thread_count));
+         DBUG_RETURN(1);
+       }
++
+     }
+   }
+@@ -5531,7 +5535,7 @@
+     into it two numbers, based on modulus of base-10 numbers.  In the ones
+     position is the number of decimals.  Tens position is unused.  In the
+     hundreds and thousands position is a two-digit decimal number representing
+-    length.  Encode this value with  (decimals*100)+length  , where
++    length.  Encode this value with  (length*100)+decimals  , where
+     0<decimals<10 and 0<=length<100 .
+   @param
+@@ -6539,6 +6543,8 @@
+   {"STATE", 64, MYSQL_TYPE_STRING, 0, 1, "State", SKIP_OPEN_TABLE},
+   {"INFO", PROCESS_LIST_INFO_WIDTH, MYSQL_TYPE_STRING, 0, 1, "Info",
+    SKIP_OPEN_TABLE},
++  {"TIME_MS", 100 * (MY_INT64_NUM_DECIMAL_DIGITS + 1) + 3, MYSQL_TYPE_DECIMAL,
++    0, 0, "Time_ms", SKIP_OPEN_TABLE},
+   {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ };
diff --git a/mysql-bug-43594.patch b/mysql-bug-43594.patch
new file mode 100644 (file)
index 0000000..b8cc07b
--- /dev/null
@@ -0,0 +1,30 @@
+=== modified file 'scripts/mysqlhotcopy.sh'
+--- scripts/mysqlhotcopy.sh    2008-03-07 20:45:40 +0000
++++ scripts/mysqlhotcopy.sh    2009-03-12 13:06:42 +0000
+@@ -777,7 +777,24 @@ sub get_list_of_tables {
+         } || [];
+     warn "Unable to retrieve list of tables in $db: $@" if $@;
+-    return (map { $_->[0] } @$tables);
++    my @ignore_tables = ();
++
++    # Ignore tables for the mysql database
++    if ($db eq 'mysql') {
++        @ignore_tables = qw(general_log slow_log schema apply_status);
++    }
++
++    my @res = ();
++    if ($#ignore_tables > 1) {
++       my @tmp = (map { $_->[0] } @$tables);
++       for my $t (@tmp) {
++           push(@res, $t) if not exists { map { $_=>1 } @ignore_tables }->{$t};
++       }
++    } else {
++       @res = (map { $_->[0] } @$tables);
++    }
++
++    return @res;
+ }
+ sub quote_names {
+
diff --git a/mysql-config.patch b/mysql-config.patch
new file mode 100644 (file)
index 0000000..7a8fc65
--- /dev/null
@@ -0,0 +1,30 @@
+--- mysql-5.1.26-rc/scripts/mysql_config.sh.orig       2008-07-01 00:36:15.000000000 +0200
++++ mysql-5.1.26-rc/scripts/mysql_config.sh    2008-08-25 19:39:20.111236513 +0200
+@@ -132,21 +132,21 @@
+               DEXTRA_DEBUG DHAVE_purify O 'O[0-9]' 'xO[0-9]' 'W[-A-Za-z]*' \
+               'mtune=[-A-Za-z0-9]*' 'mcpu=[-A-Za-z0-9]*' 'march=[-A-Za-z0-9]*' \
+               Xa xstrconst "xc99=none" AC99 \
+-              unroll2 ip mp restrict
++              unroll2 ip mp restrict 'f[-a-z0-9]*' 'g[-a-z0-9]*' 'm[-a-z0-9]*'
+ do
+   # The first option we might strip will always have a space before it because
+   # we set -I$pkgincludedir as the first option
+-  cflags=`echo "$cflags"|sed -e "s/ -$remove  */ /g"` 
++  cflags=`echo "$cflags"|sed -e "s/ \(-$remove  *\)\{1,\}/ /g"` 
+ done
+ cflags=`echo "$cflags"|sed -e 's/ *\$//'` 
+ # Same for --libs(_r)
+-for remove in lmtmalloc static-libcxa i-static static-intel
++for remove in lmtmalloc static-libcxa i-static static-intel 'Wl,-[-a-z0-9,]*'
+ do
+   # We know the strings starts with a space
+-  libs=`echo "$libs"|sed -e "s/ -$remove  */ /g"` 
+-  libs_r=`echo "$libs_r"|sed -e "s/ -$remove  */ /g"` 
+-  embedded_libs=`echo "$embedded_libs"|sed -e "s/ -$remove  */ /g"` 
++  libs=`echo "$libs"|sed -e "s/ \(-$remove  *\)\{1,\}/ /g"` 
++  libs_r=`echo "$libs_r"|sed -e "s/ \(-$remove  *\)\{1,\}/ /g"` 
++  embedded_libs=`echo "$embedded_libs"|sed -e "s/ \(-$remove  *\)\{1,\}/ /g"` 
+ done
+ # Strip trailing and ending space if any, and '+' (FIXME why?)
diff --git a/mysql-libwrap.patch b/mysql-libwrap.patch
new file mode 100644 (file)
index 0000000..78c7959
--- /dev/null
@@ -0,0 +1,19 @@
+--- mysql-4.1.9/sql/mysqld.cc.orig     2005-01-11 23:06:00.000000000 +0100
++++ mysql-4.1.9/sql/mysqld.cc  2005-02-06 17:21:26.238717200 +0100
+@@ -133,7 +133,16 @@
+ #endif /* __WIN__ */
+ #ifdef HAVE_LIBWRAP
++#define hosts_access hosts_access_old
++#define sock_host sock_host_old
++#define eval_client eval_client_old
+ #include <tcpd.h>
++#undef hosts_access
++#undef sock_host
++#undef eval_client
++extern int hosts_access(struct request_info *);
++extern int sock_host(struct request_info *);
++extern int eval_client(struct request_info *);
+ #include <syslog.h>
+ #ifdef NEED_SYS_SYSLOG_H
+ #include <sys/syslog.h>
diff --git a/mysql-microslow.patch b/mysql-microslow.patch
new file mode 100644 (file)
index 0000000..15085d8
--- /dev/null
@@ -0,0 +1,700 @@
+diff -r 0b1f42e1aacf sql/event_scheduler.cc
+--- a/sql/event_scheduler.cc   Thu Dec 04 08:55:22 2008 -0800
++++ b/sql/event_scheduler.cc   Thu Dec 04 08:55:29 2008 -0800
+@@ -192,6 +192,7 @@
+   thd->client_capabilities|= CLIENT_MULTI_RESULTS;
+   pthread_mutex_lock(&LOCK_thread_count);
+   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
++  thd->write_to_slow_log = TRUE;
+   pthread_mutex_unlock(&LOCK_thread_count);
+   /*
+diff -r 0b1f42e1aacf sql/filesort.cc
+--- a/sql/filesort.cc  Thu Dec 04 08:55:22 2008 -0800
++++ b/sql/filesort.cc  Thu Dec 04 08:55:29 2008 -0800
+@@ -188,6 +188,7 @@
+   {
+     status_var_increment(thd->status_var.filesort_scan_count);
+   }
++  thd->query_plan_flags|= QPLAN_FILESORT;
+ #ifdef CAN_TRUST_RANGE
+   if (select && select->quick && select->quick->records > 0L)
+   {
+@@ -253,6 +254,7 @@
+   }
+   else
+   {
++    thd->query_plan_flags|= QPLAN_FILESORT_DISK;
+     if (table_sort.buffpek && table_sort.buffpek_len < maxbuffer)
+     {
+       x_free(table_sort.buffpek);
+@@ -1202,6 +1204,7 @@
+   DBUG_ENTER("merge_buffers");
+   status_var_increment(current_thd->status_var.filesort_merge_passes);
++  current_thd->query_plan_fsort_passes++;
+   if (param->not_killable)
+   {
+     killed= &not_killable;
+diff -r 0b1f42e1aacf sql/log.cc
+--- a/sql/log.cc       Thu Dec 04 08:55:22 2008 -0800
++++ b/sql/log.cc       Thu Dec 04 08:55:29 2008 -0800
+@@ -963,7 +963,7 @@
+     /* fill in user_host value: the format is "%s[%s] @ %s [%s]" */
+     user_host_len= (strxnmov(user_host_buff, MAX_USER_HOST_SIZE,
+                              sctx->priv_user ? sctx->priv_user : "", "[",
+-                             sctx->user ? sctx->user : "", "] @ ",
++                             sctx->user ? sctx->user : (thd->slave_thread ? "SQL_SLAVE" : ""), "] @ ",
+                              sctx->host ? sctx->host : "", " [",
+                              sctx->ip ? sctx->ip : "", "]", NullS) -
+                     user_host_buff);
+@@ -985,6 +985,13 @@
+       query= command_name[thd->command].str;
+       query_length= command_name[thd->command].length;
+     }
++
++    if (!query_length) 
++    { 
++      thd->sent_row_count= thd->examined_row_count= 0; 
++      thd->query_plan_flags= QPLAN_NONE; 
++      thd->query_plan_fsort_passes= 0; 
++    } 
+     for (current_handler= slow_log_handler_list; *current_handler ;)
+       error= (*current_handler++)->log_slow(thd, current_time, thd->start_time,
+@@ -2242,16 +2249,31 @@
+       if (my_b_write(&log_file, (uchar*) "\n", 1))
+         tmp_errno= errno;
+     }
++    
+     /* For slow query log */
+     sprintf(query_time_buff, "%.6f", ulonglong2double(query_utime)/1000000.0);
+     sprintf(lock_time_buff,  "%.6f", ulonglong2double(lock_utime)/1000000.0);
+     if (my_b_printf(&log_file,
+-                    "# Query_time: %s  Lock_time: %s"
+-                    " Rows_sent: %lu  Rows_examined: %lu\n",
++                    "# Thread_id: %lu  Schema: %s\n" \
++                    "# Query_time: %s  Lock_time: %s  Rows_sent: %lu  Rows_examined: %lu\n",
++                    (ulong) thd->thread_id, (thd->db ? thd->db : ""),
+                     query_time_buff, lock_time_buff,
+                     (ulong) thd->sent_row_count,
+                     (ulong) thd->examined_row_count) == (uint) -1)
+       tmp_errno= errno;
++     if ((thd->variables.log_slow_verbosity & SLOG_V_QUERY_PLAN) && 
++         my_b_printf(&log_file, 
++                     "# QC_Hit: %s  Full_scan: %s  Full_join: %s  Tmp_table: %s  Tmp_table_on_disk: %s\n" \
++                     "# Filesort: %s  Filesort_on_disk: %s  Merge_passes: %lu\n", 
++                     ((thd->query_plan_flags & QPLAN_QC) ? "Yes" : "No"), 
++                     ((thd->query_plan_flags & QPLAN_FULL_SCAN) ? "Yes" : "No"), 
++                     ((thd->query_plan_flags & QPLAN_FULL_JOIN) ? "Yes" : "No"), 
++                     ((thd->query_plan_flags & QPLAN_TMP_TABLE) ? "Yes" : "No"), 
++                     ((thd->query_plan_flags & QPLAN_TMP_DISK) ? "Yes" : "No"), 
++                     ((thd->query_plan_flags & QPLAN_FILESORT) ? "Yes" : "No"), 
++                     ((thd->query_plan_flags & QPLAN_FILESORT_DISK) ? "Yes" : "No"), 
++                     thd->query_plan_fsort_passes) == (uint) -1) 
++       tmp_errno=errno; 
+     if (thd->db && strcmp(thd->db, db))
+     {                                         // Database changed
+       if (my_b_printf(&log_file,"use %s;\n",thd->db) == (uint) -1)
+diff -r 0b1f42e1aacf sql/mysql_priv.h
+--- a/sql/mysql_priv.h Thu Dec 04 08:55:22 2008 -0800
++++ b/sql/mysql_priv.h Thu Dec 04 08:55:29 2008 -0800
+@@ -597,6 +597,73 @@
+ #define WEEK_FIRST_WEEKDAY   4
+ #define STRING_BUFFER_USUAL_SIZE 80
++
++/* Slow log */
++
++struct msl_opts
++{
++  ulong val;
++  const char *name;
++};
++
++#define SLOG_V_QUERY_PLAN     1 << 1
++/* ... */
++#define SLOG_V_INVALID        1 << 31
++#define SLOG_V_NONE           1 << 0
++
++static const struct msl_opts slog_verb[]= 
++{
++  /* Basic flags */
++
++  { SLOG_V_QUERY_PLAN, "query_plan" },
++
++  /* End of baisc flags */
++
++  { 0, "" },
++
++  /* Complex flags */
++
++  { SLOG_V_QUERY_PLAN, "standard" },
++  { SLOG_V_INVALID, "full" },
++
++  /* End of complex flags */
++
++  { SLOG_V_INVALID, (char *)0 }
++};
++
++#define QPLAN_NONE            0
++#define QPLAN_QC              1 << 0
++#define QPLAN_QC_NO           1 << 1
++#define QPLAN_FULL_SCAN       1 << 2
++#define QPLAN_FULL_JOIN       1 << 3
++#define QPLAN_TMP_TABLE       1 << 4
++#define QPLAN_TMP_DISK        1 << 5
++#define QPLAN_FILESORT        1 << 6
++#define QPLAN_FILESORT_DISK   1 << 7
++/* ... */
++#define QPLAN_MAX             1 << 31
++
++#define SLOG_F_QC_NO          QPLAN_QC_NO
++#define SLOG_F_FULL_SCAN      QPLAN_FULL_SCAN
++#define SLOG_F_FULL_JOIN      QPLAN_FULL_JOIN
++#define SLOG_F_TMP_TABLE      QPLAN_TMP_TABLE
++#define SLOG_F_TMP_DISK       QPLAN_TMP_DISK
++#define SLOG_F_FILESORT       QPLAN_FILESORT
++#define SLOG_F_FILESORT_DISK  QPLAN_FILESORT_DISK
++#define SLOG_F_INVALID        1 << 31
++#define SLOG_F_NONE           0
++
++static const struct msl_opts slog_filter[]= 
++{
++  { SLOG_F_QC_NO,         "qc_miss" },
++  { SLOG_F_FULL_SCAN,     "full_scan" },
++  { SLOG_F_FULL_JOIN,     "full_join" },
++  { SLOG_F_TMP_TABLE,     "tmp_table" },
++  { SLOG_F_TMP_DISK,      "tmp_table_on_disk" },
++  { SLOG_F_FILESORT,      "filesort" },
++  { SLOG_F_FILESORT_DISK, "filesort_on_disk" },
++  { SLOG_F_INVALID,       (char *)0 }
++};
+ /*
+   Some defines for exit codes for ::is_equal class functions.
+diff -r 0b1f42e1aacf sql/mysqld.cc
+--- a/sql/mysqld.cc    Thu Dec 04 08:55:22 2008 -0800
++++ b/sql/mysqld.cc    Thu Dec 04 08:55:29 2008 -0800
+@@ -5553,6 +5553,9 @@
+   OPT_SECURE_FILE_PRIV,
+   OPT_MIN_EXAMINED_ROW_LIMIT,
+   OPT_LOG_SLOW_SLAVE_STATEMENTS,
++  OPT_LOG_SLOW_RATE_LIMIT, 
++  OPT_LOG_SLOW_VERBOSITY, 
++  OPT_LOG_SLOW_FILTER, 
+   OPT_OLD_MODE,
+   OPT_SLAVE_EXEC_MODE,
+   OPT_GENERAL_LOG_FILE,
+@@ -6480,6 +6483,17 @@
+    (uchar**) 0,
+    0, (GET_ULONG | GET_ASK_ADDR) , REQUIRED_ARG, 100,
+    1, 100, 0, 1, 0},
++  {"log_slow_filter", OPT_LOG_SLOW_FILTER,
++   "Log only the queries that followed certain execution plan. Multiple flags allowed in a comma-separated string. [qc_miss, full_scan, full_join, tmp_table, tmp_table_on_disk, filesort, filesort_on_disk]",
++   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, SLOG_F_NONE, 0, 0},
++  {"log_slow_rate_limit", OPT_LOG_SLOW_RATE_LIMIT,
++   "Rate limit statement writes to slow log to only those from every (1/log_slow_rate_limit) session.",
++   (uchar**) &global_system_variables.log_slow_rate_limit,
++   (uchar**) &max_system_variables.log_slow_rate_limit, 0, GET_ULONG,
++   REQUIRED_ARG, 1, 1, ~0L, 0, 1L, 0},
++  {"log_slow_verbosity", OPT_LOG_SLOW_VERBOSITY,
++   "Choose how verbose the messages to your slow log will be. Multiple flags allowed in a comma-separated string. [microtime, query_plan, innodb]",
++   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, SLOG_V_NONE, 0, 0},
+   {"long_query_time", OPT_LONG_QUERY_TIME,
+    "Log all queries that have taken more than long_query_time seconds to execute to file. "
+    "The argument will be treated as a decimal value with microsecond precission.",
+@@ -7556,6 +7570,10 @@
+   global_system_variables.old_passwords= 0;
+   global_system_variables.old_alter_table= 0;
+   global_system_variables.binlog_format= BINLOG_FORMAT_UNSPEC;
++  
++  global_system_variables.log_slow_verbosity= SLOG_V_NONE; 
++  global_system_variables.log_slow_filter= SLOG_F_NONE; 
++  
+   /*
+     Default behavior for 4.1 and 5.0 is to treat NULL values as unequal
+     when collecting index statistics for MyISAM tables.
+@@ -8007,6 +8025,24 @@
+   case OPT_BOOTSTRAP:
+     opt_noacl=opt_bootstrap=1;
+     break;
++  case OPT_LOG_SLOW_FILTER: 
++    if ((global_system_variables.log_slow_filter=  
++          msl_flag_resolve_by_name(slog_filter, argument, 
++                                   SLOG_F_NONE, SLOG_F_INVALID)) == SLOG_F_INVALID) 
++    { 
++      fprintf(stderr,"Invalid argument to log_slow_filter\n"); 
++      exit(1); 
++    } 
++    break; 
++  case OPT_LOG_SLOW_VERBOSITY: 
++    if ((global_system_variables.log_slow_verbosity=  
++         msl_flag_resolve_by_name(slog_verb, argument, 
++                                  SLOG_V_NONE, SLOG_V_INVALID)) == SLOG_V_INVALID) 
++    { 
++      fprintf(stderr,"Invalid argument to log_slow_verbosity\n"); 
++      exit(1); 
++    } 
++    break; 
+   case OPT_SERVER_ID:
+     server_id_supplied = 1;
+     break;
+diff -r 0b1f42e1aacf sql/set_var.cc
+--- a/sql/set_var.cc   Thu Dec 04 08:55:22 2008 -0800
++++ b/sql/set_var.cc   Thu Dec 04 08:55:29 2008 -0800
+@@ -336,6 +336,20 @@
+                                             &SV::max_join_size,
+                                             fix_max_join_size);
+ #endif
++static sys_var_thd_ulong  sys_log_slow_rate_limit(&vars, "log_slow_rate_limit", 
++              &SV::log_slow_rate_limit); 
++static sys_var_thd_msl_flag sys_log_slow_filter(&vars, "log_slow_filter", 
++              &SV::log_slow_filter, 
++                                       SLOG_F_NONE, 
++                                       SLOG_F_NONE, 
++                                       SLOG_F_INVALID, 
++                                       slog_filter); 
++static sys_var_thd_msl_flag sys_log_slow_verbosity(&vars, "log_slow_verbosity", 
++              &SV::log_slow_verbosity, 
++                                       SLOG_V_NONE, 
++                                       SLOG_V_NONE, 
++                                       SLOG_V_INVALID, 
++                                       slog_verb); 
+ static sys_var_long_ptr_global
+ sys_max_prepared_stmt_count(&vars, "max_prepared_stmt_count",
+                             &max_prepared_stmt_count,
+@@ -3631,6 +3645,192 @@
+ #endif
+ }
++/* Slow log stuff */
++
++ulong msl_option_resolve_by_name(const struct msl_opts *opts, const char *name, ulong len)
++{
++  ulong i;
++  
++  for (i=0; opts[i].name; i++)
++  {
++    if (!my_strnncoll(&my_charset_latin1,
++                      (const uchar *)name, len,
++                      (const uchar *)opts[i].name, strlen(opts[i].name)))
++      return opts[i].val;
++  }
++  return opts[i].val;
++}
++
++ulong msl_flag_resolve_by_name(const struct msl_opts *opts, const char *names_list, 
++                               const ulong none_val, const ulong invalid_val)
++{
++  const char *p, *e;
++  ulong val= none_val;
++  
++  if (!*names_list)
++    return val;
++  
++  for (p= e= names_list; ; e++)
++  {
++    ulong i;
++    
++    if (*e != ',' && *e)
++      continue;
++    for (i=0; opts[i].name; i++)
++    {
++      if (!my_strnncoll(&my_charset_latin1,
++                        (const uchar *)p, e - p,
++                        (const uchar *)opts[i].name, strlen(opts[i].name)))
++      {
++        val= val | opts[i].val;
++        break;
++      }
++    }
++    if (opts[i].val == invalid_val)
++      return invalid_val;
++    if (!*e)
++      break;
++    p= e + 1;
++  }
++  return val;
++}
++
++const char *msl_option_get_name(const struct msl_opts *opts, ulong val)
++{
++  for (ulong i=0; opts[i].name && opts[i].name[0]; i++)
++  {
++    if (opts[i].val == val)
++      return opts[i].name;
++  }
++  return "*INVALID*";
++}
++
++char *msl_flag_get_name(const struct msl_opts *opts, char *buf, ulong val)
++{
++  uint offset= 0;
++  
++  *buf= '\0';
++  for (ulong i=0; opts[i].name && opts[i].name[0]; i++)
++  {
++    if (opts[i].val & val)
++      offset+= snprintf(buf+offset, STRING_BUFFER_USUAL_SIZE - offset - 1,
++                        "%s%s", (offset ? "," : ""), opts[i].name);
++  }
++  return buf;
++}
++
++/****************************************************************************
++ Functions to handle log_slow_verbosity
++****************************************************************************/
++
++/* Based upon sys_var::check_enum() */
++
++bool sys_var_thd_msl_option::check(THD *thd, set_var *var)
++{
++  char buff[STRING_BUFFER_USUAL_SIZE];
++  String str(buff, sizeof(buff), &my_charset_latin1), *res;
++
++  if (var->value->result_type() == STRING_RESULT)
++  {
++    ulong verb= this->invalid_val;
++    if (!(res=var->value->val_str(&str)) ||
++            (var->save_result.ulong_value=
++          (ulong) (verb= msl_option_resolve_by_name(this->opts, res->ptr(), res->length()))) == this->invalid_val)
++      goto err;
++    return 0;
++  }
++
++err:
++  my_error(ER_WRONG_ARGUMENTS, MYF(0), var->var->name);
++  return 1;
++}
++
++uchar *sys_var_thd_msl_option::value_ptr(THD *thd, enum_var_type type,
++                                      LEX_STRING *base)
++{
++  ulong val;
++  val= ((type == OPT_GLOBAL) ? global_system_variables.*offset :
++        thd->variables.*offset);
++  const char *verbosity= msl_option_get_name(this->opts, val);
++  return (uchar *) verbosity;
++}
++
++
++void sys_var_thd_msl_option::set_default(THD *thd, enum_var_type type)
++{
++  if (type == OPT_GLOBAL)
++    global_system_variables.*offset= (ulong) this->default_val;
++  else
++    thd->variables.*offset= (ulong) (global_system_variables.*offset);
++}
++
++
++bool sys_var_thd_msl_option::update(THD *thd, set_var *var)
++{
++  if (var->type == OPT_GLOBAL)
++    global_system_variables.*offset= var->save_result.ulong_value;
++  else
++    thd->variables.*offset= var->save_result.ulong_value;
++  return 0;
++}
++
++/****************************************************************************
++ Functions to handle log_slow_filter
++****************************************************************************/
++  
++/* Based upon sys_var::check_enum() */
++
++bool sys_var_thd_msl_flag::check(THD *thd, set_var *var)
++{
++  char buff[2 * STRING_BUFFER_USUAL_SIZE];
++  String str(buff, sizeof(buff), &my_charset_latin1), *res;
++
++  if (var->value->result_type() == STRING_RESULT)
++  {
++    ulong filter= this->none_val;
++    if (!(res=var->value->val_str(&str)) ||
++       (var->save_result.ulong_value=
++          (ulong) (filter= msl_flag_resolve_by_name(this->flags, res->ptr(), this->none_val, 
++                                                    this->invalid_val))) == this->invalid_val)
++      goto err;
++    return 0;
++  }
++
++err:
++  my_error(ER_WRONG_ARGUMENTS, MYF(0), var->var->name);
++  return 1;
++}
++
++uchar *sys_var_thd_msl_flag::value_ptr(THD *thd, enum_var_type type,
++                                      LEX_STRING *base)
++{
++  ulong val;
++  val= ((type == OPT_GLOBAL) ? global_system_variables.*offset :
++        thd->variables.*offset);
++  msl_flag_get_name(this->flags, this->flags_string, val);
++  return (uchar *) this->flags_string;
++}
++
++
++void sys_var_thd_msl_flag::set_default(THD *thd, enum_var_type type)
++{
++  if (type == OPT_GLOBAL)
++    global_system_variables.*offset= (ulong) this->default_val;
++  else
++    thd->variables.*offset= (ulong) (global_system_variables.*offset);
++}
++
++
++bool sys_var_thd_msl_flag::update(THD *thd, set_var *var)
++{
++  if (var->type == OPT_GLOBAL)
++    global_system_variables.*offset= var->save_result.ulong_value;
++  else
++    thd->variables.*offset= var->save_result.ulong_value;
++  return 0;
++}
++
++
+ /****************************************************************************
+  Functions to handle table_type
+ ****************************************************************************/
+diff -r 0b1f42e1aacf sql/set_var.h
+--- a/sql/set_var.h    Thu Dec 04 08:55:22 2008 -0800
++++ b/sql/set_var.h    Thu Dec 04 08:55:29 2008 -0800
+@@ -1057,6 +1057,68 @@
+   bool update(THD *thd, set_var *var);
+ };
++class sys_var_thd_msl_option :public sys_var_thd
++{
++protected:
++  ulong SV::*offset;
++  const ulong none_val;
++  const ulong default_val;
++  const ulong invalid_val;
++  const struct msl_opts *opts;
++public:
++  sys_var_thd_msl_option(sys_var_chain *chain, const char *name_arg,
++                         ulong SV::*offset_arg,
++                         const ulong none_val_arg,
++                         const ulong default_val_arg,
++                         const ulong invalid_val_arg,
++                         const struct msl_opts *opts_arg)
++    :sys_var_thd(name_arg), offset(offset_arg), none_val(none_val_arg),
++     default_val(default_val_arg), invalid_val(invalid_val_arg), 
++     opts(opts_arg)
++  { chain_sys_var(chain); }
++  bool check(THD *thd, set_var *var);
++  SHOW_TYPE show_type() { return SHOW_CHAR; }
++  bool check_update_type(Item_result type)
++  {
++    return type != STRING_RESULT;             /* Only accept strings */
++  }
++  void set_default(THD *thd, enum_var_type type);
++  bool update(THD *thd, set_var *var);
++  uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
++};
++
++
++class sys_var_thd_msl_flag :public sys_var_thd
++{
++protected:
++  char flags_string[2 * STRING_BUFFER_USUAL_SIZE];
++  ulong SV::*offset;
++  const ulong none_val;
++  const ulong default_val;
++  const ulong invalid_val;
++  const struct msl_opts *flags;
++public:
++  sys_var_thd_msl_flag(sys_var_chain *chain, const char *name_arg,
++                       ulong SV::*offset_arg, 
++                       const ulong none_val_arg, 
++                       const ulong default_val_arg, 
++                       const ulong invalid_val_arg,
++                       const struct msl_opts *flags_arg)
++    :sys_var_thd(name_arg), offset(offset_arg), none_val(none_val_arg),
++     default_val(default_val_arg), invalid_val(invalid_val_arg), 
++     flags(flags_arg)
++  { chain_sys_var(chain); }
++  bool check(THD *thd, set_var *var);
++  SHOW_TYPE show_type() { return SHOW_CHAR; }
++  bool check_update_type(Item_result type)
++  {
++    return type != STRING_RESULT;             /* Only accept strings */
++  }
++  void set_default(THD *thd, enum_var_type type);
++  bool update(THD *thd, set_var *var);
++  uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
++};
++
+ /**
+   Handler for setting the system variable --read-only.
+@@ -1323,3 +1385,10 @@
+ bool process_key_caches(process_key_cache_t func);
+ void delete_elements(I_List<NAMED_LIST> *list,
+                    void (*free_element)(const char*, uchar*));
++
++/* Slow log functions */
++ulong msl_option_resolve_by_name(const struct msl_opts *opts, const char *name, ulong len);
++ulong msl_flag_resolve_by_name(const struct msl_opts *opts, const char *names_list, 
++                               const ulong none_val, const ulong invalid_val);
++const char *msl_option_get_name(const struct msl_opts *opts, ulong val);
++char *msl_flag_get_name(const struct msl_opts *opts, char *buf, ulong val);
+diff -r 0b1f42e1aacf sql/slave.cc
+--- a/sql/slave.cc     Thu Dec 04 08:55:22 2008 -0800
++++ b/sql/slave.cc     Thu Dec 04 08:55:29 2008 -0800
+@@ -1561,6 +1561,7 @@
+     + MAX_LOG_EVENT_HEADER;  /* note, incr over the global not session var */
+   thd->slave_thread = 1;
+   thd->enable_slow_log= opt_log_slow_slave_statements;
++  thd->write_to_slow_log= opt_log_slow_slave_statements;
+   set_slave_thread_options(thd);
+   thd->client_capabilities = CLIENT_LOCAL_FILES;
+   pthread_mutex_lock(&LOCK_thread_count);
+diff -r 0b1f42e1aacf sql/sql_cache.cc
+--- a/sql/sql_cache.cc Thu Dec 04 08:55:22 2008 -0800
++++ b/sql/sql_cache.cc Thu Dec 04 08:55:29 2008 -0800
+@@ -1465,6 +1465,7 @@
+   thd->limit_found_rows = query->found_rows();
+   thd->status_var.last_query_cost= 0.0;
++  thd->query_plan_flags|= QPLAN_QC;
+   thd->main_da.disable_status();
+   BLOCK_UNLOCK_RD(query_block);
+@@ -1473,6 +1474,7 @@
+ err_unlock:
+   STRUCT_UNLOCK(&structure_guard_mutex);
+ err:
++  thd->query_plan_flags|= QPLAN_QC_NO;
+   DBUG_RETURN(0);                             // Query was not cached
+ }
+diff -r 0b1f42e1aacf sql/sql_class.h
+--- a/sql/sql_class.h  Thu Dec 04 08:55:22 2008 -0800
++++ b/sql/sql_class.h  Thu Dec 04 08:55:29 2008 -0800
+@@ -396,6 +396,11 @@
+   DATE_TIME_FORMAT *datetime_format;
+   DATE_TIME_FORMAT *time_format;
+   my_bool sysdate_is_now;
++
++  ulong log_slow_rate_limit; 
++  ulong log_slow_filter; 
++  ulong log_slow_verbosity; 
++            
+ };
+@@ -1339,6 +1344,11 @@
+   
+   thr_lock_type update_lock_default;
+   Delayed_insert *di;
++
++  bool       write_to_slow_log; 
++ 
++  ulong      query_plan_flags; 
++  ulong      query_plan_fsort_passes; 
+   /* <> 0 if we are inside of trigger or stored function. */
+   uint in_sub_stmt;
+diff -r 0b1f42e1aacf sql/sql_connect.cc
+--- a/sql/sql_connect.cc       Thu Dec 04 08:55:22 2008 -0800
++++ b/sql/sql_connect.cc       Thu Dec 04 08:55:29 2008 -0800
+@@ -1124,6 +1124,15 @@
+     prepare_new_connection_state(thd);
++    /* 
++      If rate limiting of slow log writes is enabled, decide whether to log this 
++      new thread's queries or not. Uses extremely simple algorithm. :) 
++    */ 
++    thd->write_to_slow_log= FALSE; 
++    if (thd->variables.log_slow_rate_limit <= 1 ||  
++        (thd->thread_id % thd->variables.log_slow_rate_limit) == 0) 
++         thd->write_to_slow_log= TRUE; 
++
+     while (!net->error && net->vio != 0 &&
+            !(thd->killed == THD::KILL_CONNECTION))
+     {
+diff -r 0b1f42e1aacf sql/sql_parse.cc
+--- a/sql/sql_parse.cc Thu Dec 04 08:55:22 2008 -0800
++++ b/sql/sql_parse.cc Thu Dec 04 08:55:29 2008 -0800
+@@ -1692,6 +1692,27 @@
+   if (unlikely(thd->in_sub_stmt))
+     DBUG_VOID_RETURN;                           // Don't set time for sub stmt
++  /* Follow the slow log filter configuration. */ 
++  if (thd->variables.log_slow_filter != SLOG_F_NONE &&  
++      (!(thd->variables.log_slow_filter & thd->query_plan_flags) || 
++       ((thd->variables.log_slow_filter & SLOG_F_QC_NO) &&  
++        (thd->query_plan_flags & QPLAN_QC)))) 
++    DBUG_VOID_RETURN; 
++ 
++  /* 
++    Low long_query_time value most likely means user is debugging stuff and even  
++    though some thread's queries are not supposed to be logged b/c of the rate  
++    limit, if one of them takes long enough (>= 1 second) it will be sensible  
++    to make an exception and write to slow log anyway. 
++  */ 
++  if (thd->write_to_slow_log != TRUE && thd->variables.long_query_time < 1000000 && 
++      (ulong) (thd->start_utime - thd->utime_after_lock) >= 1000000) 
++    thd->write_to_slow_log= TRUE; 
++ 
++  /* Do not log this thread's queries due to rate limiting. */ 
++  if (thd->write_to_slow_log != TRUE) 
++    DBUG_VOID_RETURN; 
++
+   /*
+     Do not log administrative statements unless the appropriate option is
+     set; do not log into slow log if reading from backup.
+@@ -5773,6 +5794,9 @@
+   thd->total_warn_count=0;                    // Warnings for this query
+   thd->rand_used= 0;
+   thd->sent_row_count= thd->examined_row_count= 0;
++
++  thd->query_plan_flags= QPLAN_NONE; 
++  thd->query_plan_fsort_passes= 0; 
+   /*
+     Because we come here only for start of top-statements, binlog format is
+diff -r 0b1f42e1aacf sql/sql_select.cc
+--- a/sql/sql_select.cc        Thu Dec 04 08:55:22 2008 -0800
++++ b/sql/sql_select.cc        Thu Dec 04 08:55:29 2008 -0800
+@@ -6480,7 +6480,10 @@
+         {
+           join->thd->server_status|=SERVER_QUERY_NO_INDEX_USED;
+           if (statistics)
++          {
+             status_var_increment(join->thd->status_var.select_scan_count);
++            join->thd->query_plan_flags|= QPLAN_FULL_SCAN;
++          }
+         }
+       }
+       else
+@@ -6494,7 +6497,10 @@
+         {
+           join->thd->server_status|=SERVER_QUERY_NO_INDEX_USED;
+           if (statistics)
++          {
+             status_var_increment(join->thd->status_var.select_full_join_count);
++            join->thd->query_plan_flags|= QPLAN_FULL_JOIN;
++          }
+         }
+       }
+       if (!table->no_keyread)
+@@ -9638,6 +9644,7 @@
+               (ulong) rows_limit,test(group)));
+   status_var_increment(thd->status_var.created_tmp_tables);
++  thd->query_plan_flags|= QPLAN_TMP_TABLE;
+   if (use_temp_pool && !(test_flags & TEST_KEEP_TMP_TABLES))
+     temp_pool_slot = bitmap_lock_set_next(&temp_pool);
+@@ -10508,6 +10515,7 @@
+     goto err;
+   }
+   status_var_increment(table->in_use->status_var.created_tmp_disk_tables);
++  table->in_use->query_plan_flags|= QPLAN_TMP_DISK;
+   share->db_record_offset= 1;
+   DBUG_RETURN(0);
+  err:
diff --git a/mysql-upgrade.patch b/mysql-upgrade.patch
new file mode 100644 (file)
index 0000000..b1d0935
--- /dev/null
@@ -0,0 +1,31 @@
+--- mysql-5.1.28-rc/client/mysql_upgrade.c~    2008-09-14 23:53:11.000000000 +0300
++++ mysql-5.1.28-rc/client/mysql_upgrade.c     2008-09-15 00:02:34.047520349 +0300
+@@ -31,12 +31,12 @@
+ # endif
+ #endif
+-static char mysql_path[FN_REFLEN];
+-static char mysqlcheck_path[FN_REFLEN];
++static char *mysql_path = "/usr/bin/mysql";
++static char *mysqlcheck_path = "/usr/sbin/mysqlcheck";
+ static my_bool opt_force, opt_verbose, debug_info_flag, debug_check_flag;
+ static uint my_end_arg= 0;
+-static char *opt_user= (char*)"root";
++static char *opt_user= (char*)"mysql";
+ static DYNAMIC_STRING ds_args;
+@@ -761,12 +761,6 @@
+   dynstr_append_os_quoted(&ds_args, "--user=", opt_user, NullS);
+   dynstr_append(&ds_args, " ");
+-  /* Find mysql */
+-  find_tool(mysql_path, IF_WIN("mysql.exe", "mysql"), self_name);
+-
+-  /* Find mysqlcheck */
+-  find_tool(mysqlcheck_path, IF_WIN("mysqlcheck.exe", "mysqlcheck"), self_name);
+-
+   /*
+     Read the mysql_upgrade_info file to check if mysql_upgrade
+     already has been run for this installation of MySQL
diff --git a/mysql-userstats.patch b/mysql-userstats.patch
new file mode 100644 (file)
index 0000000..09392a1
--- /dev/null
@@ -0,0 +1,1461 @@
+diff -r 3ed7e96969f9 include/mysql_com.h
+--- a/include/mysql_com.h      Thu Dec 04 08:54:17 2008 -0800
++++ b/include/mysql_com.h      Thu Dec 04 08:54:27 2008 -0800
+@@ -115,6 +115,8 @@
+                                          thread */
+ #define REFRESH_MASTER          128     /* Remove all bin logs in the index
+                                          and truncate the index */
++#define REFRESH_TABLE_STATS     256     /* Refresh table stats hash table */
++#define REFRESH_INDEX_STATS     512     /* Refresh index stats hash table */
+ /* The following can't be set with mysql_refresh() */
+ #define REFRESH_READ_LOCK     16384   /* Lock tables for read */
+diff -r 3ed7e96969f9 sql/handler.cc
+--- a/sql/handler.cc   Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/handler.cc   Thu Dec 04 08:54:27 2008 -0800
+@@ -1205,6 +1205,7 @@
+         error=1;
+       }
+       status_var_increment(thd->status_var.ha_commit_count);
++      thd->diff_commit_trans++;
+       ha_info_next= ha_info->next();
+       ha_info->reset(); /* keep it conveniently zero-filled */
+     }
+@@ -1272,6 +1273,7 @@
+         error=1;
+       }
+       status_var_increment(thd->status_var.ha_rollback_count);
++      thd->diff_rollback_trans++;
+       ha_info_next= ha_info->next();
+       ha_info->reset(); /* keep it conveniently zero-filled */
+     }
+@@ -1724,6 +1726,7 @@
+       error=1;
+     }
+     status_var_increment(thd->status_var.ha_rollback_count);
++    thd->diff_rollback_trans++;
+     ha_info_next= ha_info->next();
+     ha_info->reset(); /* keep it conveniently zero-filled */
+   }
+@@ -2058,6 +2061,8 @@
+       dup_ref=ref+ALIGN_SIZE(ref_length);
+     cached_table_flags= table_flags();
+   }
++  rows_read = rows_changed = 0;
++  memset(index_rows_read, 0, sizeof(index_rows_read));
+   DBUG_RETURN(error);
+ }
+@@ -3487,6 +3492,97 @@
+   return;
+ }
++// Updates the global table stats with the TABLE this handler represents.
++void handler::update_global_table_stats() {
++  if (!rows_read && !rows_changed) return;  // Nothing to update.
++  // table_cache_key is db_name + '\0' + table_name + '\0'.
++  if (!table->s || !table->s->table_cache_key.str || !table->s->table_name.str) return;
++
++  TABLE_STATS* table_stats;
++  char key[NAME_LEN * 2 + 2];
++  // [db] + '.' + [table]
++  sprintf(key, "%s.%s", table->s->table_cache_key.str, table->s->table_name.str);
++
++  pthread_mutex_lock(&LOCK_global_table_stats);
++  // Gets the global table stats, creating one if necessary.
++  if (!(table_stats = (TABLE_STATS*)hash_search(&global_table_stats,
++                                                (uchar*)key,
++                                                strlen(key)))) {
++    if (!(table_stats = ((TABLE_STATS*)
++                         my_malloc(sizeof(TABLE_STATS), MYF(MY_WME))))) {
++      // Out of memory.
++      sql_print_error("Allocating table stats failed.");
++      goto end;
++    }
++    strncpy(table_stats->table, key, sizeof(table_stats->table));
++    table_stats->rows_read = 0;
++    table_stats->rows_changed = 0;
++    table_stats->rows_changed_x_indexes = 0;
++
++    if (my_hash_insert(&global_table_stats, (uchar*)table_stats)) {
++      // Out of memory.
++      sql_print_error("Inserting table stats failed.");
++      my_free((char*)table_stats, 0);
++      goto end;
++    }
++  }
++  // Updates the global table stats.
++  table_stats->rows_read += rows_read;
++  table_stats->rows_changed += rows_changed;
++  table_stats->rows_changed_x_indexes +=
++      rows_changed * (table->s->keys ? table->s->keys : 1);
++  rows_read = rows_changed = 0;
++end:
++  pthread_mutex_unlock(&LOCK_global_table_stats);
++}
++
++// Updates the global index stats with this handler's accumulated index reads.
++void handler::update_global_index_stats() {
++  // table_cache_key is db_name + '\0' + table_name + '\0'.
++  if (!table->s || !table->s->table_cache_key.str || !table->s->table_name.str) return;
++
++  for (int x = 0; x < table->s->keys; x++) {
++    if (index_rows_read[x]) {
++      // Rows were read using this index.
++      KEY* key_info = &table->key_info[x];
++
++      if (!key_info->name) continue;
++
++      INDEX_STATS* index_stats;
++      char key[NAME_LEN * 3 + 3];
++      // [db] + '.' + [table] + '.' + [index]
++      sprintf(key, "%s.%s.%s",  table->s->table_cache_key.str,
++              table->s->table_name.str, key_info->name);
++
++      pthread_mutex_lock(&LOCK_global_index_stats);
++      // Gets the global index stats, creating one if necessary.
++      if (!(index_stats = (INDEX_STATS*)hash_search(&global_index_stats,
++                                                    (uchar*)key,
++                                                    strlen(key)))) {
++        if (!(index_stats = ((INDEX_STATS*)
++                             my_malloc(sizeof(INDEX_STATS), MYF(MY_WME))))) {
++          // Out of memory.
++          sql_print_error("Allocating index stats failed.");
++          goto end;
++        }
++        strncpy(index_stats->index, key, sizeof(index_stats->index));
++        index_stats->rows_read = 0;
++
++        if (my_hash_insert(&global_index_stats, (uchar*)index_stats)) {
++          // Out of memory.
++          sql_print_error("Inserting index stats failed.");
++          my_free((char*)index_stats, 0);
++          goto end;
++        }
++      }
++      // Updates the global index stats.
++      index_stats->rows_read += index_rows_read[x];
++      index_rows_read[x] = 0;
++end:
++      pthread_mutex_unlock(&LOCK_global_index_stats);
++    }
++  }
++}
+ /****************************************************************************
+ ** Some general functions that isn't in the handler class
+diff -r 3ed7e96969f9 sql/handler.h
+--- a/sql/handler.h    Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/handler.h    Thu Dec 04 08:54:27 2008 -0800
+@@ -29,6 +29,10 @@
+ #endif
+ #define USING_TRANSACTIONS
++
++#if MAX_KEY > 128
++#error MAX_KEY is too large.  Values up to 128 are supported.
++#endif
+ // the following is for checking tables
+@@ -690,6 +694,7 @@
+    */
+    enum log_status (*get_log_status)(handlerton *hton, char *log);
++
+    /*
+      Iterators creator.
+      Presence of the pointer should be checked before using
+@@ -1137,6 +1142,10 @@
+   */
+   uint auto_inc_intervals_count;
++   ulonglong rows_read;
++   ulonglong rows_changed;
++   ulonglong index_rows_read[MAX_KEY];
++
+   handler(handlerton *ht_arg, TABLE_SHARE *share_arg)
+     :table_share(share_arg), table(0),
+     estimation_rows_to_insert(0), ht(ht_arg),
+@@ -1145,8 +1154,10 @@
+     ft_handler(0), inited(NONE),
+     locked(FALSE), implicit_emptied(0),
+     pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
+-    auto_inc_intervals_count(0)
+-    {}
++    auto_inc_intervals_count(0), rows_read(0), rows_changed(0)
++    {
++        memset(index_rows_read, 0, sizeof(index_rows_read));
++    }
+   virtual ~handler(void)
+   {
+     DBUG_ASSERT(locked == FALSE);
+@@ -1267,7 +1278,13 @@
+   {
+     table= table_arg;
+     table_share= share;
++    rows_read = rows_changed = 0;
++    memset(index_rows_read, 0, sizeof(index_rows_read));
+   }
++  
++  void update_global_table_stats();
++  void update_global_index_stats();
++
+   virtual double scan_time()
+   { return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; }
+   virtual double read_time(uint index, uint ranges, ha_rows rows)
+diff -r 3ed7e96969f9 sql/lex.h
+--- a/sql/lex.h        Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/lex.h        Thu Dec 04 08:54:27 2008 -0800
+@@ -245,6 +245,7 @@
+   { "IN",             SYM(IN_SYM)},
+   { "INDEX",          SYM(INDEX_SYM)},
+   { "INDEXES",                SYM(INDEXES)},
++  { "INDEX_STATISTICS",       SYM(INDEX_STATS_SYM)},
+   { "INFILE",         SYM(INFILE)},
+   { "INITIAL_SIZE",   SYM(INITIAL_SIZE_SYM)},
+   { "INNER",          SYM(INNER_SYM)},
+@@ -528,6 +529,7 @@
+   { "TABLES",         SYM(TABLES)},
+   { "TABLESPACE",             SYM(TABLESPACE)},
+   { "TABLE_CHECKSUM", SYM(TABLE_CHECKSUM_SYM)},
++  { "TABLE_STATISTICS",       SYM(TABLE_STATS_SYM)},
+   { "TEMPORARY",      SYM(TEMPORARY)},
+   { "TEMPTABLE",      SYM(TEMPTABLE_SYM)},
+   { "TERMINATED",     SYM(TERMINATED)},
+@@ -570,6 +572,7 @@
+   { "USE",            SYM(USE_SYM)},
+   { "USER",           SYM(USER)},
+   { "USER_RESOURCES", SYM(RESOURCES)},
++  { "USER_STATISTICS",        SYM(USER_STATS_SYM)},
+   { "USE_FRM",                SYM(USE_FRM)},
+   { "USING",          SYM(USING)},
+   { "UTC_DATE",         SYM(UTC_DATE_SYM)},
+diff -r 3ed7e96969f9 sql/mysql_priv.h
+--- a/sql/mysql_priv.h Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/mysql_priv.h Thu Dec 04 08:54:27 2008 -0800
+@@ -1060,7 +1060,19 @@
+ bool multi_delete_set_locks_and_link_aux_tables(LEX *lex);
+ void init_max_user_conn(void);
+ void init_update_queries(void);
++void init_global_user_stats(void);
++void init_global_table_stats(void);
++void init_global_index_stats(void);
+ void free_max_user_conn(void);
++void free_global_user_stats(void);
++void free_global_table_stats(void);
++void free_global_index_stats(void);
++// Uses the THD to update the global stats.
++void update_global_user_stats(THD* thd);
++// Set stats for concurrent connections displayed by mysqld_show().
++void set_concurrent_connections_stats();
++// Increments connection count for user.
++int increment_connection_count(THD* thd, bool use_lock);
+ pthread_handler_t handle_bootstrap(void *arg);
+ int mysql_execute_command(THD *thd);
+ bool do_command(THD *thd);
+@@ -2015,6 +2027,12 @@
+ extern struct system_variables max_system_variables;
+ extern struct system_status_var global_status_var;
+ extern struct rand_struct sql_rand;
++extern HASH global_user_stats;
++extern pthread_mutex_t LOCK_global_user_stats;
++extern HASH global_table_stats;
++extern pthread_mutex_t LOCK_global_table_stats;
++extern HASH global_index_stats;
++extern pthread_mutex_t LOCK_global_index_stats;
+ extern const char *opt_date_time_formats[];
+ extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
+diff -r 3ed7e96969f9 sql/mysqld.cc
+--- a/sql/mysqld.cc    Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/mysqld.cc    Thu Dec 04 08:54:27 2008 -0800
+@@ -589,6 +589,11 @@
+               LOCK_global_system_variables,
+               LOCK_user_conn, LOCK_slave_list, LOCK_active_mi,
+                 LOCK_connection_count;
++
++pthread_mutex_t LOCK_global_user_stats;
++pthread_mutex_t LOCK_global_table_stats;
++pthread_mutex_t LOCK_global_index_stats;
++
+ /**
+   The below lock protects access to two global server variables:
+   max_prepared_stmt_count and prepared_stmt_count. These variables
+@@ -1266,6 +1271,9 @@
+   x_free(opt_secure_file_priv);
+   bitmap_free(&temp_pool);
+   free_max_user_conn();
++  free_global_user_stats();
++  free_global_table_stats();
++  free_global_index_stats();
+ #ifdef HAVE_REPLICATION
+   end_slave_list();
+ #endif
+@@ -1378,6 +1386,9 @@
+   (void) pthread_cond_destroy(&COND_thread_cache);
+   (void) pthread_cond_destroy(&COND_flush_thread_cache);
+   (void) pthread_cond_destroy(&COND_manager);
++  (void) pthread_mutex_destroy(&LOCK_global_user_stats);
++  (void) pthread_mutex_destroy(&LOCK_global_table_stats);
++  (void) pthread_mutex_destroy(&LOCK_global_index_stats);
+ }
+ #endif /*EMBEDDED_LIBRARY*/
+@@ -3072,6 +3083,7 @@
+   {"show_function_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS_FUNC]), SHOW_LONG_STATUS},
+   {"show_grants",          (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS]), SHOW_LONG_STATUS},
+   {"show_keys",            (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS},
++  {"show_index_stats",     (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_INDEX_STATS]), SHOW_LONG_STATUS},
+   {"show_master_status",   (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_MASTER_STAT]), SHOW_LONG_STATUS},
+   {"show_new_master",      (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_NEW_MASTER]), SHOW_LONG_STATUS},
+   {"show_open_tables",     (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_OPEN_TABLES]), SHOW_LONG_STATUS},
+@@ -3089,9 +3101,11 @@
+   {"show_slave_status",    (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_STAT]), SHOW_LONG_STATUS},
+   {"show_status",          (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS]), SHOW_LONG_STATUS},
+   {"show_storage_engines", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STORAGE_ENGINES]), SHOW_LONG_STATUS},
++  {"show_table_stats",     (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATS]), SHOW_LONG_STATUS},
+   {"show_table_status",    (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATUS]), SHOW_LONG_STATUS},
+   {"show_tables",          (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLES]), SHOW_LONG_STATUS},
+   {"show_triggers",        (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TRIGGERS]), SHOW_LONG_STATUS},
++  {"show_user_stats",      (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_USER_STATS]), SHOW_LONG_STATUS},
+   {"show_variables",       (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_VARIABLES]), SHOW_LONG_STATUS},
+   {"show_warnings",        (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_WARNS]), SHOW_LONG_STATUS},
+   {"slave_start",          (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS},
+@@ -3507,6 +3521,9 @@
+ #endif
+   (void) pthread_mutex_init(&LOCK_server_started, MY_MUTEX_INIT_FAST);
+   (void) pthread_cond_init(&COND_server_started,NULL);
++  (void) pthread_mutex_init(&LOCK_global_user_stats, MY_MUTEX_INIT_FAST);
++  (void) pthread_mutex_init(&LOCK_global_table_stats, MY_MUTEX_INIT_FAST);
++  (void) pthread_mutex_init(&LOCK_global_index_stats, MY_MUTEX_INIT_FAST);
+   sp_cache_init();
+ #ifdef HAVE_EVENT_SCHEDULER
+   Events::init_mutexes();
+@@ -3872,6 +3889,9 @@
+   if (!errmesg[0][0])
+     unireg_abort(1);
++   init_global_table_stats();
++   init_global_index_stats();
++
+   /* We have to initialize the storage engines before CSV logging */
+   if (ha_init())
+   {
+@@ -4018,6 +4038,7 @@
+   init_max_user_conn();
+   init_update_queries();
++  init_global_user_stats();
+   DBUG_RETURN(0);
+ }
+diff -r 3ed7e96969f9 sql/sql_base.cc
+--- a/sql/sql_base.cc  Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/sql_base.cc  Thu Dec 04 08:54:27 2008 -0800
+@@ -1343,6 +1343,12 @@
+   DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str,
+                         table->s->table_name.str, (long) table));
++  if(table->file)
++  {
++    table->file->update_global_table_stats();
++    table->file->update_global_index_stats();
++  }
++
+   *table_ptr=table->next;
+   /*
+     When closing a MERGE parent or child table, detach the children first.
+@@ -1882,6 +1888,9 @@
+   DBUG_ENTER("close_temporary");
+   DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
+                           table->s->db.str, table->s->table_name.str));
++ 
++  table->file->update_global_table_stats();
++  table->file->update_global_index_stats();
+   free_io_cache(table);
+   closefrm(table, 0);
+diff -r 3ed7e96969f9 sql/sql_class.cc
+--- a/sql/sql_class.cc Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/sql_class.cc Thu Dec 04 08:54:27 2008 -0800
+@@ -578,6 +578,8 @@
+   bzero(ha_data, sizeof(ha_data));
+   mysys_var=0;
+   binlog_evt_union.do_union= FALSE;
++  busy_time = 0;
++  updated_row_count = 0;
+   enable_slow_log= 0;
+ #ifndef DBUG_OFF
+   dbug_sentry=THD_SENTRY_MAGIC;
+@@ -762,8 +764,55 @@
+   reset_current_stmt_binlog_row_based();
+   bzero((char *) &status_var, sizeof(status_var));
+   sql_log_bin_toplevel= options & OPTION_BIN_LOG;
++  reset_stats();
+ }
++// Resets stats in a THD.
++void THD::reset_stats(void) {
++  current_connect_time = time(NULL);
++  last_global_update_time = current_connect_time;
++  reset_diff_stats();
++}
++
++// Resets the 'diff' stats, which are used to update global stats.
++void THD::reset_diff_stats(void) {
++  diff_total_busy_time = 0;
++  diff_total_sent_rows = 0;
++  diff_total_updated_rows = 0;
++  diff_select_commands = 0;
++  diff_update_commands = 0;
++  diff_other_commands = 0;
++  diff_commit_trans = 0;
++  diff_rollback_trans = 0;
++}
++
++// Updates 'diff' stats of a THD.
++void THD::update_stats() {
++  diff_total_busy_time += busy_time;
++  diff_total_sent_rows += sent_row_count;
++  diff_total_updated_rows += updated_row_count;
++  // The replication thread has the COM_CONNECT command.
++  if ((old_command == COM_QUERY || command == COM_CONNECT) &&
++      (lex->sql_command >= 0 && lex->sql_command < SQLCOM_END)) {
++    // A SQL query.
++    if (lex->sql_command == SQLCOM_SELECT) {
++      if (!(sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND)) {
++        diff_select_commands++;
++      } else {
++        // 'SHOW ' commands become SQLCOM_SELECT.
++        diff_other_commands++;
++        // 'SHOW ' commands shouldn't inflate total sent row count.
++        diff_total_sent_rows -= sent_row_count;
++      }
++    } else if (is_update_query(lex->sql_command)) {
++      diff_update_commands++;
++    } else {
++      diff_other_commands++;
++    }
++  }
++  // diff_commit_trans is updated in handler.cc.
++  // diff_rollback_trans is updated in handler.cc.
++}
+ /*
+   Init THD for query processing.
+diff -r 3ed7e96969f9 sql/sql_class.h
+--- a/sql/sql_class.h  Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/sql_class.h  Thu Dec 04 08:54:27 2008 -0800
+@@ -1327,6 +1327,8 @@
+     first byte of the packet in do_command()
+   */
+   enum enum_server_command command;
++  // Used to save the command, before it is set to COM_SLEEP.
++  enum enum_server_command old_command;
+   uint32     server_id;
+   uint32     file_id;                 // for LOAD DATA INFILE
+   /* remote (peer) port */
+@@ -1616,6 +1618,7 @@
+   ulonglong  options;           /* Bitmap of states */
+   longlong   row_count_func;    /* For the ROW_COUNT() function */
+   ha_rows    cuted_fields;
++  ha_rows    updated_row_count;
+   /*
+     number of rows we actually sent to the client, including "synthetic"
+@@ -1767,6 +1770,27 @@
+   */
+   LOG_INFO*  current_linfo;
+   NET*       slave_net;                       // network connection from slave -> m.
++
++  /*
++    Used to update global user stats.  The global user stats are updated
++    occasionally with the 'diff' variables.  After the update, the 'diff'
++    variables are reset to 0.
++   */
++  // Time when the current thread connected to MySQL.
++  time_t current_connect_time;
++  // Last time when THD stats were updated in global_user_stats.
++  time_t last_global_update_time;
++  // Busy (non-idle) time for just one command.
++  double busy_time;
++  // Busy time not updated in global_user_stats yet.
++  double diff_total_busy_time;
++  // Number of rows not reflected in global_user_stats yet.
++  ha_rows diff_total_sent_rows, diff_total_updated_rows;
++  // Number of commands not reflected in global_user_stats yet.
++  ulonglong diff_select_commands, diff_update_commands, diff_other_commands;
++  // Number of transactions not reflected in global_user_stats yet.
++  ulonglong diff_commit_trans, diff_rollback_trans;
++
+   /* Used by the sys_var class to store temporary values */
+   union
+   {
+@@ -1827,6 +1851,9 @@
+     alloc_root. 
+   */
+   void init_for_queries();
++  void reset_stats(void);
++  void reset_diff_stats(void);
++  void update_stats(void);
+   void change_user(void);
+   void cleanup(void);
+   void cleanup_after_query();
+diff -r 3ed7e96969f9 sql/sql_connect.cc
+--- a/sql/sql_connect.cc       Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/sql_connect.cc       Thu Dec 04 08:54:27 2008 -0800
+@@ -520,6 +520,14 @@
+                  0,0,
+                  (hash_get_key) get_key_conn, (hash_free_key) free_user,
+                  0);
++  if (hash_init(&hash_user_connections,system_charset_info,max_connections,
++                0,0,
++                (hash_get_key) get_key_conn, (hash_free_key) free_user,
++                0)) {
++    sql_print_error("Initializing hash_user_connections failed.");
++    exit(1);
++  }
++
+ #endif
+ }
+@@ -1107,6 +1115,13 @@
+     if (login_connection(thd))
+       goto end_thread;
++    thd->reset_stats();
++    // Updates global user connection stats.
++    if (increment_connection_count(thd, true)) {
++      net_send_error(thd, ER_OUTOFMEMORY);  // Out of memory
++      goto end_thread;
++    }
++
+     prepare_new_connection_state(thd);
+     while (!net->error && net->vio != 0 &&
+@@ -1119,6 +1134,8 @@
+    
+ end_thread:
+     close_connection(thd, 0, 1);
++    thd->update_stats();
++    update_global_user_stats(thd);
+     if (thread_scheduler.end_thread(thd,1))
+       return 0;                                 // Probably no-threads
+diff -r 3ed7e96969f9 sql/sql_delete.cc
+--- a/sql/sql_delete.cc        Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/sql_delete.cc        Thu Dec 04 08:54:27 2008 -0800
+@@ -402,6 +402,7 @@
+     my_ok(thd, (ha_rows) thd->row_count_func);
+     DBUG_PRINT("info",("%ld records deleted",(long) deleted));
+   }
++  thd->updated_row_count += deleted;
+   DBUG_RETURN(error >= 0 || thd->is_error());
+ }
+@@ -938,6 +939,7 @@
+     thd->row_count_func= deleted;
+     ::my_ok(thd, (ha_rows) thd->row_count_func);
+   }
++  thd->updated_row_count += deleted;
+   return 0;
+ }
+diff -r 3ed7e96969f9 sql/sql_insert.cc
+--- a/sql/sql_insert.cc        Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/sql_insert.cc        Thu Dec 04 08:54:27 2008 -0800
+@@ -969,6 +969,7 @@
+     thd->row_count_func= info.copied + info.deleted + updated;
+     ::my_ok(thd, (ulong) thd->row_count_func, id, buff);
+   }
++  thd->updated_row_count += thd->row_count_func;
+   thd->abort_on_warning= 0;
+   DBUG_RETURN(FALSE);
+diff -r 3ed7e96969f9 sql/sql_lex.h
+--- a/sql/sql_lex.h    Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/sql_lex.h    Thu Dec 04 08:54:27 2008 -0800
+@@ -118,7 +118,7 @@
+   SQLCOM_SHOW_CREATE_TRIGGER,
+   SQLCOM_ALTER_DB_UPGRADE,
+   SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES,
+-
++  SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS, 
+   /*
+     When a command is added here, be sure it's also added in mysqld.cc
+     in "struct show_var_st status_vars[]= {" ...
+diff -r 3ed7e96969f9 sql/sql_parse.cc
+--- a/sql/sql_parse.cc Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/sql_parse.cc Thu Dec 04 08:54:27 2008 -0800
+@@ -45,6 +45,15 @@
+ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
+ static bool check_show_create_table_access(THD *thd, TABLE_LIST *table);
++
++HASH global_user_stats;
++extern pthread_mutex_t LOCK_global_user_stats;
++
++HASH global_table_stats;
++extern pthread_mutex_t LOCK_global_table_stats;
++
++HASH global_index_stats;
++extern pthread_mutex_t LOCK_global_index_stats;
+ const char *any_db="*any*";   // Special symbol for check_access
+@@ -326,7 +335,9 @@
+   sql_command_flags[SQLCOM_SHOW_FUNC_CODE]=  CF_STATUS_COMMAND;
+   sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]=  CF_STATUS_COMMAND;
+   sql_command_flags[SQLCOM_SHOW_PROFILES]= CF_STATUS_COMMAND;
+-  sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND;
++  sql_command_flags[SQLCOM_SHOW_USER_STATS]= CF_STATUS_COMMAND;
++  sql_command_flags[SQLCOM_SHOW_TABLE_STATS]= CF_STATUS_COMMAND;
++  sql_command_flags[SQLCOM_SHOW_INDEX_STATS]= CF_STATUS_COMMAND;
+    sql_command_flags[SQLCOM_SHOW_TABLES]=       (CF_STATUS_COMMAND |
+                                                  CF_SHOW_TABLE_COMMAND |
+@@ -544,6 +555,86 @@
+   DBUG_RETURN(0);
+ }
++extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
++                                    my_bool not_used __attribute__((unused)))
++{
++  *length = strlen(user_stats->user);
++  return (uchar*)user_stats->user;
++}
++
++extern "C" void free_user_stats(USER_STATS* user_stats)
++{
++  my_free((char*)user_stats, MYF(0));
++}
++
++void init_global_user_stats(void)
++{
++  if (hash_init(&global_user_stats, system_charset_info, max_connections,
++                0, 0, (hash_get_key)get_key_user_stats,
++                (hash_free_key)free_user_stats, 0)) {
++    sql_print_error("Initializing global_user_stats failed.");
++    exit(1);
++  }
++}
++
++extern "C" uchar *get_key_table_stats(TABLE_STATS *table_stats, size_t *length,
++                                     my_bool not_used __attribute__((unused)))
++{
++  *length = strlen(table_stats->table);
++  return (uchar*)table_stats->table;
++}
++
++extern "C" void free_table_stats(TABLE_STATS* table_stats)
++{
++  my_free((char*)table_stats, MYF(0));
++}
++
++void init_global_table_stats(void)
++{
++  if (hash_init(&global_table_stats, system_charset_info, max_connections,
++                0, 0, (hash_get_key)get_key_table_stats,
++                (hash_free_key)free_table_stats, 0)) {
++    sql_print_error("Initializing global_table_stats failed.");
++    exit(1);
++  }
++}
++
++extern "C" uchar *get_key_index_stats(INDEX_STATS *index_stats, size_t *length,
++                                     my_bool not_used __attribute__((unused)))
++{
++  *length = strlen(index_stats->index);
++  return (uchar*)index_stats->index;
++}
++
++extern "C" void free_index_stats(INDEX_STATS* index_stats)
++{
++  my_free((char*)index_stats, MYF(0));
++}
++
++void init_global_index_stats(void)
++{
++  if (hash_init(&global_index_stats, system_charset_info, max_connections,
++                0, 0, (hash_get_key)get_key_index_stats,
++                (hash_free_key)free_index_stats, 0)) {
++    sql_print_error("Initializing global_index_stats failed.");
++    exit(1);
++  }
++}
++
++void free_global_user_stats(void)
++{
++  hash_free(&global_user_stats);
++}
++
++void free_global_table_stats(void)
++{
++  hash_free(&global_table_stats);
++}
++
++void free_global_index_stats(void)
++{
++  hash_free(&global_index_stats);
++}
+ /**
+   @brief Check access privs for a MERGE table and fix children lock types.
+@@ -962,6 +1053,9 @@
+   DBUG_PRINT("info",("packet: '%*.s'; command: %d", packet_length, packet, command));
+   thd->command=command;
++  // To increment the correct command counter for user stats, 'command' must
++  // be saved because it is set to COM_SLEEP at the end of this function.
++  thd->old_command = command;
+   /*
+     Commands which always take a long time are logged into
+     the slow log only if opt_log_slow_admin_statements is set.
+@@ -1740,6 +1834,9 @@
+   case SCH_COLUMN_PRIVILEGES:
+   case SCH_TABLE_CONSTRAINTS:
+   case SCH_KEY_COLUMN_USAGE:
++  case SCH_USER_STATS:
++  case SCH_TABLE_STATS:
++  case SCH_INDEX_STATS:
+   default:
+     break;
+   }
+@@ -2129,6 +2226,9 @@
+     my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
+     break;
+ #endif
++  case SQLCOM_SHOW_USER_STATS:
++  case SQLCOM_SHOW_TABLE_STATS:
++  case SQLCOM_SHOW_INDEX_STATS:
+   case SQLCOM_SHOW_STATUS_PROC:
+   case SQLCOM_SHOW_STATUS_FUNC:
+     if (!(res= check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE)))
+@@ -2306,6 +2406,7 @@
+   }
+ #endif
++
+   case SQLCOM_BACKUP_TABLE:
+   {
+     DBUG_ASSERT(first_table == all_tables && first_table != 0);
+@@ -5372,6 +5473,130 @@
+ #endif /*NO_EMBEDDED_ACCESS_CHECKS*/
++// 'mysql_system_user' is used for when the user is not defined for a THD.
++static char mysql_system_user[] = "#mysql_system#";
++
++// Returns 'user' if it's not NULL.  Returns 'mysql_system_user' otherwise.
++static char* get_valid_user_string(char* user) {
++  return user ? user : mysql_system_user;
++}
++
++// Increments the global user stats connection count.  If 'use_lock' is true,
++// 'LOCK_global_user_stats' will be locked/unlocked.  Returns 0 on success,
++// 1 on error.
++int increment_connection_count(THD* thd, bool use_lock) {
++  char* user_string = get_valid_user_string(thd->main_security_ctx.user);
++
++  USER_STATS* user_stats;
++  int return_value = 0;
++
++  if (use_lock) pthread_mutex_lock(&LOCK_global_user_stats);
++  if (!(user_stats = (USER_STATS*)hash_search(&global_user_stats,
++                                              (uchar*)user_string,
++                                              strlen(user_string)))) {
++    // First connection for this user.
++    if (!(user_stats = ((USER_STATS*)
++                        my_malloc(sizeof(USER_STATS), MYF(MY_WME))))) {
++      // Out of memory.
++      return_value = 1;
++      goto end;
++    }
++    strncpy(user_stats->user, user_string, sizeof(user_stats->user));
++    user_stats->total_connections = 0;
++    user_stats->concurrent_connections = 0;
++    user_stats->connected_time = 0;
++    user_stats->busy_time = 0;
++    user_stats->rows_fetched = 0;
++    user_stats->rows_updated = 0;
++    user_stats->select_commands = 0;
++    user_stats->update_commands = 0;
++    user_stats->other_commands = 0;
++    user_stats->commit_trans = 0;
++    user_stats->rollback_trans = 0;
++
++    if (my_hash_insert(&global_user_stats, (uchar*)user_stats)) {
++      // Out of memory.
++      my_free((char*)user_stats, 0);
++      return_value = 1;
++      goto end;
++    }
++  }
++  user_stats->total_connections++;
++end:
++  if (use_lock) pthread_mutex_unlock(&LOCK_global_user_stats);
++  return return_value;
++}
++
++// Used to update the global user stats.
++static void update_global_user_stats_with_user(THD* thd,
++                                               USER_STATS* user_stats) {
++  time_t current_time = time(NULL);
++  user_stats->connected_time += current_time - thd->last_global_update_time;
++  thd->last_global_update_time = current_time;
++  user_stats->busy_time += thd->diff_total_busy_time;
++  user_stats->rows_fetched += thd->diff_total_sent_rows;
++  user_stats->rows_updated += thd->diff_total_updated_rows;
++  user_stats->select_commands += thd->diff_select_commands;
++  user_stats->update_commands += thd->diff_update_commands;
++  user_stats->other_commands += thd->diff_other_commands;
++  user_stats->commit_trans += thd->diff_commit_trans;
++  user_stats->rollback_trans += thd->diff_rollback_trans;
++}
++
++// Updates the global stats of a thread/user.
++void update_global_user_stats(THD* thd) {
++  char* user_string = get_valid_user_string(thd->main_security_ctx.user);
++
++  USER_STATS* user_stats;
++  pthread_mutex_lock(&LOCK_global_user_stats);
++  if ((user_stats = (USER_STATS*)hash_search(&global_user_stats,
++                                             (uchar*)user_string,
++                                             strlen(user_string)))) {
++    // Found user.
++    update_global_user_stats_with_user(thd, user_stats);
++    thd->reset_diff_stats();
++  } else {
++    // The user name should exist.
++    increment_connection_count(thd, false);
++  }
++  pthread_mutex_unlock(&LOCK_global_user_stats);
++}
++
++// Determines the concurrent number of connections of current threads.
++void set_concurrent_connections_stats() {
++  USER_STATS* user_stats;
++
++  pthread_mutex_lock(&LOCK_global_user_stats);
++  pthread_mutex_lock(&LOCK_thread_count);
++
++  // Resets all concurrent connections to 0.
++  for (int i = 0; i < global_user_stats.records; ++i) {
++    user_stats = (USER_STATS*)hash_element(&global_user_stats, i);
++    user_stats->concurrent_connections = 0;
++  }
++
++  I_List_iterator<THD> it(threads);
++  THD* thd;
++  // Iterates through the current threads.
++  while ((thd = it++)) {
++    char* user_string = get_valid_user_string(thd->main_security_ctx.user);
++    if ((user_stats = (USER_STATS*)hash_search(&global_user_stats,
++                                               (uchar*)user_string,
++                                               strlen(user_string)))) {
++      // Found user.
++      user_stats->concurrent_connections++;
++      update_global_user_stats_with_user(thd, user_stats);
++      thd->reset_diff_stats();
++    } else {
++      // The user name should exist.
++      increment_connection_count(thd, false);
++    }
++  }
++  pthread_mutex_unlock(&LOCK_thread_count);
++  pthread_mutex_unlock(&LOCK_global_user_stats);
++}
++
++
+ /**
+   check for global access and give descriptive error message if it fails.
+@@ -5539,6 +5764,10 @@
+     reset_dynamic(&thd->user_var_events);
+     thd->user_var_events_alloc= thd->mem_root;
+   }
++  
++  thd->updated_row_count=0;
++  thd->busy_time=0;
++
+   thd->clear_error();
+   thd->main_da.reset_diagnostics_area();
+   thd->total_warn_count=0;                    // Warnings for this query
+@@ -5722,6 +5951,16 @@
+   DBUG_ENTER("mysql_parse");
+   DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););
++
++  int start_time_error = 0;
++  int end_time_error = 0;
++  struct timeval start_time, end_time;
++  double start_usecs = 0;
++  double end_usecs = 0;
++  // Gets the start time, in order to measure how long this command takes.
++  if (!(start_time_error = gettimeofday(&start_time, NULL))) {
++    start_usecs = start_time.tv_sec * 1000000.0 + start_time.tv_usec;
++  }
+   /*
+     Warning.
+@@ -5816,6 +6055,27 @@
+     *found_semicolon= NULL;
+   }
++  // Gets the end time.
++  if (!(end_time_error = gettimeofday(&end_time, NULL))) {
++    end_usecs = end_time.tv_sec * 1000000.0 + end_time.tv_usec;
++  }
++
++  // Calculates the difference between the end and start times.
++  if (end_usecs >= start_usecs && !start_time_error && !end_time_error) {
++    thd->busy_time = (end_usecs - start_usecs) / 1000000;
++    // In case there are bad values, 2629743 is the #seconds in a month.
++    if (thd->busy_time > 2629743) {
++      thd->busy_time = 0;
++    }
++  } else {
++    // end time went back in time, or gettimeofday() failed.
++    thd->busy_time = 0;
++  }
++
++  // Updates THD stats and the global user stats.
++  thd->update_stats();
++  update_global_user_stats(thd);
++
+   DBUG_VOID_RETURN;
+ }
+@@ -6398,6 +6658,7 @@
+     tables->lock_type= lock_type;
+     tables->updating=  for_update;
+   }
++
+   DBUG_VOID_RETURN;
+ }
+@@ -6779,6 +7040,21 @@
+ #endif
+  if (options & REFRESH_USER_RESOURCES)
+    reset_mqh((LEX_USER *) NULL, 0);             /* purecov: inspected */
++ if (options & REFRESH_TABLE_STATS)
++ {
++   pthread_mutex_lock(&LOCK_global_table_stats);
++   free_global_table_stats();
++   init_global_table_stats();
++   pthread_mutex_unlock(&LOCK_global_table_stats);
++ }
++ if (options & REFRESH_INDEX_STATS)
++ {
++   pthread_mutex_lock(&LOCK_global_index_stats);
++   free_global_index_stats();
++   init_global_index_stats();
++   pthread_mutex_unlock(&LOCK_global_index_stats);
++ }
++   
+  *write_to_binlog= tmp_write_to_binlog;
+  return result;
+ }
+diff -r 3ed7e96969f9 sql/sql_show.cc
+--- a/sql/sql_show.cc  Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/sql_show.cc  Thu Dec 04 08:54:27 2008 -0800
+@@ -2260,6 +2260,90 @@
+   DBUG_RETURN(FALSE);
+ }
++
++int fill_schema_user_stats(THD* thd, TABLE_LIST* tables, COND* cond)
++{
++  TABLE *table= tables->table;
++  DBUG_ENTER("fill_schema_user_stats");
++
++  set_concurrent_connections_stats();
++
++  pthread_mutex_lock(&LOCK_global_user_stats);
++  for (int i = 0; i < global_user_stats.records; ++i) {
++    restore_record(table, s->default_values);
++    USER_STATS *user_stats = (USER_STATS*)hash_element(&global_user_stats, i);
++    table->field[0]->store(user_stats->user, strlen(user_stats->user), system_charset_info);
++    table->field[1]->store((longlong)user_stats->total_connections, TRUE);
++    table->field[2]->store((longlong)user_stats->concurrent_connections, TRUE);
++    table->field[3]->store((longlong)user_stats->connected_time, TRUE);
++    table->field[4]->store((longlong)user_stats->busy_time, TRUE);
++    table->field[5]->store((longlong)user_stats->rows_fetched, TRUE);
++    table->field[6]->store((longlong)user_stats->rows_updated, TRUE);
++    table->field[7]->store((longlong)user_stats->select_commands, TRUE);
++    table->field[8]->store((longlong)user_stats->update_commands, TRUE);
++    table->field[9]->store((longlong)user_stats->other_commands, TRUE);
++    table->field[10]->store((longlong)user_stats->commit_trans, TRUE);
++    table->field[11]->store((longlong)user_stats->rollback_trans, TRUE);
++
++    if (schema_table_store_record(thd, table))
++    {
++      VOID(pthread_mutex_unlock(&LOCK_global_user_stats));
++      DBUG_RETURN(1);
++    }
++  }
++  pthread_mutex_unlock(&LOCK_global_user_stats);
++  DBUG_RETURN(0);
++}
++
++
++int fill_schema_table_stats(THD* thd, TABLE_LIST* tables, COND* cond)
++{
++  TABLE *table= tables->table;
++  DBUG_ENTER("fill_schema_table_stats");
++
++  pthread_mutex_lock(&LOCK_global_table_stats);
++  for (int i = 0; i < global_table_stats.records; ++i) {
++    restore_record(table, s->default_values);
++    TABLE_STATS *table_stats = 
++      (TABLE_STATS*)hash_element(&global_table_stats, i);
++    table->field[0]->store(table_stats->table, strlen(table_stats->table), system_charset_info);
++    table->field[1]->store((longlong)table_stats->rows_read, TRUE);
++    table->field[2]->store((longlong)table_stats->rows_changed, TRUE);
++    table->field[3]->store((longlong)table_stats->rows_changed_x_indexes, TRUE);
++
++    if (schema_table_store_record(thd, table))
++    {
++      VOID(pthread_mutex_unlock(&LOCK_global_table_stats));
++      DBUG_RETURN(1);
++    }
++  }
++  pthread_mutex_unlock(&LOCK_global_table_stats);
++  DBUG_RETURN(0);
++}
++
++
++int fill_schema_index_stats(THD* thd, TABLE_LIST* tables, COND* cond)
++{
++  TABLE *table= tables->table;
++  DBUG_ENTER("fill_schema_index_stats");
++
++  pthread_mutex_lock(&LOCK_global_index_stats);
++  for (int i = 0; i < global_index_stats.records; ++i) {
++    restore_record(table, s->default_values);
++    INDEX_STATS *index_stats = 
++      (INDEX_STATS*)hash_element(&global_index_stats, i);
++    table->field[0]->store(index_stats->index, strlen(index_stats->index), system_charset_info);
++    table->field[1]->store((longlong)index_stats->rows_read, TRUE);
++
++    if (schema_table_store_record(thd, table))
++    {
++      VOID(pthread_mutex_unlock(&LOCK_global_index_stats));
++      DBUG_RETURN(1);
++    }
++  }
++  pthread_mutex_unlock(&LOCK_global_index_stats);
++  DBUG_RETURN(0);
++}
+ /* collect status for all running threads */
+@@ -6606,6 +6690,38 @@
+   {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ };
++ST_FIELD_INFO user_stats_fields_info[]=
++{
++  {"USER", 16, MYSQL_TYPE_STRING, 0, 0, "User", SKIP_OPEN_TABLE},
++  {"TOTAL_CONNECTIONS", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Total_connections", SKIP_OPEN_TABLE},
++  {"CONCURRENT_CONNECTIONS", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Concurrent_connections", SKIP_OPEN_TABLE},
++  {"CONNECTED_TIME", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Connected_time", SKIP_OPEN_TABLE},
++  {"BUSY_TIME", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Busy_time", SKIP_OPEN_TABLE},
++  {"ROWS_FETCHED", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_fetched", SKIP_OPEN_TABLE},
++  {"ROWS_UPDATED", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_updated", SKIP_OPEN_TABLE},
++  {"SELECT_COMMANDS", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Select_commands", SKIP_OPEN_TABLE},
++  {"UPDATE_COMMANDS", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Update_commands", SKIP_OPEN_TABLE},
++  {"OTHER_COMMANDS", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Other_commands", SKIP_OPEN_TABLE},
++  {"COMMIT_TRANSACTIONS", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Commit_transactions", SKIP_OPEN_TABLE},
++  {"ROLLBACK_TRANSACTIONS", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Rollback_transactions", SKIP_OPEN_TABLE},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
++};
++
++ST_FIELD_INFO table_stats_fields_info[]=
++{
++  {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name", SKIP_OPEN_TABLE},
++  {"ROWS_READ", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_read", SKIP_OPEN_TABLE},
++  {"ROWS_CHANGED", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_changed", SKIP_OPEN_TABLE},
++  {"ROWS_CHANGED_INDEXES", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_changed_x_#indexes", SKIP_OPEN_TABLE},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
++};
++
++ST_FIELD_INFO index_stats_fields_info[]=
++{
++  {"INDEX_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Index_name", SKIP_OPEN_TABLE},
++  {"ROWS_READ", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_read", SKIP_OPEN_TABLE},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
++};
+ /*
+   Description of ST_FIELD_INFO in table.h
+@@ -6824,6 +6824,8 @@
+    fill_status, make_old_format, 0, 0, -1, 0, 0},
+   {"GLOBAL_VARIABLES", variables_fields_info, create_schema_table,
+    fill_variables, make_old_format, 0, 0, -1, 0, 0},
++  {"INDEX_STATISTICS", index_stats_fields_info, create_schema_table,
++   fill_schema_index_stats, make_old_format, 0, -1, -1, 0, 0},
+   {"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table,
+    get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0,
+    OPEN_TABLE_ONLY},
+@@ -6683,11 +6801,15 @@
+    get_all_tables, make_table_names_old_format, 0, 1, 2, 1, 0},
+   {"TABLE_PRIVILEGES", table_privileges_fields_info, create_schema_table,
+    fill_schema_table_privileges, 0, 0, -1, -1, 0, 0},
++  {"TABLE_STATISTICS", table_stats_fields_info, create_schema_table,
++   fill_schema_table_stats, make_old_format, 0, -1, -1, 0, 0},
+   {"TRIGGERS", triggers_fields_info, create_schema_table,
+    get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0,
+    OPEN_TABLE_ONLY},
+   {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table, 
+    fill_schema_user_privileges, 0, 0, -1, -1, 0, 0},
++  {"USER_STATISTICS", user_stats_fields_info, create_schema_table, 
++   fill_schema_user_stats, make_old_format, 0, -1, -1, 0, 0},
+   {"VARIABLES", variables_fields_info, create_schema_table, fill_variables,
+    make_old_format, 0, -1, -1, 1, 0},
+   {"VIEWS", view_fields_info, create_schema_table, 
+diff -r 3ed7e96969f9 sql/sql_update.cc
+--- a/sql/sql_update.cc        Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/sql_update.cc        Thu Dec 04 08:54:27 2008 -0800
+@@ -816,6 +816,7 @@
+     thd->row_count_func=
+       (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
+     my_ok(thd, (ulong) thd->row_count_func, id, buff);
++    thd->updated_row_count += thd->row_count_func;
+     DBUG_PRINT("info",("%ld records updated", (long) updated));
+   }
+   thd->count_cuted_fields= CHECK_FIELD_IGNORE;                /* calc cuted fields */
+@@ -2038,5 +2039,6 @@
+   thd->row_count_func=
+     (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
+   ::my_ok(thd, (ulong) thd->row_count_func, id, buff);
++  thd->updated_row_count += thd->row_count_func;
+   DBUG_RETURN(FALSE);
+ }
+diff -r 3ed7e96969f9 sql/sql_yacc.yy
+--- a/sql/sql_yacc.yy  Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/sql_yacc.yy  Thu Dec 04 08:54:27 2008 -0800
+@@ -738,6 +738,7 @@
+ %token  IMPORT
+ %token  INDEXES
+ %token  INDEX_SYM
++%token        INDEX_STATS_SYM
+ %token  INFILE
+ %token  INITIAL_SIZE_SYM
+ %token  INNER_SYM                     /* SQL-2003-R */
+@@ -1026,6 +1027,7 @@
+ %token  TABLE_REF_PRIORITY
+ %token  TABLE_SYM                     /* SQL-2003-R */
+ %token  TABLE_CHECKSUM_SYM
++%token        TABLE_STATS_SYM
+ %token  TEMPORARY                     /* SQL-2003-N */
+ %token  TEMPTABLE_SYM
+ %token  TERMINATED
+@@ -1071,6 +1073,7 @@
+ %token  UPGRADE_SYM
+ %token  USAGE                         /* SQL-2003-N */
+ %token  USER                          /* SQL-2003-R */
++%token        USER_STATS_SYM
+ %token  USE_FRM
+ %token  USE_SYM
+ %token  USING                         /* SQL-2003-R */
+@@ -10090,6 +10093,27 @@
+           {
+             Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
+           }
++        | USER_STATS_SYM wild_and_where
++          {
++             LEX *lex= Lex;
++             lex->sql_command= SQLCOM_SHOW_USER_STATS;
++             if (prepare_schema_table(YYTHD, lex, 0, SCH_USER_STATS))
++               MYSQL_YYABORT;
++          }
++        | TABLE_STATS_SYM wild_and_where
++          {
++             LEX *lex= Lex;
++             lex->sql_command= SQLCOM_SHOW_TABLE_STATS;
++             if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_STATS))
++               MYSQL_YYABORT;
++          }
++        | INDEX_STATS_SYM wild_and_where
++          {
++             LEX *lex= Lex;
++             lex->sql_command= SQLCOM_SHOW_INDEX_STATS;
++             if (prepare_schema_table(YYTHD, lex, 0, SCH_INDEX_STATS))
++               MYSQL_YYABORT;
++          }
+         | CREATE PROCEDURE sp_name
+           {
+             LEX *lex= Lex;
+@@ -10304,6 +10328,10 @@
+           { Lex->type|= REFRESH_DES_KEY_FILE; }
+         | RESOURCES
+           { Lex->type|= REFRESH_USER_RESOURCES; }
++        | TABLE_STATS_SYM
++          { Lex->type|= REFRESH_TABLE_STATS; }
++        | INDEX_STATS_SYM
++          { Lex->type|= REFRESH_INDEX_STATS; }
+         ;
+ opt_table_list:
+diff -r 3ed7e96969f9 sql/structs.h
+--- a/sql/structs.h    Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/structs.h    Thu Dec 04 08:54:27 2008 -0800
+@@ -226,6 +226,28 @@
+   /* Maximum amount of resources which account is allowed to consume. */
+   USER_RESOURCES user_resources;
+ } USER_CONN;
++
++typedef struct st_user_stats {
++  char user[USERNAME_LENGTH + 1];
++  uint total_connections;
++  uint concurrent_connections;
++  time_t connected_time;  // in seconds
++  double busy_time;       // in seconds
++  ha_rows rows_fetched, rows_updated;
++  ulonglong select_commands, update_commands, other_commands;
++  ulonglong commit_trans, rollback_trans;
++} USER_STATS;
++
++typedef struct st_table_stats {
++  char table[NAME_LEN * 2 + 2];  // [db] + '.' + [table] + '\0'
++  ulonglong rows_read, rows_changed;
++  ulonglong rows_changed_x_indexes;
++} TABLE_STATS;
++
++typedef struct st_index_stats {
++  char index[NAME_LEN * 3 + 3];  // [db] + '.' + [table] + '.' + [index] + '\0'
++  ulonglong rows_read;
++} INDEX_STATS;
+       /* Bits in form->update */
+ #define REG_MAKE_DUPP         1       /* Make a copy of record when read */
+diff -r 3ed7e96969f9 sql/table.h
+--- a/sql/table.h      Thu Dec 04 08:54:17 2008 -0800
++++ b/sql/table.h      Thu Dec 04 08:54:27 2008 -0800
+@@ -879,6 +879,7 @@
+   SCH_FILES,
+   SCH_GLOBAL_STATUS,
+   SCH_GLOBAL_VARIABLES,
++  SCH_INDEX_STATS,
+   SCH_KEY_COLUMN_USAGE,
+   SCH_OPEN_TABLES,
+   SCH_PARTITIONS,
+@@ -897,8 +898,10 @@
+   SCH_TABLE_CONSTRAINTS,
+   SCH_TABLE_NAMES,
+   SCH_TABLE_PRIVILEGES,
++  SCH_TABLE_STATS,
+   SCH_TRIGGERS,
+   SCH_USER_PRIVILEGES,
++  SCH_USER_STATS,
+   SCH_VARIABLES,
+   SCH_VIEWS
+ };
+diff -r 3ed7e96969f9 storage/innobase/handler/ha_innodb.cc
+--- a/storage/innobase/handler/ha_innodb.cc    Thu Dec 04 08:54:17 2008 -0800
++++ b/storage/innobase/handler/ha_innodb.cc    Thu Dec 04 08:54:27 2008 -0800
+@@ -3494,6 +3494,8 @@
+       error = row_insert_for_mysql((byte*) record, prebuilt);
++  if (error == DB_SUCCESS) rows_changed++;
++
+       /* Handle duplicate key errors */
+       if (auto_inc_used) {
+               ulint           err;
+@@ -3571,6 +3573,8 @@
+                       break;
+               }
+       }
++
++      if (error == DB_SUCCESS) rows_changed++;
+       innodb_srv_conc_exit_innodb(prebuilt->trx);
+@@ -4178,6 +4182,8 @@
+               ret = row_search_for_mysql((byte*) buf, mode, prebuilt,
+                                          match_mode, 0);
++      if (error == DB_SUCCESS) rows_changed++;
++
+               innodb_srv_conc_exit_innodb(prebuilt->trx);
+       } else {
+@@ -4187,6 +4193,9 @@
+       if (ret == DB_SUCCESS) {
+               error = 0;
+               table->status = 0;
++              rows_read++;
++              if (active_index >= 0 && active_index < MAX_KEY)
++                      index_rows_read[active_index]++;
+       } else if (ret == DB_RECORD_NOT_FOUND) {
+               error = HA_ERR_KEY_NOT_FOUND;
+@@ -4360,6 +4369,9 @@
+       if (ret == DB_SUCCESS) {
+               error = 0;
+               table->status = 0;
++              rows_read++;
++              if (active_index >= 0 && active_index < MAX_KEY)
++                      index_rows_read[active_index]++;
+       } else if (ret == DB_RECORD_NOT_FOUND) {
+               error = HA_ERR_END_OF_FILE;
+diff -r 3ed7e96969f9 storage/myisam/ha_myisam.cc
+--- a/storage/myisam/ha_myisam.cc      Thu Dec 04 08:54:17 2008 -0800
++++ b/storage/myisam/ha_myisam.cc      Thu Dec 04 08:54:27 2008 -0800
+@@ -738,7 +738,9 @@
+     if ((error= update_auto_increment()))
+       return error;
+   }
+-  return mi_write(file,buf);
++  int error=mi_write(file,buf);
++  if (!error) rows_changed++;
++  return error;
+ }
+ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
+@@ -1589,13 +1591,17 @@
+   ha_statistic_increment(&SSV::ha_update_count);
+   if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
+     table->timestamp_field->set_time();
+-  return mi_update(file,old_data,new_data);
++  int error=mi_update(file,old_data,new_data);
++  if (!error) rows_changed++;
++  return error;
+ }
+ int ha_myisam::delete_row(const uchar *buf)
+ {
+   ha_statistic_increment(&SSV::ha_delete_count);
+-  return mi_delete(file,buf);
++  int error=mi_delete(file,buf);
++  if (!error) rows_changed++;
++  return error;
+ }
+ int ha_myisam::index_read_map(uchar *buf, const uchar *key,
+@@ -1606,6 +1612,13 @@
+   ha_statistic_increment(&SSV::ha_read_key_count);
+   int error=mi_rkey(file, buf, active_index, key, keypart_map, find_flag);
+   table->status=error ? STATUS_NOT_FOUND: 0;
++  if (!error) {
++    rows_read++;
++
++    int inx = (active_index == -1) ? file->lastinx : active_index;
++    if (inx >= 0 && inx < MAX_KEY)
++      index_rows_read[inx]++;
++  }
+   return error;
+ }
+@@ -1616,6 +1629,14 @@
+   ha_statistic_increment(&SSV::ha_read_key_count);
+   int error=mi_rkey(file, buf, index, key, keypart_map, find_flag);
+   table->status=error ? STATUS_NOT_FOUND: 0;
++  if (!error) {
++    rows_read++;
++
++//    int inx = (active_index == -1) ? file->lastinx : active_index;
++    int inx = index;
++    if (inx >= 0 && inx < MAX_KEY)
++      index_rows_read[inx]++;
++  }
+   return error;
+ }
+@@ -1637,6 +1658,13 @@
+   ha_statistic_increment(&SSV::ha_read_next_count);
+   int error=mi_rnext(file,buf,active_index);
+   table->status=error ? STATUS_NOT_FOUND: 0;
++  if (!error) {
++    rows_read++;
++
++    int inx = (active_index == -1) ? file->lastinx : active_index;
++    if (inx >= 0 && inx < MAX_KEY)
++      index_rows_read[inx]++;
++  }
+   return error;
+ }
+@@ -1646,6 +1674,13 @@
+   ha_statistic_increment(&SSV::ha_read_prev_count);
+   int error=mi_rprev(file,buf, active_index);
+   table->status=error ? STATUS_NOT_FOUND: 0;
++  if (!error) {
++    rows_read++;
++
++    int inx = (active_index == -1) ? file->lastinx : active_index;
++    if (inx >= 0 && inx < MAX_KEY)
++      index_rows_read[inx]++;
++  }
+   return error;
+ }
+@@ -1655,6 +1690,13 @@
+   ha_statistic_increment(&SSV::ha_read_first_count);
+   int error=mi_rfirst(file, buf, active_index);
+   table->status=error ? STATUS_NOT_FOUND: 0;
++  if (!error) {
++    rows_read++;
++
++    int inx = (active_index == -1) ? file->lastinx : active_index;
++    if (inx >= 0 && inx < MAX_KEY)
++      index_rows_read[inx]++;
++  }
+   return error;
+ }
+@@ -1664,6 +1706,13 @@
+   ha_statistic_increment(&SSV::ha_read_last_count);
+   int error=mi_rlast(file, buf, active_index);
+   table->status=error ? STATUS_NOT_FOUND: 0;
++  if (!error) {
++    rows_read++;
++
++    int inx = (active_index == -1) ? file->lastinx : active_index;
++    if (inx >= 0 && inx < MAX_KEY)
++      index_rows_read[inx]++;
++  }
+   return error;
+ }
+@@ -1679,6 +1728,20 @@
+     error= mi_rnext_same(file,buf);
+   } while (error == HA_ERR_RECORD_DELETED);
+   table->status=error ? STATUS_NOT_FOUND: 0;
++  if (!error) {
++    rows_read++;
++
++    int inx = (active_index == -1) ? file->lastinx : active_index;
++    if (inx >= 0 && inx < MAX_KEY)
++      index_rows_read[inx]++;
++  }
++  if (!error) {
++    rows_read++;
++
++    int inx = (active_index == -1) ? file->lastinx : active_index;
++    if (inx >= 0 && inx < MAX_KEY)
++      index_rows_read[inx]++;
++  }
+   return error;
+ }
+@@ -1695,6 +1758,7 @@
+   ha_statistic_increment(&SSV::ha_read_rnd_next_count);
+   int error=mi_scan(file, buf);
+   table->status=error ? STATUS_NOT_FOUND: 0;
++  if (!error) rows_read++;
+   return error;
+ }
+@@ -1708,6 +1772,7 @@
+   ha_statistic_increment(&SSV::ha_read_rnd_count);
+   int error=mi_rrnd(file, buf, my_get_ptr(pos,ref_length));
+   table->status=error ? STATUS_NOT_FOUND: 0;
++  if (!error) rows_read++;
+   return error;
+ }
This page took 0.104305 seconds and 4 git commands to generate.