+++ /dev/null
-diff -r 2fdaeb546d25 innobase/buf/buf0rea.c
---- a/innobase/buf/buf0rea.c Mon Sep 08 16:39:06 2008 -0700
-+++ b/innobase/buf/buf0rea.c Mon Sep 08 16:40:14 2008 -0700
-@@ -188,6 +188,10 @@
- ulint low, high;
- ulint err;
- ulint i;
-+
-+ if (!(srv_read_ahead & 1)) {
-+ return(0);
-+ }
-
- if (srv_startup_is_before_trx_rollback_phase) {
- /* No read-ahead to avoid thread deadlocks */
-@@ -396,6 +400,10 @@
- ulint err;
- ulint i;
-
-+ if (!(srv_read_ahead & 2)) {
-+ return(0);
-+ }
-+
- if (srv_startup_is_before_trx_rollback_phase) {
- /* No read-ahead to avoid thread deadlocks */
- return(0);
-diff -r 2fdaeb546d25 innobase/include/srv0srv.h
---- a/innobase/include/srv0srv.h Mon Sep 08 16:39:06 2008 -0700
-+++ b/innobase/include/srv0srv.h Mon Sep 08 16:40:14 2008 -0700
-@@ -131,6 +131,12 @@
- extern ulong srv_max_purge_lag;
- extern ibool srv_use_awe;
- extern ibool srv_use_adaptive_hash_indexes;
-+
-+extern ulint srv_read_ahead;
-+extern ulint srv_ibuf_contract_const;
-+extern ulint srv_ibuf_contract_burst;
-+extern ulint srv_buf_flush_const;
-+extern ulint srv_buf_flush_burst;
- /*-------------------------------------------*/
-
- extern ulint srv_n_rows_inserted;
-diff -r 2fdaeb546d25 innobase/srv/srv0srv.c
---- a/innobase/srv/srv0srv.c Mon Sep 08 16:39:06 2008 -0700
-+++ b/innobase/srv/srv0srv.c Mon Sep 08 16:40:14 2008 -0700
-@@ -322,6 +322,11 @@
- ibool srv_use_awe = FALSE;
- ibool srv_use_adaptive_hash_indexes = TRUE;
-
-+ulint srv_read_ahead = 3; /* 1: random 2: linear 3: Both */
-+ulint srv_ibuf_contract_const = 5;
-+ulint srv_ibuf_contract_burst = 20;
-+ulint srv_buf_flush_const = 10;
-+ulint srv_buf_flush_burst = 100;
- /*-------------------------------------------*/
- ulong srv_n_spin_wait_rounds = 20;
- ulong srv_n_free_tickets_to_enter = 500;
-@@ -2298,7 +2303,7 @@
- + buf_pool->n_pages_written;
- if (n_pend_ios < 3 && (n_ios - n_ios_old < 5)) {
- srv_main_thread_op_info = "doing insert buffer merge";
-- ibuf_contract_for_n_pages(TRUE, 5);
-+ ibuf_contract_for_n_pages(TRUE, srv_ibuf_contract_burst);
-
- srv_main_thread_op_info = "flushing log";
-
-@@ -2311,7 +2316,7 @@
- /* Try to keep the number of modified pages in the
- buffer pool under the limit wished by the user */
-
-- n_pages_flushed = buf_flush_batch(BUF_FLUSH_LIST, 100,
-+ n_pages_flushed = buf_flush_batch(BUF_FLUSH_LIST, srv_buf_flush_burst,
- ut_dulint_max);
-
- /* If we had to do the flush, it may have taken
-@@ -2349,7 +2354,7 @@
- if (n_pend_ios < 3 && (n_ios - n_ios_very_old < 200)) {
-
- srv_main_thread_op_info = "flushing buffer pool pages";
-- buf_flush_batch(BUF_FLUSH_LIST, 100, ut_dulint_max);
-+ buf_flush_batch(BUF_FLUSH_LIST, srv_buf_flush_burst, ut_dulint_max);
-
- srv_main_thread_op_info = "flushing log";
- log_buffer_flush_to_disk();
-@@ -2359,7 +2364,7 @@
- even if the server were active */
-
- srv_main_thread_op_info = "doing insert buffer merge";
-- ibuf_contract_for_n_pages(TRUE, 5);
-+ ibuf_contract_for_n_pages(TRUE, srv_ibuf_contract_const);
-
- srv_main_thread_op_info = "flushing log";
- log_buffer_flush_to_disk();
-@@ -2401,14 +2406,14 @@
- (> 70 %), we assume we can afford reserving the disk(s) for
- the time it requires to flush 100 pages */
-
-- n_pages_flushed = buf_flush_batch(BUF_FLUSH_LIST, 100,
-+ n_pages_flushed = buf_flush_batch(BUF_FLUSH_LIST, srv_buf_flush_burst,
- ut_dulint_max);
- } else {
- /* Otherwise, we only flush a small number of pages so that
- we do not unnecessarily use much disk i/o capacity from
- other work */
-
-- n_pages_flushed = buf_flush_batch(BUF_FLUSH_LIST, 10,
-+ n_pages_flushed = buf_flush_batch(BUF_FLUSH_LIST, srv_buf_flush_const,
- ut_dulint_max);
- }
-
-@@ -2497,7 +2502,7 @@
- if (srv_fast_shutdown && srv_shutdown_state > 0) {
- n_bytes_merged = 0;
- } else {
-- n_bytes_merged = ibuf_contract_for_n_pages(TRUE, 20);
-+ n_bytes_merged = ibuf_contract_for_n_pages(TRUE, srv_ibuf_contract_burst);
- }
-
- srv_main_thread_op_info = "reserving kernel mutex";
-@@ -2514,7 +2519,7 @@
-
- if (srv_fast_shutdown < 2) {
- n_pages_flushed =
-- buf_flush_batch(BUF_FLUSH_LIST, 100, ut_dulint_max);
-+ buf_flush_batch(BUF_FLUSH_LIST, srv_buf_flush_burst, ut_dulint_max);
- } else {
- /* In the fastest shutdown we do not flush the buffer pool
- to data files: we set n_pages_flushed to 0 artificially. */
-diff -r 2fdaeb546d25 patch_info/control_flush_and_merge_and_read.info
---- /dev/null Thu Jan 01 00:00:00 1970 +0000
-+++ b/patch_info/control_flush_and_merge_and_read.info Mon Sep 08 16:40:14 2008 -0700
-@@ -0,0 +1,6 @@
-+File=control_flush_and_merge_and_read.patch
-+Name=InnoDB patch to control insert buffer and flushing
-+Version=1.0
-+Author=Yasufumi Kinoshita
-+License=BSD
-+Comment=
-diff -r 2fdaeb546d25 sql/ha_innodb.h
---- a/sql/ha_innodb.h Mon Sep 08 16:39:06 2008 -0700
-+++ b/sql/ha_innodb.h Mon Sep 08 16:40:14 2008 -0700
-@@ -234,6 +234,11 @@
- extern ulong srv_thread_concurrency;
- extern ulong srv_commit_concurrency;
- extern ulong srv_flush_log_at_trx_commit;
-+extern ulong srv_read_ahead;
-+extern ulong srv_ibuf_contract_const;
-+extern ulong srv_ibuf_contract_burst;
-+extern ulong srv_buf_flush_const;
-+extern ulong srv_buf_flush_burst;
- }
-
- bool innobase_init(void);
-diff -r 2fdaeb546d25 sql/mysqld.cc
---- a/sql/mysqld.cc Mon Sep 08 16:39:06 2008 -0700
-+++ b/sql/mysqld.cc Mon Sep 08 16:40:14 2008 -0700
-@@ -5014,7 +5014,10 @@
- OPT_SECURE_FILE_PRIV,
- OPT_KEEP_FILES_ON_CREATE,
- OPT_INNODB_ADAPTIVE_HASH_INDEX,
-- OPT_FEDERATED
-+ OPT_FEDERATED,
-+ OPT_INNODB_READ_AHEAD,
-+ OPT_INNODB_IBUF_CONTRACT_CONST, OPT_INNODB_IBUF_CONTRACT_BURST,
-+ OPT_INNODB_BUF_FLUSH_CONST, OPT_INNODB_BUF_FLUSH_BURST
- };
-
-
-@@ -5321,6 +5324,26 @@
- (gptr*) &global_system_variables.innodb_table_locks,
- (gptr*) &global_system_variables.innodb_table_locks,
- 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
-+ {"innodb_read_ahead", OPT_INNODB_READ_AHEAD,
-+ "Enable/Diasable read aheads bit0:random bit1:linear",
-+ (gptr*) &srv_read_ahead, (gptr*) &srv_read_ahead,
-+ 0, GET_ULONG, REQUIRED_ARG, 3, 0, 3, 0, 0, 0},
-+ {"innodb_ibuf_contract_const", OPT_INNODB_IBUF_CONTRACT_CONST,
-+ "Const activity of merging insert buffer",
-+ (gptr*) &srv_ibuf_contract_const, (gptr*) &srv_ibuf_contract_const,
-+ 0, GET_ULONG, REQUIRED_ARG, 5, 1, 50000, 0, 0, 0},
-+ {"innodb_ibuf_contract_burst", OPT_INNODB_IBUF_CONTRACT_BURST,
-+ "Burst activity of merging insert buffer",
-+ (gptr*) &srv_ibuf_contract_burst, (gptr*) &srv_ibuf_contract_burst,
-+ 0, GET_ULONG, REQUIRED_ARG, 20, 1, 50000, 0, 0, 0},
-+ {"innodb_buf_flush_const", OPT_INNODB_BUF_FLUSH_CONST,
-+ "Const activity of flushing buffer pool",
-+ (gptr*) &srv_buf_flush_const, (gptr*) &srv_buf_flush_const,
-+ 0, GET_ULONG, REQUIRED_ARG, 10, 1, 50000, 0, 0, 0},
-+ {"innodb_buf_flush_burst", OPT_INNODB_BUF_FLUSH_BURST,
-+ "Burst activity of flushing buffer pool",
-+ (gptr*) &srv_buf_flush_burst, (gptr*) &srv_buf_flush_burst,
-+ 0, GET_ULONG, REQUIRED_ARG, 100, 1, 50000, 0, 0, 0},
- #endif /* End HAVE_INNOBASE_DB */
- {"isam", OPT_ISAM, "Obsolete. ISAM storage engine is no longer supported.",
- (gptr*) &opt_isam, (gptr*) &opt_isam, 0, GET_BOOL, NO_ARG, 0, 0, 0,
-diff -r 2fdaeb546d25 sql/set_var.cc
---- a/sql/set_var.cc Mon Sep 08 16:39:06 2008 -0700
-+++ b/sql/set_var.cc Mon Sep 08 16:40:14 2008 -0700
-@@ -476,6 +476,16 @@
- sys_var_long_ptr sys_innodb_flush_log_at_trx_commit(
- "innodb_flush_log_at_trx_commit",
- &srv_flush_log_at_trx_commit);
-+sys_var_long_ptr sys_innodb_read_ahead("innodb_read_ahead",
-+ &srv_read_ahead);
-+sys_var_long_ptr sys_innodb_ibuf_contract_const("innodb_ibuf_contract_const",
-+ &srv_ibuf_contract_const);
-+sys_var_long_ptr sys_innodb_ibuf_contract_burst("innodb_ibuf_contract_burst",
-+ &srv_ibuf_contract_burst);
-+sys_var_long_ptr sys_innodb_buf_flush_const("innodb_buf_flush_const",
-+ &srv_buf_flush_const);
-+sys_var_long_ptr sys_innodb_buf_flush_burst("innodb_buf_flush_burst",
-+ &srv_buf_flush_burst);
- #endif
-
- /* Condition pushdown to storage engine */
-@@ -818,6 +828,11 @@
- &sys_innodb_thread_concurrency,
- &sys_innodb_commit_concurrency,
- &sys_innodb_flush_log_at_trx_commit,
-+ &sys_innodb_read_ahead,
-+ &sys_innodb_ibuf_contract_const,
-+ &sys_innodb_ibuf_contract_burst,
-+ &sys_innodb_buf_flush_const,
-+ &sys_innodb_buf_flush_burst,
- #endif
- &sys_trust_routine_creators,
- &sys_trust_function_creators,
-@@ -953,6 +968,11 @@
- {sys_innodb_table_locks.name, (char*) &sys_innodb_table_locks, SHOW_SYS},
- {sys_innodb_thread_concurrency.name, (char*) &sys_innodb_thread_concurrency, SHOW_SYS},
- {sys_innodb_thread_sleep_delay.name, (char*) &sys_innodb_thread_sleep_delay, SHOW_SYS},
-+ {sys_innodb_read_ahead.name, (char*) &sys_innodb_read_ahead, SHOW_SYS},
-+ {sys_innodb_ibuf_contract_const.name, (char*) &sys_innodb_ibuf_contract_const, SHOW_SYS},
-+ {sys_innodb_ibuf_contract_burst.name, (char*) &sys_innodb_ibuf_contract_burst, SHOW_SYS},
-+ {sys_innodb_buf_flush_const.name, (char*) &sys_innodb_buf_flush_const, SHOW_SYS},
-+ {sys_innodb_buf_flush_burst.name, (char*) &sys_innodb_buf_flush_burst, SHOW_SYS},
- #endif
- {sys_interactive_timeout.name,(char*) &sys_interactive_timeout, SHOW_SYS},
- {sys_join_buffer_size.name, (char*) &sys_join_buffer_size, SHOW_SYS},
+++ /dev/null
-diff -r ab66c8ca382a include/mysql_com.h
---- a/include/mysql_com.h Thu Sep 04 12:08:00 2008 -0700
-+++ b/include/mysql_com.h Thu Sep 04 12:12:44 2008 -0700
-@@ -115,6 +115,8 @@
- thread */
- #define REFRESH_MASTER 128 /* Remove all bin logs in the index
- and truncate the index */
-+#define REFRESH_TABLE_STATS 256 /* Refresh table stats hash table */
-+#define REFRESH_INDEX_STATS 512 /* Refresh index stats hash table */
-
- /* The following can't be set with mysql_refresh() */
- #define REFRESH_READ_LOCK 16384 /* Lock tables for read */
-diff -r ab66c8ca382a patch_info/userstats.info
---- /dev/null Thu Jan 01 00:00:00 1970 +0000
-+++ b/patch_info/userstats.info Thu Sep 04 12:12:44 2008 -0700
-@@ -0,0 +1,6 @@
-+File=userstats.patch
-+Name=SHOW USER/TABLE/INDEX statistics
-+Version=1.0
-+Author=Google
-+License=GPL
-+Comment=Added INFORMATION_SCHEMA.*_STATISTICS
-diff -r ab66c8ca382a sql/handler.cc
---- a/sql/handler.cc Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/handler.cc Thu Sep 04 12:12:44 2008 -0700
-@@ -1168,6 +1168,7 @@
- error=1;
- }
- status_var_increment(thd->status_var.ha_commit_count);
-+ thd->diff_commit_trans++;
- ha_info_next= ha_info->next();
- ha_info->reset(); /* keep it conveniently zero-filled */
- }
-@@ -1235,6 +1236,7 @@
- error=1;
- }
- status_var_increment(thd->status_var.ha_rollback_count);
-+ thd->diff_rollback_trans++;
- ha_info_next= ha_info->next();
- ha_info->reset(); /* keep it conveniently zero-filled */
- }
-@@ -1682,6 +1684,7 @@
- error=1;
- }
- status_var_increment(thd->status_var.ha_rollback_count);
-+ thd->diff_rollback_trans++;
- ha_info_next= ha_info->next();
- ha_info->reset(); /* keep it conveniently zero-filled */
- }
-@@ -2016,6 +2019,8 @@
- dup_ref=ref+ALIGN_SIZE(ref_length);
- cached_table_flags= table_flags();
- }
-+ rows_read = rows_changed = 0;
-+ memset(index_rows_read, 0, sizeof(index_rows_read));
- DBUG_RETURN(error);
- }
-
-@@ -3448,6 +3453,97 @@
- return;
- }
-
-+// Updates the global table stats with the TABLE this handler represents.
-+void handler::update_global_table_stats() {
-+ if (!rows_read && !rows_changed) return; // Nothing to update.
-+ // table_cache_key is db_name + '\0' + table_name + '\0'.
-+ if (!table->s || !table->s->table_cache_key.str || !table->s->table_name.str) return;
-+
-+ TABLE_STATS* table_stats;
-+ char key[NAME_LEN * 2 + 2];
-+ // [db] + '.' + [table]
-+ sprintf(key, "%s.%s", table->s->table_cache_key.str, table->s->table_name.str);
-+
-+ pthread_mutex_lock(&LOCK_global_table_stats);
-+ // Gets the global table stats, creating one if necessary.
-+ if (!(table_stats = (TABLE_STATS*)hash_search(&global_table_stats,
-+ (uchar*)key,
-+ strlen(key)))) {
-+ if (!(table_stats = ((TABLE_STATS*)
-+ my_malloc(sizeof(TABLE_STATS), MYF(MY_WME))))) {
-+ // Out of memory.
-+ sql_print_error("Allocating table stats failed.");
-+ goto end;
-+ }
-+ strncpy(table_stats->table, key, sizeof(table_stats->table));
-+ table_stats->rows_read = 0;
-+ table_stats->rows_changed = 0;
-+ table_stats->rows_changed_x_indexes = 0;
-+
-+ if (my_hash_insert(&global_table_stats, (uchar*)table_stats)) {
-+ // Out of memory.
-+ sql_print_error("Inserting table stats failed.");
-+ my_free((char*)table_stats, 0);
-+ goto end;
-+ }
-+ }
-+ // Updates the global table stats.
-+ table_stats->rows_read += rows_read;
-+ table_stats->rows_changed += rows_changed;
-+ table_stats->rows_changed_x_indexes +=
-+ rows_changed * (table->s->keys ? table->s->keys : 1);
-+ rows_read = rows_changed = 0;
-+end:
-+ pthread_mutex_unlock(&LOCK_global_table_stats);
-+}
-+
-+// Updates the global index stats with this handler's accumulated index reads.
-+void handler::update_global_index_stats() {
-+ // table_cache_key is db_name + '\0' + table_name + '\0'.
-+ if (!table->s || !table->s->table_cache_key.str || !table->s->table_name.str) return;
-+
-+ for (int x = 0; x < table->s->keys; x++) {
-+ if (index_rows_read[x]) {
-+ // Rows were read using this index.
-+ KEY* key_info = &table->key_info[x];
-+
-+ if (!key_info->name) continue;
-+
-+ INDEX_STATS* index_stats;
-+ char key[NAME_LEN * 3 + 3];
-+ // [db] + '.' + [table] + '.' + [index]
-+ sprintf(key, "%s.%s.%s", table->s->table_cache_key.str,
-+ table->s->table_name.str, key_info->name);
-+
-+ pthread_mutex_lock(&LOCK_global_index_stats);
-+ // Gets the global index stats, creating one if necessary.
-+ if (!(index_stats = (INDEX_STATS*)hash_search(&global_index_stats,
-+ (uchar*)key,
-+ strlen(key)))) {
-+ if (!(index_stats = ((INDEX_STATS*)
-+ my_malloc(sizeof(INDEX_STATS), MYF(MY_WME))))) {
-+ // Out of memory.
-+ sql_print_error("Allocating index stats failed.");
-+ goto end;
-+ }
-+ strncpy(index_stats->index, key, sizeof(index_stats->index));
-+ index_stats->rows_read = 0;
-+
-+ if (my_hash_insert(&global_index_stats, (uchar*)index_stats)) {
-+ // Out of memory.
-+ sql_print_error("Inserting index stats failed.");
-+ my_free((char*)index_stats, 0);
-+ goto end;
-+ }
-+ }
-+ // Updates the global index stats.
-+ index_stats->rows_read += index_rows_read[x];
-+ index_rows_read[x] = 0;
-+end:
-+ pthread_mutex_unlock(&LOCK_global_index_stats);
-+ }
-+ }
-+}
-
- /****************************************************************************
- ** Some general functions that isn't in the handler class
-diff -r ab66c8ca382a sql/handler.h
---- a/sql/handler.h Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/handler.h Thu Sep 04 12:12:44 2008 -0700
-@@ -29,6 +29,10 @@
- #endif
-
- #define USING_TRANSACTIONS
-+
-+#if MAX_KEY > 128
-+#error MAX_KEY is too large. Values up to 128 are supported.
-+#endif
-
- // the following is for checking tables
-
-@@ -690,6 +694,7 @@
- */
- enum log_status (*get_log_status)(handlerton *hton, char *log);
-
-+
- /*
- Iterators creator.
- Presence of the pointer should be checked before using
-@@ -1130,6 +1135,10 @@
- */
- Discrete_interval auto_inc_interval_for_cur_row;
-
-+ ulonglong rows_read;
-+ ulonglong rows_changed;
-+ ulonglong index_rows_read[MAX_KEY];
-+
- handler(handlerton *ht_arg, TABLE_SHARE *share_arg)
- :table_share(share_arg), table(0),
- estimation_rows_to_insert(0), ht(ht_arg),
-@@ -1154,8 +1154,10 @@
- ft_handler(0), inited(NONE),
- locked(FALSE), implicit_emptied(0),
- pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
-- auto_inc_intervals_count(0)
-- {}
-+ auto_inc_intervals_count(0), rows_read(0), rows_changed(0)
-+ {
-+ memset(index_rows_read, 0, sizeof(index_rows_read));
-+ }
- virtual ~handler(void)
- {
- DBUG_ASSERT(locked == FALSE);
-@@ -1262,7 +1273,13 @@
- {
- table= table_arg;
- table_share= share;
-+ rows_read = rows_changed = 0;
-+ memset(index_rows_read, 0, sizeof(index_rows_read));
- }
-+
-+ void update_global_table_stats();
-+ void update_global_index_stats();
-+
- virtual double scan_time()
- { return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; }
- virtual double read_time(uint index, uint ranges, ha_rows rows)
-diff -r ab66c8ca382a sql/lex.h
---- a/sql/lex.h Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/lex.h Thu Sep 04 12:12:44 2008 -0700
-@@ -245,6 +245,7 @@
- { "IN", SYM(IN_SYM)},
- { "INDEX", SYM(INDEX_SYM)},
- { "INDEXES", SYM(INDEXES)},
-+ { "INDEX_STATISTICS", SYM(INDEX_STATS_SYM)},
- { "INFILE", SYM(INFILE)},
- { "INITIAL_SIZE", SYM(INITIAL_SIZE_SYM)},
- { "INNER", SYM(INNER_SYM)},
-@@ -528,6 +529,7 @@
- { "TABLES", SYM(TABLES)},
- { "TABLESPACE", SYM(TABLESPACE)},
- { "TABLE_CHECKSUM", SYM(TABLE_CHECKSUM_SYM)},
-+ { "TABLE_STATISTICS", SYM(TABLE_STATS_SYM)},
- { "TEMPORARY", SYM(TEMPORARY)},
- { "TEMPTABLE", SYM(TEMPTABLE_SYM)},
- { "TERMINATED", SYM(TERMINATED)},
-@@ -570,6 +572,7 @@
- { "USE", SYM(USE_SYM)},
- { "USER", SYM(USER)},
- { "USER_RESOURCES", SYM(RESOURCES)},
-+ { "USER_STATISTICS", SYM(USER_STATS_SYM)},
- { "USE_FRM", SYM(USE_FRM)},
- { "USING", SYM(USING)},
- { "UTC_DATE", SYM(UTC_DATE_SYM)},
-diff -r ab66c8ca382a sql/mysql_priv.h
---- a/sql/mysql_priv.h Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/mysql_priv.h Thu Sep 04 12:12:44 2008 -0700
-@@ -1058,7 +1058,19 @@
- bool multi_delete_set_locks_and_link_aux_tables(LEX *lex);
- void init_max_user_conn(void);
- void init_update_queries(void);
-+void init_global_user_stats(void);
-+void init_global_table_stats(void);
-+void init_global_index_stats(void);
- void free_max_user_conn(void);
-+void free_global_user_stats(void);
-+void free_global_table_stats(void);
-+void free_global_index_stats(void);
-+// Uses the THD to update the global stats.
-+void update_global_user_stats(THD* thd);
-+// Set stats for concurrent connections displayed by mysqld_show().
-+void set_concurrent_connections_stats();
-+// Increments connection count for user.
-+int increment_connection_count(THD* thd, bool use_lock);
- pthread_handler_t handle_bootstrap(void *arg);
- int mysql_execute_command(THD *thd);
- bool do_command(THD *thd);
-@@ -2009,6 +2021,12 @@
- extern struct system_variables max_system_variables;
- extern struct system_status_var global_status_var;
- extern struct rand_struct sql_rand;
-+extern HASH global_user_stats;
-+extern pthread_mutex_t LOCK_global_user_stats;
-+extern HASH global_table_stats;
-+extern pthread_mutex_t LOCK_global_table_stats;
-+extern HASH global_index_stats;
-+extern pthread_mutex_t LOCK_global_index_stats;
-
- extern const char *opt_date_time_formats[];
- extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
-diff -r ab66c8ca382a sql/mysqld.cc
---- a/sql/mysqld.cc Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/mysqld.cc Thu Sep 04 12:12:44 2008 -0700
-@@ -588,6 +588,11 @@
- LOCK_global_system_variables,
- LOCK_user_conn, LOCK_slave_list, LOCK_active_mi,
- LOCK_connection_count;
-+
-+pthread_mutex_t LOCK_global_user_stats;
-+pthread_mutex_t LOCK_global_table_stats;
-+pthread_mutex_t LOCK_global_index_stats;
-+
- /**
- The below lock protects access to two global server variables:
- max_prepared_stmt_count and prepared_stmt_count. These variables
-@@ -1265,6 +1270,9 @@
- x_free(opt_secure_file_priv);
- bitmap_free(&temp_pool);
- free_max_user_conn();
-+ free_global_user_stats();
-+ free_global_table_stats();
-+ free_global_index_stats();
- #ifdef HAVE_REPLICATION
- end_slave_list();
- #endif
-@@ -1377,6 +1385,9 @@
- (void) pthread_cond_destroy(&COND_thread_cache);
- (void) pthread_cond_destroy(&COND_flush_thread_cache);
- (void) pthread_cond_destroy(&COND_manager);
-+ (void) pthread_mutex_destroy(&LOCK_global_user_stats);
-+ (void) pthread_mutex_destroy(&LOCK_global_table_stats);
-+ (void) pthread_mutex_destroy(&LOCK_global_index_stats);
- }
-
- #endif /*EMBEDDED_LIBRARY*/
-@@ -3069,6 +3080,7 @@
- {"show_function_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS_FUNC]), SHOW_LONG_STATUS},
- {"show_grants", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS]), SHOW_LONG_STATUS},
- {"show_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS},
-+ {"show_index_stats", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_INDEX_STATS]), SHOW_LONG_STATUS},
- {"show_master_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_MASTER_STAT]), SHOW_LONG_STATUS},
- {"show_new_master", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_NEW_MASTER]), SHOW_LONG_STATUS},
- {"show_open_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_OPEN_TABLES]), SHOW_LONG_STATUS},
-@@ -3085,9 +3097,11 @@
- {"show_slave_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_STAT]), SHOW_LONG_STATUS},
- {"show_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS]), SHOW_LONG_STATUS},
- {"show_storage_engines", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STORAGE_ENGINES]), SHOW_LONG_STATUS},
-+ {"show_table_stats", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATS]), SHOW_LONG_STATUS},
- {"show_table_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATUS]), SHOW_LONG_STATUS},
- {"show_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLES]), SHOW_LONG_STATUS},
- {"show_triggers", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TRIGGERS]), SHOW_LONG_STATUS},
-+ {"show_user_stats", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_USER_STATS]), SHOW_LONG_STATUS},
- {"show_variables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_VARIABLES]), SHOW_LONG_STATUS},
- {"show_warnings", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_WARNS]), SHOW_LONG_STATUS},
- {"slave_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS},
-@@ -3503,6 +3517,9 @@
- #endif
- (void) pthread_mutex_init(&LOCK_server_started, MY_MUTEX_INIT_FAST);
- (void) pthread_cond_init(&COND_server_started,NULL);
-+ (void) pthread_mutex_init(&LOCK_global_user_stats, MY_MUTEX_INIT_FAST);
-+ (void) pthread_mutex_init(&LOCK_global_table_stats, MY_MUTEX_INIT_FAST);
-+ (void) pthread_mutex_init(&LOCK_global_index_stats, MY_MUTEX_INIT_FAST);
- sp_cache_init();
- #ifdef HAVE_EVENT_SCHEDULER
- Events::init_mutexes();
-@@ -3872,6 +3889,9 @@
- if (!errmesg[0][0])
- unireg_abort(1);
-
-+ init_global_table_stats();
-+ init_global_index_stats();
-+
- /* We have to initialize the storage engines before CSV logging */
- if (ha_init())
- {
-@@ -4018,6 +4038,7 @@
-
- init_max_user_conn();
- init_update_queries();
-+ init_global_user_stats();
- DBUG_RETURN(0);
- }
-
-diff -r ab66c8ca382a sql/sql_base.cc
---- a/sql/sql_base.cc Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/sql_base.cc Thu Sep 04 12:12:44 2008 -0700
-@@ -1342,6 +1342,12 @@
- DBUG_ASSERT(!table->file || table->file->inited == handler::NONE);
- 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;
- /*
-@@ -1880,6 +1886,9 @@
- DBUG_ENTER("close_temporary");
- DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
- table->s->db.str, table->s->table_name.str));
-+
-+ table->file->update_global_table_stats();
-+ table->file->update_global_index_stats();
-
- free_io_cache(table);
- closefrm(table, 0);
-diff -r ab66c8ca382a sql/sql_class.cc
---- a/sql/sql_class.cc Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/sql_class.cc Thu Sep 04 12:12:44 2008 -0700
-@@ -577,6 +577,8 @@
- bzero(ha_data, sizeof(ha_data));
- mysys_var=0;
- binlog_evt_union.do_union= FALSE;
-+ busy_time = 0;
-+ updated_row_count = 0;
- enable_slow_log= 0;
- #ifndef DBUG_OFF
- dbug_sentry=THD_SENTRY_MAGIC;
-@@ -761,8 +763,55 @@
- update_charset();
- reset_current_stmt_binlog_row_based();
- bzero((char *) &status_var, sizeof(status_var));
-+ reset_stats();
- }
-
-+// Resets stats in a THD.
-+void THD::reset_stats(void) {
-+ current_connect_time = time(NULL);
-+ last_global_update_time = current_connect_time;
-+ reset_diff_stats();
-+}
-+
-+// Resets the 'diff' stats, which are used to update global stats.
-+void THD::reset_diff_stats(void) {
-+ diff_total_busy_time = 0;
-+ diff_total_sent_rows = 0;
-+ diff_total_updated_rows = 0;
-+ diff_select_commands = 0;
-+ diff_update_commands = 0;
-+ diff_other_commands = 0;
-+ diff_commit_trans = 0;
-+ diff_rollback_trans = 0;
-+}
-+
-+// Updates 'diff' stats of a THD.
-+void THD::update_stats() {
-+ diff_total_busy_time += busy_time;
-+ diff_total_sent_rows += sent_row_count;
-+ diff_total_updated_rows += updated_row_count;
-+ // The replication thread has the COM_CONNECT command.
-+ if ((old_command == COM_QUERY || command == COM_CONNECT) &&
-+ (lex->sql_command >= 0 && lex->sql_command < SQLCOM_END)) {
-+ // A SQL query.
-+ if (lex->sql_command == SQLCOM_SELECT) {
-+ if (!(sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND)) {
-+ diff_select_commands++;
-+ } else {
-+ // 'SHOW ' commands become SQLCOM_SELECT.
-+ diff_other_commands++;
-+ // 'SHOW ' commands shouldn't inflate total sent row count.
-+ diff_total_sent_rows -= sent_row_count;
-+ }
-+ } else if (is_update_query(lex->sql_command)) {
-+ diff_update_commands++;
-+ } else {
-+ diff_other_commands++;
-+ }
-+ }
-+ // diff_commit_trans is updated in handler.cc.
-+ // diff_rollback_trans is updated in handler.cc.
-+}
-
- /*
- Init THD for query processing.
-diff -r ab66c8ca382a sql/sql_class.h
---- a/sql/sql_class.h Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/sql_class.h Thu Sep 04 12:12:44 2008 -0700
-@@ -1310,6 +1310,8 @@
- first byte of the packet in do_command()
- */
- enum enum_server_command command;
-+ // Used to save the command, before it is set to COM_SLEEP.
-+ enum enum_server_command old_command;
- uint32 server_id;
- uint32 file_id; // for LOAD DATA INFILE
- /* remote (peer) port */
-@@ -1584,6 +1586,7 @@
- ulonglong options; /* Bitmap of states */
- longlong row_count_func; /* For the ROW_COUNT() function */
- ha_rows cuted_fields;
-+ ha_rows updated_row_count;
-
- /*
- number of rows we actually sent to the client, including "synthetic"
-@@ -1735,6 +1738,27 @@
- */
- LOG_INFO* current_linfo;
- NET* slave_net; // network connection from slave -> m.
-+
-+ /*
-+ Used to update global user stats. The global user stats are updated
-+ occasionally with the 'diff' variables. After the update, the 'diff'
-+ variables are reset to 0.
-+ */
-+ // Time when the current thread connected to MySQL.
-+ time_t current_connect_time;
-+ // Last time when THD stats were updated in global_user_stats.
-+ time_t last_global_update_time;
-+ // Busy (non-idle) time for just one command.
-+ double busy_time;
-+ // Busy time not updated in global_user_stats yet.
-+ double diff_total_busy_time;
-+ // Number of rows not reflected in global_user_stats yet.
-+ ha_rows diff_total_sent_rows, diff_total_updated_rows;
-+ // Number of commands not reflected in global_user_stats yet.
-+ ulonglong diff_select_commands, diff_update_commands, diff_other_commands;
-+ // Number of transactions not reflected in global_user_stats yet.
-+ ulonglong diff_commit_trans, diff_rollback_trans;
-+
- /* Used by the sys_var class to store temporary values */
- union
- {
-@@ -1797,6 +1821,9 @@
- alloc_root.
- */
- void init_for_queries();
-+ void reset_stats(void);
-+ void reset_diff_stats(void);
-+ void update_stats(void);
- void change_user(void);
- void cleanup(void);
- void cleanup_after_query();
-diff -r ab66c8ca382a sql/sql_connect.cc
---- a/sql/sql_connect.cc Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/sql_connect.cc Thu Sep 04 12:12:44 2008 -0700
-@@ -520,6 +520,14 @@
- 0,0,
- (hash_get_key) get_key_conn, (hash_free_key) free_user,
- 0);
-+ if (hash_init(&hash_user_connections,system_charset_info,max_connections,
-+ 0,0,
-+ (hash_get_key) get_key_conn, (hash_free_key) free_user,
-+ 0)) {
-+ sql_print_error("Initializing hash_user_connections failed.");
-+ exit(1);
-+ }
-+
- #endif
- }
-
-@@ -1107,6 +1115,13 @@
- if (login_connection(thd))
- goto end_thread;
-
-+ thd->reset_stats();
-+ // Updates global user connection stats.
-+ if (increment_connection_count(thd, true)) {
-+ net_send_error(thd, ER_OUTOFMEMORY); // Out of memory
-+ goto end_thread;
-+ }
-+
- prepare_new_connection_state(thd);
-
- while (!net->error && net->vio != 0 &&
-@@ -1119,6 +1134,8 @@
-
- end_thread:
- close_connection(thd, 0, 1);
-+ thd->update_stats();
-+ update_global_user_stats(thd);
- if (thread_scheduler.end_thread(thd,1))
- return 0; // Probably no-threads
-
-diff -r ab66c8ca382a sql/sql_delete.cc
---- a/sql/sql_delete.cc Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/sql_delete.cc Thu Sep 04 12:12:44 2008 -0700
-@@ -402,6 +402,7 @@
- my_ok(thd, (ha_rows) thd->row_count_func);
- DBUG_PRINT("info",("%ld records deleted",(long) deleted));
- }
-+ thd->updated_row_count += deleted;
- DBUG_RETURN(error >= 0 || thd->is_error());
- }
-
-@@ -938,6 +939,7 @@
- thd->row_count_func= deleted;
- ::my_ok(thd, (ha_rows) thd->row_count_func);
- }
-+ thd->updated_row_count += deleted;
- return 0;
- }
-
-diff -r ab66c8ca382a sql/sql_insert.cc
---- a/sql/sql_insert.cc Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/sql_insert.cc Thu Sep 04 12:12:44 2008 -0700
-@@ -969,6 +969,7 @@
- thd->row_count_func= info.copied + info.deleted + updated;
- ::my_ok(thd, (ulong) thd->row_count_func, id, buff);
- }
-+ thd->updated_row_count += thd->row_count_func;
- thd->abort_on_warning= 0;
- DBUG_RETURN(FALSE);
-
-diff -r ab66c8ca382a sql/sql_lex.h
---- a/sql/sql_lex.h Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/sql_lex.h Thu Sep 04 12:12:44 2008 -0700
-@@ -118,7 +118,7 @@
- SQLCOM_SHOW_CREATE_TRIGGER,
- SQLCOM_ALTER_DB_UPGRADE,
- SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES,
--
-+ SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS,
- /*
- When a command is added here, be sure it's also added in mysqld.cc
- in "struct show_var_st status_vars[]= {" ...
-diff -r ab66c8ca382a sql/sql_parse.cc
---- a/sql/sql_parse.cc Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/sql_parse.cc Thu Sep 04 12:12:44 2008 -0700
-@@ -45,6 +45,15 @@
-
- static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
- static bool check_show_create_table_access(THD *thd, TABLE_LIST *table);
-+
-+HASH global_user_stats;
-+extern pthread_mutex_t LOCK_global_user_stats;
-+
-+HASH global_table_stats;
-+extern pthread_mutex_t LOCK_global_table_stats;
-+
-+HASH global_index_stats;
-+extern pthread_mutex_t LOCK_global_index_stats;
-
- const char *any_db="*any*"; // Special symbol for check_access
-
-@@ -278,7 +287,9 @@
- sql_command_flags[SQLCOM_SHOW_FUNC_CODE]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_PROFILES]= CF_STATUS_COMMAND;
-- sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND;
-+ sql_command_flags[SQLCOM_SHOW_USER_STATS]= CF_STATUS_COMMAND;
-+ sql_command_flags[SQLCOM_SHOW_TABLE_STATS]= CF_STATUS_COMMAND;
-+ sql_command_flags[SQLCOM_SHOW_INDEX_STATS]= CF_STATUS_COMMAND;
-
- sql_command_flags[SQLCOM_SHOW_TABLES]= (CF_STATUS_COMMAND |
- CF_SHOW_TABLE_COMMAND |
-@@ -481,6 +492,86 @@
- DBUG_RETURN(0);
- }
-
-+extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
-+ my_bool not_used __attribute__((unused)))
-+{
-+ *length = strlen(user_stats->user);
-+ return (uchar*)user_stats->user;
-+}
-+
-+extern "C" void free_user_stats(USER_STATS* user_stats)
-+{
-+ my_free((char*)user_stats, MYF(0));
-+}
-+
-+void init_global_user_stats(void)
-+{
-+ if (hash_init(&global_user_stats, system_charset_info, max_connections,
-+ 0, 0, (hash_get_key)get_key_user_stats,
-+ (hash_free_key)free_user_stats, 0)) {
-+ sql_print_error("Initializing global_user_stats failed.");
-+ exit(1);
-+ }
-+}
-+
-+extern "C" uchar *get_key_table_stats(TABLE_STATS *table_stats, size_t *length,
-+ my_bool not_used __attribute__((unused)))
-+{
-+ *length = strlen(table_stats->table);
-+ return (uchar*)table_stats->table;
-+}
-+
-+extern "C" void free_table_stats(TABLE_STATS* table_stats)
-+{
-+ my_free((char*)table_stats, MYF(0));
-+}
-+
-+void init_global_table_stats(void)
-+{
-+ if (hash_init(&global_table_stats, system_charset_info, max_connections,
-+ 0, 0, (hash_get_key)get_key_table_stats,
-+ (hash_free_key)free_table_stats, 0)) {
-+ sql_print_error("Initializing global_table_stats failed.");
-+ exit(1);
-+ }
-+}
-+
-+extern "C" uchar *get_key_index_stats(INDEX_STATS *index_stats, size_t *length,
-+ my_bool not_used __attribute__((unused)))
-+{
-+ *length = strlen(index_stats->index);
-+ return (uchar*)index_stats->index;
-+}
-+
-+extern "C" void free_index_stats(INDEX_STATS* index_stats)
-+{
-+ my_free((char*)index_stats, MYF(0));
-+}
-+
-+void init_global_index_stats(void)
-+{
-+ if (hash_init(&global_index_stats, system_charset_info, max_connections,
-+ 0, 0, (hash_get_key)get_key_index_stats,
-+ (hash_free_key)free_index_stats, 0)) {
-+ sql_print_error("Initializing global_index_stats failed.");
-+ exit(1);
-+ }
-+}
-+
-+void free_global_user_stats(void)
-+{
-+ hash_free(&global_user_stats);
-+}
-+
-+void free_global_table_stats(void)
-+{
-+ hash_free(&global_table_stats);
-+}
-+
-+void free_global_index_stats(void)
-+{
-+ hash_free(&global_index_stats);
-+}
-
- /**
- @brief Check access privs for a MERGE table and fix children lock types.
-@@ -899,6 +990,9 @@
- DBUG_PRINT("info",("packet: '%*.s'; command: %d", packet_length, packet, command));
-
- thd->command=command;
-+ // To increment the correct command counter for user stats, 'command' must
-+ // be saved because it is set to COM_SLEEP at the end of this function.
-+ thd->old_command = command;
- /*
- Commands which always take a long time are logged into
- the slow log only if opt_log_slow_admin_statements is set.
-@@ -1677,6 +1771,9 @@
- case SCH_COLUMN_PRIVILEGES:
- case SCH_TABLE_CONSTRAINTS:
- case SCH_KEY_COLUMN_USAGE:
-+ case SCH_USER_STATS:
-+ case SCH_TABLE_STATS:
-+ case SCH_INDEX_STATS:
- default:
- break;
- }
-@@ -2020,6 +2117,9 @@
- my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
- break;
- #endif
-+ case SQLCOM_SHOW_USER_STATS:
-+ case SQLCOM_SHOW_TABLE_STATS:
-+ case SQLCOM_SHOW_INDEX_STATS:
- case SQLCOM_SHOW_STATUS_PROC:
- case SQLCOM_SHOW_STATUS_FUNC:
- res= execute_sqlcom_select(thd, all_tables);
-@@ -2195,6 +2295,7 @@
- }
- #endif
-
-+
- case SQLCOM_BACKUP_TABLE:
- {
- DBUG_ASSERT(first_table == all_tables && first_table != 0);
-@@ -5245,6 +5346,130 @@
- #endif /*NO_EMBEDDED_ACCESS_CHECKS*/
-
-
-+// 'mysql_system_user' is used for when the user is not defined for a THD.
-+static char mysql_system_user[] = "#mysql_system#";
-+
-+// Returns 'user' if it's not NULL. Returns 'mysql_system_user' otherwise.
-+static char* get_valid_user_string(char* user) {
-+ return user ? user : mysql_system_user;
-+}
-+
-+// Increments the global user stats connection count. If 'use_lock' is true,
-+// 'LOCK_global_user_stats' will be locked/unlocked. Returns 0 on success,
-+// 1 on error.
-+int increment_connection_count(THD* thd, bool use_lock) {
-+ char* user_string = get_valid_user_string(thd->main_security_ctx.user);
-+
-+ USER_STATS* user_stats;
-+ int return_value = 0;
-+
-+ if (use_lock) pthread_mutex_lock(&LOCK_global_user_stats);
-+ if (!(user_stats = (USER_STATS*)hash_search(&global_user_stats,
-+ (uchar*)user_string,
-+ strlen(user_string)))) {
-+ // First connection for this user.
-+ if (!(user_stats = ((USER_STATS*)
-+ my_malloc(sizeof(USER_STATS), MYF(MY_WME))))) {
-+ // Out of memory.
-+ return_value = 1;
-+ goto end;
-+ }
-+ strncpy(user_stats->user, user_string, sizeof(user_stats->user));
-+ user_stats->total_connections = 0;
-+ user_stats->concurrent_connections = 0;
-+ user_stats->connected_time = 0;
-+ user_stats->busy_time = 0;
-+ user_stats->rows_fetched = 0;
-+ user_stats->rows_updated = 0;
-+ user_stats->select_commands = 0;
-+ user_stats->update_commands = 0;
-+ user_stats->other_commands = 0;
-+ user_stats->commit_trans = 0;
-+ user_stats->rollback_trans = 0;
-+
-+ if (my_hash_insert(&global_user_stats, (uchar*)user_stats)) {
-+ // Out of memory.
-+ my_free((char*)user_stats, 0);
-+ return_value = 1;
-+ goto end;
-+ }
-+ }
-+ user_stats->total_connections++;
-+end:
-+ if (use_lock) pthread_mutex_unlock(&LOCK_global_user_stats);
-+ return return_value;
-+}
-+
-+// Used to update the global user stats.
-+static void update_global_user_stats_with_user(THD* thd,
-+ USER_STATS* user_stats) {
-+ time_t current_time = time(NULL);
-+ user_stats->connected_time += current_time - thd->last_global_update_time;
-+ thd->last_global_update_time = current_time;
-+ user_stats->busy_time += thd->diff_total_busy_time;
-+ user_stats->rows_fetched += thd->diff_total_sent_rows;
-+ user_stats->rows_updated += thd->diff_total_updated_rows;
-+ user_stats->select_commands += thd->diff_select_commands;
-+ user_stats->update_commands += thd->diff_update_commands;
-+ user_stats->other_commands += thd->diff_other_commands;
-+ user_stats->commit_trans += thd->diff_commit_trans;
-+ user_stats->rollback_trans += thd->diff_rollback_trans;
-+}
-+
-+// Updates the global stats of a thread/user.
-+void update_global_user_stats(THD* thd) {
-+ char* user_string = get_valid_user_string(thd->main_security_ctx.user);
-+
-+ USER_STATS* user_stats;
-+ pthread_mutex_lock(&LOCK_global_user_stats);
-+ if ((user_stats = (USER_STATS*)hash_search(&global_user_stats,
-+ (uchar*)user_string,
-+ strlen(user_string)))) {
-+ // Found user.
-+ update_global_user_stats_with_user(thd, user_stats);
-+ thd->reset_diff_stats();
-+ } else {
-+ // The user name should exist.
-+ increment_connection_count(thd, false);
-+ }
-+ pthread_mutex_unlock(&LOCK_global_user_stats);
-+}
-+
-+// Determines the concurrent number of connections of current threads.
-+void set_concurrent_connections_stats() {
-+ USER_STATS* user_stats;
-+
-+ pthread_mutex_lock(&LOCK_global_user_stats);
-+ pthread_mutex_lock(&LOCK_thread_count);
-+
-+ // Resets all concurrent connections to 0.
-+ for (int i = 0; i < global_user_stats.records; ++i) {
-+ user_stats = (USER_STATS*)hash_element(&global_user_stats, i);
-+ user_stats->concurrent_connections = 0;
-+ }
-+
-+ I_List_iterator<THD> it(threads);
-+ THD* thd;
-+ // Iterates through the current threads.
-+ while ((thd = it++)) {
-+ char* user_string = get_valid_user_string(thd->main_security_ctx.user);
-+ if ((user_stats = (USER_STATS*)hash_search(&global_user_stats,
-+ (uchar*)user_string,
-+ strlen(user_string)))) {
-+ // Found user.
-+ user_stats->concurrent_connections++;
-+ update_global_user_stats_with_user(thd, user_stats);
-+ thd->reset_diff_stats();
-+ } else {
-+ // The user name should exist.
-+ increment_connection_count(thd, false);
-+ }
-+ }
-+ pthread_mutex_unlock(&LOCK_thread_count);
-+ pthread_mutex_unlock(&LOCK_global_user_stats);
-+}
-+
-+
- /**
- check for global access and give descriptive error message if it fails.
-
-@@ -5406,6 +5631,10 @@
- reset_dynamic(&thd->user_var_events);
- thd->user_var_events_alloc= thd->mem_root;
- }
-+
-+ thd->updated_row_count=0;
-+ thd->busy_time=0;
-+
- thd->clear_error();
- thd->main_da.reset_diagnostics_area();
- thd->total_warn_count=0; // Warnings for this query
-@@ -5585,6 +5814,16 @@
- DBUG_ENTER("mysql_parse");
-
- DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););
-+
-+ int start_time_error = 0;
-+ int end_time_error = 0;
-+ struct timeval start_time, end_time;
-+ double start_usecs = 0;
-+ double end_usecs = 0;
-+ // Gets the start time, in order to measure how long this command takes.
-+ if (!(start_time_error = gettimeofday(&start_time, NULL))) {
-+ start_usecs = start_time.tv_sec * 1000000.0 + start_time.tv_usec;
-+ }
-
- /*
- Warning.
-@@ -5674,6 +5913,27 @@
- *found_semicolon= NULL;
- }
-
-+ // Gets the end time.
-+ if (!(end_time_error = gettimeofday(&end_time, NULL))) {
-+ end_usecs = end_time.tv_sec * 1000000.0 + end_time.tv_usec;
-+ }
-+
-+ // Calculates the difference between the end and start times.
-+ if (end_usecs >= start_usecs && !start_time_error && !end_time_error) {
-+ thd->busy_time = (end_usecs - start_usecs) / 1000000;
-+ // In case there are bad values, 2629743 is the #seconds in a month.
-+ if (thd->busy_time > 2629743) {
-+ thd->busy_time = 0;
-+ }
-+ } else {
-+ // end time went back in time, or gettimeofday() failed.
-+ thd->busy_time = 0;
-+ }
-+
-+ // Updates THD stats and the global user stats.
-+ thd->update_stats();
-+ update_global_user_stats(thd);
-+
- DBUG_VOID_RETURN;
- }
-
-@@ -6256,6 +6516,7 @@
- tables->lock_type= lock_type;
- tables->updating= for_update;
- }
-+
- DBUG_VOID_RETURN;
- }
-
-@@ -6623,6 +6884,21 @@
- #endif
- if (options & REFRESH_USER_RESOURCES)
- reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
-+ if (options & REFRESH_TABLE_STATS)
-+ {
-+ pthread_mutex_lock(&LOCK_global_table_stats);
-+ free_global_table_stats();
-+ init_global_table_stats();
-+ pthread_mutex_unlock(&LOCK_global_table_stats);
-+ }
-+ if (options & REFRESH_INDEX_STATS)
-+ {
-+ pthread_mutex_lock(&LOCK_global_index_stats);
-+ free_global_index_stats();
-+ init_global_index_stats();
-+ pthread_mutex_unlock(&LOCK_global_index_stats);
-+ }
-+
- *write_to_binlog= tmp_write_to_binlog;
- return result;
- }
-diff -r ab66c8ca382a sql/sql_show.cc
---- a/sql/sql_show.cc Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/sql_show.cc Thu Sep 04 12:12:44 2008 -0700
-@@ -2239,6 +2239,90 @@
- DBUG_RETURN(FALSE);
- }
-
-+
-+int fill_schema_user_stats(THD* thd, TABLE_LIST* tables, COND* cond)
-+{
-+ TABLE *table= tables->table;
-+ DBUG_ENTER("fill_schema_user_stats");
-+
-+ set_concurrent_connections_stats();
-+
-+ pthread_mutex_lock(&LOCK_global_user_stats);
-+ for (int i = 0; i < global_user_stats.records; ++i) {
-+ restore_record(table, s->default_values);
-+ USER_STATS *user_stats = (USER_STATS*)hash_element(&global_user_stats, i);
-+ table->field[0]->store(user_stats->user, strlen(user_stats->user), system_charset_info);
-+ table->field[1]->store((longlong)user_stats->total_connections, TRUE);
-+ table->field[2]->store((longlong)user_stats->concurrent_connections, TRUE);
-+ table->field[3]->store((longlong)user_stats->connected_time, TRUE);
-+ table->field[4]->store((longlong)user_stats->busy_time, TRUE);
-+ table->field[5]->store((longlong)user_stats->rows_fetched, TRUE);
-+ table->field[6]->store((longlong)user_stats->rows_updated, TRUE);
-+ table->field[7]->store((longlong)user_stats->select_commands, TRUE);
-+ table->field[8]->store((longlong)user_stats->update_commands, TRUE);
-+ table->field[9]->store((longlong)user_stats->other_commands, TRUE);
-+ table->field[10]->store((longlong)user_stats->commit_trans, TRUE);
-+ table->field[11]->store((longlong)user_stats->rollback_trans, TRUE);
-+
-+ if (schema_table_store_record(thd, table))
-+ {
-+ VOID(pthread_mutex_unlock(&LOCK_global_user_stats));
-+ DBUG_RETURN(1);
-+ }
-+ }
-+ pthread_mutex_unlock(&LOCK_global_user_stats);
-+ DBUG_RETURN(0);
-+}
-+
-+
-+int fill_schema_table_stats(THD* thd, TABLE_LIST* tables, COND* cond)
-+{
-+ TABLE *table= tables->table;
-+ DBUG_ENTER("fill_schema_table_stats");
-+
-+ pthread_mutex_lock(&LOCK_global_table_stats);
-+ for (int i = 0; i < global_table_stats.records; ++i) {
-+ restore_record(table, s->default_values);
-+ TABLE_STATS *table_stats =
-+ (TABLE_STATS*)hash_element(&global_table_stats, i);
-+ table->field[0]->store(table_stats->table, strlen(table_stats->table), system_charset_info);
-+ table->field[1]->store((longlong)table_stats->rows_read, TRUE);
-+ table->field[2]->store((longlong)table_stats->rows_changed, TRUE);
-+ table->field[3]->store((longlong)table_stats->rows_changed_x_indexes, TRUE);
-+
-+ if (schema_table_store_record(thd, table))
-+ {
-+ VOID(pthread_mutex_unlock(&LOCK_global_table_stats));
-+ DBUG_RETURN(1);
-+ }
-+ }
-+ pthread_mutex_unlock(&LOCK_global_table_stats);
-+ DBUG_RETURN(0);
-+}
-+
-+
-+int fill_schema_index_stats(THD* thd, TABLE_LIST* tables, COND* cond)
-+{
-+ TABLE *table= tables->table;
-+ DBUG_ENTER("fill_schema_index_stats");
-+
-+ pthread_mutex_lock(&LOCK_global_index_stats);
-+ for (int i = 0; i < global_index_stats.records; ++i) {
-+ restore_record(table, s->default_values);
-+ INDEX_STATS *index_stats =
-+ (INDEX_STATS*)hash_element(&global_index_stats, i);
-+ table->field[0]->store(index_stats->index, strlen(index_stats->index), system_charset_info);
-+ table->field[1]->store((longlong)index_stats->rows_read, TRUE);
-+
-+ if (schema_table_store_record(thd, table))
-+ {
-+ VOID(pthread_mutex_unlock(&LOCK_global_index_stats));
-+ DBUG_RETURN(1);
-+ }
-+ }
-+ pthread_mutex_unlock(&LOCK_global_index_stats);
-+ DBUG_RETURN(0);
-+}
-
- /* collect status for all running threads */
-
-@@ -6565,6 +6649,38 @@
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
- };
-
-+ST_FIELD_INFO user_stats_fields_info[]=
-+{
-+ {"USER", 16, MYSQL_TYPE_STRING, 0, 0, "User", SKIP_OPEN_TABLE},
-+ {"TOTAL_CONNECTIONS", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Total_connections", SKIP_OPEN_TABLE},
-+ {"CONCURRENT_CONNECTIONS", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Concurrent_connections", SKIP_OPEN_TABLE},
-+ {"CONNECTED_TIME", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Connected_time", SKIP_OPEN_TABLE},
-+ {"BUSY_TIME", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Busy_time", SKIP_OPEN_TABLE},
-+ {"ROWS_FETCHED", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_fetched", SKIP_OPEN_TABLE},
-+ {"ROWS_UPDATED", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_updated", SKIP_OPEN_TABLE},
-+ {"SELECT_COMMANDS", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Select_commands", SKIP_OPEN_TABLE},
-+ {"UPDATE_COMMANDS", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Update_commands", SKIP_OPEN_TABLE},
-+ {"OTHER_COMMANDS", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Other_commands", SKIP_OPEN_TABLE},
-+ {"COMMIT_TRANSACTIONS", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Commit_transactions", SKIP_OPEN_TABLE},
-+ {"ROLLBACK_TRANSACTIONS", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Rollback_transactions", SKIP_OPEN_TABLE},
-+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
-+};
-+
-+ST_FIELD_INFO table_stats_fields_info[]=
-+{
-+ {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name", SKIP_OPEN_TABLE},
-+ {"ROWS_READ", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_read", SKIP_OPEN_TABLE},
-+ {"ROWS_CHANGED", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_changed", SKIP_OPEN_TABLE},
-+ {"ROWS_CHANGED_INDEXES", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_changed_x_#indexes", SKIP_OPEN_TABLE},
-+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
-+};
-+
-+ST_FIELD_INFO index_stats_fields_info[]=
-+{
-+ {"INDEX_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Index_name", SKIP_OPEN_TABLE},
-+ {"ROWS_READ", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Rows_read", SKIP_OPEN_TABLE},
-+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
-+};
-
- /*
- Description of ST_FIELD_INFO in table.h
-@@ -6601,6 +6717,8 @@
- fill_status, make_old_format, 0, -1, -1, 0, 0},
- {"GLOBAL_VARIABLES", variables_fields_info, create_schema_table,
- fill_variables, make_old_format, 0, -1, -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},
-@@ -6642,11 +6760,15 @@
- get_all_tables, make_table_names_old_format, 0, 1, 2, 1, 0},
- {"TABLE_PRIVILEGES", table_privileges_fields_info, create_schema_table,
- fill_schema_table_privileges, 0, 0, -1, -1, 0, 0},
-+ {"TABLE_STATISTICS", table_stats_fields_info, create_schema_table,
-+ fill_schema_table_stats, make_old_format, 0, -1, -1, 0, 0},
- {"TRIGGERS", triggers_fields_info, create_schema_table,
- get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0,
- OPEN_TABLE_ONLY},
- {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table,
- fill_schema_user_privileges, 0, 0, -1, -1, 0, 0},
-+ {"USER_STATISTICS", user_stats_fields_info, create_schema_table,
-+ fill_schema_user_stats, make_old_format, 0, -1, -1, 0, 0},
- {"VARIABLES", variables_fields_info, create_schema_table, fill_variables,
- make_old_format, 0, -1, -1, 1, 0},
- {"VIEWS", view_fields_info, create_schema_table,
-diff -r ab66c8ca382a sql/sql_update.cc
---- a/sql/sql_update.cc Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/sql_update.cc Thu Sep 04 12:12:45 2008 -0700
-@@ -816,6 +816,7 @@
- thd->row_count_func=
- (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
- my_ok(thd, (ulong) thd->row_count_func, id, buff);
-+ thd->updated_row_count += thd->row_count_func;
- DBUG_PRINT("info",("%ld records updated", (long) updated));
- }
- thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */
-@@ -2007,5 +2008,6 @@
- thd->row_count_func=
- (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
- ::my_ok(thd, (ulong) thd->row_count_func, id, buff);
-+ thd->updated_row_count += thd->row_count_func;
- DBUG_RETURN(FALSE);
- }
-diff -r ab66c8ca382a sql/sql_yacc.yy
---- a/sql/sql_yacc.yy Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/sql_yacc.yy Thu Sep 04 12:12:45 2008 -0700
-@@ -737,6 +737,7 @@
- %token IMPORT
- %token INDEXES
- %token INDEX_SYM
-+%token INDEX_STATS_SYM
- %token INFILE
- %token INITIAL_SIZE_SYM
- %token INNER_SYM /* SQL-2003-R */
-@@ -1025,6 +1026,7 @@
- %token TABLE_REF_PRIORITY
- %token TABLE_SYM /* SQL-2003-R */
- %token TABLE_CHECKSUM_SYM
-+%token TABLE_STATS_SYM
- %token TEMPORARY /* SQL-2003-N */
- %token TEMPTABLE_SYM
- %token TERMINATED
-@@ -1070,6 +1072,7 @@
- %token UPGRADE_SYM
- %token USAGE /* SQL-2003-N */
- %token USER /* SQL-2003-R */
-+%token USER_STATS_SYM
- %token USE_FRM
- %token USE_SYM
- %token USING /* SQL-2003-R */
-@@ -9353,6 +9356,27 @@
- {
- Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- }
-+ | USER_STATS_SYM wild_and_where
-+ {
-+ LEX *lex= Lex;
-+ lex->sql_command= SQLCOM_SHOW_USER_STATS;
-+ if (prepare_schema_table(YYTHD, lex, 0, SCH_USER_STATS))
-+ MYSQL_YYABORT;
-+ }
-+ | TABLE_STATS_SYM wild_and_where
-+ {
-+ LEX *lex= Lex;
-+ lex->sql_command= SQLCOM_SHOW_TABLE_STATS;
-+ if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_STATS))
-+ MYSQL_YYABORT;
-+ }
-+ | INDEX_STATS_SYM wild_and_where
-+ {
-+ LEX *lex= Lex;
-+ lex->sql_command= SQLCOM_SHOW_INDEX_STATS;
-+ if (prepare_schema_table(YYTHD, lex, 0, SCH_INDEX_STATS))
-+ MYSQL_YYABORT;
-+ }
- | CREATE PROCEDURE sp_name
- {
- LEX *lex= Lex;
-@@ -9567,6 +9591,10 @@
- { Lex->type|= REFRESH_DES_KEY_FILE; }
- | RESOURCES
- { Lex->type|= REFRESH_USER_RESOURCES; }
-+ | TABLE_STATS_SYM
-+ { Lex->type|= REFRESH_TABLE_STATS; }
-+ | INDEX_STATS_SYM
-+ { Lex->type|= REFRESH_INDEX_STATS; }
- ;
-
- opt_table_list:
-diff -r ab66c8ca382a sql/structs.h
---- a/sql/structs.h Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/structs.h Thu Sep 04 12:12:45 2008 -0700
-@@ -226,6 +226,28 @@
- /* Maximum amount of resources which account is allowed to consume. */
- USER_RESOURCES user_resources;
- } USER_CONN;
-+
-+typedef struct st_user_stats {
-+ char user[USERNAME_LENGTH + 1];
-+ uint total_connections;
-+ uint concurrent_connections;
-+ time_t connected_time; // in seconds
-+ double busy_time; // in seconds
-+ ha_rows rows_fetched, rows_updated;
-+ ulonglong select_commands, update_commands, other_commands;
-+ ulonglong commit_trans, rollback_trans;
-+} USER_STATS;
-+
-+typedef struct st_table_stats {
-+ char table[NAME_LEN * 2 + 2]; // [db] + '.' + [table] + '\0'
-+ ulonglong rows_read, rows_changed;
-+ ulonglong rows_changed_x_indexes;
-+} TABLE_STATS;
-+
-+typedef struct st_index_stats {
-+ char index[NAME_LEN * 3 + 3]; // [db] + '.' + [table] + '.' + [index] + '\0'
-+ ulonglong rows_read;
-+} INDEX_STATS;
-
- /* Bits in form->update */
- #define REG_MAKE_DUPP 1 /* Make a copy of record when read */
-diff -r ab66c8ca382a sql/table.h
---- a/sql/table.h Thu Sep 04 12:08:00 2008 -0700
-+++ b/sql/table.h Thu Sep 04 12:12:45 2008 -0700
-@@ -824,6 +824,7 @@
- SCH_FILES,
- SCH_GLOBAL_STATUS,
- SCH_GLOBAL_VARIABLES,
-+ SCH_INDEX_STATS,
- SCH_KEY_COLUMN_USAGE,
- SCH_OPEN_TABLES,
- SCH_PARTITIONS,
-@@ -842,8 +843,10 @@
- SCH_TABLE_CONSTRAINTS,
- SCH_TABLE_NAMES,
- SCH_TABLE_PRIVILEGES,
-+ SCH_TABLE_STATS,
- SCH_TRIGGERS,
- SCH_USER_PRIVILEGES,
-+ SCH_USER_STATS,
- SCH_VARIABLES,
- SCH_VIEWS
- };
-diff -r ab66c8ca382a storage/innobase/handler/ha_innodb.cc
---- a/storage/innobase/handler/ha_innodb.cc Thu Sep 04 12:08:00 2008 -0700
-+++ b/storage/innobase/handler/ha_innodb.cc Thu Sep 04 12:12:45 2008 -0700
-@@ -3925,6 +3925,8 @@
-
- error = row_insert_for_mysql((byte*) record, prebuilt);
-
-+ if (error == DB_SUCCESS) rows_changed++;
-+
- /* Handle duplicate key errors */
- if (auto_inc_used) {
- ulint err;
-@@ -4001,6 +4003,8 @@
- break;
- }
- }
-+
-+ if (error == DB_SUCCESS) rows_changed++;
-
- innodb_srv_conc_exit_innodb(prebuilt->trx);
-
-@@ -4647,6 +4651,8 @@
- ret = row_search_for_mysql((byte*) buf, mode, prebuilt,
- match_mode, 0);
-
-+ if (error == DB_SUCCESS) rows_changed++;
-+
- innodb_srv_conc_exit_innodb(prebuilt->trx);
- } else {
-
-@@ -4193,6 +4193,9 @@
- if (ret == DB_SUCCESS) {
- error = 0;
- table->status = 0;
-+ rows_read++;
-+ if (active_index >= 0 && active_index < MAX_KEY)
-+ index_rows_read[active_index]++;
-
- } else if (ret == DB_RECORD_NOT_FOUND) {
- error = HA_ERR_KEY_NOT_FOUND;
-@@ -4366,6 +4369,9 @@
- if (ret == DB_SUCCESS) {
- error = 0;
- table->status = 0;
-+ rows_read++;
-+ if (active_index >= 0 && active_index < MAX_KEY)
-+ index_rows_read[active_index]++;
-
- } else if (ret == DB_RECORD_NOT_FOUND) {
- error = HA_ERR_END_OF_FILE;
-diff -r ab66c8ca382a storage/myisam/ha_myisam.cc
---- a/storage/myisam/ha_myisam.cc Thu Sep 04 12:08:00 2008 -0700
-+++ b/storage/myisam/ha_myisam.cc Thu Sep 04 12:12:45 2008 -0700
-@@ -738,7 +738,9 @@
- if ((error= update_auto_increment()))
- return error;
- }
-- return mi_write(file,buf);
-+ int error=mi_write(file,buf);
-+ if (!error) rows_changed++;
-+ return error;
- }
-
- int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
-@@ -1589,13 +1591,17 @@
- ha_statistic_increment(&SSV::ha_update_count);
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
- table->timestamp_field->set_time();
-- return mi_update(file,old_data,new_data);
-+ int error=mi_update(file,old_data,new_data);
-+ if (!error) rows_changed++;
-+ return error;
- }
-
- int ha_myisam::delete_row(const uchar *buf)
- {
- ha_statistic_increment(&SSV::ha_delete_count);
-- return mi_delete(file,buf);
-+ int error=mi_delete(file,buf);
-+ if (!error) rows_changed++;
-+ return error;
- }
-
- int ha_myisam::index_read_map(uchar *buf, const uchar *key,
-@@ -1606,6 +1612,13 @@
- ha_statistic_increment(&SSV::ha_read_key_count);
- int error=mi_rkey(file, buf, active_index, key, keypart_map, find_flag);
- table->status=error ? STATUS_NOT_FOUND: 0;
-+ if (!error) {
-+ rows_read++;
-+
-+ int inx = (active_index == -1) ? file->lastinx : active_index;
-+ if (inx >= 0 && inx < MAX_KEY)
-+ index_rows_read[inx]++;
-+ }
- return error;
- }
-
-@@ -1616,6 +1629,14 @@
- ha_statistic_increment(&SSV::ha_read_key_count);
- int error=mi_rkey(file, buf, index, key, keypart_map, find_flag);
- table->status=error ? STATUS_NOT_FOUND: 0;
-+ if (!error) {
-+ rows_read++;
-+
-+// int inx = (active_index == -1) ? file->lastinx : active_index;
-+ int inx = index;
-+ if (inx >= 0 && inx < MAX_KEY)
-+ index_rows_read[inx]++;
-+ }
- return error;
- }
-
-@@ -1637,6 +1658,13 @@
- ha_statistic_increment(&SSV::ha_read_next_count);
- int error=mi_rnext(file,buf,active_index);
- table->status=error ? STATUS_NOT_FOUND: 0;
-+ if (!error) {
-+ rows_read++;
-+
-+ int inx = (active_index == -1) ? file->lastinx : active_index;
-+ if (inx >= 0 && inx < MAX_KEY)
-+ index_rows_read[inx]++;
-+ }
- return error;
- }
-
-@@ -1646,6 +1674,13 @@
- ha_statistic_increment(&SSV::ha_read_prev_count);
- int error=mi_rprev(file,buf, active_index);
- table->status=error ? STATUS_NOT_FOUND: 0;
-+ if (!error) {
-+ rows_read++;
-+
-+ int inx = (active_index == -1) ? file->lastinx : active_index;
-+ if (inx >= 0 && inx < MAX_KEY)
-+ index_rows_read[inx]++;
-+ }
- return error;
- }
-
-@@ -1655,6 +1690,13 @@
- ha_statistic_increment(&SSV::ha_read_first_count);
- int error=mi_rfirst(file, buf, active_index);
- table->status=error ? STATUS_NOT_FOUND: 0;
-+ if (!error) {
-+ rows_read++;
-+
-+ int inx = (active_index == -1) ? file->lastinx : active_index;
-+ if (inx >= 0 && inx < MAX_KEY)
-+ index_rows_read[inx]++;
-+ }
- return error;
- }
-
-@@ -1664,6 +1706,13 @@
- ha_statistic_increment(&SSV::ha_read_last_count);
- int error=mi_rlast(file, buf, active_index);
- table->status=error ? STATUS_NOT_FOUND: 0;
-+ if (!error) {
-+ rows_read++;
-+
-+ int inx = (active_index == -1) ? file->lastinx : active_index;
-+ if (inx >= 0 && inx < MAX_KEY)
-+ index_rows_read[inx]++;
-+ }
- return error;
- }
-
-@@ -1679,6 +1728,20 @@
- error= mi_rnext_same(file,buf);
- } while (error == HA_ERR_RECORD_DELETED);
- table->status=error ? STATUS_NOT_FOUND: 0;
-+ if (!error) {
-+ rows_read++;
-+
-+ int inx = (active_index == -1) ? file->lastinx : active_index;
-+ if (inx >= 0 && inx < MAX_KEY)
-+ index_rows_read[inx]++;
-+ }
-+ if (!error) {
-+ rows_read++;
-+
-+ int inx = (active_index == -1) ? file->lastinx : active_index;
-+ if (inx >= 0 && inx < MAX_KEY)
-+ index_rows_read[inx]++;
-+ }
- return error;
- }
-
-@@ -1695,6 +1758,7 @@
- ha_statistic_increment(&SSV::ha_read_rnd_next_count);
- int error=mi_scan(file, buf);
- table->status=error ? STATUS_NOT_FOUND: 0;
-+ if (!error) rows_read++;
- return error;
- }
-
-@@ -1708,6 +1772,7 @@
- ha_statistic_increment(&SSV::ha_read_rnd_count);
- int error=mi_rrnd(file, buf, my_get_ptr(pos,ref_length));
- table->status=error ? STATUS_NOT_FOUND: 0;
-+ if (!error) rows_read++;
- return error;
- }
-