← Back to team overview

maria-developers team mailing list archive

Userstats patch applied to MariaDB 5.2

 

Hi!

I just finished applying / modifying / rewriting the userstats patch
to MariaDB 5.2 and will commit this in a moment.

As far as I know, the patch was originally made by Mark and his team,
then addopted by Percona and fixed/updated by Arjen & Weldon.

I send this patch in advance here, so that people that has been
involved with the patch can comment on what/if still needs to be
done and what would be the next steps.

I will write extensive comments in the commit message, but here are
the highlights:

- Almost all counters are done through 'thd->status_var' (no separate
  counters used). This was done by creating a copy of thd->status_var
  at start of execution and adding the difference to user stats.

  The benefit of the above is:
  - Less code
  - No double counters (faster execution)
  - Trivial to add new counters to the statistics tables
    (There is a lot of counters already that can be used)
  - No need to reset counters
  - If 'userstat' is not set, very little overhead.

- Changed all of MariaBB code to use handler::ha_read... instead if 
  handler::read.  This allowed me to have all counters in the
  ha_... wrapper 

  The benefit are:
   - No need to change engine code
   - All engines are now measured

- Formatted code to 'MariaDB' style.
- Removed not called functions.
- A lot of small speed improvements
  - Optimized hash keys to not have to do strlen().
  - Store unique keys in 'keyinfo' to not have to generate
    keys on the fly.
  - Replaced 'if's with DBUG_ASSERT for things that I thought was
    impossible.

- Change naming of some variables to be more consistent
  - userstat_running -> user_stat
  - Rows_fetched -> Rows changed

- Added statistics variables (for user and client statistics)
  Rows_updated, Rows_deleted, Rows_inserted

- Changed busy and cpu timing to double (as in Weldon's patch)

- Changed position of a few columns in the information schemas to
  group things in a more logical way.

Things that I would like to have comments one from the original
authors are:

- I did not remove from sql_parse.cc:

  if (options & REFRESH_USER_RESOURCES)
    reset_mqh((LEX_USER *) NULL, 0);             /* purecov: inspected */

Don't understand why this should be removed.

- Don't call 'set_concurrent_connections_stats()' (as per Weldon's
  patch) to collect information from the running threads as this leads
  will lead to wrong counting as all variables are not updated until
  the whole command is run.

Here follows the patch (without the new test cases, the test cases
will be in the commit).
Note that I am now about to do a 'bzr gcommit'
and I may fix some minor issues while I do that.

You should be able to apply this directly to MariaDB 5.1 or you can
pull MariaDB 5.2 later today if you want to test this.

(MariaDB 5.2 is basicly MariaDB 5.1 + stable patches, so it's should
be safe to use).


=== modified file 'configure.in'
--- configure.in	2009-10-08 09:43:31 +0000
+++ configure.in	2009-10-16 13:25:43 +0000
@@ -829,7 +829,7 @@ AC_CHECK_HEADERS(fcntl.h fenv.h float.h 
  sys/timeb.h sys/types.h sys/un.h sys/vadvise.h sys/wait.h term.h \
  unistd.h utime.h sys/utime.h termio.h termios.h sched.h crypt.h alloca.h \
  sys/ioctl.h malloc.h sys/malloc.h sys/ipc.h sys/shm.h linux/config.h \
- sys/prctl.h sys/resource.h sys/param.h port.h ieeefp.h \
+ sys/prctl.h sys/resource.h sys/param.h port.h ieeefp.h linux/unistd.h \
  execinfo.h)
 
 AC_CHECK_HEADERS([xfs/xfs.h])
@@ -2096,7 +2096,18 @@ case "$target" in
 	# We also disable for SCO for the time being, the headers for the
 	# thread library we use conflicts with other headers.
     ;;
- *) AC_CHECK_FUNCS(clock_gettime)
+*) 
+   # most systems require the program be linked with librt library to use
+   # the function clock_gettime 
+   my_save_LIBS="$LIBS"
+   LIBS=""
+   AC_CHECK_LIB(rt,clock_gettime)
+   LIBRT=$LIBS
+   LIBS="$my_save_LIBS"
+   AC_SUBST(LIBRT)
+
+   LIBS="$LIBS $LIBRT"
+   AC_CHECK_FUNCS(clock_gettime)
     ;;
 esac
 
@@ -2786,7 +2797,7 @@ then
 fi
 sql_client_dirs="$sql_client_dirs client"
 
-CLIENT_LIBS="$NON_THREADED_LIBS $openssl_libs $ZLIB_LIBS $STATIC_NSS_FLAGS"
+CLIENT_LIBS="$NON_THREADED_LIBS $openssl_libs $ZLIB_LIBS $STATIC_NSS_FLAGS $LIBRT"
 
 AC_SUBST(CLIENT_LIBS)
 AC_SUBST(CLIENT_THREAD_LIBS)

=== modified file 'include/my_sys.h'
--- include/my_sys.h	2009-09-07 20:50:10 +0000
+++ include/my_sys.h	2009-10-16 13:21:53 +0000
@@ -904,6 +904,7 @@ void my_free_open_file_info(void);
 
 extern time_t my_time(myf flags);
 extern ulonglong my_getsystime(void);
+extern ulonglong my_getrealtime(void);
 extern ulonglong my_micro_time();
 extern ulonglong my_micro_time_and_time(time_t *time_arg);
 time_t my_time_possible_from_micro(ulonglong microtime);

=== modified file 'include/mysql_com.h'
--- include/mysql_com.h	2008-10-10 15:28:41 +0000
+++ include/mysql_com.h	2009-10-15 18:24:26 +0000
@@ -29,6 +29,7 @@
 
 #define SERVER_VERSION_LENGTH 60
 #define SQLSTATE_LENGTH 5
+#define LIST_PROCESS_HOST_LEN 64
 
 /*
   USER_HOST_BUFF_SIZE -- length of string buffer, that is enough to contain
@@ -115,6 +116,11 @@ enum enum_server_command
 					   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 */
+#define REFRESH_USER_STATS      1024    /* Refresh user stats hash table */
+#define REFRESH_SLOW_QUERY_LOG  4096    /* Flush slow query log and rotate*/
+#define REFRESH_CLIENT_STATS    8192    /* Refresh client stats hash table */
 
 /* The following can't be set with mysql_refresh() */
 #define REFRESH_READ_LOCK	16384	/* Lock tables for read */

=== modified file 'mysql-test/r/information_schema.result'
--- mysql-test/r/information_schema.result	2009-09-29 20:19:43 +0000
+++ mysql-test/r/information_schema.result	2009-10-18 19:37:52 +0000
@@ -45,6 +45,7 @@ NOT (table_schema = 'INFORMATION_SCHEMA'
 select * from v1 ORDER BY c COLLATE utf8_bin;
 c
 CHARACTER_SETS
+CLIENT_STATISTICS
 COLLATIONS
 COLLATION_CHARACTER_SET_APPLICABILITY
 COLUMNS
@@ -54,6 +55,7 @@ EVENTS
 FILES
 GLOBAL_STATUS
 GLOBAL_VARIABLES
+INDEX_STATISTICS
 INNODB_BUFFER_POOL_PAGES
 INNODB_BUFFER_POOL_PAGES_BLOB
 INNODB_BUFFER_POOL_PAGES_INDEX
@@ -82,8 +84,10 @@ STATISTICS
 TABLES
 TABLE_CONSTRAINTS
 TABLE_PRIVILEGES
+TABLE_STATISTICS
 TRIGGERS
 USER_PRIVILEGES
+USER_STATISTICS
 VIEWS
 XTRADB_ENHANCEMENTS
 columns_priv
@@ -121,6 +125,7 @@ c	table_name
 TABLES	TABLES
 TABLE_CONSTRAINTS	TABLE_CONSTRAINTS
 TABLE_PRIVILEGES	TABLE_PRIVILEGES
+TABLE_STATISTICS	TABLE_STATISTICS
 TRIGGERS	TRIGGERS
 tables_priv	tables_priv
 time_zone	time_zone
@@ -140,6 +145,7 @@ c	table_name
 TABLES	TABLES
 TABLE_CONSTRAINTS	TABLE_CONSTRAINTS
 TABLE_PRIVILEGES	TABLE_PRIVILEGES
+TABLE_STATISTICS	TABLE_STATISTICS
 TRIGGERS	TRIGGERS
 tables_priv	tables_priv
 time_zone	time_zone
@@ -159,6 +165,7 @@ c	table_name
 TABLES	TABLES
 TABLE_CONSTRAINTS	TABLE_CONSTRAINTS
 TABLE_PRIVILEGES	TABLE_PRIVILEGES
+TABLE_STATISTICS	TABLE_STATISTICS
 TRIGGERS	TRIGGERS
 tables_priv	tables_priv
 time_zone	time_zone
@@ -640,12 +647,13 @@ from information_schema.tables
 where table_schema='information_schema' limit 2;
 TABLE_NAME	TABLE_TYPE	ENGINE
 CHARACTER_SETS	SYSTEM VIEW	MEMORY
-COLLATIONS	SYSTEM VIEW	MEMORY
+CLIENT_STATISTICS	SYSTEM VIEW	MEMORY
 show tables from information_schema like "T%";
 Tables_in_information_schema (T%)
 TABLES
 TABLE_CONSTRAINTS
 TABLE_PRIVILEGES
+TABLE_STATISTICS
 TRIGGERS
 create database information_schema;
 ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema'
@@ -655,6 +663,7 @@ Tables_in_information_schema (T%)	Table_
 TABLES	SYSTEM VIEW
 TABLE_CONSTRAINTS	SYSTEM VIEW
 TABLE_PRIVILEGES	SYSTEM VIEW
+TABLE_STATISTICS	SYSTEM VIEW
 TRIGGERS	SYSTEM VIEW
 create table t1(a int);
 ERROR 42S02: Unknown table 't1' in information_schema
@@ -667,6 +676,7 @@ Tables_in_information_schema (T%)
 TABLES
 TABLE_CONSTRAINTS
 TABLE_PRIVILEGES
+TABLE_STATISTICS
 TRIGGERS
 select table_name from tables where table_name='user';
 table_name
@@ -856,6 +866,7 @@ TABLE_NAME	COLUMN_NAME	PRIVILEGES
 COLUMNS	TABLE_NAME	select
 COLUMN_PRIVILEGES	TABLE_NAME	select
 FILES	TABLE_NAME	select
+INDEX_STATISTICS	TABLE_NAME	select
 KEY_COLUMN_USAGE	TABLE_NAME	select
 PARTITIONS	TABLE_NAME	select
 REFERENTIAL_CONSTRAINTS	TABLE_NAME	select
@@ -863,6 +874,7 @@ STATISTICS	TABLE_NAME	select
 TABLES	TABLE_NAME	select
 TABLE_CONSTRAINTS	TABLE_NAME	select
 TABLE_PRIVILEGES	TABLE_NAME	select
+TABLE_STATISTICS	TABLE_NAME	select
 VIEWS	TABLE_NAME	select
 INNODB_BUFFER_POOL_PAGES_INDEX	table_name	select
 INNODB_INDEX_STATS	table_name	select

=== modified file 'mysql-test/r/information_schema_all_engines.result'
--- mysql-test/r/information_schema_all_engines.result	2009-08-03 20:09:53 +0000
+++ mysql-test/r/information_schema_all_engines.result	2009-10-19 01:26:40 +0000
@@ -2,6 +2,7 @@ use INFORMATION_SCHEMA;
 show tables;
 Tables_in_information_schema
 CHARACTER_SETS
+CLIENT_STATISTICS
 COLLATIONS
 COLLATION_CHARACTER_SET_APPLICABILITY
 COLUMNS
@@ -11,6 +12,7 @@ EVENTS
 FILES
 GLOBAL_STATUS
 GLOBAL_VARIABLES
+INDEX_STATISTICS
 KEY_COLUMN_USAGE
 PARTITIONS
 PLUGINS
@@ -26,8 +28,10 @@ STATISTICS
 TABLES
 TABLE_CONSTRAINTS
 TABLE_PRIVILEGES
+TABLE_STATISTICS
 TRIGGERS
 USER_PRIVILEGES
+USER_STATISTICS
 VIEWS
 INNODB_BUFFER_POOL_PAGES
 PBXT_STATISTICS
@@ -60,6 +64,7 @@ c2.column_name LIKE '%SCHEMA%'
         );
 table_name	column_name
 CHARACTER_SETS	CHARACTER_SET_NAME
+CLIENT_STATISTICS	CLIENT
 COLLATIONS	COLLATION_NAME
 COLLATION_CHARACTER_SET_APPLICABILITY	COLLATION_NAME
 COLUMNS	TABLE_SCHEMA
@@ -69,6 +74,7 @@ EVENTS	EVENT_SCHEMA
 FILES	TABLE_SCHEMA
 GLOBAL_STATUS	VARIABLE_NAME
 GLOBAL_VARIABLES	VARIABLE_NAME
+INDEX_STATISTICS	TABLE_SCHEMA
 KEY_COLUMN_USAGE	CONSTRAINT_SCHEMA
 PARTITIONS	TABLE_SCHEMA
 PLUGINS	PLUGIN_NAME
@@ -84,8 +90,10 @@ STATISTICS	TABLE_SCHEMA
 TABLES	TABLE_SCHEMA
 TABLE_CONSTRAINTS	CONSTRAINT_SCHEMA
 TABLE_PRIVILEGES	TABLE_SCHEMA
+TABLE_STATISTICS	TABLE_SCHEMA
 TRIGGERS	TRIGGER_SCHEMA
 USER_PRIVILEGES	GRANTEE
+USER_STATISTICS	USER
 VIEWS	TABLE_SCHEMA
 INNODB_BUFFER_POOL_PAGES	page_type
 PBXT_STATISTICS	ID
@@ -118,6 +126,7 @@ c2.column_name LIKE '%SCHEMA%'
         );
 table_name	column_name
 CHARACTER_SETS	CHARACTER_SET_NAME
+CLIENT_STATISTICS	CLIENT
 COLLATIONS	COLLATION_NAME
 COLLATION_CHARACTER_SET_APPLICABILITY	COLLATION_NAME
 COLUMNS	TABLE_SCHEMA
@@ -127,6 +136,7 @@ EVENTS	EVENT_SCHEMA
 FILES	TABLE_SCHEMA
 GLOBAL_STATUS	VARIABLE_NAME
 GLOBAL_VARIABLES	VARIABLE_NAME
+INDEX_STATISTICS	TABLE_SCHEMA
 KEY_COLUMN_USAGE	CONSTRAINT_SCHEMA
 PARTITIONS	TABLE_SCHEMA
 PLUGINS	PLUGIN_NAME
@@ -142,8 +152,10 @@ STATISTICS	TABLE_SCHEMA
 TABLES	TABLE_SCHEMA
 TABLE_CONSTRAINTS	CONSTRAINT_SCHEMA
 TABLE_PRIVILEGES	TABLE_SCHEMA
+TABLE_STATISTICS	TABLE_SCHEMA
 TRIGGERS	TRIGGER_SCHEMA
 USER_PRIVILEGES	GRANTEE
+USER_STATISTICS	USER
 VIEWS	TABLE_SCHEMA
 INNODB_BUFFER_POOL_PAGES	page_type
 PBXT_STATISTICS	ID
@@ -182,6 +194,7 @@ group by c2.column_type order by num lim
 group by t.table_name order by num1, t.table_name;
 table_name	group_concat(t.table_schema, '.', t.table_name)	num1
 CHARACTER_SETS	information_schema.CHARACTER_SETS	1
+CLIENT_STATISTICS	information_schema.CLIENT_STATISTICS	1
 COLLATIONS	information_schema.COLLATIONS	1
 COLLATION_CHARACTER_SET_APPLICABILITY	information_schema.COLLATION_CHARACTER_SET_APPLICABILITY	1
 COLUMNS	information_schema.COLUMNS	1
@@ -191,6 +204,7 @@ EVENTS	information_schema.EVENTS	1
 FILES	information_schema.FILES	1
 GLOBAL_STATUS	information_schema.GLOBAL_STATUS	1
 GLOBAL_VARIABLES	information_schema.GLOBAL_VARIABLES	1
+INDEX_STATISTICS	information_schema.INDEX_STATISTICS	1
 INNODB_BUFFER_POOL_PAGES	information_schema.INNODB_BUFFER_POOL_PAGES	1
 INNODB_BUFFER_POOL_PAGES_BLOB	information_schema.INNODB_BUFFER_POOL_PAGES_BLOB	1
 INNODB_BUFFER_POOL_PAGES_INDEX	information_schema.INNODB_BUFFER_POOL_PAGES_INDEX	1
@@ -220,8 +234,10 @@ STATISTICS	information_schema.STATISTICS
 TABLES	information_schema.TABLES	1
 TABLE_CONSTRAINTS	information_schema.TABLE_CONSTRAINTS	1
 TABLE_PRIVILEGES	information_schema.TABLE_PRIVILEGES	1
+TABLE_STATISTICS	information_schema.TABLE_STATISTICS	1
 TRIGGERS	information_schema.TRIGGERS	1
 USER_PRIVILEGES	information_schema.USER_PRIVILEGES	1
+USER_STATISTICS	information_schema.USER_STATISTICS	1
 VIEWS	information_schema.VIEWS	1
 XTRADB_ENHANCEMENTS	information_schema.XTRADB_ENHANCEMENTS	1
 Database: information_schema
@@ -229,6 +245,7 @@ Database: information_schema
 |                Tables                 |
 +---------------------------------------+
 | CHARACTER_SETS                        |
+| CLIENT_STATISTICS                     |
 | COLLATIONS                            |
 | COLLATION_CHARACTER_SET_APPLICABILITY |
 | COLUMNS                               |
@@ -238,6 +255,7 @@ Database: information_schema
 | FILES                                 |
 | GLOBAL_STATUS                         |
 | GLOBAL_VARIABLES                      |
+| INDEX_STATISTICS                      |
 | KEY_COLUMN_USAGE                      |
 | PARTITIONS                            |
 | PLUGINS                               |
@@ -253,8 +271,10 @@ Database: information_schema
 | TABLES                                |
 | TABLE_CONSTRAINTS                     |
 | TABLE_PRIVILEGES                      |
+| TABLE_STATISTICS                      |
 | TRIGGERS                              |
 | USER_PRIVILEGES                       |
+| USER_STATISTICS                       |
 | VIEWS                                 |
 | INNODB_BUFFER_POOL_PAGES              |
 | PBXT_STATISTICS                       |
@@ -277,6 +297,7 @@ Database: INFORMATION_SCHEMA
 |                Tables                 |
 +---------------------------------------+
 | CHARACTER_SETS                        |
+| CLIENT_STATISTICS                     |
 | COLLATIONS                            |
 | COLLATION_CHARACTER_SET_APPLICABILITY |
 | COLUMNS                               |
@@ -286,6 +307,7 @@ Database: INFORMATION_SCHEMA
 | FILES                                 |
 | GLOBAL_STATUS                         |
 | GLOBAL_VARIABLES                      |
+| INDEX_STATISTICS                      |
 | KEY_COLUMN_USAGE                      |
 | PARTITIONS                            |
 | PLUGINS                               |
@@ -301,8 +323,10 @@ Database: INFORMATION_SCHEMA
 | TABLES                                |
 | TABLE_CONSTRAINTS                     |
 | TABLE_PRIVILEGES                      |
+| TABLE_STATISTICS                      |
 | TRIGGERS                              |
 | USER_PRIVILEGES                       |
+| USER_STATISTICS                       |
 | VIEWS                                 |
 | INNODB_BUFFER_POOL_PAGES              |
 | PBXT_STATISTICS                       |
@@ -328,5 +352,5 @@ Wildcard: inf_rmation_schema
 +--------------------+
 SELECT table_schema, count(*) FROM information_schema.TABLES WHERE table_schema IN ('mysql', 'INFORMATION_SCHEMA', 'test', 'mysqltest') AND table_name<>'ndb_binlog_index' AND table_name<>'ndb_apply_status' GROUP BY TABLE_SCHEMA;
 table_schema	count(*)
-information_schema	43
+information_schema	47
 mysql	22

=== modified file 'mysql-test/r/information_schema_db.result'
--- mysql-test/r/information_schema_db.result	2009-09-07 20:50:10 +0000
+++ mysql-test/r/information_schema_db.result	2009-10-18 19:38:24 +0000
@@ -7,6 +7,7 @@ Tables_in_information_schema (T%)
 TABLES
 TABLE_CONSTRAINTS
 TABLE_PRIVILEGES
+TABLE_STATISTICS
 TRIGGERS
 create database `inf%`;
 create database mbase;

=== modified file 'mysql-test/r/log_slow.result'
--- mysql-test/r/log_slow.result	2009-09-03 14:05:38 +0000
+++ mysql-test/r/log_slow.result	2009-10-18 18:59:33 +0000
@@ -56,5 +56,6 @@ last_insert_id	int(11)	NO		NULL	
 insert_id	int(11)	NO		NULL	
 server_id	int(10) unsigned	NO		NULL	
 sql_text	mediumtext	NO		NULL	
+flush slow query logs;
 set @@log_slow_filter=default;
 set @@log_slow_verbosity=default;

=== modified file 'mysql-test/t/log_slow.test'
--- mysql-test/t/log_slow.test	2009-09-03 14:05:38 +0000
+++ mysql-test/t/log_slow.test	2009-10-18 18:25:56 +0000
@@ -36,6 +36,12 @@ select @@log_slow_verbosity;
 
 show fields from mysql.slow_log;
 
+#
+# Check flush command
+#
+
+flush slow query logs;
+
 # Reset used variables
 
 set @@log_slow_filter=default;

=== modified file 'mysys/my_getsystime.c'
--- mysys/my_getsystime.c	2008-04-28 16:24:05 +0000
+++ mysys/my_getsystime.c	2009-10-16 14:05:22 +0000
@@ -28,6 +28,10 @@
 #ifdef __NETWARE__
 #include <nks/time.h>
 #endif
+#ifdef HAVE_LINUX_UNISTD_H
+#include <linux/unistd.h>
+#endif
+
 
 ulonglong my_getsystime()
 {
@@ -222,3 +226,25 @@ time_t my_time_possible_from_micro(ulong
   return (time_t) (microtime / 1000000);
 #endif  /* defined(__WIN__) */
 }
+
+
+/*
+  Return real time in milliseconds * 10
+*/
+
+ulonglong my_getrealtime()
+{
+#ifdef HAVE_CLOCK_GETTIME
+  struct timespec tp;
+  if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp))
+    return 0;
+  return (ulonglong)tp.tv_sec*10000000+(ulonglong)tp.tv_nsec/100;
+#elif defined(__NR_clock_gettime)
+  struct timespec tp;
+  if (syscall(__NR_clock_gettime, CLOCK_THREAD_CPUTIME_ID, &tp))
+    return 0;
+  return (ulonglong)tp.tv_sec*10000000+(ulonglong)tp.tv_nsec/100;
+#else
+  return 0;
+#endif /* HAVE_CLOCK_GETTIME */
+}

=== modified file 'sql/authors.h'
--- sql/authors.h	2007-03-16 06:39:07 +0000
+++ sql/authors.h	2009-10-16 11:22:55 +0000
@@ -34,23 +34,35 @@ struct show_table_authors_st {
 */
 
 struct show_table_authors_st show_table_authors[]= {
+  { "Michael (Monty) Widenius", "Tusby, Finland",
+    "Lead developer and main author" },
+  { "David Axmark", "London, England",
+    "MySQL founder; Small stuff long time ago, Monty ripped it out!" },
+  { "Sergei Golubchik", "Kerpen, Germany",
+    "Full-text search, precision math" },
+  { "Igor Babaev", "Bellevue, USA", "Optimizer, keycache, core work"},
+  { "Sergey Petrunia", "St. Petersburg, Russia", "Optimizer"},
+  { "Oleksandr Byelkin", "Lugansk, Ukraine",
+    "Query Cache (4.0), Subqueries (4.1), Views (5.0)" },
   { "Brian (Krow) Aker", "Seattle, WA, USA",
     "Architecture, archive, federated, bunch of little stuff :)" },
-  { "Venu Anuganti", "", "Client/server protocol (4.1)" },
-  { "David Axmark", "Uppsala, Sweden",
-    "Small stuff long time ago, Monty ripped it out!" },
+  { "Kristian Nielsen", "Copenhagen, Denmark",
+    "General build stuff," },
   { "Alexander (Bar) Barkov", "Izhevsk, Russia",
     "Unicode and character sets (4.1)" },
+  { "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" },
+  { "Venu Anuganti", "", "Client/server protocol (4.1)" },
+  { "Konstantin Osipov", "Moscow, Russia",
+    "Prepared statements (4.1), Cursors (5.0)" },
+  { "Dmitri Lenev", "Moscow, Russia",
+    "Time zones support (4.1), Triggers (5.0)" },
   { "Omer BarNir", "Sunnyvale, CA, USA",
     "Testing (sometimes) and general QA stuff" },
-  { "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" },
   { "John Birrell", "", "Emulation of pthread_mutex() for OS/2" },
   { "Andreas F. Bobak", "", "AGGREGATE extension to user-defined functions" },
   { "Alexey Botchkov (Holyfoot)", "Izhevsk, Russia",
     "GIS extensions (4.1), embedded server (4.1), precision math (5.0)"},
   { "Reggie Burnett", "Nashville, TN, USA", "Windows development, Connectors" },
-  { "Oleksandr Byelkin", "Lugansk, Ukraine",
-    "Query Cache (4.0), Subqueries (4.1), Views (5.0)" },
   { "Kent Boortz", "Orebro, Sweden", "Test platform, and general build stuff" },
   { "Tim Bunce", "", "mysqlhotcopy" },
   { "Yves Carlier", "", "mysqlaccess" },
@@ -67,8 +79,6 @@ struct show_table_authors_st show_table_
   { "Yuri Dario", "", "OS/2 port" },
   { "Andrei Elkin", "Espoo, Finland", "Replication" },
   { "Patrick Galbraith", "Sharon, NH", "Federated Engine, mysqlslap" },
-  { "Sergei Golubchik", "Kerpen, Germany",
-    "Full-text search, precision math" },
   { "Lenz Grimmer", "Hamburg, Germany",
     "Production (build and release) engineering" },
   { "Nikolay Grishakin", "Austin, TX, USA", "Testing - Server" },
@@ -83,8 +93,6 @@ struct show_table_authors_st show_table_
   { "Hakan Küçükyılmaz", "Walldorf, Germany", "Testing - Server" },
   { "Greg (Groggy) Lehey", "Uchunga, SA, Australia", "Backup" },
   { "Matthias Leich", "Berlin, Germany", "Testing - Server" },
-  { "Dmitri Lenev", "Moscow, Russia",
-    "Time zones support (4.1), Triggers (5.0)" },
   { "Arjen Lentz", "Brisbane, Australia",
     "Documentation (2001-2004), Dutch error messages, LOG2()" },
   { "Marc Liyanage", "", "Created Mac OS X packages" },
@@ -96,8 +104,6 @@ struct show_table_authors_st show_table_
   { "Jonathan (Jeb) Miller", "Kyle, TX, USA",
     "Testing - Cluster, Replication" },
   { "Elliot Murphy", "Cocoa, FL, USA", "Replication and backup" },
-  { "Kristian Nielsen", "Copenhagen, Denmark",
-    "General build stuff" },
   { "Pekka Nouisiainen", "Stockholm, Sweden",
     "NDB Cluster: BLOB support, character set support, ordered indexes" },
   { "Alexander Nozdrin", "Moscow, Russia",
@@ -105,8 +111,6 @@ struct show_table_authors_st show_table_
   { "Per Eric Olsson", "", "Testing of dynamic record format" },
   { "Jonas Oreland", "Stockholm, Sweden",
     "NDB Cluster, Online Backup, lots of other things" },
-  { "Konstantin Osipov", "Moscow, Russia",
-    "Prepared statements (4.1), Cursors (5.0)" },
   { "Alexander (Sasha) Pachev", "Provo, UT, USA",
     "Statement-based replication, SHOW CREATE TABLE, mysql-bench" },
   { "Irena Pancirov", "", "Port to Windows with Borland compiler" },
@@ -144,9 +148,9 @@ struct show_table_authors_st show_table_
   { "Sergey Vojtovich", "Izhevsk, Russia", "Plugins infrastructure (5.1)" },
   { "Matt Wagner", "Northfield, MN, USA", "Bug fixing" },
   { "Jim Winstead Jr.", "Los Angeles, CA, USA", "Bug fixing" },
-  { "Michael (Monty) Widenius", "Tusby, Finland",
-    "Lead developer and main author" },
   { "Peter Zaitsev", "Tacoma, WA, USA",
     "SHA1(), AES_ENCRYPT(), AES_DECRYPT(), bug fixing" },
+  {"Mark Mark Callaghan", "Texas, USA", "Statistics patches"},
+  {"Percona", "CA, USA", "Microslow patches"},
   {NULL, NULL, NULL}
 };

=== modified file 'sql/event_data_objects.cc'
--- sql/event_data_objects.cc	2009-09-15 10:46:35 +0000
+++ sql/event_data_objects.cc	2009-10-18 14:30:18 +0000
@@ -1366,7 +1366,7 @@ Event_job_data::execute(THD *thd, bool d
 
   DBUG_ENTER("Event_job_data::execute");
 
-  mysql_reset_thd_for_next_command(thd);
+  mysql_reset_thd_for_next_command(thd, 0);
 
   /*
     MySQL parser currently assumes that current database is either

=== modified file 'sql/event_db_repository.cc'
--- sql/event_db_repository.cc	2009-02-15 10:58:34 +0000
+++ sql/event_db_repository.cc	2009-10-17 08:09:24 +0000
@@ -404,17 +404,18 @@ Event_db_repository::index_read_for_db_f
   }
 
   key_copy(key_buf, event_table->record[0], key_info, key_len);
-  if (!(ret= event_table->file->index_read_map(event_table->record[0], key_buf,
-                                               (key_part_map)1,
-                                               HA_READ_PREFIX)))
+  if (!(ret= event_table->file->ha_index_read_map(event_table->record[0],
+                                                  key_buf,
+                                                  (key_part_map)1,
+                                                  HA_READ_PREFIX)))
   {
     DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret));
     do
     {
       ret= copy_event_to_schema_table(thd, schema_table, event_table);
       if (ret == 0)
-        ret= event_table->file->index_next_same(event_table->record[0],
-                                                key_buf, key_len);
+        ret= event_table->file->ha_index_next_same(event_table->record[0],
+                                                   key_buf, key_len);
     } while (ret == 0);
   }
   DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
@@ -883,8 +884,9 @@ Event_db_repository::find_named_event(LE
 
   key_copy(key, table->record[0], table->key_info, table->key_info->key_length);
 
-  if (table->file->index_read_idx_map(table->record[0], 0, key, HA_WHOLE_KEY,
-                                      HA_READ_KEY_EXACT))
+  if (table->file->ha_index_read_idx_map(table->record[0], 0, key,
+                                         HA_WHOLE_KEY,
+                                         HA_READ_KEY_EXACT))
   {
     DBUG_PRINT("info", ("Row not found"));
     DBUG_RETURN(TRUE);

=== modified file 'sql/filesort.cc'
--- sql/filesort.cc	2009-09-03 14:05:38 +0000
+++ sql/filesort.cc	2009-10-17 08:13:46 +0000
@@ -577,11 +577,11 @@ static ha_rows find_all_keys(SORTPARAM *
 	  error= my_errno ? my_errno : -1;		/* Abort */
 	  break;
 	}
-	error=file->rnd_pos(sort_form->record[0],next_pos);
+	error=file->ha_rnd_pos(sort_form->record[0],next_pos);
       }
       else
       {
-	error=file->rnd_next(sort_form->record[0]);
+	error=file->ha_rnd_next(sort_form->record[0]);
 	if (!flag)
 	{
 	  my_store_ptr(ref_pos,ref_length,record); // Position to row

=== modified file 'sql/ha_partition.cc'
--- sql/ha_partition.cc	2009-09-07 20:50:10 +0000
+++ sql/ha_partition.cc	2009-10-17 08:13:46 +0000
@@ -1636,7 +1636,7 @@ int ha_partition::copy_partitions(ulongl
       goto error;
     while (TRUE)
     {
-      if ((result= file->rnd_next(m_rec0)))
+      if ((result= file->ha_rnd_next(m_rec0)))
       {
         if (result == HA_ERR_RECORD_DELETED)
           continue;                              //Probably MyISAM
@@ -3495,7 +3495,7 @@ int ha_partition::rnd_next(uchar *buf)
   
   while (TRUE)
   {
-    result= file->rnd_next(buf);
+    result= file->ha_rnd_next(buf);
     if (!result)
     {
       m_last_part= part_id;
@@ -4345,8 +4345,8 @@ int ha_partition::handle_unordered_next(
   }
   else if (is_next_same)
   {
-    if (!(error= file->index_next_same(buf, m_start_key.key,
-                                       m_start_key.length)))
+    if (!(error= file->ha_index_next_same(buf, m_start_key.key,
+                                          m_start_key.length)))
     {
       m_last_part= m_part_spec.start_part;
       DBUG_RETURN(0);
@@ -4354,7 +4354,7 @@ int ha_partition::handle_unordered_next(
   }
   else 
   {
-    if (!(error= file->index_next(buf)))
+    if (!(error= file->ha_index_next(buf)))
     {
       m_last_part= m_part_spec.start_part;
       DBUG_RETURN(0);                           // Row was in range
@@ -4409,24 +4409,26 @@ int ha_partition::handle_unordered_scan_
       break;
     case partition_index_read:
       DBUG_PRINT("info", ("index_read on partition %d", i));
-      error= file->index_read_map(buf, m_start_key.key,
-                                  m_start_key.keypart_map,
-                                  m_start_key.flag);
+      error= file->ha_index_read_map(buf, m_start_key.key,
+                                     m_start_key.keypart_map,
+                                     m_start_key.flag);
       break;
     case partition_index_first:
       DBUG_PRINT("info", ("index_first on partition %d", i));
-      /* MyISAM engine can fail if we call index_first() when indexes disabled */
-      /* that happens if the table is empty. */
-      /* Here we use file->stats.records instead of file->records() because */
-      /* file->records() is supposed to return an EXACT count, and it can be   */
-      /* possibly slow. We don't need an exact number, an approximate one- from*/
-      /* the last ::info() call - is sufficient. */
+      /*
+        MyISAM engine can fail if we call index_first() when indexes disabled
+        that happens if the table is empty.
+        Here we use file->stats.records instead of file->records() because
+        file->records() is supposed to return an EXACT count, and it can be
+        possibly slow. We don't need an exact number, an approximate one- from
+        the last ::info() call - is sufficient.
+      */
       if (file->stats.records == 0)
       {
         error= HA_ERR_END_OF_FILE;
         break;
       }
-      error= file->index_first(buf);
+      error= file->ha_index_first(buf);
       break;
     case partition_index_first_unordered:
       /*
@@ -4507,45 +4509,49 @@ int ha_partition::handle_ordered_index_s
 
     switch (m_index_scan_type) {
     case partition_index_read:
-      error= file->index_read_map(rec_buf_ptr,
-                                  m_start_key.key,
-                                  m_start_key.keypart_map,
-                                  m_start_key.flag);
+      error= file->ha_index_read_map(rec_buf_ptr,
+                                     m_start_key.key,
+                                     m_start_key.keypart_map,
+                                     m_start_key.flag);
       break;
     case partition_index_first:
-      /* MyISAM engine can fail if we call index_first() when indexes disabled */
-      /* that happens if the table is empty. */
-      /* Here we use file->stats.records instead of file->records() because */
-      /* file->records() is supposed to return an EXACT count, and it can be   */
-      /* possibly slow. We don't need an exact number, an approximate one- from*/
-      /* the last ::info() call - is sufficient. */
+      /*
+        MyISAM engine can fail if we call index_first() when indexes disabled
+        that happens if the table is empty.
+        Here we use file->stats.records instead of file->records() because
+        file->records() is supposed to return an EXACT count, and it can be
+        possibly slow. We don't need an exact number, an approximate one- from
+        the last ::info() call - is sufficient.
+      */
       if (file->stats.records == 0)
       {
         error= HA_ERR_END_OF_FILE;
         break;
       }
-      error= file->index_first(rec_buf_ptr);
+      error= file->ha_index_first(rec_buf_ptr);
       reverse_order= FALSE;
       break;
     case partition_index_last:
-      /* MyISAM engine can fail if we call index_last() when indexes disabled */
-      /* that happens if the table is empty. */
-      /* Here we use file->stats.records instead of file->records() because */
-      /* file->records() is supposed to return an EXACT count, and it can be   */
-      /* possibly slow. We don't need an exact number, an approximate one- from*/
-      /* the last ::info() call - is sufficient. */
+      /*
+        MyISAM engine can fail if we call index_last() when indexes disabled
+        that happens if the table is empty.
+        Here we use file->stats.records instead of file->records() because
+        file->records() is supposed to return an EXACT count, and it can be
+        possibly slow. We don't need an exact number, an approximate one- from
+        the last ::info() call - is sufficient.
+      */
       if (file->stats.records == 0)
       {
         error= HA_ERR_END_OF_FILE;
         break;
       }
-      error= file->index_last(rec_buf_ptr);
+      error= file->ha_index_last(rec_buf_ptr);
       reverse_order= TRUE;
       break;
     case partition_index_read_last:
-      error= file->index_read_last_map(rec_buf_ptr,
-                                       m_start_key.key,
-                                       m_start_key.keypart_map);
+      error= file->ha_index_read_last_map(rec_buf_ptr,
+                                          m_start_key.key,
+                                          m_start_key.keypart_map);
       reverse_order= TRUE;
       break;
     case partition_read_range:
@@ -4647,10 +4653,10 @@ int ha_partition::handle_ordered_next(uc
     memcpy(rec_buf(part_id), table->record[0], m_rec_length);
   }
   else if (!is_next_same)
-    error= file->index_next(rec_buf(part_id));
+    error= file->ha_index_next(rec_buf(part_id));
   else
-    error= file->index_next_same(rec_buf(part_id), m_start_key.key,
-				 m_start_key.length);
+    error= file->ha_index_next_same(rec_buf(part_id), m_start_key.key,
+                                    m_start_key.length);
   if (error)
   {
     if (error == HA_ERR_END_OF_FILE)
@@ -4695,7 +4701,7 @@ int ha_partition::handle_ordered_prev(uc
   handler *file= m_file[part_id];
   DBUG_ENTER("ha_partition::handle_ordered_prev");
 
-  if ((error= file->index_prev(rec_buf(part_id))))
+  if ((error= file->ha_index_prev(rec_buf(part_id))))
   {
     if (error == HA_ERR_END_OF_FILE)
     {

=== modified file 'sql/handler.cc'
--- sql/handler.cc	2009-09-09 21:06:57 +0000
+++ sql/handler.cc	2009-10-19 01:38:34 +0000
@@ -1195,6 +1195,7 @@ int ha_commit_trans(THD *thd, bool all)
     if (cookie)
       tc_log->unlog(cookie, xid);
     DBUG_EXECUTE_IF("crash_commit_after", DBUG_ABORT(););
+
 end:
     if (rw_trans)
       start_waiting_global_read_lock(thd);
@@ -1236,6 +1237,7 @@ int ha_commit_one_phase(THD *thd, bool a
         my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
         error=1;
       }
+      /* Should this be done only if is_real_trans is set ? */
       status_var_increment(thd->status_var.ha_commit_count);
       ha_info_next= ha_info->next();
       ha_info->reset(); /* keep it conveniently zero-filled */
@@ -2092,6 +2094,8 @@ int handler::ha_open(TABLE *table_arg, c
       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);
 }
 
@@ -2513,9 +2517,10 @@ void handler::get_auto_increment(ulonglo
     key_copy(key, table->record[0],
              table->key_info + table->s->next_number_index,
              table->s->next_number_key_offset);
-    error= index_read_map(table->record[1], key,
-                          make_prev_keypart_map(table->s->next_number_keypart),
-                          HA_READ_PREFIX_LAST);
+    error= ha_index_read_map(table->record[1], key,
+                             make_prev_keypart_map(table->s->
+                                                   next_number_keypart),
+                             HA_READ_PREFIX_LAST);
     /*
       MySQL needs to call us for next row: assume we are inserting ("a",null)
       here, we return 3, and next this statement will want to insert
@@ -3549,6 +3554,122 @@ void handler::get_dynamic_partition_info
 }
 
 
+/*
+  Updates the global table stats with the TABLE this handler represents
+*/
+
+void handler::update_global_table_stats()
+{
+  TABLE_STATS * table_stats;
+
+  status_var_add(table->in_use->status_var.rows_read, rows_read);
+
+  if (!opt_userstat_running)
+  {
+    rows_read= rows_changed= 0;
+    return;
+  }
+
+  if (rows_read + rows_changed == 0)
+    return;                                     // Nothing to update.
+
+  DBUG_ASSERT(table->s && table->s->table_cache_key.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*) table->s->table_cache_key.str,
+                    table->s->table_cache_key.length)))
+  {
+    if (!(table_stats = ((TABLE_STATS*)
+                         my_malloc(sizeof(TABLE_STATS),
+                                   MYF(MY_WME | MY_ZEROFILL)))))
+    {
+      /* Out of memory error already given */
+      goto end;
+    }
+    memcpy(table_stats->table, table->s->table_cache_key.str,
+           table->s->table_cache_key.length);
+    table_stats->table_name_length= table->s->table_cache_key.length;
+    table_stats->engine_type= ht->db_type;
+    /* No need to set variables to 0, as we use MY_ZEROFILL above */
+
+    if (my_hash_insert(&global_table_stats, (uchar*) table_stats))
+    {
+      /* Out of memory error is already given */
+      my_free(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()
+{
+  DBUG_ASSERT(table->s);
+
+  if (!table->in_use->userstat_running)
+  {
+    /* Reset all index read values */
+    bzero(index_rows_read, sizeof(index_rows_read[0]) * table->s->keys);
+    return;
+  }
+
+  for (uint index = 0; index < table->s->keys; index++)
+  {
+    if (index_rows_read[index])
+    {
+      INDEX_STATS* index_stats;
+      uint key_length;
+      KEY *key_info = &table->key_info[index];  // Rows were read using this
+
+      DBUG_ASSERT(key_info->cache_name);
+      if (!key_info->cache_name)
+        continue;
+      key_length= table->s->table_cache_key.length + key_info->name_length + 1;
+      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,
+                                                    key_info->cache_name,
+                                                    key_length)))
+      {
+        if (!(index_stats = ((INDEX_STATS*)
+                             my_malloc(sizeof(INDEX_STATS),
+                                       MYF(MY_WME | MY_ZEROFILL)))))
+          goto end;                             // Error is already given
+
+        memcpy(index_stats->index, key_info->cache_name, key_length);
+        index_stats->index_name_length= key_length;
+        if (my_hash_insert(&global_index_stats, (uchar*) index_stats))
+        {
+          my_free(index_stats, 0);
+          goto end;
+        }
+      }
+      /* Updates the global index stats. */
+      index_stats->rows_read+= index_rows_read[index];
+      index_rows_read[index]= 0;
+end:
+      pthread_mutex_unlock(&LOCK_global_index_stats);
+    }
+  }
+}
+
+
 /****************************************************************************
 ** Some general functions that isn't in the handler class
 ****************************************************************************/
@@ -4207,17 +4328,16 @@ int handler::read_range_first(const key_
   range_key_part= table->key_info[active_index].key_part;
 
   if (!start_key)			// Read first record
-    result= index_first(table->record[0]);
+    result= ha_index_first(table->record[0]);
   else
-    result= index_read_map(table->record[0],
-                           start_key->key,
-                           start_key->keypart_map,
-                           start_key->flag);
+    result= ha_index_read_map(table->record[0],
+                              start_key->key,
+                              start_key->keypart_map,
+                              start_key->flag);
   if (result)
     DBUG_RETURN((result == HA_ERR_KEY_NOT_FOUND) 
 		? HA_ERR_END_OF_FILE
 		: result);
-
   DBUG_RETURN (compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE);
 }
 
@@ -4243,11 +4363,11 @@ int handler::read_range_next()
   if (eq_range)
   {
     /* We trust that index_next_same always gives a row in range */
-    DBUG_RETURN(index_next_same(table->record[0],
-                                end_range->key,
-                                end_range->length));
+    DBUG_RETURN(ha_index_next_same(table->record[0],
+                                   end_range->key,
+                                   end_range->length));
   }
-  result= index_next(table->record[0]);
+  result= ha_index_next(table->record[0]);
   if (result)
     DBUG_RETURN(result);
   DBUG_RETURN(compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE);
@@ -4629,6 +4749,7 @@ int handler::ha_write_row(uchar *buf)
 
   if (unlikely(error= write_row(buf)))
     DBUG_RETURN(error);
+  rows_changed++;
   if (unlikely(error= binlog_log_row(table, 0, buf, log_func)))
     DBUG_RETURN(error); /* purecov: inspected */
   DBUG_RETURN(0);
@@ -4650,6 +4771,7 @@ int handler::ha_update_row(const uchar *
 
   if (unlikely(error= update_row(old_data, new_data)))
     return error;
+  rows_changed++;
   if (unlikely(error= binlog_log_row(table, old_data, new_data, log_func)))
     return error;
   return 0;
@@ -4664,6 +4786,7 @@ int handler::ha_delete_row(const uchar *
 
   if (unlikely(error= delete_row(buf)))
     return error;
+  rows_changed++;
   if (unlikely(error= binlog_log_row(table, buf, 0, log_func)))
     return error;
   return 0;

=== modified file 'sql/handler.h'
--- sql/handler.h	2009-09-07 20:50:10 +0000
+++ sql/handler.h	2009-10-18 20:53:48 +0000
@@ -30,6 +30,10 @@
 
 #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
 
 #define HA_ADMIN_ALREADY_DONE	  1
@@ -601,8 +605,9 @@ struct handlerton
   SHOW_COMP_OPTION state;
 
   /*
-    Historical number used for frm file to determine the correct storage engine.
-    This is going away and new engines will just use "name" for this.
+    Historical number used for frm file to determine the correct
+    storage engine.  This is going away and new engines will just use
+    "name" for this.
   */
   enum legacy_db_type db_type;
   /*
@@ -1138,6 +1143,11 @@ public:
     Interval returned by get_auto_increment() and being consumed by the
     inserter.
   */
+  /* Statistics  variables */
+  ulonglong rows_read;
+  ulonglong rows_changed;
+  ulonglong index_rows_read[MAX_KEY];
+
   Discrete_interval auto_inc_interval_for_cur_row;
   /**
      Number of reserved auto-increment intervals. Serves as a heuristic
@@ -1156,7 +1166,10 @@ public:
     locked(FALSE), implicit_emptied(0),
     pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
     auto_inc_intervals_count(0)
-    {}
+    {
+      reset_statistics();
+    }
+
   virtual ~handler(void)
   {
     DBUG_ASSERT(locked == FALSE);
@@ -1278,10 +1291,16 @@ public:
   virtual void print_error(int error, myf errflag);
   virtual bool get_error_message(int error, String *buf);
   uint get_dup_key(int error);
+  void reset_statistics()
+  {
+    rows_read= rows_changed= 0;
+    bzero(index_rows_read, sizeof(index_rows_read));
+  }
   virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share)
   {
     table= table_arg;
     table_share= share;
+    reset_statistics();
   }
   virtual double scan_time()
   { return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; }
@@ -1390,22 +1409,23 @@ public:
   }
   /**
      @brief
-     Positions an index cursor to the index specified in the handle. Fetches the
-     row if available. If the key value is null, begin at the first key of the
-     index.
+     Positions an index cursor to the index specified in the
+     handle. Fetches the row if available. If the key value is null,
+     begin at the first key of the index.
   */
+protected:
   virtual int index_read_map(uchar * buf, const uchar * key,
                              key_part_map keypart_map,
                              enum ha_rkey_function find_flag)
   {
     uint key_len= calculate_key_len(table, active_index, key, keypart_map);
-    return  index_read(buf, key, key_len, find_flag);
+    return index_read(buf, key, key_len, find_flag);
   }
   /**
      @brief
-     Positions an index cursor to the index specified in the handle. Fetches the
-     row if available. If the key value is null, begin at the first key of the
-     index.
+     Positions an index cursor to the index specified in the
+     handle. Fetches the row if available. If the key value is null,
+     begin at the first key of the index.
   */
   virtual int index_read_idx_map(uchar * buf, uint index, const uchar * key,
                                  key_part_map keypart_map,
@@ -1430,6 +1450,81 @@ public:
     uint key_len= calculate_key_len(table, active_index, key, keypart_map);
     return index_read_last(buf, key, key_len);
   }
+  inline void update_index_statistics()
+  {
+    if (active_index != MAX_KEY)
+      index_rows_read[active_index]++;
+    rows_read++;
+  }
+public:
+
+  /* Similar functions like the above, but does statistics counting */
+  inline int ha_index_read_map(uchar * buf, const uchar * key,
+                               key_part_map keypart_map,
+                               enum ha_rkey_function find_flag)
+  {
+    int error= index_read_map(buf, key, keypart_map, find_flag);
+    if (!error)
+      update_index_statistics();
+    return error;
+  }
+  inline int ha_index_read_idx_map(uchar * buf, uint index, const uchar * key,
+                                   key_part_map keypart_map,
+                                   enum ha_rkey_function find_flag)
+  {
+    int error= index_read_idx_map(buf, index, key, keypart_map, find_flag);
+    if (!error)
+    {
+      rows_read++;
+      if (index != MAX_KEY)
+        index_rows_read[index]++;
+    }
+    return error;
+  }
+  inline int ha_index_next(uchar * buf)
+  {
+    int error= index_next(buf);
+    if (!error)
+      update_index_statistics();
+    return error;
+  }
+  inline int ha_index_prev(uchar * buf)
+  {
+    int error= index_prev(buf);
+    if (!error)
+      update_index_statistics();
+    return error;
+  }
+  inline int ha_index_first(uchar * buf)
+  {
+    int error= index_first(buf);
+    if (!error)
+      update_index_statistics();
+    return error;
+  }
+  inline int ha_index_last(uchar * buf)
+  {
+    int error= index_last(buf);
+    if (!error)
+      update_index_statistics();
+    return error;
+  }
+  inline int ha_index_next_same(uchar *buf, const uchar *key, uint keylen)
+  {
+    int error= index_next_same(buf, key, keylen);
+    if (!error)
+      update_index_statistics();
+    return error;
+  }
+  inline int ha_index_read_last_map(uchar * buf, const uchar * key,
+                                    key_part_map keypart_map)
+  {
+    int error= index_read_last_map(buf, key, keypart_map);
+    if (!error)
+      update_index_statistics();
+    return error;
+  }
+
   virtual int read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
                                      KEY_MULTI_RANGE *ranges, uint range_count,
                                      bool sorted, HANDLER_BUFFER *buffer);
@@ -1443,6 +1538,7 @@ public:
   void ft_end() { ft_handler=NULL; }
   virtual FT_INFO *ft_init_ext(uint flags, uint inx,String *key)
     { return NULL; }
+private:
   virtual int ft_read(uchar *buf) { return HA_ERR_WRONG_COMMAND; }
   virtual int rnd_next(uchar *buf)=0;
   virtual int rnd_pos(uchar * buf, uchar *pos)=0;
@@ -1453,11 +1549,50 @@ public:
     handlers for random position.
   */
   virtual int rnd_pos_by_record(uchar *record)
-    {
-      position(record);
-      return rnd_pos(record, ref);
-    }
+  {
+    position(record);
+    return rnd_pos(record, ref);
+  }
   virtual int read_first_row(uchar *buf, uint primary_key);
+public:
+
+  /* Same as above, but with statistics */
+  inline int ha_ft_read(uchar *buf)
+  {
+    int error= ft_read(buf);
+    if (!error)
+      rows_read++;
+    return error;
+  }
+  inline int ha_rnd_next(uchar *buf)
+  {
+    int error= rnd_next(buf);
+    if (!error)
+      rows_read++;
+    return error;
+  }
+  inline int ha_rnd_pos(uchar *buf, uchar *pos)
+  {
+    int error= rnd_pos(buf, pos);
+    if (!error)
+      rows_read++;
+    return error;
+  }
+  inline int ha_rnd_pos_by_record(uchar *buf)
+  {
+    int error= rnd_pos_by_record(buf);
+    if (!error)
+      rows_read++;
+    return error;
+  }
+  inline int ha_read_first_row(uchar *buf, uint primary_key)
+  {
+    int error= read_first_row(buf, primary_key);
+    if (!error)
+      rows_read++;
+    return error;
+  }
+
   /**
     The following 3 function is only needed for tables that may be
     internal temporary tables during joins.
@@ -1626,6 +1761,9 @@ public:
   virtual bool is_crashed() const  { return 0; }
   virtual bool auto_repair() const { return 0; }
 
+  void update_global_table_stats();
+  void update_global_index_stats();
+
 #define CHF_CREATE_FLAG 0
 #define CHF_DELETE_FLAG 1
 #define CHF_RENAME_FLAG 2
@@ -1944,6 +2082,7 @@ private:
   { return HA_ERR_WRONG_COMMAND; }
   virtual int rename_partitions(const char *path)
   { return HA_ERR_WRONG_COMMAND; }
+  friend class ha_partition;
 };
 
 

=== modified file 'sql/item_subselect.cc'
--- sql/item_subselect.cc	2009-09-15 10:46:35 +0000
+++ sql/item_subselect.cc	2009-10-17 08:13:46 +0000
@@ -2048,7 +2048,7 @@ int subselect_uniquesubquery_engine::sca
   table->null_row= 0;
   for (;;)
   {
-    error=table->file->rnd_next(table->record[0]);
+    error=table->file->ha_rnd_next(table->record[0]);
     if (error && error != HA_ERR_END_OF_FILE)
     {
       error= report_error(table, error);
@@ -2222,10 +2222,11 @@ int subselect_uniquesubquery_engine::exe
  
   if (!table->file->inited)
     table->file->ha_index_init(tab->ref.key, 0);
-  error= table->file->index_read_map(table->record[0],
-                                     tab->ref.key_buff,
-                                     make_prev_keypart_map(tab->ref.key_parts),
-                                     HA_READ_KEY_EXACT);
+  error= table->file->ha_index_read_map(table->record[0],
+                                        tab->ref.key_buff,
+                                        make_prev_keypart_map(tab->
+                                                              ref.key_parts),
+                                        HA_READ_KEY_EXACT);
   if (error &&
       error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
     error= report_error(table, error);
@@ -2343,10 +2344,11 @@ int subselect_indexsubquery_engine::exec
 
   if (!table->file->inited)
     table->file->ha_index_init(tab->ref.key, 1);
-  error= table->file->index_read_map(table->record[0],
-                                     tab->ref.key_buff,
-                                     make_prev_keypart_map(tab->ref.key_parts),
-                                     HA_READ_KEY_EXACT);
+  error= table->file->ha_index_read_map(table->record[0],
+                                        tab->ref.key_buff,
+                                        make_prev_keypart_map(tab->
+                                                              ref.key_parts),
+                                        HA_READ_KEY_EXACT);
   if (error &&
       error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
     error= report_error(table, error);
@@ -2367,9 +2369,9 @@ int subselect_indexsubquery_engine::exec
             ((Item_in_subselect *) item)->value= 1;
           break;
         }
-        error= table->file->index_next_same(table->record[0],
-                                            tab->ref.key_buff,
-                                            tab->ref.key_length);
+        error= table->file->ha_index_next_same(table->record[0],
+                                               tab->ref.key_buff,
+                                               tab->ref.key_length);
         if (error && error != HA_ERR_END_OF_FILE)
         {
           error= report_error(table, error);

=== modified file 'sql/lex.h'
--- sql/lex.h	2009-09-07 20:50:10 +0000
+++ sql/lex.h	2009-10-16 08:46:41 +0000
@@ -106,6 +106,7 @@ static SYMBOL symbols[] = {
   { "CHECKSUM",		SYM(CHECKSUM_SYM)},
   { "CIPHER",		SYM(CIPHER_SYM)},
   { "CLIENT",		SYM(CLIENT_SYM)},
+  { "CLIENT_STATISTICS", SYM(CLIENT_STATS_SYM)},
   { "CLOSE",		SYM(CLOSE_SYM)},
   { "COALESCE",		SYM(COALESCE)},
   { "CODE",             SYM(CODE_SYM)},
@@ -245,6 +246,7 @@ static SYMBOL symbols[] = {
   { "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)},
@@ -478,6 +480,7 @@ static SYMBOL symbols[] = {
   { "SIGNED",		SYM(SIGNED_SYM)},
   { "SIMPLE",		SYM(SIMPLE_SYM)},
   { "SLAVE",            SYM(SLAVE)},
+  { "SLOW",             SYM(SLOW_SYM)},
   { "SNAPSHOT",         SYM(SNAPSHOT_SYM)},
   { "SMALLINT",		SYM(SMALLINT)},
   { "SOCKET",		SYM(SOCKET_SYM)},
@@ -526,6 +529,7 @@ static SYMBOL symbols[] = {
   { "TABLE",		SYM(TABLE_SYM)},
   { "TABLES",		SYM(TABLES)},
   { "TABLESPACE",	        SYM(TABLESPACE)},
+  { "TABLE_STATISTICS",	SYM(TABLE_STATS_SYM)},
   { "TABLE_CHECKSUM",	SYM(TABLE_CHECKSUM_SYM)},
   { "TEMPORARY",	SYM(TEMPORARY)},
   { "TEMPTABLE",	SYM(TEMPTABLE_SYM)},
@@ -569,6 +573,7 @@ static SYMBOL symbols[] = {
   { "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)},

=== modified file 'sql/log.cc'
--- sql/log.cc	2009-09-15 10:46:35 +0000
+++ sql/log.cc	2009-10-17 14:18:58 +0000
@@ -821,6 +821,13 @@ void Log_to_file_event_handler::flush()
     mysql_slow_log.reopen_file();
 }
 
+void Log_to_file_event_handler::flush_slow_log()
+{
+  /* reopen slow log file */
+  if (opt_slow_log)
+    mysql_slow_log.reopen_file();
+}
+
 /*
   Log error with all enabled log event handlers
 
@@ -916,8 +923,6 @@ void LOGGER::init_log_tables()
 
 bool LOGGER::flush_logs(THD *thd)
 {
-  int rc= 0;
-
   /*
     Now we lock logger, as nobody should be able to use logging routines while
     log tables are closed
@@ -929,7 +934,24 @@ bool LOGGER::flush_logs(THD *thd)
 
   /* end of log flush */
   logger.unlock();
-  return rc;
+  return 0;
+}
+
+
+bool LOGGER::flush_slow_log(THD *thd)
+{
+  /*
+    Now we lock logger, as nobody should be able to use logging routines while
+    log tables are closed
+  */
+  logger.lock_exclusive();
+
+  /* reopen log files */
+  file_log_handler->flush_slow_log();
+
+  /* end of log flush */
+  logger.unlock();
+  return 0;
 }
 
 
@@ -4070,6 +4092,7 @@ bool MYSQL_BIN_LOG::write(Log_event *eve
   if (likely(is_open()))
   {
     IO_CACHE *file= &log_file;
+    my_off_t my_org_b_tell;
 #ifdef HAVE_REPLICATION
     /*
       In the future we need to add to the following if tests like
@@ -4077,7 +4100,7 @@ bool MYSQL_BIN_LOG::write(Log_event *eve
       binlog_[wild_]{do|ignore}_table?" (WL#1049)"
     */
     const char *local_db= event_info->get_db();
-    if ((thd && !(thd->options & OPTION_BIN_LOG)) ||
+    if ((!(thd->options & OPTION_BIN_LOG)) ||
 	(!binlog_filter->db_ok(local_db)))
     {
       VOID(pthread_mutex_unlock(&LOCK_log));
@@ -4085,6 +4108,8 @@ bool MYSQL_BIN_LOG::write(Log_event *eve
     }
 #endif /* HAVE_REPLICATION */
 
+    my_org_b_tell= my_b_tell(file);
+
 #if defined(USING_TRANSACTIONS) 
     /*
       Should we write to the binlog cache or to the binlog on disk?
@@ -4095,7 +4120,7 @@ bool MYSQL_BIN_LOG::write(Log_event *eve
      trans/non-trans table types the best possible in binlogging)
       - or if the event asks for it (cache_stmt == TRUE).
     */
-    if (opt_using_transactions && thd)
+    if (opt_using_transactions)
     {
       if (thd->binlog_setup_trx_data())
         goto err;
@@ -4136,7 +4161,6 @@ bool MYSQL_BIN_LOG::write(Log_event *eve
       If row-based binlogging, Insert_id, Rand and other kind of "setting
       context" events are not needed.
     */
-    if (thd)
     {
       if (!thd->current_stmt_binlog_row_based)
       {
@@ -4183,16 +4207,16 @@ bool MYSQL_BIN_LOG::write(Log_event *eve
       }
     }
 
-    /*
-       Write the SQL command
-     */
-
+    /* Write the SQL command */
     if (event_info->write(file) || 
         DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0))
       goto err;
 
     if (file == &log_file) // we are writing to the real log (disk)
     {
+      ulonglong data_written= (my_b_tell(file) - my_org_b_tell);
+      status_var_add(thd->status_var.binlog_bytes_written, data_written);
+
       if (flush_and_sync())
 	goto err;
       signal_update();
@@ -4318,6 +4342,7 @@ uint MYSQL_BIN_LOG::next_file_id()
 
   SYNOPSIS
     write_cache()
+    thd      Current_thread
     cache    Cache to write to the binary log
     lock_log True if the LOCK_log mutex should be aquired, false otherwise
     sync_log True if the log should be flushed and sync:ed
@@ -4327,7 +4352,8 @@ uint MYSQL_BIN_LOG::next_file_id()
     be reset as a READ_CACHE to be able to read the contents from it.
  */
 
-int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
+int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache, bool lock_log,
+                               bool sync_log)
 {
   Mutex_sentry sentry(lock_log ? &LOCK_log : NULL);
 
@@ -4375,6 +4401,7 @@ int MYSQL_BIN_LOG::write_cache(IO_CACHE 
       /* write the first half of the split header */
       if (my_b_write(&log_file, header, carry))
         return ER_ERROR_ON_WRITE;
+      status_var_add(thd->status_var.binlog_bytes_written, carry);
 
       /*
         copy fixed second half of header to cache so the correct
@@ -4443,6 +4470,8 @@ int MYSQL_BIN_LOG::write_cache(IO_CACHE 
     /* Write data to the binary log file */
     if (my_b_write(&log_file, cache->read_pos, length))
       return ER_ERROR_ON_WRITE;
+    status_var_add(thd->status_var.binlog_bytes_written, length);
+
     cache->read_pos=cache->read_end;		// Mark buffer used up
   } while ((length= my_b_fill(cache)));
 
@@ -4494,6 +4523,8 @@ bool MYSQL_BIN_LOG::write_incident(THD *
   if (lock)
     pthread_mutex_lock(&LOCK_log);
   ev.write(&log_file);
+  status_var_add(thd->status_var.binlog_bytes_written, ev.data_written);
+
   if (lock)
   {
     if (!error && !(error= flush_and_sync()))
@@ -4565,21 +4596,28 @@ bool MYSQL_BIN_LOG::write(THD *thd, IO_C
       */
       if (qinfo.write(&log_file))
         goto err;
+      status_var_add(thd->status_var.binlog_bytes_written, qinfo.data_written);
 
       DBUG_EXECUTE_IF("crash_before_writing_xid",
                       {
-                        if ((write_error= write_cache(cache, false, true)))
+                        if ((write_error= write_cache(thd, cache, FALSE,
+                                                      TRUE)))
                           DBUG_PRINT("info", ("error writing binlog cache: %d",
                                                write_error));
                         DBUG_PRINT("info", ("crashing before writing xid"));
                         abort();
                       });
 
-      if ((write_error= write_cache(cache, false, false)))
+      if ((write_error= write_cache(thd, cache, FALSE, FALSE)))
         goto err;
 
-      if (commit_event && commit_event->write(&log_file))
-        goto err;
+      if (commit_event)
+      {
+        if (commit_event->write(&log_file))
+          goto err;
+        status_var_add(thd->status_var.binlog_bytes_written,
+                       commit_event->data_written);
+      }
 
       if (incident && write_incident(thd, FALSE))
         goto err;

=== modified file 'sql/log.h'
--- sql/log.h	2009-06-18 13:52:46 +0000
+++ sql/log.h	2009-10-17 14:15:43 +0000
@@ -359,7 +359,8 @@ public:
   bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event, bool incident);
   bool write_incident(THD *thd, bool lock);
 
-  int  write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync);
+  int  write_cache(THD *thd, IO_CACHE *cache, bool lock_log,
+                   bool flush_and_sync);
   void set_write_error(THD *thd);
   bool check_write_error(THD *thd);
 
@@ -487,6 +488,7 @@ public:
                            const char *sql_text, uint sql_text_len,
                            CHARSET_INFO *client_cs);
   void flush();
+  void flush_slow_log();
   void init_pthread_objects();
   MYSQL_QUERY_LOG *get_mysql_slow_log() { return &mysql_slow_log; }
   MYSQL_QUERY_LOG *get_mysql_log() { return &mysql_log; }
@@ -531,6 +533,7 @@ public:
   void init_base();
   void init_log_tables();
   bool flush_logs(THD *thd);
+  bool flush_slow_log(THD *thd);
   /* Perform basic logger cleanup. this will leave e.g. error log open. */
   void cleanup_base();
   /* Free memory. Nothing could be logged after this function is called */

=== modified file 'sql/log_event.cc'
--- sql/log_event.cc	2009-09-07 20:50:10 +0000
+++ sql/log_event.cc	2009-10-18 14:27:40 +0000
@@ -4465,7 +4465,7 @@ int Load_log_event::do_apply_event(NET* 
     as the present method does not call mysql_parse().
   */
   lex_start(thd);
-  mysql_reset_thd_for_next_command(thd);
+  mysql_reset_thd_for_next_command(thd, 0);
 
   if (!use_rli_only_for_errors)
   {
@@ -6262,7 +6262,7 @@ int Append_block_log_event::do_apply_eve
       as the present method does not call mysql_parse().
     */
     lex_start(thd);
-    mysql_reset_thd_for_next_command(thd);
+    mysql_reset_thd_for_next_command(thd, 0);
     my_delete(fname, MYF(0)); // old copy may exist already
     if ((fd= my_create(fname, CREATE_MODE,
 		       O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
@@ -7202,7 +7202,7 @@ int Rows_log_event::do_apply_event(Relay
       we need to do any changes to that value after this function.
     */
     lex_start(thd);
-    mysql_reset_thd_for_next_command(thd);
+    mysql_reset_thd_for_next_command(thd, 0);
     /*
       The current statement is just about to begin and 
       has not yet modified anything. Note, all.modified is reset
@@ -8465,7 +8465,7 @@ Rows_log_event::write_row(const Relay_lo
     if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
     {
       DBUG_PRINT("info",("Locating offending record using rnd_pos()"));
-      error= table->file->rnd_pos(table->record[1], table->file->dup_ref);
+      error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref);
       if (error)
       {
         DBUG_PRINT("info",("rnd_pos() returns error %d",error));
@@ -8497,10 +8497,10 @@ Rows_log_event::write_row(const Relay_lo
 
       key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum,
                0);
-      error= table->file->index_read_idx_map(table->record[1], keynum,
-                                             (const uchar*)key.get(),
-                                             HA_WHOLE_KEY,
-                                             HA_READ_KEY_EXACT);
+      error= table->file->ha_index_read_idx_map(table->record[1], keynum,
+                                                (const uchar*)key.get(),
+                                                HA_WHOLE_KEY,
+                                                HA_READ_KEY_EXACT);
       if (error)
       {
         DBUG_PRINT("info",("index_read_idx() returns %s", HA_ERR(error)));
@@ -8768,13 +8768,14 @@ int Rows_log_event::find_row(const Relay
       length. Something along these lines should work:
 
       ADD>>>  store_record(table,record[1]);
-              int error= table->file->rnd_pos(table->record[0], table->file->ref);
+              int error= table->file->ha_rnd_pos(table->record[0],
+              table->file->ref);
       ADD>>>  DBUG_ASSERT(memcmp(table->record[1], table->record[0],
                                  table->s->reclength) == 0);
 
     */
     DBUG_PRINT("info",("locating record using primary key (position)"));
-    int error= table->file->rnd_pos_by_record(table->record[0]);
+    int error= table->file->ha_rnd_pos_by_record(table->record[0]);
     if (error)
     {
       DBUG_PRINT("info",("rnd_pos returns error %d",error));
@@ -8834,9 +8835,9 @@ int Rows_log_event::find_row(const Relay
       table->record[0][table->s->null_bytes - 1]|=
         256U - (1U << table->s->last_null_bit_pos);
 
-    if ((error= table->file->index_read_map(table->record[0], m_key, 
-                                            HA_WHOLE_KEY,
-                                            HA_READ_KEY_EXACT)))
+    if ((error= table->file->ha_index_read_map(table->record[0], m_key, 
+                                               HA_WHOLE_KEY,
+                                               HA_READ_KEY_EXACT)))
     {
       DBUG_PRINT("info",("no record matching the key found in the table"));
       if (error == HA_ERR_RECORD_DELETED)
@@ -8898,7 +8899,7 @@ int Rows_log_event::find_row(const Relay
           256U - (1U << table->s->last_null_bit_pos);
       }
 
-      while ((error= table->file->index_next(table->record[0])))
+      while ((error= table->file->ha_index_next(table->record[0])))
       {
         /* We just skip records that has already been deleted */
         if (error == HA_ERR_RECORD_DELETED)
@@ -8934,7 +8935,7 @@ int Rows_log_event::find_row(const Relay
     do
     {
   restart_rnd_next:
-      error= table->file->rnd_next(table->record[0]);
+      error= table->file->ha_rnd_next(table->record[0]);
 
       DBUG_PRINT("info", ("error: %s", HA_ERR(error)));
       switch (error) {

=== modified file 'sql/log_event_old.cc'
--- sql/log_event_old.cc	2009-05-19 09:28:05 +0000
+++ sql/log_event_old.cc	2009-10-18 14:27:39 +0000
@@ -63,7 +63,7 @@ Old_rows_log_event::do_apply_event(Old_r
       we need to do any changes to that value after this function.
     */
     lex_start(thd);
-    mysql_reset_thd_for_next_command(thd);
+    mysql_reset_thd_for_next_command(thd, 0);
 
     /*
       Check if the slave is set to use SBR.  If so, it should switch
@@ -553,7 +553,7 @@ replace_record(THD *thd, TABLE *table,
      */
     if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
     {
-      error= table->file->rnd_pos(table->record[1], table->file->dup_ref);
+      error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref);
       if (error)
       {
         DBUG_PRINT("info",("rnd_pos() returns error %d",error));
@@ -579,10 +579,10 @@ replace_record(THD *thd, TABLE *table,
 
       key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum,
                0);
-      error= table->file->index_read_idx_map(table->record[1], keynum,
-                                             (const uchar*)key.get(),
-                                             HA_WHOLE_KEY,
-                                             HA_READ_KEY_EXACT);
+      error= table->file->ha_index_read_idx_map(table->record[1], keynum,
+                                                (const uchar*)key.get(),
+                                                HA_WHOLE_KEY,
+                                                HA_READ_KEY_EXACT);
       if (error)
       {
         DBUG_PRINT("info", ("index_read_idx() returns error %d", error));
@@ -694,13 +694,13 @@ static int find_and_fetch_row(TABLE *tab
       length. Something along these lines should work:
 
       ADD>>>  store_record(table,record[1]);
-              int error= table->file->rnd_pos(table->record[0], table->file->ref);
+              int error= table->file->ha_rnd_pos(table->record[0], table->file->ref);
       ADD>>>  DBUG_ASSERT(memcmp(table->record[1], table->record[0],
                                  table->s->reclength) == 0);
 
     */
     table->file->position(table->record[0]);
-    int error= table->file->rnd_pos(table->record[0], table->file->ref);
+    int error= table->file->ha_rnd_pos(table->record[0], table->file->ref);
     /*
       rnd_pos() returns the record in table->record[0], so we have to
       move it to table->record[1].
@@ -738,8 +738,9 @@ static int find_and_fetch_row(TABLE *tab
     my_ptrdiff_t const pos=
       table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0;
     table->record[1][pos]= 0xFF;
-    if ((error= table->file->index_read_map(table->record[1], key, HA_WHOLE_KEY,
-                                            HA_READ_KEY_EXACT)))
+    if ((error= table->file->ha_index_read_map(table->record[1], key,
+                                               HA_WHOLE_KEY,
+                                               HA_READ_KEY_EXACT)))
     {
       table->file->print_error(error, MYF(0));
       table->file->ha_index_end();
@@ -793,7 +794,7 @@ static int find_and_fetch_row(TABLE *tab
           256U - (1U << table->s->last_null_bit_pos);
       }
 
-      while ((error= table->file->index_next(table->record[1])))
+      while ((error= table->file->ha_index_next(table->record[1])))
       {
         /* We just skip records that has already been deleted */
         if (error == HA_ERR_RECORD_DELETED)
@@ -822,7 +823,7 @@ static int find_and_fetch_row(TABLE *tab
     do
     {
   restart_rnd_next:
-      error= table->file->rnd_next(table->record[1]);
+      error= table->file->ha_rnd_next(table->record[1]);
 
       DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
       DBUG_DUMP("record[1]", table->record[1], table->s->reclength);
@@ -2115,7 +2116,7 @@ Old_rows_log_event::write_row(const Rela
     if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
     {
       DBUG_PRINT("info",("Locating offending record using rnd_pos()"));
-      error= table->file->rnd_pos(table->record[1], table->file->dup_ref);
+      error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref);
       if (error)
       {
         DBUG_PRINT("info",("rnd_pos() returns error %d",error));
@@ -2147,10 +2148,10 @@ Old_rows_log_event::write_row(const Rela
 
       key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum,
                0);
-      error= table->file->index_read_idx_map(table->record[1], keynum,
-                                             (const uchar*)key.get(),
-                                             HA_WHOLE_KEY,
-                                             HA_READ_KEY_EXACT);
+      error= table->file->ha_index_read_idx_map(table->record[1], keynum,
+                                                (const uchar*)key.get(),
+                                                HA_WHOLE_KEY,
+                                                HA_READ_KEY_EXACT);
       if (error)
       {
         DBUG_PRINT("info",("index_read_idx() returns error %d", error));
@@ -2301,13 +2302,13 @@ int Old_rows_log_event::find_row(const R
       length. Something along these lines should work:
 
       ADD>>>  store_record(table,record[1]);
-              int error= table->file->rnd_pos(table->record[0], table->file->ref);
+              int error= table->file->ha_rnd_pos(table->record[0], table->file->ref);
       ADD>>>  DBUG_ASSERT(memcmp(table->record[1], table->record[0],
                                  table->s->reclength) == 0);
 
     */
     DBUG_PRINT("info",("locating record using primary key (position)"));
-    int error= table->file->rnd_pos_by_record(table->record[0]);
+    int error= table->file->ha_rnd_pos_by_record(table->record[0]);
     if (error)
     {
       DBUG_PRINT("info",("rnd_pos returns error %d",error));
@@ -2367,9 +2368,9 @@ int Old_rows_log_event::find_row(const R
       table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0;
     table->record[0][pos]= 0xFF;
     
-    if ((error= table->file->index_read_map(table->record[0], m_key, 
-                                            HA_WHOLE_KEY,
-                                            HA_READ_KEY_EXACT)))
+    if ((error= table->file->ha_index_read_map(table->record[0], m_key, 
+                                               HA_WHOLE_KEY,
+                                               HA_READ_KEY_EXACT)))
     {
       DBUG_PRINT("info",("no record matching the key found in the table"));
       if (error == HA_ERR_RECORD_DELETED)
@@ -2431,7 +2432,7 @@ int Old_rows_log_event::find_row(const R
           256U - (1U << table->s->last_null_bit_pos);
       }
 
-      while ((error= table->file->index_next(table->record[0])))
+      while ((error= table->file->ha_index_next(table->record[0])))
       {
         /* We just skip records that has already been deleted */
         if (error == HA_ERR_RECORD_DELETED)
@@ -2467,7 +2468,7 @@ int Old_rows_log_event::find_row(const R
     do
     {
   restart_rnd_next:
-      error= table->file->rnd_next(table->record[0]);
+      error= table->file->ha_rnd_next(table->record[0]);
 
       switch (error) {
 

=== modified file 'sql/mysql_priv.h'
--- sql/mysql_priv.h	2009-10-06 14:53:46 +0000
+++ sql/mysql_priv.h	2009-10-18 12:46:24 +0000
@@ -1063,6 +1063,7 @@ bool setup_connection_thread_globals(THD
 bool login_connection(THD *thd);
 void end_connection(THD *thd);
 void prepare_new_connection_state(THD* thd);
+void update_global_user_stats(THD* thd, bool create_user, time_t now);
 
 int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent);
 bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create);
@@ -1099,14 +1100,22 @@ bool is_update_query(enum enum_sql_comma
 bool is_log_table_write_query(enum enum_sql_command command);
 bool alloc_query(THD *thd, const char *packet, uint packet_length);
 void mysql_init_select(LEX *lex);
-void mysql_reset_thd_for_next_command(THD *thd);
+void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat);
 bool mysql_new_select(LEX *lex, bool move_down);
 void create_select_for_variable(const char *var_name);
 void mysql_init_multi_delete(LEX *lex);
 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 init_global_client_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);
+void free_global_client_stats(void);
 pthread_handler_t handle_bootstrap(void *arg);
 int mysql_execute_command(THD *thd);
 bool do_command(THD *thd);
@@ -1967,6 +1976,7 @@ extern ulong max_connect_errors, connect
 extern ulong extra_max_connections;
 extern ulong slave_net_timeout, slave_trans_retries;
 extern uint max_user_connections;
+extern ulonglong denied_connections;
 extern ulong what_to_log,flush_time;
 extern ulong query_buff_size;
 extern ulong max_prepared_stmt_count, prepared_stmt_count;
@@ -2020,6 +2030,7 @@ extern my_bool opt_safe_show_db, opt_loc
 extern my_bool opt_slave_compressed_protocol, use_temp_pool;
 extern ulong slave_exec_mode_options;
 extern my_bool opt_readonly, lower_case_file_system;
+extern my_bool opt_userstat_running;
 extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
 extern my_bool opt_secure_auth;
 extern char* opt_secure_file_priv;
@@ -2060,6 +2071,11 @@ extern pthread_mutex_t LOCK_des_key_file
 #endif
 extern pthread_mutex_t LOCK_server_started;
 extern pthread_cond_t COND_server_started;
+extern pthread_mutex_t LOCK_global_user_client_stats;
+extern pthread_mutex_t LOCK_global_table_stats;
+extern pthread_mutex_t LOCK_global_index_stats;
+extern pthread_mutex_t LOCK_stats;
+
 extern int mysqld_server_started;
 extern rw_lock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
 extern rw_lock_t LOCK_system_variables_hash;
@@ -2086,6 +2102,11 @@ extern KNOWN_DATE_TIME_FORMAT known_date
 
 extern String null_string;
 extern HASH open_cache, lock_db_cache;
+extern HASH global_user_stats;
+extern HASH global_client_stats;
+extern HASH global_table_stats;
+extern HASH global_index_stats;
+
 extern TABLE *unused_tables;
 extern const char* any_db;
 extern struct my_option my_long_options[];

=== modified file 'sql/mysqld.cc'
--- sql/mysqld.cc	2009-10-07 13:07:10 +0000
+++ sql/mysqld.cc	2009-10-18 18:48:55 +0000
@@ -416,6 +416,7 @@ static pthread_cond_t COND_thread_cache,
 
 bool opt_update_log, opt_bin_log, opt_ignore_builtin_innodb= 0;
 my_bool opt_log, opt_slow_log;
+my_bool opt_userstat_running;
 ulong log_output_options;
 my_bool opt_log_queries_not_using_indexes= 0;
 bool opt_error_log= IF_WIN(1,0);
@@ -548,6 +549,7 @@ ulong binlog_cache_use= 0, binlog_cache_
 ulong max_connections, max_connect_errors;
 ulong extra_max_connections;
 uint  max_user_connections= 0;
+ulonglong denied_connections;
 /**
   Limit of the total number of prepared statements in the server.
   Is necessary to protect the server against out-of-memory attacks.
@@ -649,6 +651,9 @@ pthread_mutex_t LOCK_mysql_create_db, LO
 	        LOCK_global_system_variables,
                 LOCK_user_conn, LOCK_slave_list, LOCK_active_mi,
                 LOCK_connection_count, LOCK_uuid_generator;
+pthread_mutex_t LOCK_stats, LOCK_global_user_client_stats;
+pthread_mutex_t LOCK_global_table_stats, LOCK_global_index_stats;
+
 /**
   The below lock protects access to two global server variables:
   max_prepared_stmt_count and prepared_stmt_count. These variables
@@ -1342,6 +1347,10 @@ void clean_up(bool print_message)
   x_free(opt_secure_file_priv);
   bitmap_free(&temp_pool);
   free_max_user_conn();
+  free_global_user_stats();
+  free_global_client_stats();
+  free_global_table_stats();
+  free_global_index_stats();
 #ifdef HAVE_REPLICATION
   end_slave_list();
 #endif
@@ -1428,6 +1437,11 @@ static void clean_up_mutexes()
   (void) pthread_mutex_destroy(&LOCK_bytes_received);
   (void) pthread_mutex_destroy(&LOCK_user_conn);
   (void) pthread_mutex_destroy(&LOCK_connection_count);
+  (void) pthread_mutex_destroy(&LOCK_stats);
+  (void) pthread_mutex_destroy(&LOCK_global_user_client_stats);
+  (void) pthread_mutex_destroy(&LOCK_global_table_stats);
+  (void) pthread_mutex_destroy(&LOCK_global_index_stats);
+
   Events::destroy_mutexes();
 #ifdef HAVE_OPENSSL
   (void) pthread_mutex_destroy(&LOCK_des_key_file);
@@ -3203,6 +3217,7 @@ SHOW_VAR com_status_vars[]= {
   {"show_binlog_events",   (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOG_EVENTS]), SHOW_LONG_STATUS},
   {"show_binlogs",         (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOGS]), SHOW_LONG_STATUS},
   {"show_charsets",        (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CHARSETS]), SHOW_LONG_STATUS},
+  {"show_client_stats",    (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CLIENT_STATS]), SHOW_LONG_STATUS},
   {"show_collations",      (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLLATIONS]), SHOW_LONG_STATUS},
   {"show_column_types",    (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLUMN_TYPES]), SHOW_LONG_STATUS},
   {"show_contributors",    (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CONTRIBUTORS]), SHOW_LONG_STATUS},
@@ -3225,6 +3240,7 @@ SHOW_VAR com_status_vars[]= {
   {"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},
@@ -3241,9 +3257,11 @@ SHOW_VAR com_status_vars[]= {
   {"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},
@@ -3642,6 +3660,12 @@ static int init_thread_environment()
   (void) pthread_mutex_init(&LOCK_prepared_stmt_count, MY_MUTEX_INIT_FAST);
   (void) pthread_mutex_init(&LOCK_uuid_generator, MY_MUTEX_INIT_FAST);
   (void) pthread_mutex_init(&LOCK_connection_count, MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_stats, MY_MUTEX_INIT_FAST);
+  (void) pthread_mutex_init(&LOCK_global_user_client_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);
+
 #ifdef HAVE_OPENSSL
   (void) pthread_mutex_init(&LOCK_des_key_file,MY_MUTEX_INIT_FAST);
 #ifndef HAVE_YASSL
@@ -4005,6 +4029,9 @@ server.");
   /* call ha_init_key_cache() on all key caches to init them */
   process_key_caches(&ha_init_key_cache);
 
+  init_global_table_stats();
+  init_global_index_stats();
+
   /* Allow storage engine to give real error messages */
   if (ha_init_errors())
     DBUG_RETURN(1);
@@ -4210,6 +4237,8 @@ server.");
 
   init_max_user_conn();
   init_update_queries();
+  init_global_user_stats();
+  init_global_client_stats();
   DBUG_RETURN(0);
 }
 
@@ -5019,6 +5048,7 @@ static void create_new_thread(THD *thd)
 
     DBUG_PRINT("error",("Too many connections"));
     close_connection(thd, ER_CON_COUNT_ERROR, 1);
+    statistic_increment(denied_connections, &LOCK_status);
     delete thd;
     DBUG_VOID_RETURN;
   }
@@ -5810,6 +5840,7 @@ enum options_mysqld
   OPT_LOG_SLOW_RATE_LIMIT, 
   OPT_LOG_SLOW_VERBOSITY, 
   OPT_LOG_SLOW_FILTER, 
+  OPT_USERSTAT,
   OPT_GENERAL_LOG_FILE,
   OPT_SLOW_QUERY_LOG_FILE,
   OPT_IGNORE_BUILTIN_INNODB
@@ -7209,6 +7240,10 @@ The minimum value for this variable is 4
    (uchar**) &max_system_variables.net_wait_timeout, 0, GET_ULONG,
    REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT),
    0, 1, 0},
+  {"userstat", OPT_USERSTAT,
+   "Control USER_STATISTICS, CLIENT_STATISTICS, INDEX_STATISTICS and TABLE_STATISTICS running",
+   (uchar**) &opt_userstat_running, (uchar**) &opt_userstat_running,
+   0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 1, 0},
   {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
 };
 
@@ -7579,19 +7614,24 @@ static int show_ssl_get_cipher_list(THD 
 SHOW_VAR status_vars[]= {
   {"Aborted_clients",          (char*) &aborted_threads,        SHOW_LONG},
   {"Aborted_connects",         (char*) &aborted_connects,       SHOW_LONG},
+  {"Access_denied_errors",     (char*) offsetof(STATUS_VAR, access_denied_errors), SHOW_LONG_STATUS},
   {"Binlog_cache_disk_use",    (char*) &binlog_cache_disk_use,  SHOW_LONG},
   {"Binlog_cache_use",         (char*) &binlog_cache_use,       SHOW_LONG},
+  {"Busy_time",                (char*) offsetof(STATUS_VAR, busy_time), SHOW_DOUBLE_STATUS},
   {"Bytes_received",           (char*) offsetof(STATUS_VAR, bytes_received), SHOW_LONGLONG_STATUS},
   {"Bytes_sent",               (char*) offsetof(STATUS_VAR, bytes_sent), SHOW_LONGLONG_STATUS},
+  {"Binlog_bytes_written",     (char*) offsetof(STATUS_VAR, binlog_bytes_written), SHOW_LONGLONG_STATUS},
   {"Com",                      (char*) com_status_vars, SHOW_ARRAY},
   {"Compression",              (char*) &show_net_compression, SHOW_FUNC},
   {"Connections",              (char*) &thread_id,              SHOW_LONG_NOFLUSH},
+  {"Cpu_time",                 (char*) offsetof(STATUS_VAR, cpu_time), SHOW_DOUBLE_STATUS},
   {"Created_tmp_disk_tables",  (char*) offsetof(STATUS_VAR, created_tmp_disk_tables), SHOW_LONG_STATUS},
   {"Created_tmp_files",	       (char*) &my_tmp_file_created,	SHOW_LONG},
   {"Created_tmp_tables",       (char*) offsetof(STATUS_VAR, created_tmp_tables), SHOW_LONG_STATUS},
   {"Delayed_errors",           (char*) &delayed_insert_errors,  SHOW_LONG},
   {"Delayed_insert_threads",   (char*) &delayed_insert_threads, SHOW_LONG_NOFLUSH},
   {"Delayed_writes",           (char*) &delayed_insert_writes,  SHOW_LONG},
+  {"Empty_queries",            (char*) offsetof(STATUS_VAR, empty_queries), SHOW_LONG_STATUS},
   {"Flush_commands",           (char*) &refresh_version,        SHOW_LONG_NOFLUSH},
   {"Handler_commit",           (char*) offsetof(STATUS_VAR, ha_commit_count), SHOW_LONG_STATUS},
   {"Handler_delete",           (char*) offsetof(STATUS_VAR, ha_delete_count), SHOW_LONG_STATUS},
@@ -7626,6 +7666,8 @@ SHOW_VAR status_vars[]= {
   {"Opened_tables",            (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS},
   {"Opened_table_definitions", (char*) offsetof(STATUS_VAR, opened_shares), SHOW_LONG_STATUS},
   {"Prepared_stmt_count",      (char*) &show_prepared_stmt_count, SHOW_FUNC},
+  {"Rows_sent",                (char*) offsetof(STATUS_VAR, rows_sent), SHOW_LONG_STATUS},
+  {"Rows_read",                (char*) offsetof(STATUS_VAR, rows_read), SHOW_LONG_STATUS},
 #ifdef HAVE_QUERY_CACHE
   {"Qcache_free_blocks",       (char*) &query_cache.free_memory_blocks, SHOW_LONG_NOFLUSH},
   {"Qcache_free_memory",       (char*) &query_cache.free_memory, SHOW_LONG_NOFLUSH},
@@ -9110,6 +9152,8 @@ void refresh_status(THD *thd)
 
   /* Reset thread's status variables */
   bzero((uchar*) &thd->status_var, sizeof(thd->status_var));
+  bzero((uchar*) &thd->org_status_var, sizeof(thd->org_status_var)); 
+  thd->start_bytes_received= 0;
 
   /* Reset some global variables */
   reset_status_vars();

=== modified file 'sql/opt_range.cc'
--- sql/opt_range.cc	2009-09-09 21:59:28 +0000
+++ sql/opt_range.cc	2009-10-17 08:13:46 +0000
@@ -8230,7 +8230,7 @@ int QUICK_ROR_INTERSECT_SELECT::get_next
 
     /* We get here if we got the same row ref in all scans. */
     if (need_to_fetch_row)
-      error= head->file->rnd_pos(head->record[0], last_rowid);
+      error= head->file->ha_rnd_pos(head->record[0], last_rowid);
   } while (error == HA_ERR_RECORD_DELETED);
   DBUG_RETURN(error);
 }
@@ -8296,7 +8296,7 @@ int QUICK_ROR_UNION_SELECT::get_next()
     cur_rowid= prev_rowid;
     prev_rowid= tmp;
 
-    error= head->file->rnd_pos(quick->record, prev_rowid);
+    error= head->file->ha_rnd_pos(quick->record, prev_rowid);
   } while (error == HA_ERR_RECORD_DELETED);
   DBUG_RETURN(error);
 }
@@ -8521,10 +8521,12 @@ int QUICK_RANGE_SELECT::get_next_prefix(
     key_range start_key, end_key;
     if (last_range)
     {
-      /* Read the next record in the same range with prefix after cur_prefix. */
+      /*
+        Read the next record in the same range with prefix after cur_prefix.
+       */
       DBUG_ASSERT(cur_prefix != 0);
-      result= file->index_read_map(record, cur_prefix, keypart_map,
-                                   HA_READ_AFTER_KEY);
+      result= file->ha_index_read_map(record, cur_prefix, keypart_map,
+                                      HA_READ_AFTER_KEY);
       if (result || (file->compare_key(file->end_range) <= 0))
         DBUG_RETURN(result);
     }
@@ -8580,8 +8582,8 @@ int QUICK_RANGE_SELECT_GEOM::get_next()
     if (last_range)
     {
       // Already read through key
-      result= file->index_next_same(record, last_range->min_key,
-				    last_range->min_length);
+      result= file->ha_index_next_same(record, last_range->min_key,
+                                       last_range->min_length);
       if (result != HA_ERR_END_OF_FILE)
 	DBUG_RETURN(result);
     }
@@ -8595,10 +8597,10 @@ int QUICK_RANGE_SELECT_GEOM::get_next()
     }
     last_range= *(cur_range++);
 
-    result= file->index_read_map(record, last_range->min_key,
-                                 last_range->min_keypart_map,
-                                 (ha_rkey_function)(last_range->flag ^
-                                                    GEOM_FLAG));
+    result= file->ha_index_read_map(record, last_range->min_key,
+                                    last_range->min_keypart_map,
+                                    (ha_rkey_function)(last_range->flag ^
+                                                       GEOM_FLAG));
     if (result != HA_ERR_KEY_NOT_FOUND && result != HA_ERR_END_OF_FILE)
       DBUG_RETURN(result);
     last_range= 0;				// Not found, to next range
@@ -8710,9 +8712,9 @@ int QUICK_SELECT_DESC::get_next()
     {						// Already read through key
       result = ((last_range->flag & EQ_RANGE && 
                  used_key_parts <= head->key_info[index].key_parts) ? 
-                file->index_next_same(record, last_range->min_key,
+                file->ha_index_next_same(record, last_range->min_key,
                                       last_range->min_length) :
-                file->index_prev(record));
+                file->ha_index_prev(record));
       if (!result)
       {
 	if (cmp_prev(*rev_it.ref()) == 0)
@@ -8728,7 +8730,7 @@ int QUICK_SELECT_DESC::get_next()
     if (last_range->flag & NO_MAX_RANGE)        // Read last record
     {
       int local_error;
-      if ((local_error=file->index_last(record)))
+      if ((local_error=file->ha_index_last(record)))
 	DBUG_RETURN(local_error);		// Empty table
       if (cmp_prev(last_range) == 0)
 	DBUG_RETURN(0);
@@ -8740,9 +8742,9 @@ int QUICK_SELECT_DESC::get_next()
         used_key_parts <= head->key_info[index].key_parts)
 
     {
-      result = file->index_read_map(record, last_range->max_key,
-                                    last_range->max_keypart_map,
-                                    HA_READ_KEY_EXACT);
+      result= file->ha_index_read_map(record, last_range->max_key,
+                                      last_range->max_keypart_map,
+                                      HA_READ_KEY_EXACT);
     }
     else
     {
@@ -8750,11 +8752,11 @@ int QUICK_SELECT_DESC::get_next()
                   (last_range->flag & EQ_RANGE && 
                    used_key_parts > head->key_info[index].key_parts) ||
                   range_reads_after_key(last_range));
-      result=file->index_read_map(record, last_range->max_key,
-                                  last_range->max_keypart_map,
-                                  ((last_range->flag & NEAR_MAX) ?
-                                   HA_READ_BEFORE_KEY :
-                                   HA_READ_PREFIX_LAST_OR_PREV));
+      result= file->ha_index_read_map(record, last_range->max_key,
+                                      last_range->max_keypart_map,
+                                      ((last_range->flag & NEAR_MAX) ?
+                                       HA_READ_BEFORE_KEY :
+                                       HA_READ_PREFIX_LAST_OR_PREV));
     }
     if (result)
     {
@@ -10467,7 +10469,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::reset(vo
     DBUG_RETURN(result);
   if (quick_prefix_select && quick_prefix_select->reset())
     DBUG_RETURN(1);
-  result= file->index_last(record);
+  result= file->ha_index_last(record);
   if (result == HA_ERR_END_OF_FILE)
     DBUG_RETURN(0);
   /* Save the prefix of the last group. */
@@ -10569,9 +10571,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::get_next
       first sub-group with the extended prefix.
     */
     if (!have_min && !have_max && key_infix_len > 0)
-      result= file->index_read_map(record, group_prefix,
-                                   make_prev_keypart_map(real_key_parts),
-                                   HA_READ_KEY_EXACT);
+      result= file->ha_index_read_map(record, group_prefix,
+                                      make_prev_keypart_map(real_key_parts),
+                                      HA_READ_KEY_EXACT);
 
     result= have_min ? min_res : have_max ? max_res : result;
   } while ((result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE) &&
@@ -10633,9 +10635,10 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min
     /* Apply the constant equality conditions to the non-group select fields */
     if (key_infix_len > 0)
     {
-      if ((result= file->index_read_map(record, group_prefix,
-                                        make_prev_keypart_map(real_key_parts),
-                                        HA_READ_KEY_EXACT)))
+      if ((result=
+           file->ha_index_read_map(record, group_prefix,
+                                   make_prev_keypart_map(real_key_parts),
+                                   HA_READ_KEY_EXACT)))
         DBUG_RETURN(result);
     }
 
@@ -10650,9 +10653,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min
     {
       /* Find the first subsequent record without NULL in the MIN/MAX field. */
       key_copy(tmp_record, record, index_info, 0);
-      result= file->index_read_map(record, tmp_record,
-                                   make_keypart_map(real_key_parts),
-                                   HA_READ_AFTER_KEY);
+      result= file->ha_index_read_map(record, tmp_record,
+                                      make_keypart_map(real_key_parts),
+                                      HA_READ_AFTER_KEY);
       /*
         Check if the new record belongs to the current group by comparing its
         prefix with the group's prefix. If it is from the next group, then the
@@ -10707,9 +10710,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max
   if (min_max_ranges.elements > 0)
     result= next_max_in_range();
   else
-    result= file->index_read_map(record, group_prefix,
-                                 make_prev_keypart_map(real_key_parts),
-                                 HA_READ_PREFIX_LAST);
+    result= file->ha_index_read_map(record, group_prefix,
+                                    make_prev_keypart_map(real_key_parts),
+                                    HA_READ_PREFIX_LAST);
   DBUG_RETURN(result);
 }
 
@@ -10752,7 +10755,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_pre
   {
     if (!seen_first_key)
     {
-      result= file->index_first(record);
+      result= file->ha_index_first(record);
       if (result)
         DBUG_RETURN(result);
       seen_first_key= TRUE;
@@ -10760,9 +10763,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_pre
     else
     {
       /* Load the first key in this group into record. */
-      result= file->index_read_map(record, group_prefix,
-                                   make_prev_keypart_map(group_key_parts),
-                                   HA_READ_AFTER_KEY);
+      result= file->ha_index_read_map(record, group_prefix,
+                                      make_prev_keypart_map(group_key_parts),
+                                      HA_READ_AFTER_KEY);
       if (result)
         DBUG_RETURN(result);
     }
@@ -10839,7 +10842,8 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min
                  HA_READ_AFTER_KEY : HA_READ_KEY_OR_NEXT;
     }
 
-    result= file->index_read_map(record, group_prefix, keypart_map, find_flag);
+    result= file->ha_index_read_map(record, group_prefix, keypart_map,
+                                    find_flag);
     if (result)
     {
       if ((result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE) &&
@@ -10978,7 +10982,8 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max
                  HA_READ_BEFORE_KEY : HA_READ_PREFIX_LAST_OR_PREV;
     }
 
-    result= file->index_read_map(record, group_prefix, keypart_map, find_flag);
+    result= file->ha_index_read_map(record, group_prefix, keypart_map,
+                                    find_flag);
 
     if (result)
     {

=== modified file 'sql/opt_range.h'
--- sql/opt_range.h	2009-09-02 08:40:18 +0000
+++ sql/opt_range.h	2009-10-17 14:09:21 +0000
@@ -727,7 +727,7 @@ public:
   ~FT_SELECT() { file->ft_end(); }
   int init() { return error=file->ft_init(); }
   int reset() { return 0; }
-  int get_next() { return error=file->ft_read(record); }
+  int get_next() { return error= file->ha_ft_read(record); }
   int get_type() { return QS_TYPE_FULLTEXT; }
 };
 

=== modified file 'sql/opt_sum.cc'
--- sql/opt_sum.cc	2009-09-07 20:50:10 +0000
+++ sql/opt_sum.cc	2009-10-17 08:09:24 +0000
@@ -254,7 +254,7 @@ int opt_sum_query(TABLE_LIST *tables, Li
           error= table->file->ha_index_init((uint) ref.key, 1);
 
           if (!ref.key_length)
-            error= table->file->index_first(table->record[0]);
+            error= table->file->ha_index_first(table->record[0]);
           else 
           {
             /*
@@ -276,10 +276,10 @@ int opt_sum_query(TABLE_LIST *tables, Li
                  Closed interval: Either The MIN argument is non-nullable, or
                  we have a >= predicate for the MIN argument.
               */
-              error= table->file->index_read_map(table->record[0],
-                                                 ref.key_buff,
-                                                 make_prev_keypart_map(ref.key_parts),
-                                                 HA_READ_KEY_OR_NEXT);
+              error= table->file->ha_index_read_map(table->record[0],
+                                                    ref.key_buff,
+                                                    make_prev_keypart_map(ref.key_parts),
+                                                    HA_READ_KEY_OR_NEXT);
             else
             {
               /*
@@ -288,10 +288,10 @@ int opt_sum_query(TABLE_LIST *tables, Li
                 2) there is a > predicate on it, nullability is irrelevant.
                 We need to scan the next bigger record first.
               */
-              error= table->file->index_read_map(table->record[0],
-                                                 ref.key_buff, 
-                                                 make_prev_keypart_map(ref.key_parts),
-                                                 HA_READ_AFTER_KEY);
+              error= table->file->ha_index_read_map(table->record[0],
+                                                    ref.key_buff, 
+                                                    make_prev_keypart_map(ref.key_parts),
+                                                    HA_READ_AFTER_KEY);
               /* 
                  If the found record is outside the group formed by the search
                  prefix, or there is no such record at all, check if all
@@ -314,10 +314,10 @@ int opt_sum_query(TABLE_LIST *tables, Li
                    key_cmp_if_same(table, ref.key_buff, ref.key, prefix_len)))
               {
                 DBUG_ASSERT(item_field->field->real_maybe_null());
-                error= table->file->index_read_map(table->record[0],
-                                                   ref.key_buff,
-                                                   make_prev_keypart_map(ref.key_parts),
-                                                   HA_READ_KEY_EXACT);
+                error= table->file->ha_index_read_map(table->record[0],
+                                                      ref.key_buff,
+                                                      make_prev_keypart_map(ref.key_parts),
+                                                      HA_READ_KEY_EXACT);
               }
             }
           }
@@ -402,13 +402,13 @@ int opt_sum_query(TABLE_LIST *tables, Li
           error= table->file->ha_index_init((uint) ref.key, 1);
 
           if (!ref.key_length)
-            error= table->file->index_last(table->record[0]);
+            error= table->file->ha_index_last(table->record[0]);
           else
-	    error= table->file->index_read_map(table->record[0], key_buff,
-                                               make_prev_keypart_map(ref.key_parts),
-                                               range_fl & NEAR_MAX ?
-                                               HA_READ_BEFORE_KEY :
-                                               HA_READ_PREFIX_LAST_OR_PREV);
+	    error= table->file->ha_index_read_map(table->record[0], key_buff,
+                                                  make_prev_keypart_map(ref.key_parts),
+                                                  range_fl & NEAR_MAX ?
+                                                  HA_READ_BEFORE_KEY :
+                                                  HA_READ_PREFIX_LAST_OR_PREV);
 	  if (!error && reckey_in_range(1, &ref, item_field->field,
 			                conds, range_fl, prefix_len))
 	    error= HA_ERR_KEY_NOT_FOUND;

=== modified file 'sql/records.cc'
--- sql/records.cc	2009-05-06 12:03:24 +0000
+++ sql/records.cc	2009-10-17 08:13:46 +0000
@@ -342,7 +342,7 @@ static int rr_quick(READ_RECORD *info)
 
 static int rr_index_first(READ_RECORD *info)
 {
-  int tmp= info->file->index_first(info->record);
+  int tmp= info->file->ha_index_first(info->record);
   info->read_record= rr_index;
   if (tmp)
     tmp= rr_handle_error(info, tmp);
@@ -368,7 +368,7 @@ static int rr_index_first(READ_RECORD *i
 
 static int rr_index(READ_RECORD *info)
 {
-  int tmp= info->file->index_next(info->record);
+  int tmp= info->file->ha_index_next(info->record);
   if (tmp)
     tmp= rr_handle_error(info, tmp);
   return tmp;
@@ -378,7 +378,7 @@ static int rr_index(READ_RECORD *info)
 int rr_sequential(READ_RECORD *info)
 {
   int tmp;
-  while ((tmp=info->file->rnd_next(info->record)))
+  while ((tmp=info->file->ha_rnd_next(info->record)))
   {
     if (info->thd->killed)
     {
@@ -406,7 +406,7 @@ static int rr_from_tempfile(READ_RECORD 
   {
     if (my_b_read(info->io_cache,info->ref_pos,info->ref_length))
       return -1;					/* End of file */
-    if (!(tmp=info->file->rnd_pos(info->record,info->ref_pos)))
+    if (!(tmp=info->file->ha_rnd_pos(info->record,info->ref_pos)))
       break;
     /* The following is extremely unlikely to happen */
     if (tmp == HA_ERR_RECORD_DELETED ||
@@ -457,7 +457,7 @@ static int rr_from_pointers(READ_RECORD 
     cache_pos= info->cache_pos;
     info->cache_pos+= info->ref_length;
 
-    if (!(tmp=info->file->rnd_pos(info->record,cache_pos)))
+    if (!(tmp=info->file->ha_rnd_pos(info->record,cache_pos)))
       break;
 
     /* The following is extremely unlikely to happen */
@@ -590,7 +590,7 @@ static int rr_from_cache(READ_RECORD *in
       record=uint3korr(position);
       position+=3;
       record_pos=info->cache+record*info->reclength;
-      if ((error=(int16) info->file->rnd_pos(record_pos,info->ref_pos)))
+      if ((error=(int16) info->file->ha_rnd_pos(record_pos,info->ref_pos)))
       {
 	record_pos[info->error_offset]=1;
 	shortstore(record_pos,error);

=== modified file 'sql/set_var.cc'
--- sql/set_var.cc	2009-09-15 10:46:35 +0000
+++ sql/set_var.cc	2009-10-18 16:39:56 +0000
@@ -511,6 +511,9 @@ static sys_var_const            sys_prot
 static sys_var_thd_ulong	sys_read_buff_size(&vars, "read_buffer_size",
 					   &SV::read_buff_size);
 static sys_var_opt_readonly	sys_readonly(&vars, "read_only", &opt_readonly);
+static sys_var_bool_ptr	        sys_userstat(&vars, "userstat",
+                                             &opt_userstat_running);
+
 static sys_var_thd_ulong	sys_read_rnd_buff_size(&vars, "read_rnd_buffer_size",
 					       &SV::read_rnd_buff_size);
 static sys_var_thd_ulong	sys_div_precincrement(&vars, "div_precision_increment",

=== modified file 'sql/sp.cc'
--- sql/sp.cc	2009-07-28 22:39:58 +0000
+++ sql/sp.cc	2009-10-17 08:09:24 +0000
@@ -344,8 +344,9 @@ db_find_routine_aux(THD *thd, int type, 
   key_copy(key, table->record[0], table->key_info,
            table->key_info->key_length);
 
-  if (table->file->index_read_idx_map(table->record[0], 0, key, HA_WHOLE_KEY,
-                                      HA_READ_KEY_EXACT))
+  if (table->file->ha_index_read_idx_map(table->record[0], 0, key,
+                                         HA_WHOLE_KEY,
+                                         HA_READ_KEY_EXACT))
     DBUG_RETURN(SP_KEY_NOT_FOUND);
 
   DBUG_RETURN(SP_OK);
@@ -1101,9 +1102,9 @@ sp_drop_db_routines(THD *thd, char *db)
 
   ret= SP_OK;
   table->file->ha_index_init(0, 1);
-  if (! table->file->index_read_map(table->record[0],
-                                    (uchar *)table->field[MYSQL_PROC_FIELD_DB]->ptr,
-                                    (key_part_map)1, HA_READ_KEY_EXACT))
+  if (! table->file->ha_index_read_map(table->record[0],
+                                       (uchar *)table->field[MYSQL_PROC_FIELD_DB]->ptr,
+                                       (key_part_map)1, HA_READ_KEY_EXACT))
   {
     int nxtres;
     bool deleted= FALSE;
@@ -1118,9 +1119,11 @@ sp_drop_db_routines(THD *thd, char *db)
 	nxtres= 0;
 	break;
       }
-    } while (! (nxtres= table->file->index_next_same(table->record[0],
-                                (uchar *)table->field[MYSQL_PROC_FIELD_DB]->ptr,
-						     key_len)));
+    } while (!(nxtres= table->file->
+               ha_index_next_same(table->record[0],
+                                  (uchar *)table->field[MYSQL_PROC_FIELD_DB]->
+                                  ptr,
+                                  key_len)));
     if (nxtres != HA_ERR_END_OF_FILE)
       ret= SP_KEY_NOT_FOUND;
     if (deleted)

=== modified file 'sql/sql_acl.cc'
--- sql/sql_acl.cc	2009-09-07 20:50:10 +0000
+++ sql/sql_acl.cc	2009-10-17 08:13:46 +0000
@@ -1834,9 +1834,9 @@ static bool update_user_table(THD *thd, 
   key_copy((uchar *) user_key, table->record[0], table->key_info,
            table->key_info->key_length);
 
-  if (table->file->index_read_idx_map(table->record[0], 0,
-                                      (uchar *) user_key, HA_WHOLE_KEY,
-                                      HA_READ_KEY_EXACT))
+  if (table->file->ha_index_read_idx_map(table->record[0], 0,
+                                         (uchar *) user_key, HA_WHOLE_KEY,
+                                         HA_READ_KEY_EXACT))
   {
     my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
                MYF(0));	/* purecov: deadcode */
@@ -1927,9 +1927,9 @@ static int replace_user_table(THD *thd, 
   key_copy(user_key, table->record[0], table->key_info,
            table->key_info->key_length);
 
-  if (table->file->index_read_idx_map(table->record[0], 0, user_key,
-                                      HA_WHOLE_KEY,
-                                      HA_READ_KEY_EXACT))
+  if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
+                                         HA_WHOLE_KEY,
+                                         HA_READ_KEY_EXACT))
   {
     /* what == 'N' means revoke */
     if (what == 'N')
@@ -2151,9 +2151,9 @@ static int replace_db_table(TABLE *table
   key_copy(user_key, table->record[0], table->key_info,
            table->key_info->key_length);
 
-  if (table->file->index_read_idx_map(table->record[0],0, user_key,
-                                      HA_WHOLE_KEY,
-                                      HA_READ_KEY_EXACT))
+  if (table->file->ha_index_read_idx_map(table->record[0],0, user_key,
+                                         HA_WHOLE_KEY,
+                                         HA_READ_KEY_EXACT))
   {
     if (what == 'N')
     { // no row, no revoke
@@ -2369,8 +2369,9 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TA
     col_privs->field[4]->store("",0, &my_charset_latin1);
 
     col_privs->file->ha_index_init(0, 1);
-    if (col_privs->file->index_read_map(col_privs->record[0], (uchar*) key,
-                                        (key_part_map)15, HA_READ_KEY_EXACT))
+    if (col_privs->file->ha_index_read_map(col_privs->record[0], (uchar*) key,
+                                           (key_part_map)15,
+                                           HA_READ_KEY_EXACT))
     {
       cols = 0; /* purecov: deadcode */
       col_privs->file->ha_index_end();
@@ -2391,7 +2392,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TA
         return;				/* purecov: deadcode */
       }
       my_hash_insert(&hash_columns, (uchar *) mem_check);
-    } while (!col_privs->file->index_next(col_privs->record[0]) &&
+    } while (!col_privs->file->ha_index_next(col_privs->record[0]) &&
              !key_cmp_if_same(col_privs,key,0,key_prefix_len));
     col_privs->file->ha_index_end();
   }
@@ -2532,8 +2533,8 @@ static int replace_column_table(GRANT_TA
     key_copy(user_key, table->record[0], table->key_info,
              table->key_info->key_length);
 
-    if (table->file->index_read_map(table->record[0], user_key, HA_WHOLE_KEY,
-                                    HA_READ_KEY_EXACT))
+    if (table->file->ha_index_read_map(table->record[0], user_key,
+                                       HA_WHOLE_KEY, HA_READ_KEY_EXACT))
     {
       if (revoke_grant)
       {
@@ -2610,9 +2611,9 @@ static int replace_column_table(GRANT_TA
     key_copy(user_key, table->record[0], table->key_info,
              key_prefix_length);
 
-    if (table->file->index_read_map(table->record[0], user_key,
-                                    (key_part_map)15,
-                                    HA_READ_KEY_EXACT))
+    if (table->file->ha_index_read_map(table->record[0], user_key,
+                                       (key_part_map)15,
+                                       HA_READ_KEY_EXACT))
       goto end;
 
     /* Scan through all rows with the same host,db,user and table */
@@ -2663,7 +2664,7 @@ static int replace_column_table(GRANT_TA
 	    hash_delete(&g_t->hash_columns,(uchar*) grant_column);
 	}
       }
-    } while (!table->file->index_next(table->record[0]) &&
+    } while (!table->file->ha_index_next(table->record[0]) &&
 	     !key_cmp_if_same(table, key, 0, key_prefix_length));
   }
 
@@ -2713,9 +2714,9 @@ static int replace_table_table(THD *thd,
   key_copy(user_key, table->record[0], table->key_info,
            table->key_info->key_length);
 
-  if (table->file->index_read_idx_map(table->record[0], 0, user_key,
-                                      HA_WHOLE_KEY,
-                                      HA_READ_KEY_EXACT))
+  if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
+                                         HA_WHOLE_KEY,
+                                         HA_READ_KEY_EXACT))
   {
     /*
       The following should never happen as we first check the in memory
@@ -2840,10 +2841,10 @@ static int replace_routine_table(THD *th
                          TRUE);
   store_record(table,record[1]);			// store at pos 1
 
-  if (table->file->index_read_idx_map(table->record[0], 0,
-                                      (uchar*) table->field[0]->ptr,
-                                      HA_WHOLE_KEY,
-                                      HA_READ_KEY_EXACT))
+  if (table->file->ha_index_read_idx_map(table->record[0], 0,
+                                         (uchar*) table->field[0]->ptr,
+                                         HA_WHOLE_KEY,
+                                         HA_READ_KEY_EXACT))
   {
     /*
       The following should never happen as we first check the in memory
@@ -3548,7 +3549,7 @@ static my_bool grant_load_procs_priv(TAB
   p_table->file->ha_index_init(0, 1);
   p_table->use_all_columns();
 
-  if (!p_table->file->index_first(p_table->record[0]))
+  if (!p_table->file->ha_index_first(p_table->record[0]))
   {
     memex_ptr= &memex;
     my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
@@ -3600,7 +3601,7 @@ static my_bool grant_load_procs_priv(TAB
         goto end_unlock;
       }
     }
-    while (!p_table->file->index_next(p_table->record[0]));
+    while (!p_table->file->ha_index_next(p_table->record[0]));
   }
   /* Return ok */
   return_val= 0;
@@ -3650,7 +3651,7 @@ static my_bool grant_load(THD *thd, TABL
   t_table->use_all_columns();
   c_table->use_all_columns();
 
-  if (!t_table->file->index_first(t_table->record[0]))
+  if (!t_table->file->ha_index_first(t_table->record[0]))
   {
     memex_ptr= &memex;
     my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
@@ -3685,7 +3686,7 @@ static my_bool grant_load(THD *thd, TABL
 	goto end_unlock;
       }
     }
-    while (!t_table->file->index_next(t_table->record[0]));
+    while (!t_table->file->ha_index_next(t_table->record[0]));
   }
 
   return_val=0;					// Return ok
@@ -3957,6 +3958,8 @@ err:
   {
     char command[128];
     get_privilege_desc(command, sizeof(command), want_access);
+    status_var_increment(thd->status_var.access_denied_errors);
+
     my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
              command,
              sctx->priv_user,
@@ -5203,9 +5206,9 @@ static int handle_grant_table(TABLE_LIST
                         table->key_info->key_part[1].store_length);
     key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
 
-    if ((error= table->file->index_read_idx_map(table->record[0], 0,
-                                                user_key, (key_part_map)3,
-                                                HA_READ_KEY_EXACT)))
+    if ((error= table->file->ha_index_read_idx_map(table->record[0], 0,
+                                                   user_key, (key_part_map)3,
+                                                   HA_READ_KEY_EXACT)))
     {
       if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
       {
@@ -5240,7 +5243,7 @@ static int handle_grant_table(TABLE_LIST
       DBUG_PRINT("info",("scan table: '%s'  search: '%s'@'%s'",
                          table->s->table_name.str, user_str, host_str));
 #endif
-      while ((error= table->file->rnd_next(table->record[0])) != 
+      while ((error= table->file->ha_rnd_next(table->record[0])) != 
              HA_ERR_END_OF_FILE)
       {
         if (error)

=== modified file 'sql/sql_base.cc'
--- sql/sql_base.cc	2009-09-07 20:50:10 +0000
+++ sql/sql_base.cc	2009-10-19 02:07:41 +0000
@@ -1373,6 +1373,12 @@ bool close_thread_table(THD *thd, TABLE 
   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.
@@ -1902,6 +1908,13 @@ void close_temporary(TABLE *table, bool 
   DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
                           table->s->db.str, table->s->table_name.str));
 
+  /* in_use is not set for replication temporary tables during shutdown */
+  if (table->in_use)
+  {
+    table->file->update_global_table_stats();
+    table->file->update_global_index_stats();
+  }
+
   free_io_cache(table);
   closefrm(table, 0);
   if (delete_table)

=== modified file 'sql/sql_class.cc'
--- sql/sql_class.cc	2009-09-15 10:46:35 +0000
+++ sql/sql_class.cc	2009-10-18 17:24:57 +0000
@@ -615,6 +615,7 @@ THD::THD()
   mysys_var=0;
   binlog_evt_union.do_union= FALSE;
   enable_slow_log= 0;
+
 #ifndef DBUG_OFF
   dbug_sentry=THD_SENTRY_MAGIC;
 #endif
@@ -817,7 +818,63 @@ void THD::init(void)
   update_charset();
   reset_current_stmt_binlog_row_based();
   bzero((char *) &status_var, sizeof(status_var));
+  bzero((char *) &org_status_var, sizeof(org_status_var));
   sql_log_bin_toplevel= options & OPTION_BIN_LOG;
+  select_commands= update_commands= other_commands= 0;
+  /* Set to handle counting of aborted connections */
+  userstat_running= opt_userstat_running;
+  last_global_update_time= current_connect_time= time(NULL);
+}
+
+ 
+/* Updates some status variables to be used by update_global_user_stats */
+
+void THD::update_stats(void)
+{
+  /* sql_command == SQLCOM_END in case of parse errors or quit */
+  if (lex->sql_command != SQLCOM_END)
+  {
+    /* The replication thread has the COM_CONNECT command */
+    DBUG_ASSERT(command == COM_QUERY || command == COM_CONNECT);
+
+    /* A SQL query. */
+    if (lex->sql_command == SQLCOM_SELECT)
+      select_commands++;
+    else if (! sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND)
+    {
+      /* Ignore 'SHOW ' commands */
+    }
+    else if (is_update_query(lex->sql_command))
+      update_commands++;
+    else
+      other_commands++;
+  }
+}
+
+
+void THD::update_all_stats()
+{
+  time_t save_time;
+  ulonglong end_cpu_time, end_utime;
+  double busy_time, cpu_time;
+
+  /* This is set at start of query if opt_userstat_running was set */
+  if (!userstat_running)
+    return;
+
+  end_cpu_time= my_getrealtime();
+  end_utime=    my_micro_time_and_time(&save_time);
+  busy_time= (end_utime - start_utime)  / 1000000.0;
+  cpu_time=  (end_cpu_time - start_cpu_time) / 10000000.0;
+  /* In case there are bad values, 2629743 is the #seconds in a month. */
+  if (cpu_time > 2629743.0)
+    cpu_time= 0;
+  status_var_add(status_var.cpu_time, cpu_time);
+  status_var_add(status_var.busy_time, busy_time);
+
+  /* Updates THD stats and the global user stats. */
+  update_stats();
+  update_global_user_stats(this, TRUE, save_time);
 }
 
 
@@ -984,9 +1041,8 @@ THD::~THD()
    from_var     from this array
 
   NOTES
-    This function assumes that all variables are long/ulong.
-    If this assumption will change, then we have to explictely add
-    the other variables after the while loop
+    This function assumes that all variables at start are long/ulong and
+    other types are handled explicitely
 */
 
 void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var)
@@ -998,6 +1054,13 @@ void add_to_status(STATUS_VAR *to_var, S
 
   while (to != end)
     *(to++)+= *(from++);
+
+  /* Handle the not ulong variables. See end of system_status_var */
+  to_var->bytes_received=       from_var->bytes_received;
+  to_var->bytes_sent+=          from_var->bytes_sent;
+  to_var->binlog_bytes_written= from_var->binlog_bytes_written;
+  to_var->cpu_time+=            from_var->cpu_time;
+  to_var->busy_time+=           from_var->busy_time;
 }
 
 /*
@@ -1010,7 +1073,8 @@ void add_to_status(STATUS_VAR *to_var, S
     dec_var      minus this array
   
   NOTE
-    This function assumes that all variables are long/ulong.
+    This function assumes that all variables at start are long/ulong and
+    other types are handled explicitely
 */
 
 void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
@@ -1023,6 +1087,14 @@ void add_diff_to_status(STATUS_VAR *to_v
 
   while (to != end)
     *(to++)+= *(from++) - *(dec++);
+
+  to_var->bytes_received=       (from_var->bytes_received -
+                                 dec_var->bytes_received);
+  to_var->bytes_sent+=          from_var->bytes_sent - dec_var->bytes_sent;
+  to_var->binlog_bytes_written= (from_var->binlog_bytes_written -
+                                 dec_var->binlog_bytes_written);
+  to_var->cpu_time+=            from_var->cpu_time - dec_var->cpu_time;
+  to_var->busy_time+=           from_var->busy_time - dec_var->busy_time;
 }
 
 #define SECONDS_TO_WAIT_FOR_KILL 2
@@ -2773,7 +2845,8 @@ void thd_increment_bytes_sent(ulong leng
 {
   THD *thd=current_thd;
   if (likely(thd != 0))
-  { /* current_thd==0 when close_connection() calls net_send_error() */
+  {
+    /* current_thd == 0 when close_connection() calls net_send_error() */
     thd->status_var.bytes_sent+= length;
   }
 }

=== modified file 'sql/sql_class.h'
--- sql/sql_class.h	2009-09-15 10:46:35 +0000
+++ sql/sql_class.h	2009-10-18 17:18:46 +0000
@@ -415,8 +415,6 @@ struct system_variables
 
 typedef struct system_status_var
 {
-  ulonglong bytes_received;
-  ulonglong bytes_sent;
   ulong com_other;
   ulong com_stat[(uint) SQLCOM_END];
   ulong created_tmp_disk_tables;
@@ -455,6 +453,9 @@ typedef struct system_status_var
   ulong select_range_count;
   ulong select_range_check_count;
   ulong select_scan_count;
+  ulong rows_examined;
+  ulong rows_read;
+  ulong rows_sent;
   ulong long_query_count;
   ulong filesort_merge_passes;
   ulong filesort_range_count;
@@ -472,6 +473,9 @@ typedef struct system_status_var
     Number of statements sent from the client
   */
   ulong questions;
+  ulong empty_queries;
+  ulong access_denied_errors;                   /* Can only be 0 or 1 */
+  ulong lost_connections;
   /*
     IMPORTANT!
     SEE last_system_status_var DEFINITION BELOW.
@@ -480,12 +484,16 @@ typedef struct system_status_var
     Status variables which it does not make sense to add to
     global status variable counter
   */
+  ulonglong bytes_received;
+  ulonglong bytes_sent;
+  ulonglong binlog_bytes_written;
   double last_query_cost;
+  double cpu_time, busy_time;
 } STATUS_VAR;
 
 /*
   This is used for 'SHOW STATUS'. It must be updated to the last ulong
-  variable in system_status_var which is makes sens to add to the global
+  variable in system_status_var which is makes sense to add to the global
   counter
 */
 
@@ -1299,6 +1307,7 @@ public:
   struct  my_rnd_struct rand;		// used for authentication
   struct  system_variables variables;	// Changeable local variables
   struct  system_status_var status_var; // Per thread statistic vars
+  struct  system_status_var org_status_var; // For user statistics
   struct  system_status_var *initial_status_var; /* used by show status */
   THR_LOCK_INFO lock_info;              // Locking info of this thread
   THR_LOCK_OWNER main_lock_id;          // To use for conventional queries
@@ -1399,6 +1408,8 @@ public:
   uint in_sub_stmt;
   /* TRUE when the current top has SQL_LOG_BIN ON */
   bool sql_log_bin_toplevel;
+  /* True when opt_userstat_running is set at start of query */
+  bool userstat_running;
 
   /* container for handler's private per-connection data */
   Ha_data ha_data[MAX_HA];
@@ -1752,6 +1763,7 @@ public:
   /* variables.transaction_isolation is reset to this after each commit */
   enum_tx_isolation session_tx_isolation;
   enum_check_fields count_cuted_fields;
+  ha_rows    updated_row_count;
 
   DYNAMIC_ARRAY user_var_events;        /* For user variables replication */
   MEM_ROOT      *user_var_events_alloc; /* Allocate above array elements here */
@@ -1842,6 +1854,26 @@ public:
   */
   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;
+  /* Number of commands not reflected in global_user_stats yet. */
+  uint select_commands, update_commands, other_commands;
+  ulonglong start_cpu_time;
+  ulonglong start_bytes_received;
+  /*
+    Per account query delay in miliseconds. When not 0, sleep this number of
+    milliseconds before every SQL command.
+  */
+  ulonglong query_delay_millis;
+
   /* Used by the sys_var class to store temporary values */
   union
   {
@@ -1902,6 +1934,12 @@ public:
     alloc_root.
   */
   void init_for_queries();
+  void update_all_stats();
+  void reset_stats(void);
+  void reset_diff_stats(void);
+  // ran_command is true when this is called immediately after a
+  // command has been run.
+  void update_stats(void);
   void change_user(void);
   void cleanup(void);
   void cleanup_after_query();
@@ -2319,7 +2357,6 @@ private:
   MEM_ROOT main_mem_root;
 };
 
-
 /** A short cut for thd->main_da.set_ok_status(). */
 
 inline void

=== modified file 'sql/sql_connect.cc'
--- sql/sql_connect.cc	2009-09-07 20:50:10 +0000
+++ sql/sql_connect.cc	2009-10-18 19:37:12 +0000
@@ -20,6 +20,13 @@
 
 #include "mysql_priv.h"
 
+HASH global_user_stats, global_client_stats, global_table_stats;
+HASH global_index_stats;
+/* Protects the above global stats */
+extern pthread_mutex_t LOCK_global_user_client_stats;
+extern pthread_mutex_t LOCK_global_table_stats;
+extern pthread_mutex_t LOCK_global_index_stats;
+
 #ifdef HAVE_OPENSSL
 /*
   Without SSL the handshake consists of one packet. This packet
@@ -459,6 +466,7 @@ check_user(THD *thd, enum enum_server_co
 	  check_for_max_user_connections(thd, thd->user_connect))
       {
         /* The error is set in check_for_max_user_connections(). */
+        status_var_increment(denied_connections);
         DBUG_RETURN(1);
       }
 
@@ -470,6 +478,7 @@ check_user(THD *thd, enum enum_server_co
           /* mysql_change_db() has pushed the error message. */
           if (thd->user_connect)
             decrease_user_connections(thd->user_connect);
+          status_var_increment(thd->status_var.access_denied_errors);
           DBUG_RETURN(1);
         }
       }
@@ -493,6 +502,8 @@ check_user(THD *thd, enum enum_server_co
                     thd->main_security_ctx.user,
                     thd->main_security_ctx.host_or_ip,
                     passwd_len ? ER(ER_YES) : ER(ER_NO));
+  status_var_increment(thd->status_var.access_denied_errors);
+
   DBUG_RETURN(1);
 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
 }
@@ -520,10 +531,14 @@ extern "C" void free_user(struct user_co
 void init_max_user_conn(void)
 {
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
-  (void) hash_init(&hash_user_connections,system_charset_info,max_connections,
-		   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
 }
 
@@ -576,6 +591,449 @@ void reset_mqh(LEX_USER *lu, bool get_th
 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
 }
 
+/*****************************************************************************
+ Handle users statistics
+*****************************************************************************/
+
+/* 'mysql_system_user' is used for when the user is not defined for a THD. */
+static const char mysql_system_user[]= "#mysql_system#";
+
+// Returns 'user' if it's not NULL.  Returns 'mysql_system_user' otherwise.
+static const char * get_valid_user_string(char* user)
+{
+  return user ? user : mysql_system_user;
+}
+
+/*
+  Returns string as 'IP' for the client-side of the connection represented by
+  'client'. Does not allocate memory. May return "".
+*/
+
+static const char *get_client_host(THD *client)
+{
+  return client->security_ctx->host_or_ip[0] ?
+    client->security_ctx->host_or_ip :
+    client->security_ctx->host ? client->security_ctx->host : "";
+}
+
+extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
+                                     my_bool not_used __attribute__((unused)))
+{
+  *length= user_stats->user_name_length;
+  return (uchar*) user_stats->user;
+}
+
+void free_user_stats(USER_STATS* user_stats)
+{
+  my_free(user_stats, MYF(0));
+}
+
+void init_user_stats(USER_STATS *user_stats,
+                     const char *user,
+                     size_t user_length,
+                     const char *priv_user,
+                     uint total_connections,
+                     uint concurrent_connections,
+                     time_t connected_time,
+                     double busy_time,
+                     double cpu_time,
+                     ulonglong bytes_received,
+                     ulonglong bytes_sent,
+                     ulonglong binlog_bytes_written,
+                     ha_rows rows_sent,
+                     ha_rows rows_read,
+                     ha_rows rows_inserted,
+                     ha_rows rows_deleted,
+                     ha_rows rows_updated,
+                     ulonglong select_commands,
+                     ulonglong update_commands,
+                     ulonglong other_commands,
+                     ulonglong commit_trans,
+                     ulonglong rollback_trans,
+                     ulonglong denied_connections,
+                     ulonglong lost_connections,
+                     ulonglong access_denied_errors,
+                     ulonglong empty_queries)
+{
+  DBUG_ENTER("init_user_stats");
+  DBUG_PRINT("info",
+             ("Add user_stats entry for user %s - priv_user %s",
+              user, priv_user));
+  user_length= min(user_length, sizeof(user_stats->user)-1);
+  memcpy(user_stats->user, user, user_length);
+  user_stats->user[user_length]= 0;
+  user_stats->user_name_length= user_length;
+  strmake(user_stats->priv_user, priv_user, sizeof(user_stats->priv_user)-1);
+
+  user_stats->total_connections= total_connections;
+  user_stats->concurrent_connections= concurrent_connections;
+  user_stats->connected_time= connected_time;
+  user_stats->busy_time= busy_time;
+  user_stats->cpu_time= cpu_time;
+  user_stats->bytes_received= bytes_received;
+  user_stats->bytes_sent= bytes_sent;
+  user_stats->binlog_bytes_written= binlog_bytes_written;
+  user_stats->rows_sent= rows_sent;
+  user_stats->rows_updated= rows_updated;
+  user_stats->rows_read= rows_read;
+  user_stats->select_commands= select_commands;
+  user_stats->update_commands= update_commands;
+  user_stats->other_commands= other_commands;
+  user_stats->commit_trans= commit_trans;
+  user_stats->rollback_trans= rollback_trans;
+  user_stats->denied_connections= denied_connections;
+  user_stats->lost_connections= lost_connections;
+  user_stats->access_denied_errors= access_denied_errors;
+  user_stats->empty_queries= empty_queries;
+  DBUG_VOID_RETURN;
+}
+
+
+#ifdef COMPLEAT_PATCH_NOT_ADDED_YET
+
+void add_user_stats(USER_STATS *user_stats,
+                    uint total_connections,
+                    uint concurrent_connections,
+                    time_t connected_time,
+                    double busy_time,
+                    double cpu_time,
+                    ulonglong bytes_received,
+                    ulonglong bytes_sent,
+                    ulonglong binlog_bytes_written,
+                    ha_rows rows_sent,
+                    ha_rows rows_read,
+                    ha_rows rows_inserted,
+                    ha_rows rows_deleted,
+                    ha_rows rows_updated,
+                    ulonglong select_commands,
+                    ulonglong update_commands,
+                    ulonglong other_commands,
+                    ulonglong commit_trans,
+                    ulonglong rollback_trans,
+                    ulonglong denied_connections,
+                    ulonglong lost_connections,
+                    ulonglong access_denied_errors,
+                    ulonglong empty_queries)
+{
+  user_stats->total_connections+= total_connections;
+  user_stats->concurrent_connections+= concurrent_connections;
+  user_stats->connected_time+= connected_time;
+  user_stats->busy_time+= busy_time;
+  user_stats->cpu_time+= cpu_time;
+  user_stats->bytes_received+= bytes_received;
+  user_stats->bytes_sent+= bytes_sent;
+  user_stats->binlog_bytes_written+= binlog_bytes_written;
+  user_stats->rows_sent+=  rows_sent;
+  user_stats->rows_inserted+= rows_inserted;
+  user_stats->rows_deleted+=  rows_deleted;
+  user_stats->rows_updated+=  rows_updated;
+  user_stats->rows_read+= rows_read;
+  user_stats->select_commands+= select_commands;
+  user_stats->update_commands+= update_commands;
+  user_stats->other_commands+= other_commands;
+  user_stats->commit_trans+= commit_trans;
+  user_stats->rollback_trans+= rollback_trans;
+  user_stats->denied_connections+= denied_connections;
+  user_stats->lost_connections+= lost_connections;
+  user_stats->access_denied_errors+= access_denied_errors;
+  user_stats->empty_queries+= empty_queries;
+}
+#endif
+
+
+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);
+  }
+}
+
+void init_global_client_stats(void)
+{
+  if (hash_init(&global_client_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_client_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= table_stats->table_name_length;
+  return (uchar*) table_stats->table;
+}
+
+extern "C" void free_table_stats(TABLE_STATS* table_stats)
+{
+  my_free(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= index_stats->index_name_length;
+  return (uchar*) index_stats->index;
+}
+
+extern "C" void free_index_stats(INDEX_STATS* index_stats)
+{
+  my_free(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);
+}
+
+void free_global_client_stats(void)
+{
+  hash_free(&global_client_stats);
+}
+
+/*
+  Increments the global stats connection count for an entry from
+  global_client_stats or global_user_stats. Returns 0 on success
+  and 1 on error.
+*/
+
+static int increment_count_by_name(const char *name, size_t name_length,
+                                   const char *role_name,
+                                   HASH *users_or_clients, THD *thd)
+{
+  USER_STATS *user_stats;
+
+  if (!(user_stats= (USER_STATS*) hash_search(users_or_clients, (uchar*) name,
+                                              name_length)))
+  {
+    // First connection for this user or client
+    if (!(user_stats= ((USER_STATS*)
+                        my_malloc(sizeof(USER_STATS),
+                                  MYF(MY_WME | MY_ZEROFILL)))))
+    {
+      return 1; // Out of memory
+    }
+
+    init_user_stats(user_stats, name, name_length, role_name,
+                    0, 0,      // connections
+                    0, 0, 0,   // time
+                    0, 0, 0,   // bytes sent, received and written
+                    0, 0,      // Rows sent and read
+                    0, 0, 0,   // rows inserted, deleted and updated
+                    0, 0, 0,   // select, update and other commands
+                    0, 0,      // commit and rollback trans
+                    thd->status_var.access_denied_errors,
+                    0,         // lost connections
+                    0,         // access denied errors
+                    0);        // empty queries
+
+    if (my_hash_insert(users_or_clients, (uchar*)user_stats))
+    {
+      my_free(user_stats, 0);
+      return 1; // Out of memory
+    }
+  }
+  user_stats->total_connections++;
+  return 0;
+}
+
+
+/*
+  Increments the global user and client stats connection count.
+
+  @param use_lock  if true, LOCK_global_user_client_stats will be locked
+
+  @retval  0 ok
+  @retval 1 error.
+*/
+
+static int increment_connection_count(THD* thd, bool use_lock)
+{
+  const char *user_string= get_valid_user_string(thd->main_security_ctx.user);
+  const char *client_string= get_client_host(thd);
+  int return_value= 0;
+
+  if (!thd->userstat_running)
+    return 0;
+
+  if (use_lock)
+    pthread_mutex_lock(&LOCK_global_user_client_stats);
+
+  if (increment_count_by_name(user_string, strlen(user_string), user_string,
+                              &global_user_stats, thd))
+  {
+    return_value= 1;
+    goto end;
+  }
+  if (increment_count_by_name(client_string, strlen(client_string),
+                              user_string, &global_client_stats, thd))
+  {
+    return_value= 1;
+    goto end;
+  }
+
+end:
+  if (use_lock)
+    pthread_mutex_unlock(&LOCK_global_user_client_stats);
+  return return_value;
+}
+
+
+/*
+  Used to update the global user and client stats
+*/
+
+static void update_global_user_stats_with_user(THD *thd,
+                                               USER_STATS *user_stats,
+                                               time_t now)
+{
+  DBUG_ASSERT(thd->userstat_running);
+
+  user_stats->connected_time+= now - thd->last_global_update_time;
+  user_stats->busy_time+=  (thd->status_var.busy_time -
+                            thd->org_status_var.busy_time);
+  user_stats->cpu_time+=   (thd->status_var.cpu_time -
+                            thd->org_status_var.cpu_time); 
+  /*
+    This is handle specially as bytes_recieved is incremented BEFORE
+    org_status_var is copied.
+  */
+  user_stats->bytes_received+= (thd->org_status_var.bytes_received-
+                                thd->start_bytes_received);
+  user_stats->bytes_sent+= (thd->status_var.bytes_sent -
+                            thd->org_status_var.bytes_sent);
+  user_stats->binlog_bytes_written+=
+    (thd->status_var.binlog_bytes_written -
+     thd->org_status_var.binlog_bytes_written);
+  user_stats->rows_read+=  (thd->status_var.rows_read -
+                               thd->org_status_var.rows_read);
+  user_stats->rows_sent+=  (thd->status_var.rows_sent -
+                               thd->org_status_var.rows_sent);
+  user_stats->rows_inserted+= (thd->status_var.ha_write_count -
+                               thd->org_status_var.ha_write_count);
+  user_stats->rows_deleted+=  (thd->status_var.ha_delete_count -
+                               thd->org_status_var.ha_delete_count);
+  user_stats->rows_updated+=  (thd->status_var.ha_update_count -
+                               thd->org_status_var.ha_update_count);
+  user_stats->select_commands+= thd->select_commands;
+  user_stats->update_commands+= thd->update_commands;
+  user_stats->other_commands+=  thd->other_commands;
+  user_stats->commit_trans+=  (thd->status_var.ha_commit_count -
+                               thd->org_status_var.ha_commit_count);
+  user_stats->rollback_trans+= (thd->status_var.ha_rollback_count +
+                                thd->status_var.ha_savepoint_rollback_count -
+                                thd->org_status_var.ha_rollback_count -
+                                thd->org_status_var.
+                                ha_savepoint_rollback_count);
+  user_stats->access_denied_errors+=
+    (thd->status_var.access_denied_errors -
+     thd->org_status_var.access_denied_errors);
+  user_stats->empty_queries+=   (thd->status_var.empty_queries -
+                                 thd->org_status_var.empty_queries);
+
+  /* The following can only contain 0 or 1 and then connection ends */
+  user_stats->denied_connections+= thd->status_var.access_denied_errors;
+  user_stats->lost_connections+=   thd->status_var.lost_connections;
+}
+
+
+/*  Updates the global stats of a user or client */
+void update_global_user_stats(THD *thd, bool create_user, time_t now)
+{
+  const char *user_string, *client_string;
+  USER_STATS *user_stats;
+  size_t user_string_length, client_string_length;
+
+  if (!thd->userstat_running)
+    return;
+
+  user_string= get_valid_user_string(thd->main_security_ctx.user);
+  user_string_length= strlen(user_string);
+  client_string= get_client_host(thd);
+  client_string_length= strlen(client_string);
+
+  pthread_mutex_lock(&LOCK_global_user_client_stats);
+
+  // Update by user name
+  if ((user_stats= (USER_STATS*) hash_search(&global_user_stats,
+                                             (uchar*) user_string,
+                                             user_string_length)))
+  {
+    /* Found user. */
+    update_global_user_stats_with_user(thd, user_stats, now);
+  }
+  else
+  {
+    /* Create the entry */
+    if (create_user)
+    {
+      increment_count_by_name(user_string, user_string_length, user_string,
+                              &global_user_stats, thd);
+    }
+  }
+
+  /* Update by client IP */
+  if ((user_stats= (USER_STATS*)hash_search(&global_client_stats,
+                                            (uchar*) client_string,
+                                            client_string_length)))
+  {
+    // Found by client IP
+    update_global_user_stats_with_user(thd, user_stats, now);
+  }
+  else
+  {
+    // Create the entry
+    if (create_user)
+    {
+      increment_count_by_name(client_string, client_string_length,
+                              user_string, &global_client_stats, thd);
+    }
+  }
+  /* Reset variables only used for counting */
+  thd->select_commands= thd->update_commands= thd->other_commands= 0;
+  thd->last_global_update_time= now;
+
+  pthread_mutex_unlock(&LOCK_global_user_client_stats);
+}
+
 
 void thd_init_client_charset(THD *thd, uint cs_number)
 {
@@ -970,6 +1428,14 @@ bool login_connection(THD *thd)
   /* Connect completed, set read/write timeouts back to default */
   my_net_set_read_timeout(net, thd->variables.net_read_timeout);
   my_net_set_write_timeout(net, thd->variables.net_write_timeout);
+
+  /*  Updates global user connection stats. */
+  if (increment_connection_count(thd, TRUE))
+  {
+    net_send_error(thd, ER_OUTOFMEMORY);  // Out of memory
+    DBUG_RETURN(1);
+  }
+
   DBUG_RETURN(0);
 }
 
@@ -991,6 +1457,7 @@ void end_connection(THD *thd)
   if (thd->killed || (net->error && net->vio != 0))
   {
     statistic_increment(aborted_threads,&LOCK_status);
+    status_var_increment(thd->status_var.lost_connections);
   }
 
   if (net->error && net->vio != 0)
@@ -1117,10 +1584,14 @@ pthread_handler_t handle_one_connection(
   for (;;)
   {
     NET *net= &thd->net;
+    bool create_user= TRUE;
 
     lex_start(thd);
     if (login_connection(thd))
+    {
+      create_user= FALSE;
       goto end_thread;
+    }      
 
     prepare_new_connection_state(thd);
 
@@ -1134,12 +1605,13 @@ pthread_handler_t handle_one_connection(
    
 end_thread:
     close_connection(thd, 0, 1);
+    update_global_user_stats(thd, create_user, time(NULL));
+
     if (thd->scheduler->end_thread(thd,1))
       return 0;                                 // Probably no-threads
 
     /*
-      If end_thread() returns, we are either running with
-      thread-handler=no-threads or this thread has been schedule to
+      If end_thread() returns, this thread has been schedule to
       handle the next connection.
     */
     thd= current_thd;

=== modified file 'sql/sql_cursor.cc'
--- sql/sql_cursor.cc	2008-12-10 14:16:21 +0000
+++ sql/sql_cursor.cc	2009-10-17 08:13:46 +0000
@@ -655,7 +655,7 @@ void Materialized_cursor::fetch(ulong nu
   result->begin_dataset();
   for (fetch_limit+= num_rows; fetch_count < fetch_limit; fetch_count++)
   {
-    if ((res= table->file->rnd_next(table->record[0])))
+    if ((res= table->file->ha_rnd_next(table->record[0])))
       break;
     /* Send data only if the read was successful. */
     result->send_data(item_list);

=== modified file 'sql/sql_handler.cc'
--- sql/sql_handler.cc	2009-07-15 23:23:57 +0000
+++ sql/sql_handler.cc	2009-10-17 08:09:24 +0000
@@ -565,8 +565,8 @@ retry:
       if (table->file->inited != handler::NONE)
       {
         error=keyname ?
-	  table->file->index_next(table->record[0]) :
-	  table->file->rnd_next(table->record[0]);
+	  table->file->ha_index_next(table->record[0]) :
+	  table->file->ha_rnd_next(table->record[0]);
         break;
       }
       /* else fall through */
@@ -575,13 +575,13 @@ retry:
       {
         table->file->ha_index_or_rnd_end();
         table->file->ha_index_init(keyno, 1);
-        error= table->file->index_first(table->record[0]);
+        error= table->file->ha_index_first(table->record[0]);
       }
       else
       {
         table->file->ha_index_or_rnd_end();
 	if (!(error= table->file->ha_rnd_init(1)))
-          error= table->file->rnd_next(table->record[0]);
+          error= table->file->ha_rnd_next(table->record[0]);
       }
       mode=RNEXT;
       break;
@@ -589,7 +589,7 @@ retry:
       DBUG_ASSERT(keyname != 0);
       if (table->file->inited != handler::NONE)
       {
-        error=table->file->index_prev(table->record[0]);
+        error=table->file->ha_index_prev(table->record[0]);
         break;
       }
       /* else fall through */
@@ -597,13 +597,13 @@ retry:
       DBUG_ASSERT(keyname != 0);
       table->file->ha_index_or_rnd_end();
       table->file->ha_index_init(keyno, 1);
-      error= table->file->index_last(table->record[0]);
+      error= table->file->ha_index_last(table->record[0]);
       mode=RPREV;
       break;
     case RNEXT_SAME:
       /* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...)  */
       DBUG_ASSERT(keyname != 0);
-      error= table->file->index_next_same(table->record[0], key, key_len);
+      error= table->file->ha_index_next_same(table->record[0], key, key_len);
       break;
     case RKEY:
     {
@@ -643,8 +643,8 @@ retry:
       table->file->ha_index_or_rnd_end();
       table->file->ha_index_init(keyno, 1);
       key_copy(key, table->record[0], table->key_info + keyno, key_len);
-      error= table->file->index_read_map(table->record[0],
-                                         key, keypart_map, ha_rkey_mode);
+      error= table->file->ha_index_read_map(table->record[0],
+                                            key, keypart_map, ha_rkey_mode);
       mode=rkey_to_rnext[(int)ha_rkey_mode];
       break;
     }

=== modified file 'sql/sql_help.cc'
--- sql/sql_help.cc	2009-09-07 20:50:10 +0000
+++ sql/sql_help.cc	2009-10-17 08:09:23 +0000
@@ -294,13 +294,13 @@ int get_topics_for_keyword(THD *thd, TAB
 
   rkey_id->store((longlong) key_id, TRUE);
   rkey_id->get_key_image(buff, rkey_id->pack_length(), Field::itRAW);
-  int key_res= relations->file->index_read_map(relations->record[0],
-                                               buff, (key_part_map) 1,
-                                               HA_READ_KEY_EXACT);
+  int key_res= relations->file->ha_index_read_map(relations->record[0],
+                                                  buff, (key_part_map) 1,
+                                                  HA_READ_KEY_EXACT);
 
   for ( ;
         !key_res && key_id == (int16) rkey_id->val_int() ;
-	key_res= relations->file->index_next(relations->record[0]))
+	key_res= relations->file->ha_index_next(relations->record[0]))
   {
     uchar topic_id_buff[8];
     longlong topic_id= rtopic_id->val_int();
@@ -308,8 +308,8 @@ int get_topics_for_keyword(THD *thd, TAB
     field->store((longlong) topic_id, TRUE);
     field->get_key_image(topic_id_buff, field->pack_length(), Field::itRAW);
 
-    if (!topics->file->index_read_map(topics->record[0], topic_id_buff,
-                                      (key_part_map)1, HA_READ_KEY_EXACT))
+    if (!topics->file->ha_index_read_map(topics->record[0], topic_id_buff,
+                                         (key_part_map)1, HA_READ_KEY_EXACT))
     {
       memorize_variant_topic(thd,topics,count,find_fields,
 			     names,name,description,example);

=== modified file 'sql/sql_insert.cc'
--- sql/sql_insert.cc	2009-09-07 20:50:10 +0000
+++ sql/sql_insert.cc	2009-10-17 14:12:45 +0000
@@ -1425,7 +1425,7 @@ int write_record(THD *thd, TABLE *table,
 	goto err;
       if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
       {
-	if (table->file->rnd_pos(table->record[1],table->file->dup_ref))
+	if (table->file->ha_rnd_pos(table->record[1],table->file->dup_ref))
 	  goto err;
       }
       else
@@ -1446,9 +1446,10 @@ int write_record(THD *thd, TABLE *table,
 	  }
 	}
 	key_copy((uchar*) key,table->record[0],table->key_info+key_nr,0);
-	if ((error=(table->file->index_read_idx_map(table->record[1],key_nr,
-                                                    (uchar*) key, HA_WHOLE_KEY,
-                                                    HA_READ_KEY_EXACT))))
+	if ((error=(table->file->ha_index_read_idx_map(table->record[1],key_nr,
+                                                       (uchar*) key,
+                                                       HA_WHOLE_KEY,
+                                                       HA_READ_KEY_EXACT))))
 	  goto err;
       }
       if (info->handle_duplicates == DUP_UPDATE)

=== modified file 'sql/sql_lex.h'
--- sql/sql_lex.h	2009-09-15 10:46:35 +0000
+++ sql/sql_lex.h	2009-10-16 10:20:13 +0000
@@ -118,6 +118,8 @@ enum enum_sql_command {
   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,
+  SQLCOM_SHOW_CLIENT_STATS,
 
   /*
     When a command is added here, be sure it's also added in mysqld.cc

=== modified file 'sql/sql_parse.cc'
--- sql/sql_parse.cc	2009-09-15 10:46:35 +0000
+++ sql/sql_parse.cc	2009-10-18 17:19:36 +0000
@@ -549,7 +549,6 @@ end:
   DBUG_RETURN(0);
 }
 
-
 /**
   @brief Check access privs for a MERGE table and fix children lock types.
 
@@ -801,6 +800,8 @@ bool do_command(THD *thd)
 
   net_new_transaction(net);
 
+  /* Save for user statistics */
+  thd->start_bytes_received= thd->status_var.bytes_received;
   packet_length= my_net_read(net);
 #if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
   thd->profiling.start_new_query();
@@ -1324,7 +1325,7 @@ bool dispatch_command(enum enum_server_c
     table_list.select_lex= &(thd->lex->select_lex);
 
     lex_start(thd);
-    mysql_reset_thd_for_next_command(thd);
+    mysql_reset_thd_for_next_command(thd, opt_userstat_running);
 
     thd->lex->
       select_lex.table_list.link_in_list((uchar*) &table_list,
@@ -1609,6 +1610,9 @@ bool dispatch_command(enum enum_server_c
   /* Free tables */
   close_thread_tables(thd);
 
+  /* Update status; Must be done after close_thread_tables */
+  thd->update_all_stats();
+
   log_slow_statement(thd);
 
   thd_proc_info(thd, "cleaning up");
@@ -1777,6 +1781,11 @@ int prepare_schema_table(THD *thd, LEX *
     thd->profiling.discard_current_query();
 #endif
     break;
+  case SCH_USER_STATS:
+  case SCH_CLIENT_STATS:
+    return check_global_access(thd, SUPER_ACL | PROCESS_ACL);
+  case SCH_TABLE_STATS:
+  case SCH_INDEX_STATS:
   case SCH_OPEN_TABLES:
   case SCH_VARIABLES:
   case SCH_STATUS:
@@ -5059,6 +5068,10 @@ static bool execute_sqlcom_select(THD *t
         delete result;
     }
   }
+  /* Count number of empty select queries */
+  if (!thd->sent_row_count)
+    status_var_increment(thd->status_var.empty_queries);
+  status_var_add(thd->status_var.rows_sent, thd->sent_row_count);
   return res;
 }
 
@@ -5220,6 +5233,7 @@ check_access(THD *thd, ulong want_access
       if (!no_errors)
       {
         const char *db_name= db ? db : thd->db;
+        status_var_increment(thd->status_var.access_denied_errors);
         my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
                  sctx->priv_user, sctx->priv_host, db_name);
       }
@@ -5252,12 +5266,15 @@ check_access(THD *thd, ulong want_access
   {						// We can never grant this
     DBUG_PRINT("error",("No possible access"));
     if (!no_errors)
+    {
+      status_var_increment(thd->status_var.access_denied_errors);
       my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
                sctx->priv_user,
                sctx->priv_host,
                (thd->password ?
                 ER(ER_YES) :
                 ER(ER_NO)));                    /* purecov: tested */
+    }
     DBUG_RETURN(TRUE);				/* purecov: tested */
   }
 
@@ -5283,11 +5300,14 @@ check_access(THD *thd, ulong want_access
 
   DBUG_PRINT("error",("Access denied"));
   if (!no_errors)
+  {
+    status_var_increment(thd->status_var.access_denied_errors);
     my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
              sctx->priv_user, sctx->priv_host,
              (db ? db : (thd->db ?
                          thd->db :
                          "unknown")));          /* purecov: tested */
+  }
   DBUG_RETURN(TRUE);				/* purecov: tested */
 }
 
@@ -5316,6 +5336,7 @@ static bool check_show_access(THD *thd, 
 
     if (!thd->col_access && check_grant_db(thd, dst_db_name))
     {
+      status_var_increment(thd->status_var.access_denied_errors);
       my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
                thd->security_ctx->priv_user,
                thd->security_ctx->priv_host,
@@ -5378,14 +5399,14 @@ check_table_access(THD *thd, ulong want_
 {
   TABLE_LIST *org_tables= tables;
   TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
-  uint i= 0;
   Security_context *sctx= thd->security_ctx, *backup_ctx= thd->security_ctx;
+  uint i;
   /*
     The check that first_not_own_table is not reached is for the case when
     the given table list refers to the list for prelocking (contains tables
     of other queries). For simple queries first_not_own_table is 0.
   */
-  for (; i < number && tables != first_not_own_table;
+  for (i=0; i < number && tables != first_not_own_table;
        tables= tables->next_global, i++)
   {
     if (tables->security_ctx)
@@ -5397,9 +5418,12 @@ check_table_access(THD *thd, ulong want_
         (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
     {
       if (!no_errors)
+      {
+        status_var_increment(thd->status_var.access_denied_errors);
         my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
                  sctx->priv_user, sctx->priv_host,
                  INFORMATION_SCHEMA_NAME.str);
+      }
       return TRUE;
     }
     /*
@@ -5563,6 +5587,7 @@ bool check_global_access(THD *thd, ulong
     return 0;
   get_privilege_desc(command, sizeof(command), want_access);
   my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
+  status_var_increment(thd->status_var.access_denied_errors);
   return 1;
 #else
   return 0;
@@ -5666,7 +5691,7 @@ bool my_yyoverflow(short **yyss, YYSTYPE
    Call it after we use THD for queries, not before.
 */
 
-void mysql_reset_thd_for_next_command(THD *thd)
+void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat)
 {
   DBUG_ENTER("mysql_reset_thd_for_next_command");
   DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */
@@ -5711,6 +5736,16 @@ void mysql_reset_thd_for_next_command(TH
   thd->total_warn_count=0;			// Warnings for this query
   thd->rand_used= 0;
   thd->sent_row_count= thd->examined_row_count= 0;
+  thd->updated_row_count=0;
+
+  /* Copy data for user stats */
+  if ((thd->userstat_running= calculate_userstat))
+  {
+    thd->start_cpu_time= my_getrealtime();
+    memcpy(&thd->org_status_var, &thd->status_var, sizeof(thd->status_var));
+    thd->select_commands= thd->update_commands= thd->other_commands= 0;
+  }
+
   thd->query_plan_flags= QPLAN_INIT;
   thd->query_plan_fsort_passes= 0;
 
@@ -5909,7 +5944,6 @@ void mysql_parse(THD *thd, const char *i
                  const char ** found_semicolon)
 {
   DBUG_ENTER("mysql_parse");
-
   DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););
 
   /*
@@ -5929,7 +5963,7 @@ void mysql_parse(THD *thd, const char *i
     FIXME: cleanup the dependencies in the code to simplify this.
   */
   lex_start(thd);
-  mysql_reset_thd_for_next_command(thd);
+  mysql_reset_thd_for_next_command(thd, opt_userstat_running);
 
   if (query_cache_send_result_to_client(thd, (char*) inBuf, length) <= 0)
   {
@@ -6001,10 +6035,11 @@ void mysql_parse(THD *thd, const char *i
   }
   else
   {
+    /* Update statistics for getting the query from the cache */
+    thd->lex->sql_command= SQLCOM_SELECT;
     /* There are no multi queries in the cache. */
     *found_semicolon= NULL;
   }
-
   DBUG_VOID_RETURN;
 }
 
@@ -6028,7 +6063,7 @@ bool mysql_test_parse_for_slave(THD *thd
 
   Parser_state parser_state(thd, inBuf, length);
   lex_start(thd);
-  mysql_reset_thd_for_next_command(thd);
+  mysql_reset_thd_for_next_command(thd, 0);
 
   if (!parse_sql(thd, & parser_state, NULL) &&
       all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
@@ -6867,6 +6902,12 @@ bool reload_acl_and_cache(THD *thd, ulon
     if (flush_error_log())
       result=1;
   }
+  if (options & REFRESH_SLOW_QUERY_LOG)
+  {
+    /* We are only flushing slow query log */
+    logger.flush_slow_log(thd);
+  }
+
 #ifdef HAVE_QUERY_CACHE
   if (options & REFRESH_QUERY_CACHE_FREE)
   {
@@ -6949,26 +6990,55 @@ bool reload_acl_and_cache(THD *thd, ulon
   }
 #endif
 #ifdef OPENSSL
-   if (options & REFRESH_DES_KEY_FILE)
-   {
-     if (des_key_file && load_des_key_file(des_key_file))
-         result= 1;
-   }
+  if (options & REFRESH_DES_KEY_FILE)
+  {
+    if (des_key_file && load_des_key_file(des_key_file))
+      result= 1;
+  }
 #endif
 #ifdef HAVE_REPLICATION
- if (options & REFRESH_SLAVE)
- {
-   tmp_write_to_binlog= 0;
-   pthread_mutex_lock(&LOCK_active_mi);
-   if (reset_slave(thd, active_mi))
-     result=1;
-   pthread_mutex_unlock(&LOCK_active_mi);
- }
-#endif
- if (options & REFRESH_USER_RESOURCES)
-   reset_mqh((LEX_USER *) NULL, 0);             /* purecov: inspected */
- *write_to_binlog= tmp_write_to_binlog;
- return result;
+  if (options & REFRESH_SLAVE)
+  {
+    tmp_write_to_binlog= 0;
+    pthread_mutex_lock(&LOCK_active_mi);
+    if (reset_slave(thd, active_mi))
+      result=1;
+    pthread_mutex_unlock(&LOCK_active_mi);
+  }
+#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);
+  }
+  if (options & (REFRESH_USER_STATS | REFRESH_CLIENT_STATS))
+  {
+    pthread_mutex_lock(&LOCK_global_user_client_stats);
+    if (options & REFRESH_USER_STATS)
+    {
+      free_global_user_stats();
+      init_global_user_stats();
+    }
+    if (options & REFRESH_CLIENT_STATS)
+    {
+      free_global_client_stats();
+      init_global_client_stats();
+    }
+    pthread_mutex_unlock(&LOCK_global_user_client_stats);
+  }
+  *write_to_binlog= tmp_write_to_binlog;
+  return result;
 }
 
 
@@ -7004,7 +7074,6 @@ uint kill_one_thread(THD *thd, ulong id,
   VOID(pthread_mutex_unlock(&LOCK_thread_count));
   if (tmp)
   {
-
     /*
       If we're SUPER, we can KILL anything, including system-threads.
       No further checks.

=== modified file 'sql/sql_plugin.cc'
--- sql/sql_plugin.cc	2009-10-01 21:27:39 +0000
+++ sql/sql_plugin.cc	2009-10-17 08:09:23 +0000
@@ -1790,10 +1790,10 @@ bool mysql_uninstall_plugin(THD *thd, co
 
   table->use_all_columns();
   table->field[0]->store(name->str, name->length, system_charset_info);
-  if (! table->file->index_read_idx_map(table->record[0], 0,
-                                        (uchar *)table->field[0]->ptr,
-                                        HA_WHOLE_KEY,
-                                        HA_READ_KEY_EXACT))
+  if (! table->file->ha_index_read_idx_map(table->record[0], 0,
+                                           (uchar *)table->field[0]->ptr,
+                                           HA_WHOLE_KEY,
+                                           HA_READ_KEY_EXACT))
   {
     int error;
     /*

=== modified file 'sql/sql_prepare.cc'
--- sql/sql_prepare.cc	2009-09-07 20:50:10 +0000
+++ sql/sql_prepare.cc	2009-10-18 16:38:12 +0000
@@ -2067,14 +2067,13 @@ void mysqld_stmt_prepare(THD *thd, const
   Prepared_statement *stmt;
   bool error;
   DBUG_ENTER("mysqld_stmt_prepare");
-
   DBUG_PRINT("prep_query", ("%s", packet));
 
   /* First of all clear possible warnings from the previous command */
-  mysql_reset_thd_for_next_command(thd);
+  mysql_reset_thd_for_next_command(thd, opt_userstat_running);
 
   if (! (stmt= new Prepared_statement(thd)))
-    DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */
+    goto end;           /* out of memory: error is set in Sql_alloc */
 
   if (thd->stmt_map.insert(thd, stmt))
   {
@@ -2082,7 +2081,7 @@ void mysqld_stmt_prepare(THD *thd, const
       The error is set in the insert. The statement itself
       will be also deleted there (this is how the hash works).
     */
-    DBUG_VOID_RETURN;
+    goto end;
   }
 
   /* Reset warnings from previous command */
@@ -2109,6 +2108,7 @@ void mysqld_stmt_prepare(THD *thd, const
   thd->protocol= save_protocol;
 
   /* check_prepared_statemnt sends the metadata packet in case of success */
+end:
   DBUG_VOID_RETURN;
 }
 
@@ -2450,7 +2450,7 @@ void mysqld_stmt_execute(THD *thd, char 
   packet+= 9;                               /* stmt_id + 5 bytes of flags */
 
   /* First of all clear possible warnings from the previous command */
-  mysql_reset_thd_for_next_command(thd);
+  mysql_reset_thd_for_next_command(thd, opt_userstat_running);
 
   if (!(stmt= find_prepared_statement(thd, stmt_id)))
   {
@@ -2549,7 +2549,8 @@ void mysqld_stmt_fetch(THD *thd, char *p
   DBUG_ENTER("mysqld_stmt_fetch");
 
   /* First of all clear possible warnings from the previous command */
-  mysql_reset_thd_for_next_command(thd);
+  mysql_reset_thd_for_next_command(thd, opt_userstat_running);
+
   status_var_increment(thd->status_var.com_stmt_fetch);
   if (!(stmt= find_prepared_statement(thd, stmt_id)))
   {
@@ -2615,7 +2616,7 @@ void mysqld_stmt_reset(THD *thd, char *p
   DBUG_ENTER("mysqld_stmt_reset");
 
   /* First of all clear possible warnings from the previous command */
-  mysql_reset_thd_for_next_command(thd);
+  mysql_reset_thd_for_next_command(thd, opt_userstat_running);
 
   status_var_increment(thd->status_var.com_stmt_reset);
   if (!(stmt= find_prepared_statement(thd, stmt_id)))

=== modified file 'sql/sql_select.cc'
--- sql/sql_select.cc	2009-09-15 10:46:35 +0000
+++ sql/sql_select.cc	2009-10-17 14:09:17 +0000
@@ -10949,7 +10949,7 @@ create_internal_tmp_table_from_heap2(THD
     is safe as this is a temporary MyISAM table without timestamp/autoincrement
     or partitioning.
   */
-  while (!table->file->rnd_next(new_table.record[1]))
+  while (!table->file->ha_rnd_next(new_table.record[1]))
   {
     write_err= new_table.file->ha_write_row(new_table.record[1]);
     DBUG_EXECUTE_IF("raise_error", write_err= HA_ERR_FOUND_DUPP_KEY ;);
@@ -11746,10 +11746,10 @@ int safe_index_read(JOIN_TAB *tab)
 {
   int error;
   TABLE *table= tab->table;
-  if ((error=table->file->index_read_map(table->record[0],
-                                         tab->ref.key_buff,
-                                         make_prev_keypart_map(tab->ref.key_parts),
-                                         HA_READ_KEY_EXACT)))
+  if ((error=table->file->ha_index_read_map(table->record[0],
+                                            tab->ref.key_buff,
+                                            make_prev_keypart_map(tab->ref.key_parts),
+                                            HA_READ_KEY_EXACT)))
     return report_error(table, error);
   return 0;
 }
@@ -11858,8 +11858,8 @@ join_read_system(JOIN_TAB *tab)
   int error;
   if (table->status & STATUS_GARBAGE)		// If first read
   {
-    if ((error=table->file->read_first_row(table->record[0],
-					   table->s->primary_key)))
+    if ((error=table->file->ha_read_first_row(table->record[0],
+                                              table->s->primary_key)))
     {
       if (error != HA_ERR_END_OF_FILE)
 	return report_error(table, error);
@@ -11901,10 +11901,10 @@ join_read_const(JOIN_TAB *tab)
       error=HA_ERR_KEY_NOT_FOUND;
     else
     {
-      error=table->file->index_read_idx_map(table->record[0],tab->ref.key,
-                                            (uchar*) tab->ref.key_buff,
-                                            make_prev_keypart_map(tab->ref.key_parts),
-                                            HA_READ_KEY_EXACT);
+      error=table->file->ha_index_read_idx_map(table->record[0],tab->ref.key,
+                                               (uchar*) tab->ref.key_buff,
+                                               make_prev_keypart_map(tab->ref.key_parts),
+                                               HA_READ_KEY_EXACT);
     }
     if (error)
     {
@@ -11949,10 +11949,10 @@ join_read_key(JOIN_TAB *tab)
       table->status=STATUS_NOT_FOUND;
       return -1;
     }
-    error=table->file->index_read_map(table->record[0],
-                                      tab->ref.key_buff,
-                                      make_prev_keypart_map(tab->ref.key_parts),
-                                      HA_READ_KEY_EXACT);
+    error=table->file->ha_index_read_map(table->record[0],
+                                         tab->ref.key_buff,
+                                         make_prev_keypart_map(tab->ref.key_parts),
+                                         HA_READ_KEY_EXACT);
     if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
       return report_error(table, error);
   }
@@ -12005,10 +12005,10 @@ join_read_always_key(JOIN_TAB *tab)
 
   if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref))
     return -1;
-  if ((error=table->file->index_read_map(table->record[0],
-                                         tab->ref.key_buff,
-                                         make_prev_keypart_map(tab->ref.key_parts),
-                                         HA_READ_KEY_EXACT)))
+  if ((error=table->file->ha_index_read_map(table->record[0],
+                                            tab->ref.key_buff,
+                                            make_prev_keypart_map(tab->ref.key_parts),
+                                            HA_READ_KEY_EXACT)))
   {
     if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
       return report_error(table, error);
@@ -12039,9 +12039,9 @@ join_read_last_key(JOIN_TAB *tab)
   }
   if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref))
     return -1;
-  if ((error=table->file->index_read_last_map(table->record[0],
-                                              tab->ref.key_buff,
-                                              make_prev_keypart_map(tab->ref.key_parts))))
+  if ((error= table->file->ha_index_read_last_map(table->record[0],
+                                                  tab->ref.key_buff,
+                                                  make_prev_keypart_map(tab->ref.key_parts))))
   {
     if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
       return report_error(table, error);
@@ -12066,9 +12066,9 @@ join_read_next_same(READ_RECORD *info)
   TABLE *table= info->table;
   JOIN_TAB *tab=table->reginfo.join_tab;
 
-  if ((error=table->file->index_next_same(table->record[0],
-					  tab->ref.key_buff,
-					  tab->ref.key_length)))
+  if ((error= table->file->ha_index_next_same(table->record[0],
+                                              tab->ref.key_buff,
+                                              tab->ref.key_length)))
   {
     if (error != HA_ERR_END_OF_FILE)
       return report_error(table, error);
@@ -12086,7 +12086,7 @@ join_read_prev_same(READ_RECORD *info)
   TABLE *table= info->table;
   JOIN_TAB *tab=table->reginfo.join_tab;
 
-  if ((error=table->file->index_prev(table->record[0])))
+  if ((error= table->file->ha_index_prev(table->record[0])))
     return report_error(table, error);
   if (key_cmp_if_same(table, tab->ref.key_buff, tab->ref.key,
                       tab->ref.key_length))
@@ -12158,7 +12158,7 @@ join_read_first(JOIN_TAB *tab)
     error= table->file->ha_index_init(tab->index, tab->sorted);
   if (!error)
     error= table->file->prepare_index_scan();
-  if (error || (error=tab->table->file->index_first(tab->table->record[0])))
+  if (error || (error=tab->table->file->ha_index_first(tab->table->record[0])))
   {
     if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
       report_error(table, error);
@@ -12172,7 +12172,7 @@ static int
 join_read_next(READ_RECORD *info)
 {
   int error;
-  if ((error=info->file->index_next(info->record)))
+  if ((error= info->file->ha_index_next(info->record)))
     return report_error(info->table, error);
   return 0;
 }
@@ -12199,7 +12199,7 @@ join_read_last(JOIN_TAB *tab)
     error= table->file->ha_index_init(tab->index, 1);
   if (!error)
     error= table->file->prepare_index_scan();
-  if (error || (error= tab->table->file->index_last(tab->table->record[0])))
+  if (error || (error= tab->table->file->ha_index_last(tab->table->record[0])))
     return report_error(table, error);
   return 0;
 }
@@ -12209,7 +12209,7 @@ static int
 join_read_prev(READ_RECORD *info)
 {
   int error;
-  if ((error= info->file->index_prev(info->record)))
+  if ((error= info->file->ha_index_prev(info->record)))
     return report_error(info->table, error);
   return 0;
 }
@@ -12234,7 +12234,7 @@ join_ft_read_first(JOIN_TAB *tab)
 #endif
   table->file->ft_init();
 
-  if ((error= table->file->ft_read(table->record[0])))
+  if ((error= table->file->ha_ft_read(table->record[0])))
     return report_error(table, error);
   return 0;
 }
@@ -12243,7 +12243,7 @@ static int
 join_ft_read_next(READ_RECORD *info)
 {
   int error;
-  if ((error= info->file->ft_read(info->table->record[0])))
+  if ((error= info->file->ha_ft_read(info->table->record[0])))
     return report_error(info->table, error);
   return 0;
 }
@@ -12590,10 +12590,10 @@ end_update(JOIN *join, JOIN_TAB *join_ta
     if (item->maybe_null)
       group->buff[-1]= (char) group->field->is_null();
   }
-  if (!table->file->index_read_map(table->record[1],
-                                   join->tmp_table_param.group_buff,
-                                   HA_WHOLE_KEY,
-                                   HA_READ_KEY_EXACT))
+  if (!table->file->ha_index_read_map(table->record[1],
+                                      join->tmp_table_param.group_buff,
+                                      HA_WHOLE_KEY,
+                                      HA_READ_KEY_EXACT))
   {						/* Update old record */
     restore_record(table,record[1]);
     update_tmptable_sum_func(join->sum_funcs,table);
@@ -12671,7 +12671,7 @@ end_unique_update(JOIN *join, JOIN_TAB *
       table->file->print_error(error,MYF(0));	/* purecov: inspected */
       DBUG_RETURN(NESTED_LOOP_ERROR);            /* purecov: inspected */
     }
-    if (table->file->rnd_pos(table->record[1],table->file->dup_ref))
+    if (table->file->ha_rnd_pos(table->record[1],table->file->dup_ref))
     {
       table->file->print_error(error,MYF(0));	/* purecov: inspected */
       DBUG_RETURN(NESTED_LOOP_ERROR);            /* purecov: inspected */
@@ -14016,7 +14016,7 @@ static int remove_dup_with_compare(THD *
   new_record=(char*) table->record[1]+offset;
 
   file->ha_rnd_init(1);
-  error=file->rnd_next(record);
+  error=file->ha_rnd_next(record);
   for (;;)
   {
     if (thd->killed)
@@ -14037,7 +14037,7 @@ static int remove_dup_with_compare(THD *
     {
       if ((error=file->ha_delete_row(record)))
 	goto err;
-      error=file->rnd_next(record);
+      error=file->ha_rnd_next(record);
       continue;
     }
     if (copy_blobs(first_field))
@@ -14052,7 +14052,7 @@ static int remove_dup_with_compare(THD *
     bool found=0;
     for (;;)
     {
-      if ((error=file->rnd_next(record)))
+      if ((error=file->ha_rnd_next(record)))
       {
 	if (error == HA_ERR_RECORD_DELETED)
 	  continue;
@@ -14152,7 +14152,7 @@ static int remove_dup_with_hash_index(TH
       error=0;
       goto err;
     }
-    if ((error=file->rnd_next(record)))
+    if ((error= file->ha_rnd_next(record)))
     {
       if (error == HA_ERR_RECORD_DELETED)
 	continue;

=== modified file 'sql/sql_servers.cc'
--- sql/sql_servers.cc	2009-03-20 14:27:53 +0000
+++ sql/sql_servers.cc	2009-10-17 08:09:23 +0000
@@ -520,10 +520,10 @@ int insert_server_record(TABLE *table, F
                          system_charset_info);
 
   /* read index until record is that specified in server_name */
-  if ((error= table->file->index_read_idx_map(table->record[0], 0,
-                                              (uchar *)table->field[0]->ptr,
-                                              HA_WHOLE_KEY,
-                                              HA_READ_KEY_EXACT)))
+  if ((error= table->file->ha_index_read_idx_map(table->record[0], 0,
+                                                 (uchar *)table->field[0]->ptr,
+                                                 HA_WHOLE_KEY,
+                                                 HA_READ_KEY_EXACT)))
   {
     /* if not found, err */
     if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
@@ -863,10 +863,10 @@ update_server_record(TABLE *table, FOREI
                          server->server_name_length,
                          system_charset_info);
 
-  if ((error= table->file->index_read_idx_map(table->record[0], 0,
-                                              (uchar *)table->field[0]->ptr,
-                                              ~(longlong)0,
-                                              HA_READ_KEY_EXACT)))
+  if ((error= table->file->ha_index_read_idx_map(table->record[0], 0,
+                                                 (uchar *)table->field[0]->ptr,
+                                                 ~(longlong)0,
+                                                 HA_READ_KEY_EXACT)))
   {
     if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
       table->file->print_error(error, MYF(0));
@@ -920,10 +920,10 @@ delete_server_record(TABLE *table,
   /* set the field that's the PK to the value we're looking for */
   table->field[0]->store(server_name, server_name_length, system_charset_info);
 
-  if ((error= table->file->index_read_idx_map(table->record[0], 0,
-                                          (uchar *)table->field[0]->ptr,
-                                          HA_WHOLE_KEY,
-                                          HA_READ_KEY_EXACT)))
+  if ((error= table->file->ha_index_read_idx_map(table->record[0], 0,
+                                                 (uchar *)table->field[0]->ptr,
+                                                 HA_WHOLE_KEY,
+                                                 HA_READ_KEY_EXACT)))
   {
     if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
       table->file->print_error(error, MYF(0));

=== modified file 'sql/sql_show.cc'
--- sql/sql_show.cc	2009-09-23 11:03:47 +0000
+++ sql/sql_show.cc	2009-10-18 19:28:52 +0000
@@ -714,6 +714,7 @@ bool mysqld_show_create_db(THD *thd, cha
 		sctx->master_access);
   if (!(db_access & DB_ACLS) && check_grant_db(thd,dbname))
   {
+    status_var_increment(thd->status_var.access_denied_errors);
     my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
              sctx->priv_user, sctx->host_or_ip, dbname);
     general_log_print(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
@@ -2100,11 +2101,6 @@ void remove_status_vars(SHOW_VAR *list)
   }
 }
 
-inline void make_upper(char *buf)
-{
-  for (; *buf; buf++)
-    *buf= my_toupper(system_charset_info, *buf);
-}
 
 static bool show_status_array(THD *thd, const char *wild,
                               SHOW_VAR *variables,
@@ -2143,7 +2139,7 @@ static bool show_status_array(THD *thd, 
     strnmov(prefix_end, variables->name, len);
     name_buffer[sizeof(name_buffer)-1]=0;       /* Safety */
     if (ucase_names)
-      make_upper(name_buffer);
+      my_caseup_str(system_charset_info, name_buffer);
 
     restore_record(table, s->default_values);
     table->field[0]->store(name_buffer, strlen(name_buffer),
@@ -2270,6 +2266,323 @@ end:
   DBUG_RETURN(res);
 }
 
+#ifdef COMPLEAT_PATCH_NOT_ADDED_YET
+/*
+  Aggregate values for mapped_user entries by their role.
+
+  SYNOPSIS
+  aggregate_user_stats
+  all_user_stats - input to aggregate
+  agg_user_stats - returns aggregated values
+
+  RETURN
+  0 - OK
+  1 - error
+*/
+
+static int aggregate_user_stats(HASH *all_user_stats, HASH *agg_user_stats)
+{
+  DBUG_ENTER("aggregate_user_stats");
+  if (hash_init(agg_user_stats, system_charset_info,
+                max(all_user_stats->records, 1),
+                0, 0, (hash_get_key)get_key_user_stats,
+                (hash_free_key)free_user_stats, 0))
+  {
+    sql_print_error("Malloc in aggregate_user_stats failed");
+    DBUG_RETURN(1);
+  }
+
+  for (uint i= 0; i < all_user_stats->records; i++)
+  {
+    USER_STATS *user= (USER_STATS*)hash_element(all_user_stats, i);
+    USER_STATS *agg_user;
+    uint name_length= strlen(user->priv_user);
+
+    if (!(agg_user= (USER_STATS*) hash_search(agg_user_stats,
+                                              (uchar*)user->priv_user,
+                                              name_length)))
+    {
+      // First entry for this role.
+      if (!(agg_user= (USER_STATS*) my_malloc(sizeof(USER_STATS),
+                                              MYF(MY_WME | MY_ZEROFILL))))
+      {
+        sql_print_error("Malloc in aggregate_user_stats failed");
+        DBUG_RETURN(1);
+      }
+
+      init_user_stats(agg_user, user->priv_user, name_length,
+                      user->priv_user,
+                      user->total_connections, user->concurrent_connections,
+                      user->connected_time, user->busy_time, user->cpu_time,
+                      user->bytes_received, user->bytes_sent,
+                      user->binlog_bytes_written,
+                      user->rows_sent, user->rows_read,
+                      user->rows_inserted, user->rows_deleted,
+                      user->rows_updated, 
+                      user->select_commands, user->update_commands,
+                      user->other_commands,
+                      user->commit_trans, user->rollback_trans,
+                      user->denied_connections, user->lost_connections,
+                      user->access_denied_errors, user->empty_queries);
+
+      if (my_hash_insert(agg_user_stats, (uchar*) agg_user))
+      {
+        /* Out of memory */
+        my_free(agg_user, 0);
+        sql_print_error("Malloc in aggregate_user_stats failed");
+        DBUG_RETURN(1);
+      }
+    }
+    else
+    {
+      /* Aggregate with existing values for this role. */
+      add_user_stats(agg_user,
+                     user->total_connections, user->concurrent_connections,
+                     user->connected_time, user->busy_time, user->cpu_time,
+                     user->bytes_received, user->bytes_sent,
+                     user->binlog_bytes_written,
+                     user->rows_sent, user->rows_read,
+                     user->rows_inserted, user->rows_deleted,
+                     user->rows_updated,
+                     user->select_commands, user->update_commands,
+                     user->other_commands,
+                     user->commit_trans, user->rollback_trans,
+                     user->denied_connections, user->lost_connections,
+                     user->access_denied_errors, user->empty_queries);
+    }
+  }
+  DBUG_PRINT("exit", ("aggregated %lu input into %lu output entries",
+                      all_user_stats->records, agg_user_stats->records));
+  DBUG_RETURN(0);
+}
+#endif
+
+/*
+  Write result to network for SHOW USER_STATISTICS
+
+  SYNOPSIS
+  send_user_stats
+  all_user_stats - values to return
+  table - I_S table
+
+  RETURN
+  0 - OK
+  1 - error
+*/
+
+int send_user_stats(THD* thd, HASH *all_user_stats, TABLE *table)
+{
+  DBUG_ENTER("send_user_stats");
+
+  for (uint i= 0; i < all_user_stats->records; i++)
+  {
+    uint j= 0;
+    USER_STATS *user_stats= (USER_STATS*) hash_element(all_user_stats, i);
+    
+    table->field[j++]->store(user_stats->user, user_stats->user_name_length,
+                             system_charset_info);
+    table->field[j++]->store((longlong)user_stats->total_connections,TRUE);
+    table->field[j++]->store((longlong)user_stats->concurrent_connections);
+    table->field[j++]->store((longlong)user_stats->connected_time);
+    table->field[j++]->store((double)user_stats->busy_time);
+    table->field[j++]->store((double)user_stats->cpu_time);
+    table->field[j++]->store((longlong)user_stats->bytes_received, TRUE);
+    table->field[j++]->store((longlong)user_stats->bytes_sent, TRUE);
+    table->field[j++]->store((longlong)user_stats->binlog_bytes_written, TRUE);
+    table->field[j++]->store((longlong)user_stats->rows_read, TRUE);
+    table->field[j++]->store((longlong)user_stats->rows_sent, TRUE);
+    table->field[j++]->store((longlong)user_stats->rows_deleted, TRUE);
+    table->field[j++]->store((longlong)user_stats->rows_inserted, TRUE);
+    table->field[j++]->store((longlong)user_stats->rows_updated, TRUE);
+    table->field[j++]->store((longlong)user_stats->select_commands, TRUE);
+    table->field[j++]->store((longlong)user_stats->update_commands, TRUE);
+    table->field[j++]->store((longlong)user_stats->other_commands, TRUE);
+    table->field[j++]->store((longlong)user_stats->commit_trans, TRUE);
+    table->field[j++]->store((longlong)user_stats->rollback_trans, TRUE);
+    table->field[j++]->store((longlong)user_stats->denied_connections, TRUE);
+    table->field[j++]->store((longlong)user_stats->lost_connections, TRUE);
+    table->field[j++]->store((longlong)user_stats->access_denied_errors, TRUE);
+    table->field[j++]->store((longlong)user_stats->empty_queries, TRUE);
+    if (schema_table_store_record(thd, table))
+    {
+      DBUG_PRINT("error", ("store record error"));
+      DBUG_RETURN(1);
+    }
+  }
+  DBUG_RETURN(0);
+}
+
+/*
+  Process SHOW USER_STATISTICS
+
+  SYNOPSIS
+  mysqld_show_user_stats
+  thd - current thread
+  wild - limit results to the entry for this user
+  with_roles - when true, display role for mapped users
+
+  RETURN
+  0 - OK
+  1 - error
+*/
+
+int fill_schema_user_stats(THD* thd, TABLE_LIST* tables, COND* cond)
+{
+  TABLE *table= tables->table;
+  int result;
+  DBUG_ENTER("fill_schema_user_stats");
+
+  if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
+    DBUG_RETURN(1);
+
+  /*
+    Iterates through all the global stats and sends them to the client.
+    Pattern matching on the client IP is supported.
+  */
+
+  pthread_mutex_lock(&LOCK_global_user_client_stats);
+  result= send_user_stats(thd, &global_user_stats, table) != 0;
+  pthread_mutex_unlock(&LOCK_global_user_client_stats);
+
+  DBUG_PRINT("exit", ("result: %d", result));
+  DBUG_RETURN(result);
+}
+
+/*
+   Process SHOW CLIENT_STATISTICS
+
+   SYNOPSIS
+     mysqld_show_client_stats
+       thd - current thread
+       wild - limit results to the entry for this client
+
+   RETURN
+     0 - OK
+     1 - error
+*/
+
+int fill_schema_client_stats(THD* thd, TABLE_LIST* tables, COND* cond)
+{
+  TABLE *table= tables->table;
+  int result;
+  DBUG_ENTER("fill_schema_client_stats");
+
+  if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
+    DBUG_RETURN(1);
+
+  /*
+    Iterates through all the global stats and sends them to the client.
+    Pattern matching on the client IP is supported.
+  */
+
+  pthread_mutex_lock(&LOCK_global_user_client_stats);
+  result= send_user_stats(thd, &global_client_stats, table) != 0;
+  pthread_mutex_unlock(&LOCK_global_user_client_stats);
+
+  DBUG_PRINT("exit", ("result: %d", result));
+  DBUG_RETURN(result);
+}
+
+
+/* Sends the global table stats back to the client. */
+
+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 (uint i= 0; i < global_table_stats.records; i++)
+  {
+    char *end_of_schema;
+    TABLE_STATS *table_stats= 
+      (TABLE_STATS*)hash_element(&global_table_stats, i);
+    TABLE_LIST tmp_table;
+    size_t schema_length, table_name_length;
+
+    end_of_schema= strend(table_stats->table);
+    schema_length= (size_t) (end_of_schema - table_stats->table);
+    table_name_length= strlen(table_stats->table + schema_length + 1);
+
+    bzero((char*) &tmp_table,sizeof(tmp_table));
+    tmp_table.db=         table_stats->table;
+    tmp_table.table_name= end_of_schema+1;
+    tmp_table.grant.privilege= 0;
+    if (check_access(thd, SELECT_ACL | EXTRA_ACL, tmp_table.db,
+                     &tmp_table.grant.privilege, 0, 0,
+                     is_schema_db(tmp_table.db)) ||
+        check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX,
+                    1))
+      continue;
+
+    table->field[0]->store(table_stats->table, schema_length,
+                           system_charset_info);
+    table->field[1]->store(table_stats->table + schema_length+1,
+                           table_name_length, system_charset_info);
+    table->field[2]->store((longlong)table_stats->rows_read, TRUE);
+    table->field[3]->store((longlong)table_stats->rows_changed, TRUE);
+    table->field[4]->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);
+}
+
+
+/*  Sends the global index stats back to the client */
+
+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 (uint i= 0; i < global_index_stats.records; i++)
+  {
+    INDEX_STATS *index_stats =
+      (INDEX_STATS*) hash_element(&global_index_stats, i);
+    TABLE_LIST tmp_table;
+    char *index_name;
+    size_t schema_name_length, table_name_length, index_name_length;
+
+    bzero((char*) &tmp_table,sizeof(tmp_table));
+    tmp_table.db=         index_stats->index;
+    tmp_table.table_name= strend(index_stats->index)+1;
+    tmp_table.grant.privilege= 0;
+    if (check_access(thd, SELECT_ACL | EXTRA_ACL, tmp_table.db,
+                      &tmp_table.grant.privilege, 0, 0,
+                      is_schema_db(tmp_table.db)) ||
+        check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, 1))
+      continue;
+
+    index_name=         strend(tmp_table.table_name)+1; 
+    schema_name_length= (tmp_table.table_name - index_stats->index) -1;
+    table_name_length=  (index_name - tmp_table.table_name)-1;
+    index_name_length=  (index_stats->index_name_length - schema_name_length -
+                         table_name_length - 3);
+
+    table->field[0]->store(tmp_table.db, schema_name_length,
+                           system_charset_info);
+    table->field[1]->store(tmp_table.table_name, table_name_length,
+                           system_charset_info);
+    table->field[2]->store(index_name, index_name_length, system_charset_info);
+    table->field[3]->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 */
 
@@ -4206,7 +4519,7 @@ int fill_schema_proc(THD *thd, TABLE_LIS
     DBUG_RETURN(1);
   }
   proc_table->file->ha_index_init(0, 1);
-  if ((res= proc_table->file->index_first(proc_table->record[0])))
+  if ((res= proc_table->file->ha_index_first(proc_table->record[0])))
   {
     res= (res == HA_ERR_END_OF_FILE) ? 0 : 1;
     goto err;
@@ -4216,7 +4529,7 @@ int fill_schema_proc(THD *thd, TABLE_LIS
     res= 1;
     goto err;
   }
-  while (!proc_table->file->index_next(proc_table->record[0]))
+  while (!proc_table->file->ha_index_next(proc_table->record[0]))
   {
     if (store_schema_proc(thd, table, proc_table, wild, full_access, definer))
     {
@@ -5462,6 +5775,81 @@ struct schema_table_ref
   ST_SCHEMA_TABLE *schema_table;
 };
 
+ST_FIELD_INFO user_stats_fields_info[]=
+{
+  {"USER", USERNAME_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User", SKIP_OPEN_TABLE},
+  {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections",SKIP_OPEN_TABLE},
+  {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections",SKIP_OPEN_TABLE},
+  {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time",SKIP_OPEN_TABLE},
+  {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_DOUBLE, 0, 0, "Busy_time",SKIP_OPEN_TABLE},
+  {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_DOUBLE, 0, 0, "Cpu_time",SKIP_OPEN_TABLE},
+  {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_received",SKIP_OPEN_TABLE},
+  {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_sent",SKIP_OPEN_TABLE},
+  {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Binlog_bytes_written",SKIP_OPEN_TABLE},
+  {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read",SKIP_OPEN_TABLE},
+  {"ROWS_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_sent",SKIP_OPEN_TABLE},
+  {"ROWS_DELETED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_deleted",SKIP_OPEN_TABLE},
+  {"ROWS_INSERTED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_inserted",SKIP_OPEN_TABLE},
+  {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated",SKIP_OPEN_TABLE},
+  {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Select_commands",SKIP_OPEN_TABLE},
+  {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Update_commands",SKIP_OPEN_TABLE},
+  {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Other_commands",SKIP_OPEN_TABLE},
+  {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Commit_transactions",SKIP_OPEN_TABLE},
+  {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rollback_transactions",SKIP_OPEN_TABLE},
+  {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Denied_connections",SKIP_OPEN_TABLE},
+  {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Lost_connections",SKIP_OPEN_TABLE},
+  {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Access_denied",SKIP_OPEN_TABLE},
+  {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Empty_queries",SKIP_OPEN_TABLE},
+  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
+};
+
+ST_FIELD_INFO client_stats_fields_info[]=
+{
+  {"CLIENT", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Client",SKIP_OPEN_TABLE},
+  {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections",SKIP_OPEN_TABLE},
+  {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections",SKIP_OPEN_TABLE},
+  {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time",SKIP_OPEN_TABLE},
+  {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_DOUBLE, 0, 0, "Busy_time",SKIP_OPEN_TABLE},
+  {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_DOUBLE, 0, 0, "Cpu_time",SKIP_OPEN_TABLE},
+  {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_received",SKIP_OPEN_TABLE},
+  {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_sent",SKIP_OPEN_TABLE},
+  {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Binlog_bytes_written",SKIP_OPEN_TABLE},
+  {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read",SKIP_OPEN_TABLE},
+  {"ROWS_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_sent",SKIP_OPEN_TABLE},
+  {"ROWS_DELETED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_deleted",SKIP_OPEN_TABLE},
+  {"ROWS_INSERTED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_inserted",SKIP_OPEN_TABLE},
+  {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated",SKIP_OPEN_TABLE},
+  {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Select_commands",SKIP_OPEN_TABLE},
+  {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Update_commands",SKIP_OPEN_TABLE},
+  {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Other_commands",SKIP_OPEN_TABLE},
+  {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Commit_transactions",SKIP_OPEN_TABLE},
+  {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rollback_transactions",SKIP_OPEN_TABLE},
+  {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Denied_connections",SKIP_OPEN_TABLE},
+  {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Lost_connections",SKIP_OPEN_TABLE},
+  {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Access_denied",SKIP_OPEN_TABLE},
+  {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Empty_queries",SKIP_OPEN_TABLE},
+  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
+};
+
+
+ST_FIELD_INFO table_stats_fields_info[]=
+{
+  {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_schema",SKIP_OPEN_TABLE},
+  {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name",SKIP_OPEN_TABLE},
+  {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read",SKIP_OPEN_TABLE},
+  {"ROWS_CHANGED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_changed",SKIP_OPEN_TABLE},
+  {"ROWS_CHANGED_X_INDEXES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_changed_x_#indexes",SKIP_OPEN_TABLE},
+  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
+};
+
+ST_FIELD_INFO index_stats_fields_info[]=
+{
+  {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_schema",SKIP_OPEN_TABLE},
+  {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name",SKIP_OPEN_TABLE},
+  {"INDEX_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Index_name",SKIP_OPEN_TABLE},
+  {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read",SKIP_OPEN_TABLE},
+  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0,0}
+};
 
 /*
   Find schema_tables elment by name
@@ -6683,6 +7071,8 @@ ST_SCHEMA_TABLE schema_tables[]=
 {
   {"CHARACTER_SETS", charsets_fields_info, create_schema_table,
    fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0, 0},
+  {"CLIENT_STATISTICS", client_stats_fields_info, create_schema_table, 
+   fill_schema_client_stats, make_old_format, 0, -1, -1, 0, 0},
   {"COLLATIONS", collation_fields_info, create_schema_table,
    fill_schema_collation, make_old_format, 0, -1, -1, 0, 0},
   {"COLLATION_CHARACTER_SET_APPLICABILITY", coll_charset_app_fields_info,
@@ -6707,6 +7097,8 @@ ST_SCHEMA_TABLE schema_tables[]=
    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},
@@ -6748,11 +7140,15 @@ ST_SCHEMA_TABLE schema_tables[]=
    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, 0, -1, 1, 0},
   {"VIEWS", view_fields_info, create_schema_table,

=== modified file 'sql/sql_table.cc'
--- sql/sql_table.cc	2009-09-18 01:04:43 +0000
+++ sql/sql_table.cc	2009-10-17 08:13:46 +0000
@@ -7815,7 +7815,7 @@ bool mysql_checksum_table(THD *thd, TABL
               goto err;
             }
 	    ha_checksum row_crc= 0;
-            int error= t->file->rnd_next(t->record[0]);
+            int error= t->file->ha_rnd_next(t->record[0]);
             if (unlikely(error))
             {
               if (error == HA_ERR_RECORD_DELETED)

=== modified file 'sql/sql_udf.cc'
--- sql/sql_udf.cc	2009-05-15 12:57:51 +0000
+++ sql/sql_udf.cc	2009-10-17 08:09:23 +0000
@@ -567,10 +567,10 @@ int mysql_drop_function(THD *thd,const L
     goto err;
   table->use_all_columns();
   table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin);
-  if (!table->file->index_read_idx_map(table->record[0], 0,
-                                       (uchar*) table->field[0]->ptr,
-                                       HA_WHOLE_KEY,
-                                       HA_READ_KEY_EXACT))
+  if (!table->file->ha_index_read_idx_map(table->record[0], 0,
+                                          (uchar*) table->field[0]->ptr,
+                                          HA_WHOLE_KEY,
+                                          HA_READ_KEY_EXACT))
   {
     int error;
     if ((error = table->file->ha_delete_row(table->record[0])))

=== modified file 'sql/sql_update.cc'
--- sql/sql_update.cc	2009-09-07 20:50:10 +0000
+++ sql/sql_update.cc	2009-10-17 14:13:49 +0000
@@ -142,7 +142,7 @@ static void prepare_record_for_error_mes
   /* Tell the engine about the new set. */
   table->file->column_bitmaps_signal();
   /* Read record that is identified by table->file->ref. */
-  (void) table->file->rnd_pos(table->record[1], table->file->ref);
+  (void) table->file->ha_rnd_pos(table->record[1], table->file->ref);
   /* Copy the newly read columns into the new record. */
   for (field_p= table->field; (field= *field_p); field_p++)
     if (bitmap_is_set(&unique_map, field->field_index))
@@ -1928,7 +1928,7 @@ int multi_update::do_updates()
     {
       if (thd->killed && trans_safe)
 	goto err;
-      if ((local_error=tmp_table->file->rnd_next(tmp_table->record[0])))
+      if ((local_error= tmp_table->file->ha_rnd_next(tmp_table->record[0])))
       {
 	if (local_error == HA_ERR_END_OF_FILE)
 	  break;
@@ -1943,12 +1943,12 @@ int multi_update::do_updates()
       uint field_num= 0;
       do
       {
-        if((local_error=
-              tbl->file->rnd_pos(tbl->record[0],
-                                (uchar *) tmp_table->field[field_num]->ptr)))
+        if ((local_error=
+             tbl->file->ha_rnd_pos(tbl->record[0],
+                                   (uchar*) tmp_table->field[field_num]->ptr)))
           goto err;
         field_num++;
-      } while((tbl= check_opt_it++));
+      } while ((tbl= check_opt_it++));
 
       table->status|= STATUS_UPDATED;
       store_record(table,record[1]);

=== modified file 'sql/sql_yacc.yy'
--- sql/sql_yacc.yy	2009-09-07 20:50:10 +0000
+++ sql/sql_yacc.yy	2009-10-17 07:26:29 +0000
@@ -598,6 +598,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  CHECK_SYM                     /* SQL-2003-R */
 %token  CIPHER_SYM
 %token  CLIENT_SYM
+%token  CLIENT_STATS_SYM
 %token  CLOSE_SYM                     /* SQL-2003-R */
 %token  COALESCE                      /* SQL-2003-N */
 %token  CODE_SYM
@@ -744,6 +745,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  IMPORT
 %token  INDEXES
 %token  INDEX_SYM
+%token	INDEX_STATS_SYM
 %token  INFILE
 %token  INITIAL_SIZE_SYM
 %token  INNER_SYM                     /* SQL-2003-R */
@@ -985,6 +987,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  SIGNED_SYM
 %token  SIMPLE_SYM                    /* SQL-2003-N */
 %token  SLAVE
+%token  SLOW_SYM
 %token  SMALLINT                      /* SQL-2003-R */
 %token  SNAPSHOT_SYM
 %token  SOCKET_SYM
@@ -1029,6 +1032,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  TABLES
 %token  TABLESPACE
 %token  TABLE_REF_PRIORITY
+%token  TABLE_STATS_SYM
 %token  TABLE_SYM                     /* SQL-2003-R */
 %token  TABLE_CHECKSUM_SYM
 %token  TEMPORARY                     /* SQL-2003-N */
@@ -1076,6 +1080,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %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 */
@@ -9995,12 +10000,12 @@ show_param:
             if (prepare_schema_table(YYTHD, lex, 0, SCH_ENGINES))
               MYSQL_YYABORT;
           }
-        | AUTHORS_SYM
+        | AUTHORS_SYM wild_and_where
           {
             LEX *lex=Lex;
             lex->sql_command= SQLCOM_SHOW_AUTHORS;
           }
-        | CONTRIBUTORS_SYM
+        | CONTRIBUTORS_SYM wild_and_where
           {
             LEX *lex=Lex;
             lex->sql_command= SQLCOM_SHOW_CONTRIBUTORS;
@@ -10131,6 +10136,34 @@ show_param:
           {
             Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
           }
+        | CLIENT_STATS_SYM wild_and_where 
+          {
+           LEX *lex= Lex;
+           lex->sql_command= SQLCOM_SHOW_CLIENT_STATS;
+           if (prepare_schema_table(YYTHD, lex, 0, SCH_CLIENT_STATS))
+             MYSQL_YYABORT;
+          }
+        | 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;
@@ -10339,6 +10372,16 @@ flush_option:
           { Lex->type|= REFRESH_STATUS; }
         | SLAVE
           { Lex->type|= REFRESH_SLAVE; }
+ 	| SLOW_SYM QUERY_SYM LOGS_SYM
+          { Lex->type |= REFRESH_SLOW_QUERY_LOG; }
+  	| CLIENT_STATS_SYM
+          { Lex->type|= REFRESH_CLIENT_STATS; }
+  	| USER_STATS_SYM
+         { Lex->type|= REFRESH_USER_STATS; }
+  	| TABLE_STATS_SYM
+          { Lex->type|= REFRESH_TABLE_STATS; }
+  	| INDEX_STATS_SYM
+          { Lex->type|= REFRESH_INDEX_STATS; }
         | MASTER_SYM
           { Lex->type|= REFRESH_MASTER; }
         | DES_KEY_FILE
@@ -11447,6 +11490,7 @@ keyword_sp:
         | CHAIN_SYM                {}
         | CHANGED                  {}
         | CIPHER_SYM               {}
+        | CLIENT_STATS_SYM         {}
         | CLIENT_SYM               {}
         | COALESCE                 {}
         | CODE_SYM                 {}
@@ -11508,6 +11552,7 @@ keyword_sp:
         | HOSTS_SYM                {}
         | HOUR_SYM                 {}
         | IDENTIFIED_SYM           {}
+        | INDEX_STATS_SYM          {}
         | INVOKER_SYM              {}
         | IMPORT                   {}
         | INDEXES                  {}
@@ -11631,6 +11676,7 @@ keyword_sp:
         | SIMPLE_SYM               {}
         | SHARE_SYM                {}
         | SHUTDOWN                 {}
+        | SLOW_SYM                 {}
         | SNAPSHOT_SYM             {}
         | SOUNDS_SYM               {}
         | SOURCE_SYM               {}
@@ -11650,6 +11696,7 @@ keyword_sp:
         | SUSPEND_SYM              {}
         | SWAPS_SYM                {}
         | SWITCHES_SYM             {}
+        | TABLE_STATS_SYM          {}
         | TABLES                   {}
         | TABLE_CHECKSUM_SYM       {}
         | TABLESPACE               {}
@@ -11675,6 +11722,7 @@ keyword_sp:
         | UNKNOWN_SYM              {}
         | UNTIL_SYM                {}
         | USER                     {}
+        | USER_STATS_SYM           {}
         | USE_FRM                  {}
         | VARIABLES                {}
         | VIEW_SYM                 {}

=== modified file 'sql/structs.h'
--- sql/structs.h	2009-06-26 19:57:42 +0000
+++ sql/structs.h	2009-10-18 15:39:18 +0000
@@ -76,6 +76,7 @@ typedef struct st_key {
   uint  extra_length;
   uint	usable_key_parts;		/* Should normally be = key_parts */
   uint  block_size;
+  uint  name_length;
   enum  ha_key_alg algorithm;
   /*
     Note that parser is used when the table is opened for use, and
@@ -88,6 +89,8 @@ typedef struct st_key {
   };
   KEY_PART_INFO *key_part;
   char	*name;				/* Name of key */
+  /* Unique name for cache;  db + \0 + table_name + \0 + key_name + \0 */
+  uchar *cache_name;
   /*
     Array of AVG(#records with the same field value) for 1st ... Nth key part.
     0 means 'not known'.
@@ -231,6 +234,111 @@ typedef struct  user_conn {
   USER_RESOURCES user_resources;
 } USER_CONN;
 
+typedef struct st_user_stats
+{
+  char user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1];
+  // Account name the user is mapped to when this is a user from mapped_user.
+  // Otherwise, the same value as user.
+  char priv_user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1];
+  uint user_name_length;
+  uint total_connections;
+  uint concurrent_connections;
+  time_t connected_time;  // in seconds
+  double busy_time;       // in seconds
+  double cpu_time;        // in seconds
+  ulonglong bytes_received;
+  ulonglong bytes_sent;
+  ulonglong binlog_bytes_written;
+  ha_rows rows_examined, rows_read, rows_sent;
+  ha_rows rows_updated, rows_deleted, rows_inserted;
+  ulonglong select_commands, update_commands, other_commands;
+  ulonglong commit_trans, rollback_trans;
+  ulonglong denied_connections, lost_connections;
+  ulonglong access_denied_errors;
+  ulonglong empty_queries;
+} USER_STATS;
+
+/* Lookup function for hash tables with USER_STATS entries */
+extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
+                                     my_bool not_used __attribute__((unused)));
+
+/* Free all memory for a hash table with USER_STATS entries */
+extern void free_user_stats(USER_STATS* user_stats);
+
+/* Intialize an instance of USER_STATS */
+extern void
+init_user_stats(USER_STATS *user_stats,
+                const char *user,
+                size_t user_length,
+                const char *priv_user,
+                uint total_connections,
+                uint concurrent_connections,
+                time_t connected_time,
+                double busy_time,
+                double cpu_time,
+                ulonglong bytes_received,
+                ulonglong bytes_sent,
+                ulonglong binlog_bytes_written,
+                ha_rows rows_sent,
+                ha_rows rows_read,
+                ha_rows rows_inserted,
+                ha_rows rows_deleted,
+                ha_rows rows_updated,
+                ulonglong select_commands,
+                ulonglong update_commands,
+                ulonglong other_commands,
+                ulonglong commit_trans,
+                ulonglong rollback_trans,
+                ulonglong denied_connections,
+                ulonglong lost_connections,
+                ulonglong access_denied_errors,
+                ulonglong empty_queries);
+
+/* Increment values of an instance of USER_STATS */
+extern void
+add_user_stats(USER_STATS *user_stats,
+               uint total_connections,
+               uint concurrent_connections,
+               time_t connected_time,
+               double busy_time,
+               double cpu_time,
+               ulonglong bytes_received,
+               ulonglong bytes_sent,
+               ulonglong binlog_bytes_written,
+               ha_rows rows_sent,
+               ha_rows rows_read,
+               ha_rows rows_inserted,
+               ha_rows rows_deleted,
+               ha_rows rows_updated,
+               ulonglong select_commands,
+               ulonglong update_commands,
+               ulonglong other_commands,
+               ulonglong commit_trans,
+               ulonglong rollback_trans,
+               ulonglong denied_connections,
+               ulonglong lost_connections,
+               ulonglong access_denied_errors,
+               ulonglong empty_queries);
+
+typedef struct st_table_stats
+{
+  char table[NAME_LEN * 2 + 2];  // [db] + '\0' + [table] + '\0'
+  uint table_name_length;
+  ulonglong rows_read, rows_changed;
+  ulonglong rows_changed_x_indexes;
+  /* Stores enum db_type, but forward declarations cannot be done */
+  int engine_type;
+} TABLE_STATS;
+
+typedef struct st_index_stats
+{
+  // [db] + '\0' + [table] + '\0' + [index] + '\0'
+  char index[NAME_LEN * 3 + 3];
+  uint index_name_length;                       /* Length of 'index' */
+  ulonglong rows_read;
+} INDEX_STATS;
+
+
 	/* Bits in form->update */
 #define REG_MAKE_DUPP		1	/* Make a copy of record when read */
 #define REG_NEW_RECORD		2	/* Write a new record if not found */

=== modified file 'sql/table.cc'
--- sql/table.cc	2009-09-09 21:06:57 +0000
+++ sql/table.cc	2009-10-17 14:00:24 +0000
@@ -1325,6 +1325,19 @@ static int open_binary_frm(THD *thd, TAB
     {
       uint usable_parts= 0;
       keyinfo->name=(char*) share->keynames.type_names[key];
+      keyinfo->name_length= strlen(keyinfo->name);
+      keyinfo->cache_name=
+        (uchar*) alloc_root(&share->mem_root,
+                            share->table_cache_key.length+
+                            keyinfo->name_length + 1);
+      if (keyinfo->cache_name)           // If not out of memory
+      {
+        uchar *pos= keyinfo->cache_name;
+        memcpy(pos, share->table_cache_key.str, share->table_cache_key.length);
+        memcpy(pos + share->table_cache_key.length, keyinfo->name,
+               keyinfo->name_length+1);
+      }
+
       /* Fix fulltext keys for old .frm files */
       if (share->key_info[key].flags & HA_FULLTEXT)
 	share->key_info[key].algorithm= HA_KEY_ALG_FULLTEXT;

=== modified file 'sql/table.h'
--- sql/table.h	2009-09-15 10:46:35 +0000
+++ sql/table.h	2009-10-17 07:32:14 +0000
@@ -878,6 +878,7 @@ typedef struct st_foreign_key_info
 enum enum_schema_tables
 {
   SCH_CHARSETS= 0,
+  SCH_CLIENT_STATS,
   SCH_COLLATIONS,
   SCH_COLLATION_CHARACTER_SET_APPLICABILITY,
   SCH_COLUMNS,
@@ -887,6 +888,7 @@ enum enum_schema_tables
   SCH_FILES,
   SCH_GLOBAL_STATUS,
   SCH_GLOBAL_VARIABLES,
+  SCH_INDEX_STATS,
   SCH_KEY_COLUMN_USAGE,
   SCH_OPEN_TABLES,
   SCH_PARTITIONS,
@@ -905,8 +907,10 @@ enum enum_schema_tables
   SCH_TABLE_CONSTRAINTS,
   SCH_TABLE_NAMES,
   SCH_TABLE_PRIVILEGES,
+  SCH_TABLE_STATS,
   SCH_TRIGGERS,
   SCH_USER_PRIVILEGES,
+  SCH_USER_STATS,
   SCH_VARIABLES,
   SCH_VIEWS
 };

=== modified file 'sql/tztime.cc'
--- sql/tztime.cc	2009-09-07 20:50:10 +0000
+++ sql/tztime.cc	2009-10-17 08:09:23 +0000
@@ -1676,7 +1676,7 @@ my_tz_init(THD *org_thd, const char *def
 
   tz_leapcnt= 0;
 
-  res= table->file->index_first(table->record[0]);
+  res= table->file->ha_index_first(table->record[0]);
 
   while (!res)
   {
@@ -1698,7 +1698,7 @@ my_tz_init(THD *org_thd, const char *def
                 tz_leapcnt, (ulong) tz_lsis[tz_leapcnt-1].ls_trans,
                 tz_lsis[tz_leapcnt-1].ls_corr));
 
-    res= table->file->index_next(table->record[0]);
+    res= table->file->ha_index_next(table->record[0]);
   }
 
   (void)table->file->ha_index_end();
@@ -1865,8 +1865,8 @@ tz_load_from_open_tables(const String *t
   */
   (void)table->file->ha_index_init(0, 1);
 
-  if (table->file->index_read_map(table->record[0], table->field[0]->ptr,
-                                  HA_WHOLE_KEY, HA_READ_KEY_EXACT))
+  if (table->file->ha_index_read_map(table->record[0], table->field[0]->ptr,
+                                     HA_WHOLE_KEY, HA_READ_KEY_EXACT))
   {
 #ifdef EXTRA_DEBUG
     /*
@@ -1893,8 +1893,8 @@ tz_load_from_open_tables(const String *t
   table->field[0]->store((longlong) tzid, TRUE);
   (void)table->file->ha_index_init(0, 1);
 
-  if (table->file->index_read_map(table->record[0], table->field[0]->ptr,
-                                  HA_WHOLE_KEY, HA_READ_KEY_EXACT))
+  if (table->file->ha_index_read_map(table->record[0], table->field[0]->ptr,
+                                     HA_WHOLE_KEY, HA_READ_KEY_EXACT))
   {
     sql_print_error("Can't find description of time zone '%u'", tzid);
     goto end;
@@ -1920,8 +1920,8 @@ tz_load_from_open_tables(const String *t
   table->field[0]->store((longlong) tzid, TRUE);
   (void)table->file->ha_index_init(0, 1);
 
-  res= table->file->index_read_map(table->record[0], table->field[0]->ptr,
-                                   (key_part_map)1, HA_READ_KEY_EXACT);
+  res= table->file->ha_index_read_map(table->record[0], table->field[0]->ptr,
+                                      (key_part_map)1, HA_READ_KEY_EXACT);
   while (!res)
   {
     ttid= (uint)table->field[1]->val_int();
@@ -1968,8 +1968,8 @@ tz_load_from_open_tables(const String *t
 
     tmp_tz_info.typecnt= ttid + 1;
 
-    res= table->file->index_next_same(table->record[0],
-                                      table->field[0]->ptr, 4);
+    res= table->file->ha_index_next_same(table->record[0],
+                                         table->field[0]->ptr, 4);
   }
 
   if (res != HA_ERR_END_OF_FILE)
@@ -1991,8 +1991,8 @@ tz_load_from_open_tables(const String *t
   table->field[0]->store((longlong) tzid, TRUE);
   (void)table->file->ha_index_init(0, 1);
 
-  res= table->file->index_read_map(table->record[0], table->field[0]->ptr,
-                                   (key_part_map)1, HA_READ_KEY_EXACT);
+  res= table->file->ha_index_read_map(table->record[0], table->field[0]->ptr,
+                                      (key_part_map)1, HA_READ_KEY_EXACT);
   while (!res)
   {
     ttime= (my_time_t)table->field[1]->val_int();
@@ -2021,8 +2021,8 @@ tz_load_from_open_tables(const String *t
       ("time_zone_transition table: tz_id: %u  tt_time: %lu  tt_id: %u",
        tzid, (ulong) ttime, ttid));
 
-    res= table->file->index_next_same(table->record[0],
-                                      table->field[0]->ptr, 4);
+    res= table->file->ha_index_next_same(table->record[0],
+                                         table->field[0]->ptr, 4);
   }
 
   /*

-----------

Regards,
Monty



Follow ups