raven-rhel6/mysqlclient/userstat5_5_17.patch
2024-02-21 20:14:44 +06:00

3501 lines
127 KiB
Diff

diff --git a/include/mysql/plugin.h b/include/mysql/plugin.h
index 3e6ab24..fa8d0fc 100644
--- a/include/mysql/plugin.h
+++ b/include/mysql/plugin.h
@@ -16,6 +16,8 @@
#ifndef _my_plugin_h
#define _my_plugin_h
+#define EXTENDED_FOR_USERSTAT
+
/*
On Windows, exports from DLL need to be declared
Also, plugin needs to be declared as extern "C" because MSVC
diff --git a/include/mysql_com.h b/include/mysql_com.h
index f2345be..21e3427 100644
--- a/include/mysql_com.h
+++ b/include/mysql_com.h
@@ -31,6 +31,7 @@
#define SERVER_VERSION_LENGTH 60
#define SQLSTATE_LENGTH 5
+#define LIST_PROCESS_HOST_LEN 64
/*
Maximum length of comments
@@ -146,6 +147,12 @@ enum enum_server_command
#define REFRESH_DES_KEY_FILE 0x40000L
#define REFRESH_USER_RESOURCES 0x80000L
+#define REFRESH_TABLE_STATS 0x200000L /* Refresh table stats my_hash table */
+#define REFRESH_INDEX_STATS 0x400000L /* Refresh index stats my_hash table */
+#define REFRESH_USER_STATS 0x800000L /* Refresh user stats my_hash table */
+#define REFRESH_CLIENT_STATS 0x1000000L /* Refresh client stats my_hash table */
+#define REFRESH_THREAD_STATS 0x2000000L /* Refresh thread stats my_hash table */
+
#define CLIENT_LONG_PASSWORD 1 /* new more secure passwords */
#define CLIENT_FOUND_ROWS 2 /* Found instead of affected rows */
#define CLIENT_LONG_FLAG 4 /* Get all column flags */
diff --git a/mysql-test/r/userstat_bug602047.result b/mysql-test/r/userstat_bug602047.result
new file mode 100644
index 0000000..966439b
--- /dev/null
+++ b/mysql-test/r/userstat_bug602047.result
@@ -0,0 +1,15 @@
+DROP TABLE IF EXISTS t1;
+SET GLOBAL userstat=ON;
+CREATE TABLE t1 ( id int(10), PRIMARY KEY (id)) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+10
+SELECT ROWS_READ FROM information_schema.table_statistics WHERE TABLE_NAME='t1';
+ROWS_READ
+10
+SELECT ROWS_READ FROM information_schema.index_statistics WHERE TABLE_NAME='t1';
+ROWS_READ
+10
+SET GLOBAL userstat=OFF;
+DROP TABLE t1;
diff --git a/mysql-test/t/userstat_bug602047.test b/mysql-test/t/userstat_bug602047.test
new file mode 100644
index 0000000..436b864
--- /dev/null
+++ b/mysql-test/t/userstat_bug602047.test
@@ -0,0 +1,11 @@
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+SET GLOBAL userstat=ON;
+CREATE TABLE t1 ( id int(10), PRIMARY KEY (id)) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
+SELECT COUNT(*) FROM t1;
+SELECT ROWS_READ FROM information_schema.table_statistics WHERE TABLE_NAME='t1';
+SELECT ROWS_READ FROM information_schema.index_statistics WHERE TABLE_NAME='t1';
+SET GLOBAL userstat=OFF;
+DROP TABLE t1;
\ No newline at end of file
diff --git a/patch_info/userstats.patch b/patch_info/userstats.patch
new file mode 100644
index 0000000..f5d8b86
--- /dev/null
+++ b/patch_info/userstats.patch
@@ -0,0 +1,17 @@
+File=userstats.patch
+Name=SHOW USER/TABLE/INDEX statistics
+Version=V2
+Author=Google
+License=GPL
+Comment=Added INFORMATION_SCHEMA.*_STATISTICS
+2008-12-01
+YK: fix behavior for prepared statements
+
+2008-11-26
+YK: add switch variable "userstat" to control INFORMATION_SCHEMA.*_STATISTICS (default:OFF)
+2010-12-31
+Ported to 5.5.8
+2011-1-5
+Fix porting
+2011-02
+Rename variable USERSTAT_RUNNING => USERSTAT
diff --git a/sql/handler.cc b/sql/handler.cc
index 5806772..01d53c0 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1244,6 +1244,8 @@ int ha_commit_trans(THD *thd, bool all)
goto end;
}
DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
+ if (is_real_trans)
+ thd->diff_commit_trans++;
RUN_HOOK(transaction, after_commit, (thd, FALSE));
end:
if (rw_trans && mdl_request.ticket)
@@ -1398,6 +1400,8 @@ int ha_rollback_trans(THD *thd, bool all)
/* Always cleanup. Even if nht==0. There may be savepoints. */
if (is_real_trans)
thd->transaction.cleanup();
+
+ thd->diff_rollback_trans++;
if (all)
thd->transaction_rollback_request= FALSE;
@@ -1802,6 +1806,7 @@ int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv)
ha_info->reset(); /* keep it conveniently zero-filled */
}
trans->ha_list= sv->ha_list;
+ thd->diff_rollback_trans++;
DBUG_RETURN(error);
}
@@ -2178,6 +2183,8 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode,
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);
}
@@ -3631,6 +3638,127 @@ void handler::get_dynamic_partition_info(PARTITION_STATS *stat_info,
return;
}
+// Updates the global table stats with the TABLE this handler represents.
+void handler::update_global_table_stats()
+{
+ if (!opt_userstat)
+ {
+ rows_read= rows_changed= 0;
+ return;
+ }
+
+ 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);
+
+ mysql_mutex_lock(&LOCK_global_table_stats);
+ // Gets the global table stats, creating one if necessary.
+ if (!(table_stats = (TABLE_STATS *) my_hash_search(&global_table_stats,
+ (uchar*)key,
+ strlen(key))))
+ {
+ if (!(table_stats = ((TABLE_STATS *)
+ my_malloc(sizeof(TABLE_STATS), MYF(MY_WME | MY_ZEROFILL)))))
+ {
+ // 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;
+ table_stats->engine_type= (int) ht->db_type;
+
+ 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);
+ 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);
+ current_thd->diff_total_read_rows+= rows_read;
+ rows_read= rows_changed= 0;
+end:
+ mysql_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;
+
+ if (!opt_userstat)
+ {
+ for (uint x= 0; x < table->s->keys; ++x)
+ {
+ index_rows_read[x]= 0;
+ }
+ return;
+ }
+
+ for (uint 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);
+
+ mysql_mutex_lock(&LOCK_global_index_stats);
+ // Gets the global index stats, creating one if necessary.
+ if (!(index_stats = (INDEX_STATS *) my_hash_search(&global_index_stats,
+ (uchar *) key,
+ strlen(key))))
+ {
+ if (!(index_stats = ((INDEX_STATS *)
+ my_malloc(sizeof(INDEX_STATS), MYF(MY_WME | MY_ZEROFILL)))))
+ {
+ // 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);
+ goto end;
+ }
+ }
+ // Updates the global index stats.
+ index_stats->rows_read+= index_rows_read[x];
+ index_rows_read[x]= 0;
+ end:
+ mysql_mutex_unlock(&LOCK_global_index_stats);
+ }
+ }
+}
/****************************************************************************
** Some general functions that isn't in the handler class
diff --git a/sql/handler.h b/sql/handler.h
index d1222cb..eb2a804 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -36,6 +36,10 @@
#include <ft_global.h>
#include <keycache.h>
+#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
@@ -560,10 +564,12 @@ struct TABLE;
enum enum_schema_tables
{
SCH_CHARSETS= 0,
+ SCH_CLIENT_STATS,
SCH_COLLATIONS,
SCH_COLLATION_CHARACTER_SET_APPLICABILITY,
SCH_COLUMNS,
SCH_COLUMN_PRIVILEGES,
+ SCH_INDEX_STATS,
SCH_ENGINES,
SCH_EVENTS,
SCH_FILES,
@@ -589,8 +595,11 @@ enum enum_schema_tables
SCH_TABLE_CONSTRAINTS,
SCH_TABLE_NAMES,
SCH_TABLE_PRIVILEGES,
+ SCH_TABLE_STATS,
+ SCH_THREAD_STATS,
SCH_TRIGGERS,
SCH_USER_PRIVILEGES,
+ SCH_USER_STATS,
SCH_VARIABLES,
SCH_VIEWS
};
@@ -1226,6 +1235,9 @@ public:
bool locked;
bool implicit_emptied; /* Can be !=0 only if HEAP */
const COND *pushed_cond;
+ ulonglong rows_read;
+ ulonglong rows_changed;
+ ulonglong index_rows_read[MAX_KEY];
/**
next_insert_id is the next value which should be inserted into the
auto_increment column: in a inserting-multi-row statement (like INSERT
@@ -1277,10 +1289,12 @@ public:
ref_length(sizeof(my_off_t)),
ft_handler(0), inited(NONE),
locked(FALSE), implicit_emptied(0),
- pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
+ pushed_cond(0), rows_read(0), rows_changed(0), next_insert_id(0), insert_id_for_cur_row(0),
auto_inc_intervals_count(0),
m_psi(NULL)
- {}
+ {
+ memset(index_rows_read, 0, sizeof(index_rows_read));
+ }
virtual ~handler(void)
{
DBUG_ASSERT(locked == FALSE);
@@ -1403,6 +1417,8 @@ public:
{
table= table_arg;
table_share= share;
+ rows_read = rows_changed= 0;
+ memset(index_rows_read, 0, sizeof(index_rows_read));
}
virtual double scan_time()
{ return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; }
@@ -1798,6 +1814,8 @@ 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
diff --git a/sql/lex.h b/sql/lex.h
index 4a49858..9e90725 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -111,6 +111,7 @@ static SYMBOL symbols[] = {
{ "CIPHER", SYM(CIPHER_SYM)},
{ "CLASS_ORIGIN", SYM(CLASS_ORIGIN_SYM)},
{ "CLIENT", SYM(CLIENT_SYM)},
+ { "CLIENT_STATISTICS", SYM(CLIENT_STATS_SYM)},
{ "CLOSE", SYM(CLOSE_SYM)},
{ "COALESCE", SYM(COALESCE)},
{ "CODE", SYM(CODE_SYM)},
@@ -257,6 +258,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)},
@@ -547,12 +549,14 @@ static SYMBOL symbols[] = {
{ "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)},
{ "TEXT", SYM(TEXT_SYM)},
{ "THAN", SYM(THAN_SYM)},
{ "THEN", SYM(THEN_SYM)},
+ { "THREAD_STATISTICS", SYM(THREAD_STATS_SYM)},
{ "TIME", SYM(TIME_SYM)},
{ "TIMESTAMP", SYM(TIMESTAMP)},
{ "TIMESTAMPADD", SYM(TIMESTAMP_ADD)},
@@ -588,6 +592,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)},
diff --git a/sql/log.cc b/sql/log.cc
index c6b4144..c39ff8f 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -1005,6 +1005,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
@@ -4943,6 +4950,8 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info)
thd->first_successful_insert_id_in_prev_stmt_for_binlog);
if (e.write(file))
goto err;
+ if (file == &log_file)
+ thd->binlog_bytes_written+= e.data_written;
}
if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
{
@@ -4954,12 +4963,16 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info)
minimum());
if (e.write(file))
goto err;
+ if (file == &log_file)
+ thd->binlog_bytes_written+= e.data_written;
}
if (thd->rand_used)
{
Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2);
if (e.write(file))
goto err;
+ if (file == &log_file)
+ thd->binlog_bytes_written+= e.data_written;
}
if (thd->user_var_events.elements)
{
@@ -4982,6 +4995,8 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info)
flags);
if (e.write(file))
goto err;
+ if (file == &log_file)
+ thd->binlog_bytes_written+= e.data_written;
}
}
}
@@ -4993,6 +5008,8 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info)
if (event_info->write(file) ||
DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0))
goto err;
+ if (file == &log_file)
+ thd->binlog_bytes_written+= event_info->data_written;
error= 0;
err:
@@ -5178,7 +5195,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);
@@ -5225,6 +5243,7 @@ int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
/* write the first half of the split header */
if (my_b_write(&log_file, header, carry))
return ER_ERROR_ON_WRITE;
+ thd->binlog_bytes_written+= carry;
/*
copy fixed second half of header to cache so the correct
@@ -5293,6 +5312,7 @@ int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
/* Write data to the binary log file */
if (my_b_write(&log_file, cache->read_pos, length))
return ER_ERROR_ON_WRITE;
+ thd->binlog_bytes_written+= length;
cache->read_pos=cache->read_end; // Mark buffer used up
} while ((length= my_b_fill(cache)));
@@ -5407,20 +5427,23 @@ bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event,
Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), TRUE, FALSE, TRUE, 0);
if (qinfo.write(&log_file))
goto err;
+ thd->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"));
DBUG_SUICIDE();
});
- 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)
+ thd->binlog_bytes_written+= commit_event->data_written;
if (incident && write_incident(thd, FALSE))
goto err;
diff --git a/sql/log.h b/sql/log.h
index 0f0f81d..75e0167 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -437,7 +437,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 is_transactional);
bool check_write_error(THD *thd);
@@ -589,6 +590,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; }
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 0f5087c..6ab3d2a 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -430,6 +430,7 @@ uint opt_large_page_size= 0;
MYSQL_PLUGIN_IMPORT uint opt_debug_sync_timeout= 0;
#endif /* defined(ENABLED_DEBUG_SYNC) */
my_bool opt_old_style_user_limits= 0, trust_function_creators= 0;
+my_bool opt_userstat= 0, opt_thread_statistics= 0;
/*
True if there is at least one per-hour limit for some user, so we should
check them before each query (and possibly reset counters when hour is
@@ -480,6 +481,7 @@ ulong specialflag=0;
ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
ulong binlog_stmt_cache_use= 0, binlog_stmt_cache_disk_use= 0;
ulong max_connections, max_connect_errors;
+ulonglong denied_connections= 0;
/*
Maximum length of parameter value which can be set through
mysql_send_long_data() call.
@@ -622,7 +624,9 @@ mysql_mutex_t
LOCK_crypt,
LOCK_global_system_variables,
LOCK_user_conn, LOCK_slave_list, LOCK_active_mi,
- LOCK_connection_count, LOCK_error_messages;
+ LOCK_connection_count, LOCK_error_messages,
+ LOCK_stats, LOCK_global_user_client_stats,
+ 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
@@ -1477,6 +1481,11 @@ void clean_up(bool print_message)
my_free(opt_bin_logname);
bitmap_free(&temp_pool);
free_max_user_conn();
+ free_global_user_stats();
+ free_global_client_stats();
+ free_global_thread_stats();
+ free_global_table_stats();
+ free_global_index_stats();
#ifdef HAVE_REPLICATION
end_slave_list();
#endif
@@ -1580,6 +1589,10 @@ static void clean_up_mutexes()
mysql_cond_destroy(&COND_thread_cache);
mysql_cond_destroy(&COND_flush_thread_cache);
mysql_cond_destroy(&COND_manager);
+ mysql_mutex_destroy(&LOCK_stats);
+ mysql_mutex_destroy(&LOCK_global_user_client_stats);
+ mysql_mutex_destroy(&LOCK_global_table_stats);
+ mysql_mutex_destroy(&LOCK_global_index_stats);
}
#endif /*EMBEDDED_LIBRARY*/
@@ -3061,6 +3074,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_statistics",(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_contributors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CONTRIBUTORS]), SHOW_LONG_STATUS},
{"show_create_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_DB]), SHOW_LONG_STATUS},
@@ -3081,6 +3095,7 @@ SHOW_VAR com_status_vars[]= {
#endif
{"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_index_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_INDEX_STATS]), SHOW_LONG_STATUS},
{"show_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS},
{"show_master_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_MASTER_STAT]), SHOW_LONG_STATUS},
{"show_open_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_OPEN_TABLES]), SHOW_LONG_STATUS},
@@ -3098,9 +3113,12 @@ 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_statistics",(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_thread_statistics",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_THREAD_STATS]), SHOW_LONG_STATUS},
{"show_triggers", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TRIGGERS]), SHOW_LONG_STATUS},
+ {"show_user_statistics", (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},
@@ -3638,6 +3656,13 @@ static int init_thread_environment()
mysql_mutex_init(key_LOCK_server_started,
&LOCK_server_started, MY_MUTEX_INIT_FAST);
mysql_cond_init(key_COND_server_started, &COND_server_started, NULL);
+ mysql_mutex_init(key_LOCK_stats, &LOCK_stats, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_global_user_client_stats,
+ &LOCK_global_user_client_stats, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_global_table_stats,
+ &LOCK_global_table_stats, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_global_index_stats,
+ &LOCK_global_index_stats, MY_MUTEX_INIT_FAST);
sp_cache_init();
#ifdef HAVE_EVENT_SCHEDULER
Events::init_mutexes();
@@ -4004,6 +4029,9 @@ a file name for --log-bin-index option", opt_binlog_index_name);
unireg_abort(1);
/* We have to initialize the storage engines before CSV logging */
+ init_global_table_stats();
+ init_global_index_stats();
+
if (ha_init())
{
sql_print_error("Can't init databases");
@@ -4140,6 +4168,9 @@ a file name for --log-bin-index option", opt_binlog_index_name);
init_max_user_conn();
init_update_queries();
+ init_global_user_stats();
+ init_global_client_stats();
+ init_global_thread_stats();
DBUG_RETURN(0);
}
@@ -5091,6 +5122,7 @@ static void create_new_thread(THD *thd)
DBUG_PRINT("error",("Too many connections"));
close_connection(thd, ER_CON_COUNT_ERROR);
+ statistic_increment(denied_connections, &LOCK_status);
delete thd;
DBUG_VOID_RETURN;
}
@@ -7805,6 +7837,8 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids,
key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
+ key_LOCK_stats, key_LOCK_global_user_client_stats,
+ key_LOCK_global_table_stats, key_LOCK_global_index_stats,
key_LOCK_gdl, key_LOCK_global_system_variables,
key_LOCK_manager,
key_LOCK_prepared_stmt_count,
@@ -7844,6 +7878,13 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_LOCK_delayed_insert, "LOCK_delayed_insert", PSI_FLAG_GLOBAL},
{ &key_LOCK_delayed_status, "LOCK_delayed_status", PSI_FLAG_GLOBAL},
{ &key_LOCK_error_log, "LOCK_error_log", PSI_FLAG_GLOBAL},
+ { &key_LOCK_stats, "LOCK_stats", PSI_FLAG_GLOBAL},
+ { &key_LOCK_global_user_client_stats,
+ "LOCK_global_user_client_stats", PSI_FLAG_GLOBAL},
+ { &key_LOCK_global_table_stats,
+ "LOCK_global_table_stats", PSI_FLAG_GLOBAL},
+ { &key_LOCK_global_index_stats,
+ "LOCK_global_index_stats", PSI_FLAG_GLOBAL},
{ &key_LOCK_gdl, "LOCK_gdl", PSI_FLAG_GLOBAL},
{ &key_LOCK_global_system_variables, "LOCK_global_system_variables", PSI_FLAG_GLOBAL},
{ &key_LOCK_manager, "LOCK_manager", PSI_FLAG_GLOBAL},
diff --git a/sql/mysqld.h b/sql/mysqld.h
index fc9182e..7cb423d 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -23,6 +23,7 @@
#include "my_atomic.h" /* my_atomic_rwlock_t */
#include "mysql/psi/mysql_file.h" /* MYSQL_FILE */
#include "sql_list.h" /* I_List */
+#include "hash.h"
class THD;
struct handlerton;
@@ -109,6 +110,7 @@ extern ulong slave_exec_mode_options;
extern ulonglong slave_type_conversions_options;
extern my_bool read_only, opt_readonly;
extern my_bool lower_case_file_system;
+extern my_bool opt_userstat, opt_thread_statistics;
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;
@@ -172,6 +174,7 @@ extern LEX_CSTRING reason_slave_blocked;
extern ulong slave_trans_retries;
extern uint slave_net_timeout;
extern uint max_user_connections;
+extern ulonglong denied_connections;
extern ulong what_to_log,flush_time;
extern ulong max_prepared_stmt_count, prepared_stmt_count;
extern ulong open_files_limit;
@@ -197,6 +200,11 @@ extern SHOW_VAR status_vars[];
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 HASH global_client_stats;
+extern HASH global_thread_stats;
+extern HASH global_table_stats;
+extern HASH global_index_stats;
extern const char *opt_date_time_formats[];
extern handlerton *partition_hton;
extern handlerton *myisam_hton;
@@ -236,6 +244,8 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids,
key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
+ key_LOCK_stats, key_LOCK_global_user_client_stats,
+ key_LOCK_global_table_stats, key_LOCK_global_index_stats,
key_LOCK_gdl, key_LOCK_global_system_variables,
key_LOCK_logger, key_LOCK_manager,
key_LOCK_prepared_stmt_count,
@@ -335,7 +345,9 @@ extern mysql_mutex_t
LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
LOCK_slave_list, LOCK_active_mi, LOCK_manager,
LOCK_global_system_variables, LOCK_user_conn,
- LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count;
+ LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count,
+ LOCK_stats, LOCK_global_user_client_stats,
+ LOCK_global_table_stats, LOCK_global_index_stats;
extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thread_count;
#ifdef HAVE_OPENSSL
extern mysql_mutex_t LOCK_des_key_file;
@@ -447,6 +459,16 @@ inline query_id_t get_query_id()
return id;
}
+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 init_global_thread_stats(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);
+void free_global_thread_stats(void);
/*
TODO: Replace this with an inline function.
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 644796e..4d4db4a 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1587,6 +1587,11 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
table->mdl_ticket= NULL;
mysql_mutex_lock(&thd->LOCK_thd_data);
+ if(table->file)
+ {
+ table->file->update_global_table_stats();
+ table->file->update_global_index_stats();
+ }
*table_ptr=table->next;
mysql_mutex_unlock(&thd->LOCK_thd_data);
@@ -2212,6 +2217,8 @@ void close_temporary(TABLE *table, bool free_share, bool delete_table)
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);
if (delete_table)
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 9b5772d..cc2ef60 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -818,6 +818,13 @@ THD::THD()
mysys_var=0;
binlog_evt_union.do_union= FALSE;
enable_slow_log= 0;
+ busy_time= 0;
+ cpu_time= 0;
+ bytes_received= 0;
+ bytes_sent= 0;
+ binlog_bytes_written= 0;
+ updated_row_count= 0;
+ sent_row_count_2= 0;
#ifndef DBUG_OFF
dbug_sentry=THD_SENTRY_MAGIC;
#endif
@@ -1193,6 +1200,7 @@ void THD::init(void)
variables.option_bits|= OPTION_BIN_LOG;
else
variables.option_bits&= ~OPTION_BIN_LOG;
+ reset_stats();
#if defined(ENABLED_DEBUG_SYNC)
/* Initialize the Debug Sync Facility. See debug_sync.cc. */
@@ -1200,6 +1208,94 @@ void THD::init(void)
#endif /* defined(ENABLED_DEBUG_SYNC) */
}
+// 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_cpu_time= 0;
+ diff_total_bytes_received= 0;
+ diff_total_bytes_sent= 0;
+ diff_total_binlog_bytes_written= 0;
+ diff_total_sent_rows= 0;
+ diff_total_updated_rows= 0;
+ diff_total_read_rows= 0;
+ diff_select_commands= 0;
+ diff_update_commands= 0;
+ diff_other_commands= 0;
+ diff_commit_trans= 0;
+ diff_rollback_trans= 0;
+ diff_denied_connections= 0;
+ diff_lost_connections= 0;
+ diff_access_denied_errors= 0;
+ diff_empty_queries= 0;
+}
+
+// Updates 'diff' stats of a THD.
+void THD::update_stats(bool ran_command)
+{
+ if (opt_userstat)
+ {
+ diff_total_busy_time+= busy_time;
+ diff_total_cpu_time+= cpu_time;
+ diff_total_bytes_received+= bytes_received;
+ diff_total_bytes_sent+= bytes_sent;
+ diff_total_binlog_bytes_written+= binlog_bytes_written;
+ diff_total_sent_rows+= sent_row_count_2;
+ diff_total_updated_rows+= updated_row_count;
+ // diff_total_read_rows is updated in handler.cc.
+
+ if (ran_command)
+ {
+ // 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)
+ {
+ diff_select_commands++;
+ if (!sent_row_count_2)
+ diff_empty_queries++;
+ }
+ else if (!sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND)
+ {
+ // 'SHOW ' commands become SQLCOM_SELECT.
+ diff_other_commands++;
+ // 'SHOW ' commands shouldn't inflate total sent row count.
+ diff_total_sent_rows-= sent_row_count_2;
+ } 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.
+ // diff_denied_connections is updated in sql_parse.cc.
+ // diff_lost_connections is updated in sql_parse.cc.
+ // diff_access_denied_errors is updated in sql_parse.cc.
+
+ /* reset counters to zero to avoid double-counting since values
+ are already store in diff_total_*.
+ */
+ }
+ busy_time= 0;
+ cpu_time= 0;
+ bytes_received= 0;
+ bytes_sent= 0;
+ binlog_bytes_written= 0;
+ updated_row_count= 0;
+ sent_row_count_2= 0;
+}
/*
Init THD for query processing.
@@ -1953,6 +2049,32 @@ void THD::close_active_vio()
}
#endif
+char *THD::get_client_host_port(THD *client)
+{
+ Security_context *client_sctx= client->security_ctx;
+ char *client_host= NULL;
+
+ if (client->peer_port && (client_sctx->host || client_sctx->ip) &&
+ security_ctx->host_or_ip[0])
+ {
+ if ((client_host= (char *) this->alloc(LIST_PROCESS_HOST_LEN+1)))
+ my_snprintf((char *) client_host, LIST_PROCESS_HOST_LEN,
+ "%s:%u", client_sctx->host_or_ip, client->peer_port);
+ }
+ else
+ client_host= this->strdup(client_sctx->host_or_ip[0] ?
+ client_sctx->host_or_ip :
+ client_sctx->host ? client_sctx->host : "");
+
+ return client_host;
+}
+
+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 : "";
+}
struct Item_change_record: public ilink
{
@@ -2129,6 +2251,7 @@ bool select_send::send_data(List<Item> &items)
}
thd->sent_row_count++;
+ thd->sent_row_count_2++;
if (thd->vio_ok())
DBUG_RETURN(protocol->write());
@@ -2221,6 +2344,7 @@ select_to_file::~select_to_file()
select_export::~select_export()
{
thd->sent_row_count=row_count;
+ thd->sent_row_count_2= row_count;
}
@@ -3244,6 +3368,7 @@ void thd_increment_bytes_sent(ulong length)
if (likely(thd != 0))
{ /* current_thd==0 when close_connection() calls net_send_error() */
thd->status_var.bytes_sent+= length;
+ thd->bytes_sent+= length;
}
}
@@ -3251,6 +3376,7 @@ void thd_increment_bytes_sent(ulong length)
void thd_increment_bytes_received(ulong length)
{
current_thd->status_var.bytes_received+= length;
+ current_thd->bytes_received+= length;
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 9f13908..3653f62 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1570,6 +1570,8 @@ public:
*/
enum enum_server_command command;
uint32 server_id;
+ // Used to save the command, before it is set to COM_SLEEP.
+ enum enum_server_command old_command;
uint32 file_id; // for LOAD DATA INFILE
/* remote (peer) port */
uint16 peer_port;
@@ -2014,6 +2016,8 @@ public:
*/
enum_tx_isolation tx_isolation;
enum_check_fields count_cuted_fields;
+ ha_rows updated_row_count;
+ ha_rows sent_row_count_2; /* for userstat */
DYNAMIC_ARRAY user_var_events; /* For user variables replication */
MEM_ROOT *user_var_events_alloc; /* Allocate above array elements here */
@@ -2108,6 +2112,49 @@ 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;
+ // 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;
+ // Cpu (non-idle) time for just one thread.
+ double cpu_time;
+ // Cpu time not updated in global_user_stats yet.
+ double diff_total_cpu_time;
+ /* bytes counting */
+ ulonglong bytes_received;
+ ulonglong diff_total_bytes_received;
+ ulonglong bytes_sent;
+ ulonglong diff_total_bytes_sent;
+ ulonglong binlog_bytes_written;
+ ulonglong diff_total_binlog_bytes_written;
+
+ // Number of rows not reflected in global_user_stats yet.
+ ha_rows diff_total_sent_rows, diff_total_updated_rows, diff_total_read_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;
+ // Number of connection errors not reflected in global_user_stats yet.
+ ulonglong diff_denied_connections, diff_lost_connections;
+ // Number of db access denied, not reflected in global_user_stats yet.
+ ulonglong diff_access_denied_errors;
+ // Number of queries that return 0 rows
+ ulonglong diff_empty_queries;
+
+ // 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
{
@@ -2188,6 +2235,11 @@ public:
alloc_root.
*/
void init_for_queries();
+ 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(bool ran_command);
void change_user(void);
void cleanup(void);
void cleanup_after_query();
@@ -2660,6 +2712,15 @@ public:
}
thd_scheduler scheduler;
+ /* Returns string as 'IP:port' for the client-side
+ of the connnection represented
+ by 'client' as displayed by SHOW PROCESSLIST.
+ Allocates memory from the heap of
+ this THD and that is not reclaimed
+ immediately, so use sparingly. May return NULL.
+ */
+ char *get_client_host_port(THD *client);
+
public:
inline Internal_error_handler *get_internal_handler()
{ return m_internal_handler; }
@@ -2860,6 +2921,10 @@ private:
LEX_STRING invoker_host;
};
+/* Returns string as 'IP' for the client-side of the connection represented by
+ 'client'. Does not allocate memory. May return "".
+*/
+const char *get_client_host(THD *client);
/** A short cut for thd->stmt_da->set_ok_status(). */
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index e96d0e8..099ad60 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -56,6 +56,24 @@
#define MIN_HANDSHAKE_SIZE 6
#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
+// Increments connection count for user.
+static int increment_connection_count(THD* thd, bool use_lock);
+
+// Uses the THD to update the global stats by user name and client IP
+void update_global_user_stats(THD* thd, bool create_user, time_t now);
+
+HASH global_user_stats;
+HASH global_client_stats;
+HASH global_thread_stats;
+// Protects global_user_stats and global_client_stats
+extern mysql_mutex_t LOCK_global_user_client_stats;
+
+HASH global_table_stats;
+extern mysql_mutex_t LOCK_global_table_stats;
+
+HASH global_index_stats;
+extern mysql_mutex_t LOCK_global_index_stats;
+
/*
Get structure for logging connection data for the current user
*/
@@ -113,6 +131,586 @@ end:
}
+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" uchar *get_key_thread_stats(THREAD_STATS *thread_stats, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= sizeof(my_thread_id);
+ return (uchar *) &(thread_stats->id);
+}
+
+void free_user_stats(USER_STATS* user_stats)
+{
+ my_free((char *) user_stats);
+}
+
+void free_thread_stats(THREAD_STATS* thread_stats)
+{
+ my_free((char *) thread_stats);
+}
+
+void init_user_stats(USER_STATS *user_stats,
+ const char *user,
+ 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_fetched,
+ ha_rows rows_updated,
+ ha_rows rows_read,
+ 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));
+ strncpy(user_stats->user, user, sizeof(user_stats->user));
+ strncpy(user_stats->priv_user, priv_user, sizeof(user_stats->priv_user));
+
+ 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_fetched= rows_fetched;
+ 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;
+}
+
+void init_thread_stats(THREAD_STATS *thread_stats,
+ my_thread_id id,
+ 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_fetched,
+ ha_rows rows_updated,
+ ha_rows rows_read,
+ 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_thread_stats");
+ DBUG_PRINT("info",
+ ("Add thread_stats entry for thread %lu",
+ id));
+ thread_stats->id= id;
+
+ thread_stats->total_connections= total_connections;
+ thread_stats->concurrent_connections= concurrent_connections;
+ thread_stats->connected_time= connected_time;
+ thread_stats->busy_time= busy_time;
+ thread_stats->cpu_time= cpu_time;
+ thread_stats->bytes_received= bytes_received;
+ thread_stats->bytes_sent= bytes_sent;
+ thread_stats->binlog_bytes_written= binlog_bytes_written;
+ thread_stats->rows_fetched= rows_fetched;
+ thread_stats->rows_updated= rows_updated;
+ thread_stats->rows_read= rows_read;
+ thread_stats->select_commands= select_commands;
+ thread_stats->update_commands= update_commands;
+ thread_stats->other_commands= other_commands;
+ thread_stats->commit_trans= commit_trans;
+ thread_stats->rollback_trans= rollback_trans;
+ thread_stats->denied_connections= denied_connections;
+ thread_stats->lost_connections= lost_connections;
+ thread_stats->access_denied_errors= access_denied_errors;
+ thread_stats->empty_queries= empty_queries;
+ DBUG_VOID_RETURN;
+}
+
+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_fetched,
+ ha_rows rows_updated,
+ ha_rows rows_read,
+ 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_fetched+= rows_fetched;
+ 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;
+}
+
+void add_thread_stats(THREAD_STATS *thread_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_fetched,
+ ha_rows rows_updated,
+ ha_rows rows_read,
+ 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)
+{
+ thread_stats->total_connections+= total_connections;
+ thread_stats->concurrent_connections+= concurrent_connections;
+ thread_stats->connected_time+= connected_time;
+ thread_stats->busy_time+= busy_time;
+ thread_stats->cpu_time+= cpu_time;
+ thread_stats->bytes_received+= bytes_received;
+ thread_stats->bytes_sent+= bytes_sent;
+ thread_stats->binlog_bytes_written+= binlog_bytes_written;
+ thread_stats->rows_fetched+= rows_fetched;
+ thread_stats->rows_updated+= rows_updated;
+ thread_stats->rows_read+= rows_read;
+ thread_stats->select_commands+= select_commands;
+ thread_stats->update_commands+= update_commands;
+ thread_stats->other_commands+= other_commands;
+ thread_stats->commit_trans+= commit_trans;
+ thread_stats->rollback_trans+= rollback_trans;
+ thread_stats->denied_connections+= denied_connections;
+ thread_stats->lost_connections+= lost_connections;
+ thread_stats->access_denied_errors+= access_denied_errors;
+ thread_stats->empty_queries+= empty_queries;
+}
+
+void init_global_user_stats(void)
+{
+ if (my_hash_init(&global_user_stats, system_charset_info, max_connections,
+ 0, 0, (my_hash_get_key)get_key_user_stats,
+ (my_hash_free_key)free_user_stats, 0)) {
+ sql_print_error("Initializing global_user_stats failed.");
+ exit(1);
+ }
+}
+
+void init_global_client_stats(void)
+{
+ if (my_hash_init(&global_client_stats, system_charset_info, max_connections,
+ 0, 0, (my_hash_get_key)get_key_user_stats,
+ (my_hash_free_key)free_user_stats, 0)) {
+ sql_print_error("Initializing global_client_stats failed.");
+ exit(1);
+ }
+}
+
+void init_global_thread_stats(void)
+{
+ if (my_hash_init(&global_thread_stats, &my_charset_bin, max_connections,
+ 0, 0, (my_hash_get_key) get_key_thread_stats,
+ (my_hash_free_key) free_thread_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= strlen(table_stats->table);
+ return (uchar*) table_stats->table;
+}
+
+extern "C" void free_table_stats(TABLE_STATS* table_stats)
+{
+ my_free((char*) table_stats);
+}
+
+void init_global_table_stats(void)
+{
+ if (my_hash_init(&global_table_stats, system_charset_info, max_connections,
+ 0, 0, (my_hash_get_key)get_key_table_stats,
+ (my_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);
+}
+
+void init_global_index_stats(void)
+{
+ if (my_hash_init(&global_index_stats, system_charset_info, max_connections,
+ 0, 0, (my_hash_get_key)get_key_index_stats,
+ (my_hash_free_key)free_index_stats, 0)) {
+ sql_print_error("Initializing global_index_stats failed.");
+ exit(1);
+ }
+}
+
+void free_global_user_stats(void)
+{
+ my_hash_free(&global_user_stats);
+}
+
+void free_global_thread_stats(void)
+{
+ my_hash_free(&global_thread_stats);
+}
+
+void free_global_table_stats(void)
+{
+ my_hash_free(&global_table_stats);
+}
+
+void free_global_index_stats(void)
+{
+ my_hash_free(&global_index_stats);
+}
+
+void free_global_client_stats(void)
+{
+ my_hash_free(&global_client_stats);
+}
+
+// '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 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, const char *role_name,
+ HASH *users_or_clients, THD *thd)
+{
+ USER_STATS* user_stats;
+
+ if (!(user_stats = (USER_STATS *) my_hash_search(users_or_clients,
+ (uchar*) name,
+ strlen(name))))
+ {
+ // 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, role_name,
+ 0, 0, // connections
+ 0, 0, 0, // time
+ 0, 0, 0, // bytes sent, received and written
+ 0, 0, 0, // rows fetched, updated and read
+ 0, 0, 0, // select, update and other commands
+ 0, 0, // commit and rollback trans
+ thd->diff_denied_connections,
+ 0, // lost connections
+ 0, // access denied errors
+ 0); // empty queries
+
+ if (my_hash_insert(users_or_clients, (uchar *) user_stats))
+ {
+ my_free((char *) user_stats);
+ return 1; // Out of memory
+ }
+ }
+ user_stats->total_connections++;
+ return 0;
+}
+
+static int increment_count_by_id(my_thread_id id,
+ HASH *users_or_clients, THD *thd)
+{
+ THREAD_STATS* thread_stats;
+
+ if (!(thread_stats = (THREAD_STATS *) my_hash_search(users_or_clients,
+ (uchar*) &id,
+ sizeof(my_thread_id))))
+ {
+ // First connection for this user or client
+ if (!(thread_stats = ((THREAD_STATS *)
+ my_malloc(sizeof(THREAD_STATS), MYF(MY_WME | MY_ZEROFILL)))))
+ {
+ return 1; // Out of memory
+ }
+
+ init_thread_stats(thread_stats, id,
+ 0, 0, // connections
+ 0, 0, 0, // time
+ 0, 0, 0, // bytes sent, received and written
+ 0, 0, 0, // rows fetched, updated and read
+ 0, 0, 0, // select, update and other commands
+ 0, 0, // commit and rollback trans
+ thd->diff_denied_connections,
+ 0, // lost connections
+ 0, // access denied errors
+ 0); // empty queries
+
+ if (my_hash_insert(users_or_clients, (uchar *) thread_stats))
+ {
+ my_free((char *) thread_stats);
+ return 1; // Out of memory
+ }
+ }
+ thread_stats->total_connections++;
+ return 0;
+}
+
+/* Increments the global user and client stats connection count. If 'use_lock'
+ is true, LOCK_global_user_client_stats will be locked/unlocked. Returns
+ 0 on success, 1 on error.
+*/
+static int increment_connection_count(THD* thd, bool use_lock)
+{
+ 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 (!opt_userstat)
+ return return_value;
+
+ if (use_lock)
+ mysql_mutex_lock(&LOCK_global_user_client_stats);
+
+ if (increment_count_by_name(user_string, user_string,
+ &global_user_stats, thd))
+ {
+ return_value= 1;
+ goto end;
+ }
+ if (increment_count_by_name(client_string,
+ user_string,
+ &global_client_stats, thd))
+ {
+ return_value= 1;
+ goto end;
+ }
+ if (opt_thread_statistics)
+ {
+ if (increment_count_by_id(thd->thread_id, &global_thread_stats, thd))
+ {
+ return_value= 1;
+ goto end;
+ }
+ }
+
+end:
+ if (use_lock)
+ mysql_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)
+{
+ user_stats->connected_time+= now - thd->last_global_update_time;
+//thd->last_global_update_time= now;
+ user_stats->busy_time+= thd->diff_total_busy_time;
+ user_stats->cpu_time+= thd->diff_total_cpu_time;
+ user_stats->bytes_received+= thd->diff_total_bytes_received;
+ user_stats->bytes_sent+= thd->diff_total_bytes_sent;
+ user_stats->binlog_bytes_written+= thd->diff_total_binlog_bytes_written;
+ user_stats->rows_fetched+= thd->diff_total_sent_rows;
+ user_stats->rows_updated+= thd->diff_total_updated_rows;
+ user_stats->rows_read+= thd->diff_total_read_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;
+ user_stats->denied_connections+= thd->diff_denied_connections;
+ user_stats->lost_connections+= thd->diff_lost_connections;
+ user_stats->access_denied_errors+= thd->diff_access_denied_errors;
+ user_stats->empty_queries+= thd->diff_empty_queries;
+}
+
+static void update_global_thread_stats_with_thread(THD* thd,
+ THREAD_STATS* thread_stats,
+ time_t now)
+{
+ thread_stats->connected_time+= now - thd->last_global_update_time;
+//thd->last_global_update_time= now;
+ thread_stats->busy_time+= thd->diff_total_busy_time;
+ thread_stats->cpu_time+= thd->diff_total_cpu_time;
+ thread_stats->bytes_received+= thd->diff_total_bytes_received;
+ thread_stats->bytes_sent+= thd->diff_total_bytes_sent;
+ thread_stats->binlog_bytes_written+= thd->diff_total_binlog_bytes_written;
+ thread_stats->rows_fetched+= thd->diff_total_sent_rows;
+ thread_stats->rows_updated+= thd->diff_total_updated_rows;
+ thread_stats->rows_read+= thd->diff_total_read_rows;
+ thread_stats->select_commands+= thd->diff_select_commands;
+ thread_stats->update_commands+= thd->diff_update_commands;
+ thread_stats->other_commands+= thd->diff_other_commands;
+ thread_stats->commit_trans+= thd->diff_commit_trans;
+ thread_stats->rollback_trans+= thd->diff_rollback_trans;
+ thread_stats->denied_connections+= thd->diff_denied_connections;
+ thread_stats->lost_connections+= thd->diff_lost_connections;
+ thread_stats->access_denied_errors+= thd->diff_access_denied_errors;
+ thread_stats->empty_queries+= thd->diff_empty_queries;
+}
+
+// Updates the global stats of a user or client
+void update_global_user_stats(THD* thd, bool create_user, time_t now)
+{
+ if (opt_userstat)
+ {
+ char* user_string= get_valid_user_string(thd->main_security_ctx.user);
+ const char* client_string= get_client_host(thd);
+
+ USER_STATS* user_stats;
+ THREAD_STATS* thread_stats;
+ mysql_mutex_lock(&LOCK_global_user_client_stats);
+
+ // Update by user name
+ if ((user_stats = (USER_STATS *) my_hash_search(&global_user_stats,
+ (uchar *) user_string,
+ strlen(user_string))))
+ {
+ // 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,
+ &global_user_stats, thd);
+ }
+ }
+
+ // Update by client IP
+ if ((user_stats = (USER_STATS *) my_hash_search(&global_client_stats,
+ (uchar *) client_string,
+ strlen(client_string))))
+ {
+ // 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,
+ user_string,
+ &global_client_stats, thd);
+ }
+ }
+
+ if (opt_thread_statistics)
+ {
+ // Update by thread ID
+ if ((thread_stats = (THREAD_STATS *) my_hash_search(&global_thread_stats,
+ (uchar *) &(thd->thread_id),
+ sizeof(my_thread_id))))
+ {
+ // Found by thread ID
+ update_global_thread_stats_with_thread(thd, thread_stats, now);
+ }
+ else
+ {
+ // Create the entry
+ if (create_user)
+ {
+ increment_count_by_id(thd->thread_id,
+ &global_thread_stats, thd);
+ }
+ }
+ }
+
+ thd->last_global_update_time = now;
+ thd->reset_diff_stats();
+
+ mysql_mutex_unlock(&LOCK_global_user_client_stats);
+ }
+ else
+ {
+ thd->reset_diff_stats();
+ }
+}
/*
check if user has already too many connections
@@ -170,6 +768,7 @@ end:
if (error)
{
uc->connections--; // no need for decrease_user_connections() here
+ statistic_increment(denied_connections, &LOCK_status);
/*
The thread may returned back to the pool and assigned to a user
that doesn't have a limit. Ensure the user is not using resources
@@ -589,11 +1188,18 @@ bool login_connection(THD *thd)
my_sleep(1000); /* must wait after eof() */
#endif
statistic_increment(aborted_connects,&LOCK_status);
+ thd->diff_denied_connections++;
DBUG_RETURN(1);
}
/* 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);
+
+ thd->reset_stats();
+ // Updates global user connection stats.
+ if (increment_connection_count(thd, true))
+ DBUG_RETURN(1);
+
DBUG_RETURN(0);
}
@@ -623,6 +1229,7 @@ void end_connection(THD *thd)
if (thd->killed || (net->error && net->vio != 0))
{
statistic_increment(aborted_threads,&LOCK_status);
+ thd->diff_lost_connections++;
}
if (net->error && net->vio != 0)
@@ -778,10 +1385,14 @@ void do_handle_one_connection(THD *thd_arg)
for (;;)
{
bool rc;
+ bool create_user= TRUE;
rc= thd_prepare_connection(thd);
if (rc)
+ {
+ create_user= FALSE;
goto end_thread;
+ }
while (thd_is_connection_alive(thd))
{
@@ -793,6 +1404,8 @@ void do_handle_one_connection(THD *thd_arg)
end_thread:
close_connection(thd);
+ thd->update_stats(false);
+ update_global_user_stats(thd, create_user, time(NULL));
if (MYSQL_CALLBACK_ELSE(thread_scheduler, end_thread, (thd, 1), 0))
return; // Probably no-threads
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 4fd1815..5b972a5 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -411,6 +411,7 @@ cleanup:
my_ok(thd, deleted);
DBUG_PRINT("info",("%ld records deleted",(long) deleted));
}
+ thd->updated_row_count+= deleted;
DBUG_RETURN(error >= 0 || thd->is_error());
}
@@ -1005,6 +1006,7 @@ bool multi_delete::send_eof()
{
::my_ok(thd, deleted);
}
+ thd->updated_row_count+= deleted;
return 0;
}
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index eaf7f4c..3848051 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1070,13 +1070,14 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
if (error)
goto abort;
+ ha_rows row_count;
if (values_list.elements == 1 && (!(thd->variables.option_bits & OPTION_WARNINGS) ||
!thd->cuted_fields))
{
- my_ok(thd, info.copied + info.deleted +
+ row_count= info.copied + info.deleted +
((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
- info.touched : info.updated),
- id);
+ info.touched : info.updated);
+ my_ok(thd, row_count, id);
}
else
{
@@ -1092,8 +1093,10 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
(ulong) (info.deleted + updated),
(ulong) thd->warning_info->statement_warn_count());
- ::my_ok(thd, info.copied + info.deleted + updated, id, buff);
+ row_count= info.copied + info.deleted + updated;
+ ::my_ok(thd, row_count, id, buff);
}
+ thd->updated_row_count+= row_count;
thd->abort_on_warning= 0;
DBUG_RETURN(FALSE);
@@ -3535,6 +3538,7 @@ bool select_insert::send_eof()
thd->first_successful_insert_id_in_prev_stmt :
(info.copied ? autoinc_value_of_last_inserted_row : 0));
::my_ok(thd, row_count, id, buff);
+ thd->updated_row_count+= row_count;
DBUG_RETURN(0);
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 8794006..ce9f794 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -190,6 +190,9 @@ enum enum_sql_command {
SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES,
SQLCOM_SIGNAL, SQLCOM_RESIGNAL,
SQLCOM_SHOW_RELAYLOG_EVENTS,
+ // TODO(mcallaghan): update status_vars in mysqld to export these
+ SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS,
+ SQLCOM_SHOW_CLIENT_STATS, SQLCOM_SHOW_THREAD_STATS,
/*
When a command is added here, be sure it's also added in mysqld.cc
in "struct show_var_st status_vars[]= {" ...
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 885db29..37e240f 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -115,6 +115,9 @@
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
static void sql_kill(THD *thd, ulong id, bool only_kill_query);
+// Uses the THD to update the global stats by user name and client IP
+void update_global_user_stats(THD* thd, bool create_user, time_t now);
+
const char *any_db="*any*"; // Special symbol for check_access
const LEX_STRING command_name[]={
@@ -695,6 +698,12 @@ bool do_command(THD *thd)
*/
thd->clear_error(); // Clear error message
thd->stmt_da->reset_diagnostics_area();
+ thd->updated_row_count= 0;
+ thd->busy_time= 0;
+ thd->cpu_time= 0;
+ thd->bytes_received= 0;
+ thd->bytes_sent= 0;
+ thd->binlog_bytes_written= 0;
net_new_transaction(net);
@@ -880,6 +889,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
(char *) thd->security_ctx->host_or_ip);
thd->command=command;
+ /* To increment the corrent 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.
@@ -1569,6 +1582,13 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
thd->profiling.discard_current_query();
#endif
break;
+ case SCH_USER_STATS:
+ case SCH_CLIENT_STATS:
+ case SCH_THREAD_STATS:
+ if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
+ DBUG_RETURN(1);
+ case SCH_TABLE_STATS:
+ case SCH_INDEX_STATS:
case SCH_OPEN_TABLES:
case SCH_VARIABLES:
case SCH_STATUS:
@@ -1725,6 +1745,7 @@ bool sp_process_definer(THD *thd)
thd->security_ctx->priv_host)) &&
check_global_access(thd, SUPER_ACL))
{
+ thd->diff_access_denied_errors++;
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
DBUG_RETURN(TRUE);
}
@@ -4696,6 +4717,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
case ACL_INTERNAL_ACCESS_DENIED:
if (! no_errors)
{
+ thd->diff_access_denied_errors++;
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
sctx->priv_user, sctx->priv_host, db);
}
@@ -4746,6 +4768,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
DBUG_PRINT("error",("No possible access"));
if (!no_errors)
{
+ thd->diff_access_denied_errors++;
if (thd->password == 2)
my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
sctx->priv_user,
@@ -4860,6 +4883,7 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
if (!thd->col_access && check_grant_db(thd, dst_db_name))
{
+ thd->diff_access_denied_errors++;
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
thd->security_ctx->priv_user,
thd->security_ctx->priv_host,
@@ -5130,6 +5154,7 @@ bool check_global_access(THD *thd, ulong want_access)
if ((thd->security_ctx->master_access & want_access))
return 0;
get_privilege_desc(command, sizeof(command), want_access);
+ thd->diff_access_denied_errors++;
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
return 1;
#else
@@ -5496,6 +5521,32 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
lex_start(thd);
mysql_reset_thd_for_next_command(thd);
+ int start_time_error= 0;
+ int end_time_error= 0;
+ struct timeval start_time, end_time;
+ double start_usecs= 0;
+ double end_usecs= 0;
+ /* cpu time */
+ int cputime_error= 0;
+ struct timespec tp;
+ double start_cpu_nsecs= 0;
+ double end_cpu_nsecs= 0;
+
+ if (opt_userstat)
+ {
+#ifdef HAVE_CLOCK_GETTIME
+ /* get start cputime */
+ if (!(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
+ start_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
+#endif
+
+ // 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;
+ }
+ }
+
if (query_cache_send_result_to_client(thd, rawbuf, length) <= 0)
{
LEX *lex= thd->lex;
@@ -5564,6 +5615,52 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
DBUG_ASSERT(thd->change_list.is_empty());
}
+ if (opt_userstat)
+ {
+ // 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 (start_usecs && 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;
+ }
+
+#ifdef HAVE_CLOCK_GETTIME
+ /* get end cputime */
+ if (!cputime_error &&
+ !(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
+ end_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
+#endif
+ if (start_cpu_nsecs && !cputime_error)
+ {
+ thd->cpu_time = (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
+ // In case there are bad values, 2629743 is the #seconds in a month.
+ if (thd->cpu_time > 2629743)
+ {
+ thd->cpu_time = 0;
+ }
+ }
+ else
+ thd->cpu_time = 0;
+ }
+ // Updates THD stats and the global user stats.
+ thd->update_stats(true);
+ update_global_user_stats(thd, true, time(NULL));
+
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 48b6886..7ade489 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -114,6 +114,9 @@ When one supplies long data for a placeholder:
#endif
#include "lock.h" // MYSQL_OPEN_FORCE_SHARED_MDL
+// Uses the THD to update the global stats by user name and client IP
+void update_global_user_stats(THD* thd, bool create_user, time_t now);
+
/**
A result class used to send cursor rows using the binary protocol.
*/
@@ -2173,8 +2176,34 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
/* First of all clear possible warnings from the previous command */
mysql_reset_thd_for_next_command(thd);
+ int start_time_error= 0;
+ int end_time_error= 0;
+ struct timeval start_time, end_time;
+ double start_usecs= 0;
+ double end_usecs= 0;
+ /* cpu time */
+ int cputime_error= 0;
+ struct timespec tp;
+ double start_cpu_nsecs= 0;
+ double end_cpu_nsecs= 0;
+
+ if (opt_userstat)
+ {
+#ifdef HAVE_CLOCK_GETTIME
+ /* get start cputime */
+ if (!(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
+ start_cpu_nsecs= tp.tv_sec * 1000000000.0 + tp.tv_nsec;
+#endif
+
+ // 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;
+ }
+ }
+
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))
{
@@ -2182,7 +2211,7 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
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;
}
thd->protocol= &thd->protocol_binary;
@@ -2196,6 +2225,53 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
thd->protocol= save_protocol;
/* check_prepared_statemnt sends the metadata packet in case of success */
+end:
+ if (opt_userstat)
+ {
+ // 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 (start_usecs && 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;
+ }
+
+#ifdef HAVE_CLOCK_GETTIME
+ /* get end cputime */
+ if (!cputime_error &&
+ !(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
+ end_cpu_nsecs= tp.tv_sec*1000000000.0+tp.tv_nsec;
+#endif
+ if (start_cpu_nsecs && !cputime_error)
+ {
+ thd->cpu_time= (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
+ // In case there are bad values, 2629743 is the #seconds in a month.
+ if (thd->cpu_time > 2629743)
+ {
+ thd->cpu_time= 0;
+ }
+ }
+ else
+ thd->cpu_time = 0;
+ }
+ // Updates THD stats and the global user stats.
+ thd->update_stats(true);
+ update_global_user_stats(thd, true, time(NULL));
+
DBUG_VOID_RETURN;
}
@@ -2540,12 +2616,38 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
/* First of all clear possible warnings from the previous command */
mysql_reset_thd_for_next_command(thd);
+ int start_time_error= 0;
+ int end_time_error= 0;
+ struct timeval start_time, end_time;
+ double start_usecs= 0;
+ double end_usecs= 0;
+ /* cpu time */
+ int cputime_error= 0;
+ struct timespec tp;
+ double start_cpu_nsecs= 0;
+ double end_cpu_nsecs= 0;
+
+ if (opt_userstat)
+ {
+#ifdef HAVE_CLOCK_GETTIME
+ /* get start cputime */
+ if (!(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
+ start_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
+#endif
+
+ // 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;
+ }
+ }
+
if (!(stmt= find_prepared_statement(thd, stmt_id)))
{
char llbuf[22];
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(sizeof(llbuf)),
llstr(stmt_id, llbuf), "mysqld_stmt_execute");
- DBUG_VOID_RETURN;
+ goto end;
}
#if defined(ENABLED_PROFILING)
@@ -2563,6 +2665,53 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
/* Close connection socket; for use with client testing (Bug#43560). */
DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio););
+end:
+ if (opt_userstat)
+ {
+ // 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 (start_usecs && 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;
+ }
+
+#ifdef HAVE_CLOCK_GETTIME
+ /* get end cputime */
+ if (!cputime_error &&
+ !(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
+ end_cpu_nsecs= tp.tv_sec*1000000000.0+tp.tv_nsec;
+#endif
+ if (start_cpu_nsecs && !cputime_error)
+ {
+ thd->cpu_time= (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
+ // In case there are bad values, 2629743 is the #seconds in a month.
+ if (thd->cpu_time > 2629743)
+ {
+ thd->cpu_time= 0;
+ }
+ }
+ else
+ thd->cpu_time = 0;
+ }
+ // Updates THD stats and the global user stats.
+ thd->update_stats(true);
+ update_global_user_stats(thd, true, time(NULL));
+
DBUG_VOID_RETURN;
}
@@ -2635,20 +2784,47 @@ void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length)
/* First of all clear possible warnings from the previous command */
mysql_reset_thd_for_next_command(thd);
+
+ int start_time_error= 0;
+ int end_time_error= 0;
+ struct timeval start_time, end_time;
+ double start_usecs= 0;
+ double end_usecs= 0;
+ /* cpu time */
+ int cputime_error= 0;
+ struct timespec tp;
+ double start_cpu_nsecs= 0;
+ double end_cpu_nsecs= 0;
+
+ if (opt_userstat)
+ {
+#ifdef HAVE_CLOCK_GETTIME
+ /* get start cputime */
+ if (!(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
+ start_cpu_nsecs= tp.tv_sec*1000000000.0+tp.tv_nsec;
+#endif
+
+ // 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;
+ }
+ }
+
status_var_increment(thd->status_var.com_stmt_fetch);
if (!(stmt= find_prepared_statement(thd, stmt_id)))
{
char llbuf[22];
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(sizeof(llbuf)),
llstr(stmt_id, llbuf), "mysqld_stmt_fetch");
- DBUG_VOID_RETURN;
+ goto end;
}
cursor= stmt->cursor;
if (!cursor)
{
my_error(ER_STMT_HAS_NO_OPEN_CURSOR, MYF(0), stmt_id);
- DBUG_VOID_RETURN;
+ goto end;
}
thd->stmt_arena= stmt;
@@ -2665,6 +2841,52 @@ void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length)
thd->restore_backup_statement(stmt, &stmt_backup);
thd->stmt_arena= thd;
+end:
+ if (opt_userstat)
+ {
+ // 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 (start_usecs && 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;
+ }
+
+#ifdef HAVE_CLOCK_GETTIME
+ /* get end cputime */
+ if (!cputime_error &&
+ !(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
+ end_cpu_nsecs= tp.tv_sec*1000000000.0+tp.tv_nsec;
+#endif
+ if (start_cpu_nsecs && !cputime_error)
+ {
+ thd->cpu_time= (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
+ // In case there are bad values, 2629743 is the #seconds in a month.
+ if (thd->cpu_time > 2629743)
+ {
+ thd->cpu_time= 0;
+ }
+ } else
+ thd->cpu_time= 0;
+ }
+ // Updates THD stats and the global user stats.
+ thd->update_stats(true);
+ update_global_user_stats(thd, true, time(NULL));
+
DBUG_VOID_RETURN;
}
@@ -2695,13 +2917,39 @@ void mysqld_stmt_reset(THD *thd, char *packet)
/* First of all clear possible warnings from the previous command */
mysql_reset_thd_for_next_command(thd);
+ int start_time_error= 0;
+ int end_time_error= 0;
+ struct timeval start_time, end_time;
+ double start_usecs= 0;
+ double end_usecs= 0;
+ /* cpu time */
+ int cputime_error= 0;
+ struct timespec tp;
+ double start_cpu_nsecs= 0;
+ double end_cpu_nsecs= 0;
+
+ if (opt_userstat)
+ {
+#ifdef HAVE_CLOCK_GETTIME
+ /* get start cputime */
+ if (!(cputime_error= clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
+ start_cpu_nsecs= tp.tv_sec * 1000000000.0+tp.tv_nsec;
+#endif
+
+ // 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;
+ }
+ }
+
status_var_increment(thd->status_var.com_stmt_reset);
if (!(stmt= find_prepared_statement(thd, stmt_id)))
{
char llbuf[22];
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(sizeof(llbuf)),
llstr(stmt_id, llbuf), "mysqld_stmt_reset");
- DBUG_VOID_RETURN;
+ goto end;
}
stmt->close_cursor();
@@ -2718,6 +2966,53 @@ void mysqld_stmt_reset(THD *thd, char *packet)
my_ok(thd);
+end:
+ if (opt_userstat)
+ {
+ // 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 (start_usecs && 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;
+ }
+
+#ifdef HAVE_CLOCK_GETTIME
+ /* get end cputime */
+ if (!cputime_error &&
+ !(cputime_error = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)))
+ end_cpu_nsecs = tp.tv_sec*1000000000.0+tp.tv_nsec;
+#endif
+ if (start_cpu_nsecs && !cputime_error)
+ {
+ thd->cpu_time = (end_cpu_nsecs - start_cpu_nsecs) / 1000000000;
+ // In case there are bad values, 2629743 is the #seconds in a month.
+ if (thd->cpu_time > 2629743)
+ {
+ thd->cpu_time= 0;
+ }
+ }
+ else
+ thd->cpu_time= 0;
+ }
+ // Updates THD stats and the global user stats.
+ thd->update_stats(true);
+ update_global_user_stats(thd, true, time(NULL));
+
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc
index b567e3a..4821b25 100644
--- a/sql/sql_reload.cc
+++ b/sql/sql_reload.cc
@@ -295,7 +295,41 @@ bool reload_acl_and_cache(THD *thd, unsigned long options,
}
#endif
if (options & REFRESH_USER_RESOURCES)
- reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
+ reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
+ if (options & REFRESH_TABLE_STATS)
+ {
+ mysql_mutex_lock(&LOCK_global_table_stats);
+ free_global_table_stats();
+ init_global_table_stats();
+ mysql_mutex_unlock(&LOCK_global_table_stats);
+ }
+ if (options & REFRESH_INDEX_STATS)
+ {
+ mysql_mutex_lock(&LOCK_global_index_stats);
+ free_global_index_stats();
+ init_global_index_stats();
+ mysql_mutex_unlock(&LOCK_global_index_stats);
+ }
+ if (options & (REFRESH_USER_STATS | REFRESH_CLIENT_STATS | REFRESH_THREAD_STATS))
+ {
+ mysql_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();
+ }
+ if (options & REFRESH_THREAD_STATS)
+ {
+ free_global_thread_stats();
+ init_global_thread_stats();
+ }
+ mysql_mutex_unlock(&LOCK_global_user_client_stats);
+ }
if (*write_to_binlog != -1)
*write_to_binlog= tmp_write_to_binlog;
/*
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 887115b..67b5f35 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -113,6 +113,43 @@ append_algorithm(TABLE_LIST *table, String *buff);
static COND * make_cond_for_info_schema(COND *cond, TABLE_LIST *table);
+/*
+ * Solaris 10 does not have strsep().
+ *
+ * based on getToken from http://www.winehq.org/pipermail/wine-patches/2001-November/001322.html
+ *
+*/
+
+#ifndef HAVE_STRSEP
+static char* strsep(char** str, const char* delims)
+{
+ char *token;
+
+ if (*str == NULL)
+ {
+ /* No more tokens */
+ return NULL;
+ }
+
+ token= *str;
+ while (**str != '\0')
+ {
+ if (strchr(delims, **str) != NULL)
+ {
+ **str= '\0';
+ (*str)++;
+ return token;
+ }
+ (*str)++;
+ }
+
+ /* There is not another token */
+ *str= NULL;
+
+ return token;
+}
+#endif
+
/***************************************************************************
** List all table types supported
***************************************************************************/
@@ -799,6 +836,7 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
sctx->master_access);
if (!(db_access & DB_ACLS) && check_grant_db(thd,dbname))
{
+ thd->diff_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),
@@ -2344,6 +2382,284 @@ end:
DBUG_RETURN(res);
}
+/*
+ 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)
+ {
+ restore_record(table, s->default_values);
+ USER_STATS *user_stats = (USER_STATS *) my_hash_element(all_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);
+ table->field[2]->store((longlong)user_stats->concurrent_connections);
+ table->field[3]->store((longlong)user_stats->connected_time);
+ table->field[4]->store((longlong)user_stats->busy_time);
+ table->field[5]->store((longlong)user_stats->cpu_time);
+ table->field[6]->store((longlong)user_stats->bytes_received);
+ table->field[7]->store((longlong)user_stats->bytes_sent);
+ table->field[8]->store((longlong)user_stats->binlog_bytes_written);
+ table->field[9]->store((longlong)user_stats->rows_fetched);
+ table->field[10]->store((longlong)user_stats->rows_updated);
+ table->field[11]->store((longlong)user_stats->rows_read);
+ table->field[12]->store((longlong)user_stats->select_commands);
+ table->field[13]->store((longlong)user_stats->update_commands);
+ table->field[14]->store((longlong)user_stats->other_commands);
+ table->field[15]->store((longlong)user_stats->commit_trans);
+ table->field[16]->store((longlong)user_stats->rollback_trans);
+ table->field[17]->store((longlong)user_stats->denied_connections);
+ table->field[18]->store((longlong)user_stats->lost_connections);
+ table->field[19]->store((longlong)user_stats->access_denied_errors);
+ table->field[20]->store((longlong)user_stats->empty_queries);
+ if (schema_table_store_record(thd, table))
+ {
+ DBUG_PRINT("error", ("store record error"));
+ DBUG_RETURN(1);
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+int send_thread_stats(THD* thd, HASH *all_thread_stats, TABLE *table)
+{
+ DBUG_ENTER("send_thread_stats");
+ for (uint i = 0; i < all_thread_stats->records; ++i)
+ {
+ restore_record(table, s->default_values);
+ THREAD_STATS *user_stats = (THREAD_STATS *) my_hash_element(all_thread_stats, i);
+ table->field[0]->store((longlong)user_stats->id);
+ table->field[1]->store((longlong)user_stats->total_connections);
+ table->field[2]->store((longlong)user_stats->concurrent_connections);
+ table->field[3]->store((longlong)user_stats->connected_time);
+ table->field[4]->store((longlong)user_stats->busy_time);
+ table->field[5]->store((longlong)user_stats->cpu_time);
+ table->field[6]->store((longlong)user_stats->bytes_received);
+ table->field[7]->store((longlong)user_stats->bytes_sent);
+ table->field[8]->store((longlong)user_stats->binlog_bytes_written);
+ table->field[9]->store((longlong)user_stats->rows_fetched);
+ table->field[10]->store((longlong)user_stats->rows_updated);
+ table->field[11]->store((longlong)user_stats->rows_read);
+ table->field[12]->store((longlong)user_stats->select_commands);
+ table->field[13]->store((longlong)user_stats->update_commands);
+ table->field[14]->store((longlong)user_stats->other_commands);
+ table->field[15]->store((longlong)user_stats->commit_trans);
+ table->field[16]->store((longlong)user_stats->rollback_trans);
+ table->field[17]->store((longlong)user_stats->denied_connections);
+ table->field[18]->store((longlong)user_stats->lost_connections);
+ table->field[19]->store((longlong)user_stats->access_denied_errors);
+ table->field[20]->store((longlong)user_stats->empty_queries);
+ 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;
+ 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.
+
+ mysql_mutex_lock(&LOCK_global_user_client_stats);
+ int result= send_user_stats(thd, &global_user_stats, table);
+ mysql_mutex_unlock(&LOCK_global_user_client_stats);
+ if (result)
+ goto err;
+
+ DBUG_PRINT("exit", ("fill_schema_user_stats result is 0"));
+ DBUG_RETURN(0);
+
+ err:
+ DBUG_PRINT("exit", ("fill_schema_user_stats result is 1"));
+ DBUG_RETURN(1);
+}
+
+/*
+ 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;
+ 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.
+
+ mysql_mutex_lock(&LOCK_global_user_client_stats);
+ int result= send_user_stats(thd, &global_client_stats, table);
+ mysql_mutex_unlock(&LOCK_global_user_client_stats);
+ if (result)
+ goto err;
+
+ DBUG_PRINT("exit", ("mysqld_show_client_stats result is 0"));
+ DBUG_RETURN(0);
+
+ err:
+ DBUG_PRINT("exit", ("mysqld_show_client_stats result is 1"));
+ DBUG_RETURN(1);
+}
+
+int fill_schema_thread_stats(THD* thd, TABLE_LIST* tables, COND* cond)
+{
+ TABLE *table= tables->table;
+ DBUG_ENTER("fill_schema_thread_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.
+
+ mysql_mutex_lock(&LOCK_global_user_client_stats);
+ int result= send_thread_stats(thd, &global_thread_stats, table);
+ mysql_mutex_unlock(&LOCK_global_user_client_stats);
+ if (result)
+ goto err;
+
+ DBUG_PRINT("exit", ("mysqld_show_thread_stats result is 0"));
+ DBUG_RETURN(0);
+
+ err:
+ DBUG_PRINT("exit", ("mysqld_show_thread_stats result is 1"));
+ DBUG_RETURN(1);
+}
+
+// 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");
+ char *table_full_name, *table_schema;
+
+ mysql_mutex_lock(&LOCK_global_table_stats);
+ for (uint i = 0; i < global_table_stats.records; ++i)
+ {
+ restore_record(table, s->default_values);
+ TABLE_STATS *table_stats =
+ (TABLE_STATS *) my_hash_element(&global_table_stats, i);
+
+ table_full_name= thd->strdup(table_stats->table);
+ table_schema= strsep(&table_full_name, ".");
+
+ TABLE_LIST tmp_table;
+ bzero((char *) &tmp_table,sizeof(tmp_table));
+ tmp_table.table_name= table_full_name;
+ tmp_table.db= table_schema;
+ tmp_table.grant.privilege= 0;
+ if (check_access(thd, SELECT_ACL, tmp_table.db,
+ &tmp_table.grant.privilege, 0, 0,
+ is_infoschema_db(table_schema)) ||
+ check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, 1))
+ continue;
+
+ table->field[0]->store(table_schema, strlen(table_schema), system_charset_info);
+ table->field[1]->store(table_full_name, strlen(table_full_name), 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))
+ {
+ mysql_mutex_unlock(&LOCK_global_table_stats);
+ DBUG_RETURN(1);
+ }
+ }
+ mysql_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");
+ char *index_full_name, *table_schema, *table_name;
+
+ mysql_mutex_lock(&LOCK_global_index_stats);
+ for (uint i = 0; i < global_index_stats.records; ++i)
+ {
+ restore_record(table, s->default_values);
+ INDEX_STATS *index_stats =
+ (INDEX_STATS *) my_hash_element(&global_index_stats, i);
+
+ index_full_name= thd->strdup(index_stats->index);
+ table_schema= strsep(&index_full_name, ".");
+ table_name= strsep(&index_full_name, ".");
+
+ TABLE_LIST tmp_table;
+ bzero((char *) &tmp_table,sizeof(tmp_table));
+ tmp_table.table_name= table_name;
+ tmp_table.db= table_schema;
+ tmp_table.grant.privilege= 0;
+ if (check_access(thd, SELECT_ACL, tmp_table.db,
+ &tmp_table.grant.privilege, 0, 0,
+ is_infoschema_db(table_schema)) ||
+ check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, 1))
+ continue;
+
+ table->field[0]->store(table_schema, strlen(table_schema), system_charset_info);
+ table->field[1]->store(table_name, strlen(table_name), system_charset_info);
+ table->field[2]->store(index_full_name, strlen(index_full_name), system_charset_info);
+ table->field[3]->store((longlong)index_stats->rows_read, TRUE);
+
+ if (schema_table_store_record(thd, table))
+ {
+ mysql_mutex_unlock(&LOCK_global_index_stats);
+ DBUG_RETURN(1);
+ }
+ }
+ mysql_mutex_unlock(&LOCK_global_index_stats);
+ DBUG_RETURN(0);
+}
+
/* collect status for all running threads */
@@ -7452,6 +7768,104 @@ ST_FIELD_INFO variables_fields_info[]=
};
+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_LONG, 0, 0, "Busy_time", SKIP_OPEN_TABLE},
+ {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 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_FETCHED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_fetched", SKIP_OPEN_TABLE},
+ {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated", SKIP_OPEN_TABLE},
+ {"TABLE_ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Table_rows_read", 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_LONG, 0, 0, "Busy_time", SKIP_OPEN_TABLE},
+ {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 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_FETCHED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_fetched", SKIP_OPEN_TABLE},
+ {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated", SKIP_OPEN_TABLE},
+ {"TABLE_ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Table_rows_read", 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 thread_stats_fields_info[]=
+{
+ {"THREAD_ID", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Thread_id", 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_LONG, 0, 0, "Busy_time", SKIP_OPEN_TABLE},
+ {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 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_FETCHED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_fetched", SKIP_OPEN_TABLE},
+ {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated", SKIP_OPEN_TABLE},
+ {"TABLE_ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Table_rows_read", 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}
+};
+
+
ST_FIELD_INFO processlist_fields_info[]=
{
{"ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Id", SKIP_OPEN_TABLE},
@@ -7631,6 +8045,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,
@@ -7640,6 +8056,8 @@ ST_SCHEMA_TABLE schema_tables[]=
OPTIMIZE_I_S_TABLE|OPEN_VIEW_FULL},
{"COLUMN_PRIVILEGES", column_privileges_fields_info, create_schema_table,
fill_schema_column_privileges, 0, 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},
{"ENGINES", engines_fields_info, create_schema_table,
fill_schema_engines, make_old_format, 0, -1, -1, 0, 0},
#ifdef HAVE_EVENT_SCHEDULER
@@ -7702,11 +8120,17 @@ 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},
+ {"THREAD_STATISTICS", thread_stats_fields_info, create_schema_table,
+ fill_schema_thread_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_TRIGGER_ONLY|OPTIMIZE_I_S_TABLE},
{"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,
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 754170e..1f0d624 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -900,8 +900,10 @@ int mysql_update(THD *thd,
my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), (ulong) found,
(ulong) updated,
(ulong) thd->warning_info->statement_warn_count());
- my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
- id, buff);
+ ha_rows row_count=
+ (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
+ my_ok(thd, row_count, id, buff);
+ thd->updated_row_count += row_count;
DBUG_PRINT("info",("%ld records updated", (long) updated));
}
thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */
@@ -2252,7 +2254,9 @@ bool multi_update::send_eof()
thd->first_successful_insert_id_in_prev_stmt : 0;
my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO),
(ulong) found, (ulong) updated, (ulong) thd->cuted_fields);
- ::my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
- id, buff);
+ ha_rows row_count=
+ (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
+ ::my_ok(thd, row_count, id, buff);
+ thd->updated_row_count+= row_count;
DBUG_RETURN(FALSE);
}
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 6fe9626..319a25c 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -865,6 +865,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token CIPHER_SYM
%token CLASS_ORIGIN_SYM /* SQL-2003-N */
%token CLIENT_SYM
+%token CLIENT_STATS_SYM
%token CLOSE_SYM /* SQL-2003-R */
%token COALESCE /* SQL-2003-N */
%token CODE_SYM
@@ -1018,6 +1019,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token IMPORT
%token INDEXES
%token INDEX_SYM
+%token INDEX_STATS_SYM
%token INFILE
%token INITIAL_SIZE_SYM
%token INNER_SYM /* SQL-2003-R */
@@ -1313,6 +1315,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token TABLESPACE
%token TABLE_REF_PRIORITY
%token TABLE_SYM /* SQL-2003-R */
+%token TABLE_STATS_SYM
%token TABLE_CHECKSUM_SYM
%token TABLE_NAME_SYM /* SQL-2003-N */
%token TEMPORARY /* SQL-2003-N */
@@ -1322,6 +1325,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token TEXT_SYM
%token THAN_SYM
%token THEN_SYM /* SQL-2003-R */
+%token THREAD_STATS_SYM
%token TIMESTAMP /* SQL-2003-R */
%token TIMESTAMP_ADD
%token TIMESTAMP_DIFF
@@ -1359,6 +1363,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%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 */
@@ -11086,6 +11091,41 @@ show_param:
{
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
}
+ | CLIENT_STATS_SYM wild_and_where
+ {
+ LEX *lex= Lex;
+ Lex->sql_command= SQLCOM_SELECT;
+ 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_SELECT;
+ if (prepare_schema_table(YYTHD, lex, 0, SCH_USER_STATS))
+ MYSQL_YYABORT;
+ }
+ | THREAD_STATS_SYM wild_and_where
+ {
+ LEX *lex= Lex;
+ Lex->sql_command= SQLCOM_SELECT;
+ if (prepare_schema_table(YYTHD, lex, 0, SCH_THREAD_STATS))
+ MYSQL_YYABORT;
+ }
+ | TABLE_STATS_SYM wild_and_where
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SELECT;
+ 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_SELECT;
+ if (prepare_schema_table(YYTHD, lex, 0, SCH_INDEX_STATS))
+ MYSQL_YYABORT;
+ }
| CREATE PROCEDURE_SYM sp_name
{
LEX *lex= Lex;
@@ -11325,6 +11365,16 @@ flush_option:
Lex->type|= REFRESH_SLAVE;
Lex->reset_slave_info.all= false;
}
+ | CLIENT_STATS_SYM
+ { Lex->type|= REFRESH_CLIENT_STATS; }
+ | USER_STATS_SYM
+ { Lex->type|= REFRESH_USER_STATS; }
+ | THREAD_STATS_SYM
+ { Lex->type|= REFRESH_THREAD_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
@@ -12469,6 +12519,7 @@ keyword_sp:
| CHAIN_SYM {}
| CHANGED {}
| CIPHER_SYM {}
+ | CLIENT_STATS_SYM {}
| CLIENT_SYM {}
| CLASS_ORIGIN_SYM {}
| COALESCE {}
@@ -12537,6 +12588,7 @@ keyword_sp:
| HOSTS_SYM {}
| HOUR_SYM {}
| IDENTIFIED_SYM {}
+ | INDEX_STATS_SYM {}
| IGNORE_SERVER_IDS_SYM {}
| INVOKER_SYM {}
| IMPORT {}
@@ -12687,6 +12739,7 @@ keyword_sp:
| SUSPEND_SYM {}
| SWAPS_SYM {}
| SWITCHES_SYM {}
+ | TABLE_STATS_SYM {}
| TABLE_NAME_SYM {}
| TABLES {}
| TABLE_CHECKSUM_SYM {}
@@ -12712,6 +12765,7 @@ keyword_sp:
| UNKNOWN_SYM {}
| UNTIL_SYM {}
| USER {}
+ | USER_STATS_SYM {}
| USE_FRM {}
| VARIABLES {}
| VIEW_SYM {}
diff --git a/sql/structs.h b/sql/structs.h
index 8ac855a..af9ce47 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -25,6 +25,7 @@
#include "my_time.h" /* enum_mysql_timestamp_type */
#include "thr_lock.h" /* thr_lock_type */
#include "my_base.h" /* ha_rows, ha_key_alg */
+#include "mysql_com.h"
struct TABLE;
class Field;
@@ -218,6 +219,171 @@ 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 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_fetched, rows_updated, rows_read;
+ 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 my_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 my_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,
+ 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_fetched,
+ ha_rows rows_updated,
+ ha_rows rows_read,
+ 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_fetched,
+ ha_rows rows_updated,
+ ha_rows rows_read,
+ 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_thread_stats {
+ my_thread_id id;
+ 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_fetched, rows_updated, rows_read;
+ ulonglong select_commands, update_commands, other_commands;
+ ulonglong commit_trans, rollback_trans;
+ ulonglong denied_connections, lost_connections;
+ ulonglong access_denied_errors;
+ ulonglong empty_queries;
+} THREAD_STATS;
+
+/* Lookup function for my_hash tables with THREAD_STATS entries */
+extern "C" uchar *get_key_thread_stats(THREAD_STATS *thread_stats, size_t *length,
+ my_bool not_used __attribute__((unused)));
+
+/* Free all memory for a my_hash table with THREAD_STATS entries */
+extern void free_thread_stats(THREAD_STATS* thread_stats);
+
+/* Intialize an instance of THREAD_STATS */
+extern void
+init_thread_stats(THREAD_STATS *thread_stats,
+ my_thread_id id,
+ 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_fetched,
+ ha_rows rows_updated,
+ ha_rows rows_read,
+ 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 THREAD_STATS */
+extern void
+add_thread_stats(THREAD_STATS *thread_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_fetched,
+ ha_rows rows_updated,
+ ha_rows rows_read,
+ 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] + '.' + [table] + '\0'
+ 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 {
+ 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 */
#define REG_NEW_RECORD 2 /* Write a new record if not found */
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index da8e61d..6a65b26 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -1599,6 +1599,17 @@ static Sys_var_mybool Sys_readonly(
NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(check_read_only), ON_UPDATE(fix_read_only));
+static Sys_var_mybool Sys_userstat(
+ "userstat",
+ "Control USER_STATISTICS, CLIENT_STATISTICS, THREAD_STATISTICS, "
+ "INDEX_STATISTICS and TABLE_STATISTICS running",
+ GLOBAL_VAR(opt_userstat), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+
+static Sys_var_mybool Sys_thread_statistics(
+ "thread_statistics",
+ "Control TABLE_STATISTICS running, when userstat is enabled",
+ GLOBAL_VAR(opt_thread_statistics), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+
// Small lower limit to be able to test MRR
static Sys_var_ulong Sys_read_rnd_buff_size(
"read_rnd_buffer_size",
diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc
index b2b6405..c7a4c47 100644
--- a/storage/myisam/ha_myisam.cc
+++ b/storage/myisam/ha_myisam.cc
@@ -770,6 +770,7 @@ int ha_myisam::close(void)
int ha_myisam::write_row(uchar *buf)
{
+ int error;
ha_statistic_increment(&SSV::ha_write_count);
/* If we have a timestamp column, update it to the current time */
@@ -782,11 +783,13 @@ int ha_myisam::write_row(uchar *buf)
*/
if (table->next_number_field && buf == table->record[0])
{
- int error;
if ((error= update_auto_increment()))
return error;
}
- return mi_write(file,buf);
+ error=mi_write(file,buf);
+ if (!error)
+ rows_changed++;
+ return error;
}
int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
@@ -1537,16 +1540,24 @@ bool ha_myisam::is_crashed() const
int ha_myisam::update_row(const uchar *old_data, uchar *new_data)
{
+ int error;
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);
+ error=mi_update(file,old_data,new_data);
+ if (!error)
+ rows_changed++;
+ return error;
}
int ha_myisam::delete_row(const uchar *buf)
{
+ int error;
ha_statistic_increment(&SSV::ha_delete_count);
- return mi_delete(file,buf);
+ error=mi_delete(file,buf);
+ if (!error)
+ rows_changed++;
+ return error;
}
int ha_myisam::index_read_map(uchar *buf, const uchar *key,
@@ -1558,6 +1569,14 @@ int ha_myisam::index_read_map(uchar *buf, const uchar *key,
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 == MAX_KEY) ? file->lastinx : active_index;
+ if (inx >= 0 && inx < MAX_KEY)
+ index_rows_read[inx]++;
+ }
MYSQL_INDEX_READ_ROW_DONE(error);
return error;
}
@@ -1570,6 +1589,14 @@ int ha_myisam::index_read_idx_map(uchar *buf, uint index, const uchar *key,
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 = index;
+ if (inx >= 0 && inx < MAX_KEY)
+ index_rows_read[inx]++;
+ }
MYSQL_INDEX_READ_ROW_DONE(error);
return error;
}
@@ -1584,6 +1611,14 @@ int ha_myisam::index_read_last_map(uchar *buf, const uchar *key,
int error=mi_rkey(file, buf, active_index, key, keypart_map,
HA_READ_PREFIX_LAST);
table->status=error ? STATUS_NOT_FOUND: 0;
+ if (!error)
+ {
+ rows_read++;
+
+ int inx = (active_index == MAX_KEY) ? file->lastinx : active_index;
+ if (inx >= 0 && inx < MAX_KEY)
+ index_rows_read[inx]++;
+ }
MYSQL_INDEX_READ_ROW_DONE(error);
DBUG_RETURN(error);
}
@@ -1595,6 +1630,13 @@ int ha_myisam::index_next(uchar *buf)
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 == MAX_KEY) ? file->lastinx : active_index;
+ if (inx >= 0 && inx < MAX_KEY)
+ index_rows_read[inx]++;
+ }
MYSQL_INDEX_READ_ROW_DONE(error);
return error;
}
@@ -1606,6 +1648,13 @@ int ha_myisam::index_prev(uchar *buf)
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 == MAX_KEY) ? file->lastinx : active_index;
+ if (inx >= 0 && inx < MAX_KEY)
+ index_rows_read[inx]++;
+ }
MYSQL_INDEX_READ_ROW_DONE(error);
return error;
}
@@ -1617,6 +1666,14 @@ int ha_myisam::index_first(uchar *buf)
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 == MAX_KEY) ? file->lastinx : active_index;
+ if (inx >= 0 && inx < MAX_KEY)
+ index_rows_read[inx]++;
+ }
MYSQL_INDEX_READ_ROW_DONE(error);
return error;
}
@@ -1628,6 +1685,14 @@ int ha_myisam::index_last(uchar *buf)
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 == MAX_KEY) ? file->lastinx : active_index;
+ if (inx >= 0 && inx < MAX_KEY)
+ index_rows_read[inx]++;
+ }
MYSQL_INDEX_READ_ROW_DONE(error);
return error;
}
@@ -1645,6 +1710,14 @@ int ha_myisam::index_next_same(uchar *buf,
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 == MAX_KEY) ? file->lastinx : active_index;
+ if (inx >= 0 && inx < MAX_KEY)
+ index_rows_read[inx]++;
+ }
MYSQL_INDEX_READ_ROW_DONE(error);
return error;
}
@@ -1664,6 +1737,8 @@ int ha_myisam::rnd_next(uchar *buf)
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++;
MYSQL_READ_ROW_DONE(error);
return error;
}
@@ -1680,6 +1755,8 @@ int ha_myisam::rnd_pos(uchar *buf, uchar *pos)
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++;
MYSQL_READ_ROW_DONE(error);
return error;
}