Hi! I just finished applying / modifying / rewriting the userstats patch to MariaDB 5.2 and will commit this in a moment. As far as I know, the patch was originally made by Mark and his team, then addopted by Percona and fixed/updated by Arjen & Weldon. I send this patch in advance here, so that people that has been involved with the patch can comment on what/if still needs to be done and what would be the next steps. I will write extensive comments in the commit message, but here are the highlights: - Almost all counters are done through 'thd->status_var' (no separate counters used). This was done by creating a copy of thd->status_var at start of execution and adding the difference to user stats. The benefit of the above is: - Less code - No double counters (faster execution) - Trivial to add new counters to the statistics tables (There is a lot of counters already that can be used) - No need to reset counters - If 'userstat' is not set, very little overhead. - Changed all of MariaBB code to use handler::ha_read... instead if handler::read. This allowed me to have all counters in the ha_... wrapper The benefit are: - No need to change engine code - All engines are now measured - Formatted code to 'MariaDB' style. - Removed not called functions. - A lot of small speed improvements - Optimized hash keys to not have to do strlen(). - Store unique keys in 'keyinfo' to not have to generate keys on the fly. - Replaced 'if's with DBUG_ASSERT for things that I thought was impossible. - Change naming of some variables to be more consistent - userstat_running -> user_stat - Rows_fetched -> Rows changed - Added statistics variables (for user and client statistics) Rows_updated, Rows_deleted, Rows_inserted - Changed busy and cpu timing to double (as in Weldon's patch) - Changed position of a few columns in the information schemas to group things in a more logical way. Things that I would like to have comments one from the original authors are: - I did not remove from sql_parse.cc: if (options & REFRESH_USER_RESOURCES) reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */ Don't understand why this should be removed. - Don't call 'set_concurrent_connections_stats()' (as per Weldon's patch) to collect information from the running threads as this leads will lead to wrong counting as all variables are not updated until the whole command is run. Here follows the patch (without the new test cases, the test cases will be in the commit). Note that I am now about to do a 'bzr gcommit' and I may fix some minor issues while I do that. You should be able to apply this directly to MariaDB 5.1 or you can pull MariaDB 5.2 later today if you want to test this. (MariaDB 5.2 is basicly MariaDB 5.1 + stable patches, so it's should be safe to use). === modified file 'configure.in' --- configure.in 2009-10-08 09:43:31 +0000 +++ configure.in 2009-10-16 13:25:43 +0000 @@ -829,7 +829,7 @@ AC_CHECK_HEADERS(fcntl.h fenv.h float.h sys/timeb.h sys/types.h sys/un.h sys/vadvise.h sys/wait.h term.h \ unistd.h utime.h sys/utime.h termio.h termios.h sched.h crypt.h alloca.h \ sys/ioctl.h malloc.h sys/malloc.h sys/ipc.h sys/shm.h linux/config.h \ - sys/prctl.h sys/resource.h sys/param.h port.h ieeefp.h \ + sys/prctl.h sys/resource.h sys/param.h port.h ieeefp.h linux/unistd.h \ execinfo.h) AC_CHECK_HEADERS([xfs/xfs.h]) @@ -2096,7 +2096,18 @@ case "$target" in # We also disable for SCO for the time being, the headers for the # thread library we use conflicts with other headers. ;; - *) AC_CHECK_FUNCS(clock_gettime) +*) + # most systems require the program be linked with librt library to use + # the function clock_gettime + my_save_LIBS="$LIBS" + LIBS="" + AC_CHECK_LIB(rt,clock_gettime) + LIBRT=$LIBS + LIBS="$my_save_LIBS" + AC_SUBST(LIBRT) + + LIBS="$LIBS $LIBRT" + AC_CHECK_FUNCS(clock_gettime) ;; esac @@ -2786,7 +2797,7 @@ then fi sql_client_dirs="$sql_client_dirs client" -CLIENT_LIBS="$NON_THREADED_LIBS $openssl_libs $ZLIB_LIBS $STATIC_NSS_FLAGS" +CLIENT_LIBS="$NON_THREADED_LIBS $openssl_libs $ZLIB_LIBS $STATIC_NSS_FLAGS $LIBRT" AC_SUBST(CLIENT_LIBS) AC_SUBST(CLIENT_THREAD_LIBS) === modified file 'include/my_sys.h' --- include/my_sys.h 2009-09-07 20:50:10 +0000 +++ include/my_sys.h 2009-10-16 13:21:53 +0000 @@ -904,6 +904,7 @@ void my_free_open_file_info(void); extern time_t my_time(myf flags); extern ulonglong my_getsystime(void); +extern ulonglong my_getrealtime(void); extern ulonglong my_micro_time(); extern ulonglong my_micro_time_and_time(time_t *time_arg); time_t my_time_possible_from_micro(ulonglong microtime); === modified file 'include/mysql_com.h' --- include/mysql_com.h 2008-10-10 15:28:41 +0000 +++ include/mysql_com.h 2009-10-15 18:24:26 +0000 @@ -29,6 +29,7 @@ #define SERVER_VERSION_LENGTH 60 #define SQLSTATE_LENGTH 5 +#define LIST_PROCESS_HOST_LEN 64 /* USER_HOST_BUFF_SIZE -- length of string buffer, that is enough to contain @@ -115,6 +116,11 @@ enum enum_server_command thread */ #define REFRESH_MASTER 128 /* Remove all bin logs in the index and truncate the index */ +#define REFRESH_TABLE_STATS 256 /* Refresh table stats hash table */ +#define REFRESH_INDEX_STATS 512 /* Refresh index stats hash table */ +#define REFRESH_USER_STATS 1024 /* Refresh user stats hash table */ +#define REFRESH_SLOW_QUERY_LOG 4096 /* Flush slow query log and rotate*/ +#define REFRESH_CLIENT_STATS 8192 /* Refresh client stats hash table */ /* The following can't be set with mysql_refresh() */ #define REFRESH_READ_LOCK 16384 /* Lock tables for read */ === modified file 'mysql-test/r/information_schema.result' --- mysql-test/r/information_schema.result 2009-09-29 20:19:43 +0000 +++ mysql-test/r/information_schema.result 2009-10-18 19:37:52 +0000 @@ -45,6 +45,7 @@ NOT (table_schema = 'INFORMATION_SCHEMA' select * from v1 ORDER BY c COLLATE utf8_bin; c CHARACTER_SETS +CLIENT_STATISTICS COLLATIONS COLLATION_CHARACTER_SET_APPLICABILITY COLUMNS @@ -54,6 +55,7 @@ EVENTS FILES GLOBAL_STATUS GLOBAL_VARIABLES +INDEX_STATISTICS INNODB_BUFFER_POOL_PAGES INNODB_BUFFER_POOL_PAGES_BLOB INNODB_BUFFER_POOL_PAGES_INDEX @@ -82,8 +84,10 @@ STATISTICS TABLES TABLE_CONSTRAINTS TABLE_PRIVILEGES +TABLE_STATISTICS TRIGGERS USER_PRIVILEGES +USER_STATISTICS VIEWS XTRADB_ENHANCEMENTS columns_priv @@ -121,6 +125,7 @@ c table_name TABLES TABLES TABLE_CONSTRAINTS TABLE_CONSTRAINTS TABLE_PRIVILEGES TABLE_PRIVILEGES +TABLE_STATISTICS TABLE_STATISTICS TRIGGERS TRIGGERS tables_priv tables_priv time_zone time_zone @@ -140,6 +145,7 @@ c table_name TABLES TABLES TABLE_CONSTRAINTS TABLE_CONSTRAINTS TABLE_PRIVILEGES TABLE_PRIVILEGES +TABLE_STATISTICS TABLE_STATISTICS TRIGGERS TRIGGERS tables_priv tables_priv time_zone time_zone @@ -159,6 +165,7 @@ c table_name TABLES TABLES TABLE_CONSTRAINTS TABLE_CONSTRAINTS TABLE_PRIVILEGES TABLE_PRIVILEGES +TABLE_STATISTICS TABLE_STATISTICS TRIGGERS TRIGGERS tables_priv tables_priv time_zone time_zone @@ -640,12 +647,13 @@ from information_schema.tables where table_schema='information_schema' limit 2; TABLE_NAME TABLE_TYPE ENGINE CHARACTER_SETS SYSTEM VIEW MEMORY -COLLATIONS SYSTEM VIEW MEMORY +CLIENT_STATISTICS SYSTEM VIEW MEMORY show tables from information_schema like "T%"; Tables_in_information_schema (T%) TABLES TABLE_CONSTRAINTS TABLE_PRIVILEGES +TABLE_STATISTICS TRIGGERS create database information_schema; ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema' @@ -655,6 +663,7 @@ Tables_in_information_schema (T%) Table_ TABLES SYSTEM VIEW TABLE_CONSTRAINTS SYSTEM VIEW TABLE_PRIVILEGES SYSTEM VIEW +TABLE_STATISTICS SYSTEM VIEW TRIGGERS SYSTEM VIEW create table t1(a int); ERROR 42S02: Unknown table 't1' in information_schema @@ -667,6 +676,7 @@ Tables_in_information_schema (T%) TABLES TABLE_CONSTRAINTS TABLE_PRIVILEGES +TABLE_STATISTICS TRIGGERS select table_name from tables where table_name='user'; table_name @@ -856,6 +866,7 @@ TABLE_NAME COLUMN_NAME PRIVILEGES COLUMNS TABLE_NAME select COLUMN_PRIVILEGES TABLE_NAME select FILES TABLE_NAME select +INDEX_STATISTICS TABLE_NAME select KEY_COLUMN_USAGE TABLE_NAME select PARTITIONS TABLE_NAME select REFERENTIAL_CONSTRAINTS TABLE_NAME select @@ -863,6 +874,7 @@ STATISTICS TABLE_NAME select TABLES TABLE_NAME select TABLE_CONSTRAINTS TABLE_NAME select TABLE_PRIVILEGES TABLE_NAME select +TABLE_STATISTICS TABLE_NAME select VIEWS TABLE_NAME select INNODB_BUFFER_POOL_PAGES_INDEX table_name select INNODB_INDEX_STATS table_name select === modified file 'mysql-test/r/information_schema_all_engines.result' --- mysql-test/r/information_schema_all_engines.result 2009-08-03 20:09:53 +0000 +++ mysql-test/r/information_schema_all_engines.result 2009-10-19 01:26:40 +0000 @@ -2,6 +2,7 @@ use INFORMATION_SCHEMA; show tables; Tables_in_information_schema CHARACTER_SETS +CLIENT_STATISTICS COLLATIONS COLLATION_CHARACTER_SET_APPLICABILITY COLUMNS @@ -11,6 +12,7 @@ EVENTS FILES GLOBAL_STATUS GLOBAL_VARIABLES +INDEX_STATISTICS KEY_COLUMN_USAGE PARTITIONS PLUGINS @@ -26,8 +28,10 @@ STATISTICS TABLES TABLE_CONSTRAINTS TABLE_PRIVILEGES +TABLE_STATISTICS TRIGGERS USER_PRIVILEGES +USER_STATISTICS VIEWS INNODB_BUFFER_POOL_PAGES PBXT_STATISTICS @@ -60,6 +64,7 @@ c2.column_name LIKE '%SCHEMA%' ); table_name column_name CHARACTER_SETS CHARACTER_SET_NAME +CLIENT_STATISTICS CLIENT COLLATIONS COLLATION_NAME COLLATION_CHARACTER_SET_APPLICABILITY COLLATION_NAME COLUMNS TABLE_SCHEMA @@ -69,6 +74,7 @@ EVENTS EVENT_SCHEMA FILES TABLE_SCHEMA GLOBAL_STATUS VARIABLE_NAME GLOBAL_VARIABLES VARIABLE_NAME +INDEX_STATISTICS TABLE_SCHEMA KEY_COLUMN_USAGE CONSTRAINT_SCHEMA PARTITIONS TABLE_SCHEMA PLUGINS PLUGIN_NAME @@ -84,8 +90,10 @@ STATISTICS TABLE_SCHEMA TABLES TABLE_SCHEMA TABLE_CONSTRAINTS CONSTRAINT_SCHEMA TABLE_PRIVILEGES TABLE_SCHEMA +TABLE_STATISTICS TABLE_SCHEMA TRIGGERS TRIGGER_SCHEMA USER_PRIVILEGES GRANTEE +USER_STATISTICS USER VIEWS TABLE_SCHEMA INNODB_BUFFER_POOL_PAGES page_type PBXT_STATISTICS ID @@ -118,6 +126,7 @@ c2.column_name LIKE '%SCHEMA%' ); table_name column_name CHARACTER_SETS CHARACTER_SET_NAME +CLIENT_STATISTICS CLIENT COLLATIONS COLLATION_NAME COLLATION_CHARACTER_SET_APPLICABILITY COLLATION_NAME COLUMNS TABLE_SCHEMA @@ -127,6 +136,7 @@ EVENTS EVENT_SCHEMA FILES TABLE_SCHEMA GLOBAL_STATUS VARIABLE_NAME GLOBAL_VARIABLES VARIABLE_NAME +INDEX_STATISTICS TABLE_SCHEMA KEY_COLUMN_USAGE CONSTRAINT_SCHEMA PARTITIONS TABLE_SCHEMA PLUGINS PLUGIN_NAME @@ -142,8 +152,10 @@ STATISTICS TABLE_SCHEMA TABLES TABLE_SCHEMA TABLE_CONSTRAINTS CONSTRAINT_SCHEMA TABLE_PRIVILEGES TABLE_SCHEMA +TABLE_STATISTICS TABLE_SCHEMA TRIGGERS TRIGGER_SCHEMA USER_PRIVILEGES GRANTEE +USER_STATISTICS USER VIEWS TABLE_SCHEMA INNODB_BUFFER_POOL_PAGES page_type PBXT_STATISTICS ID @@ -182,6 +194,7 @@ group by c2.column_type order by num lim group by t.table_name order by num1, t.table_name; table_name group_concat(t.table_schema, '.', t.table_name) num1 CHARACTER_SETS information_schema.CHARACTER_SETS 1 +CLIENT_STATISTICS information_schema.CLIENT_STATISTICS 1 COLLATIONS information_schema.COLLATIONS 1 COLLATION_CHARACTER_SET_APPLICABILITY information_schema.COLLATION_CHARACTER_SET_APPLICABILITY 1 COLUMNS information_schema.COLUMNS 1 @@ -191,6 +204,7 @@ EVENTS information_schema.EVENTS 1 FILES information_schema.FILES 1 GLOBAL_STATUS information_schema.GLOBAL_STATUS 1 GLOBAL_VARIABLES information_schema.GLOBAL_VARIABLES 1 +INDEX_STATISTICS information_schema.INDEX_STATISTICS 1 INNODB_BUFFER_POOL_PAGES information_schema.INNODB_BUFFER_POOL_PAGES 1 INNODB_BUFFER_POOL_PAGES_BLOB information_schema.INNODB_BUFFER_POOL_PAGES_BLOB 1 INNODB_BUFFER_POOL_PAGES_INDEX information_schema.INNODB_BUFFER_POOL_PAGES_INDEX 1 @@ -220,8 +234,10 @@ STATISTICS information_schema.STATISTICS TABLES information_schema.TABLES 1 TABLE_CONSTRAINTS information_schema.TABLE_CONSTRAINTS 1 TABLE_PRIVILEGES information_schema.TABLE_PRIVILEGES 1 +TABLE_STATISTICS information_schema.TABLE_STATISTICS 1 TRIGGERS information_schema.TRIGGERS 1 USER_PRIVILEGES information_schema.USER_PRIVILEGES 1 +USER_STATISTICS information_schema.USER_STATISTICS 1 VIEWS information_schema.VIEWS 1 XTRADB_ENHANCEMENTS information_schema.XTRADB_ENHANCEMENTS 1 Database: information_schema @@ -229,6 +245,7 @@ Database: information_schema | Tables | +---------------------------------------+ | CHARACTER_SETS | +| CLIENT_STATISTICS | | COLLATIONS | | COLLATION_CHARACTER_SET_APPLICABILITY | | COLUMNS | @@ -238,6 +255,7 @@ Database: information_schema | FILES | | GLOBAL_STATUS | | GLOBAL_VARIABLES | +| INDEX_STATISTICS | | KEY_COLUMN_USAGE | | PARTITIONS | | PLUGINS | @@ -253,8 +271,10 @@ Database: information_schema | TABLES | | TABLE_CONSTRAINTS | | TABLE_PRIVILEGES | +| TABLE_STATISTICS | | TRIGGERS | | USER_PRIVILEGES | +| USER_STATISTICS | | VIEWS | | INNODB_BUFFER_POOL_PAGES | | PBXT_STATISTICS | @@ -277,6 +297,7 @@ Database: INFORMATION_SCHEMA | Tables | +---------------------------------------+ | CHARACTER_SETS | +| CLIENT_STATISTICS | | COLLATIONS | | COLLATION_CHARACTER_SET_APPLICABILITY | | COLUMNS | @@ -286,6 +307,7 @@ Database: INFORMATION_SCHEMA | FILES | | GLOBAL_STATUS | | GLOBAL_VARIABLES | +| INDEX_STATISTICS | | KEY_COLUMN_USAGE | | PARTITIONS | | PLUGINS | @@ -301,8 +323,10 @@ Database: INFORMATION_SCHEMA | TABLES | | TABLE_CONSTRAINTS | | TABLE_PRIVILEGES | +| TABLE_STATISTICS | | TRIGGERS | | USER_PRIVILEGES | +| USER_STATISTICS | | VIEWS | | INNODB_BUFFER_POOL_PAGES | | PBXT_STATISTICS | @@ -328,5 +352,5 @@ Wildcard: inf_rmation_schema +--------------------+ SELECT table_schema, count(*) FROM information_schema.TABLES WHERE table_schema IN ('mysql', 'INFORMATION_SCHEMA', 'test', 'mysqltest') AND table_name<>'ndb_binlog_index' AND table_name<>'ndb_apply_status' GROUP BY TABLE_SCHEMA; table_schema count(*) -information_schema 43 +information_schema 47 mysql 22 === modified file 'mysql-test/r/information_schema_db.result' --- mysql-test/r/information_schema_db.result 2009-09-07 20:50:10 +0000 +++ mysql-test/r/information_schema_db.result 2009-10-18 19:38:24 +0000 @@ -7,6 +7,7 @@ Tables_in_information_schema (T%) TABLES TABLE_CONSTRAINTS TABLE_PRIVILEGES +TABLE_STATISTICS TRIGGERS create database `inf%`; create database mbase; === modified file 'mysql-test/r/log_slow.result' --- mysql-test/r/log_slow.result 2009-09-03 14:05:38 +0000 +++ mysql-test/r/log_slow.result 2009-10-18 18:59:33 +0000 @@ -56,5 +56,6 @@ last_insert_id int(11) NO NULL insert_id int(11) NO NULL server_id int(10) unsigned NO NULL sql_text mediumtext NO NULL +flush slow query logs; set @@log_slow_filter=default; set @@log_slow_verbosity=default; === modified file 'mysql-test/t/log_slow.test' --- mysql-test/t/log_slow.test 2009-09-03 14:05:38 +0000 +++ mysql-test/t/log_slow.test 2009-10-18 18:25:56 +0000 @@ -36,6 +36,12 @@ select @@log_slow_verbosity; show fields from mysql.slow_log; +# +# Check flush command +# + +flush slow query logs; + # Reset used variables set @@log_slow_filter=default; === modified file 'mysys/my_getsystime.c' --- mysys/my_getsystime.c 2008-04-28 16:24:05 +0000 +++ mysys/my_getsystime.c 2009-10-16 14:05:22 +0000 @@ -28,6 +28,10 @@ #ifdef __NETWARE__ #include <nks/time.h> #endif +#ifdef HAVE_LINUX_UNISTD_H +#include <linux/unistd.h> +#endif + ulonglong my_getsystime() { @@ -222,3 +226,25 @@ time_t my_time_possible_from_micro(ulong return (time_t) (microtime / 1000000); #endif /* defined(__WIN__) */ } + + +/* + Return real time in milliseconds * 10 +*/ + +ulonglong my_getrealtime() +{ +#ifdef HAVE_CLOCK_GETTIME + struct timespec tp; + if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp)) + return 0; + return (ulonglong)tp.tv_sec*10000000+(ulonglong)tp.tv_nsec/100; +#elif defined(__NR_clock_gettime) + struct timespec tp; + if (syscall(__NR_clock_gettime, CLOCK_THREAD_CPUTIME_ID, &tp)) + return 0; + return (ulonglong)tp.tv_sec*10000000+(ulonglong)tp.tv_nsec/100; +#else + return 0; +#endif /* HAVE_CLOCK_GETTIME */ +} === modified file 'sql/authors.h' --- sql/authors.h 2007-03-16 06:39:07 +0000 +++ sql/authors.h 2009-10-16 11:22:55 +0000 @@ -34,23 +34,35 @@ struct show_table_authors_st { */ struct show_table_authors_st show_table_authors[]= { + { "Michael (Monty) Widenius", "Tusby, Finland", + "Lead developer and main author" }, + { "David Axmark", "London, England", + "MySQL founder; Small stuff long time ago, Monty ripped it out!" }, + { "Sergei Golubchik", "Kerpen, Germany", + "Full-text search, precision math" }, + { "Igor Babaev", "Bellevue, USA", "Optimizer, keycache, core work"}, + { "Sergey Petrunia", "St. Petersburg, Russia", "Optimizer"}, + { "Oleksandr Byelkin", "Lugansk, Ukraine", + "Query Cache (4.0), Subqueries (4.1), Views (5.0)" }, { "Brian (Krow) Aker", "Seattle, WA, USA", "Architecture, archive, federated, bunch of little stuff :)" }, - { "Venu Anuganti", "", "Client/server protocol (4.1)" }, - { "David Axmark", "Uppsala, Sweden", - "Small stuff long time ago, Monty ripped it out!" }, + { "Kristian Nielsen", "Copenhagen, Denmark", + "General build stuff," }, { "Alexander (Bar) Barkov", "Izhevsk, Russia", "Unicode and character sets (4.1)" }, + { "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" }, + { "Venu Anuganti", "", "Client/server protocol (4.1)" }, + { "Konstantin Osipov", "Moscow, Russia", + "Prepared statements (4.1), Cursors (5.0)" }, + { "Dmitri Lenev", "Moscow, Russia", + "Time zones support (4.1), Triggers (5.0)" }, { "Omer BarNir", "Sunnyvale, CA, USA", "Testing (sometimes) and general QA stuff" }, - { "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" }, { "John Birrell", "", "Emulation of pthread_mutex() for OS/2" }, { "Andreas F. Bobak", "", "AGGREGATE extension to user-defined functions" }, { "Alexey Botchkov (Holyfoot)", "Izhevsk, Russia", "GIS extensions (4.1), embedded server (4.1), precision math (5.0)"}, { "Reggie Burnett", "Nashville, TN, USA", "Windows development, Connectors" }, - { "Oleksandr Byelkin", "Lugansk, Ukraine", - "Query Cache (4.0), Subqueries (4.1), Views (5.0)" }, { "Kent Boortz", "Orebro, Sweden", "Test platform, and general build stuff" }, { "Tim Bunce", "", "mysqlhotcopy" }, { "Yves Carlier", "", "mysqlaccess" }, @@ -67,8 +79,6 @@ struct show_table_authors_st show_table_ { "Yuri Dario", "", "OS/2 port" }, { "Andrei Elkin", "Espoo, Finland", "Replication" }, { "Patrick Galbraith", "Sharon, NH", "Federated Engine, mysqlslap" }, - { "Sergei Golubchik", "Kerpen, Germany", - "Full-text search, precision math" }, { "Lenz Grimmer", "Hamburg, Germany", "Production (build and release) engineering" }, { "Nikolay Grishakin", "Austin, TX, USA", "Testing - Server" }, @@ -83,8 +93,6 @@ struct show_table_authors_st show_table_ { "Hakan Küçükyılmaz", "Walldorf, Germany", "Testing - Server" }, { "Greg (Groggy) Lehey", "Uchunga, SA, Australia", "Backup" }, { "Matthias Leich", "Berlin, Germany", "Testing - Server" }, - { "Dmitri Lenev", "Moscow, Russia", - "Time zones support (4.1), Triggers (5.0)" }, { "Arjen Lentz", "Brisbane, Australia", "Documentation (2001-2004), Dutch error messages, LOG2()" }, { "Marc Liyanage", "", "Created Mac OS X packages" }, @@ -96,8 +104,6 @@ struct show_table_authors_st show_table_ { "Jonathan (Jeb) Miller", "Kyle, TX, USA", "Testing - Cluster, Replication" }, { "Elliot Murphy", "Cocoa, FL, USA", "Replication and backup" }, - { "Kristian Nielsen", "Copenhagen, Denmark", - "General build stuff" }, { "Pekka Nouisiainen", "Stockholm, Sweden", "NDB Cluster: BLOB support, character set support, ordered indexes" }, { "Alexander Nozdrin", "Moscow, Russia", @@ -105,8 +111,6 @@ struct show_table_authors_st show_table_ { "Per Eric Olsson", "", "Testing of dynamic record format" }, { "Jonas Oreland", "Stockholm, Sweden", "NDB Cluster, Online Backup, lots of other things" }, - { "Konstantin Osipov", "Moscow, Russia", - "Prepared statements (4.1), Cursors (5.0)" }, { "Alexander (Sasha) Pachev", "Provo, UT, USA", "Statement-based replication, SHOW CREATE TABLE, mysql-bench" }, { "Irena Pancirov", "", "Port to Windows with Borland compiler" }, @@ -144,9 +148,9 @@ struct show_table_authors_st show_table_ { "Sergey Vojtovich", "Izhevsk, Russia", "Plugins infrastructure (5.1)" }, { "Matt Wagner", "Northfield, MN, USA", "Bug fixing" }, { "Jim Winstead Jr.", "Los Angeles, CA, USA", "Bug fixing" }, - { "Michael (Monty) Widenius", "Tusby, Finland", - "Lead developer and main author" }, { "Peter Zaitsev", "Tacoma, WA, USA", "SHA1(), AES_ENCRYPT(), AES_DECRYPT(), bug fixing" }, + {"Mark Mark Callaghan", "Texas, USA", "Statistics patches"}, + {"Percona", "CA, USA", "Microslow patches"}, {NULL, NULL, NULL} }; === modified file 'sql/event_data_objects.cc' --- sql/event_data_objects.cc 2009-09-15 10:46:35 +0000 +++ sql/event_data_objects.cc 2009-10-18 14:30:18 +0000 @@ -1366,7 +1366,7 @@ Event_job_data::execute(THD *thd, bool d DBUG_ENTER("Event_job_data::execute"); - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, 0); /* MySQL parser currently assumes that current database is either === modified file 'sql/event_db_repository.cc' --- sql/event_db_repository.cc 2009-02-15 10:58:34 +0000 +++ sql/event_db_repository.cc 2009-10-17 08:09:24 +0000 @@ -404,17 +404,18 @@ Event_db_repository::index_read_for_db_f } key_copy(key_buf, event_table->record[0], key_info, key_len); - if (!(ret= event_table->file->index_read_map(event_table->record[0], key_buf, - (key_part_map)1, - HA_READ_PREFIX))) + if (!(ret= event_table->file->ha_index_read_map(event_table->record[0], + key_buf, + (key_part_map)1, + HA_READ_PREFIX))) { DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret)); do { ret= copy_event_to_schema_table(thd, schema_table, event_table); if (ret == 0) - ret= event_table->file->index_next_same(event_table->record[0], - key_buf, key_len); + ret= event_table->file->ha_index_next_same(event_table->record[0], + key_buf, key_len); } while (ret == 0); } DBUG_PRINT("info", ("Scan finished. ret=%d", ret)); @@ -883,8 +884,9 @@ Event_db_repository::find_named_event(LE key_copy(key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx_map(table->record[0], 0, key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_idx_map(table->record[0], 0, key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { DBUG_PRINT("info", ("Row not found")); DBUG_RETURN(TRUE); === modified file 'sql/filesort.cc' --- sql/filesort.cc 2009-09-03 14:05:38 +0000 +++ sql/filesort.cc 2009-10-17 08:13:46 +0000 @@ -577,11 +577,11 @@ static ha_rows find_all_keys(SORTPARAM * error= my_errno ? my_errno : -1; /* Abort */ break; } - error=file->rnd_pos(sort_form->record[0],next_pos); + error=file->ha_rnd_pos(sort_form->record[0],next_pos); } else { - error=file->rnd_next(sort_form->record[0]); + error=file->ha_rnd_next(sort_form->record[0]); if (!flag) { my_store_ptr(ref_pos,ref_length,record); // Position to row === modified file 'sql/ha_partition.cc' --- sql/ha_partition.cc 2009-09-07 20:50:10 +0000 +++ sql/ha_partition.cc 2009-10-17 08:13:46 +0000 @@ -1636,7 +1636,7 @@ int ha_partition::copy_partitions(ulongl goto error; while (TRUE) { - if ((result= file->rnd_next(m_rec0))) + if ((result= file->ha_rnd_next(m_rec0))) { if (result == HA_ERR_RECORD_DELETED) continue; //Probably MyISAM @@ -3495,7 +3495,7 @@ int ha_partition::rnd_next(uchar *buf) while (TRUE) { - result= file->rnd_next(buf); + result= file->ha_rnd_next(buf); if (!result) { m_last_part= part_id; @@ -4345,8 +4345,8 @@ int ha_partition::handle_unordered_next( } else if (is_next_same) { - if (!(error= file->index_next_same(buf, m_start_key.key, - m_start_key.length))) + if (!(error= file->ha_index_next_same(buf, m_start_key.key, + m_start_key.length))) { m_last_part= m_part_spec.start_part; DBUG_RETURN(0); @@ -4354,7 +4354,7 @@ int ha_partition::handle_unordered_next( } else { - if (!(error= file->index_next(buf))) + if (!(error= file->ha_index_next(buf))) { m_last_part= m_part_spec.start_part; DBUG_RETURN(0); // Row was in range @@ -4409,24 +4409,26 @@ int ha_partition::handle_unordered_scan_ break; case partition_index_read: DBUG_PRINT("info", ("index_read on partition %d", i)); - error= file->index_read_map(buf, m_start_key.key, - m_start_key.keypart_map, - m_start_key.flag); + error= file->ha_index_read_map(buf, m_start_key.key, + m_start_key.keypart_map, + m_start_key.flag); break; case partition_index_first: DBUG_PRINT("info", ("index_first on partition %d", i)); - /* MyISAM engine can fail if we call index_first() when indexes disabled */ - /* that happens if the table is empty. */ - /* Here we use file->stats.records instead of file->records() because */ - /* file->records() is supposed to return an EXACT count, and it can be */ - /* possibly slow. We don't need an exact number, an approximate one- from*/ - /* the last ::info() call - is sufficient. */ + /* + MyISAM engine can fail if we call index_first() when indexes disabled + that happens if the table is empty. + Here we use file->stats.records instead of file->records() because + file->records() is supposed to return an EXACT count, and it can be + possibly slow. We don't need an exact number, an approximate one- from + the last ::info() call - is sufficient. + */ if (file->stats.records == 0) { error= HA_ERR_END_OF_FILE; break; } - error= file->index_first(buf); + error= file->ha_index_first(buf); break; case partition_index_first_unordered: /* @@ -4507,45 +4509,49 @@ int ha_partition::handle_ordered_index_s switch (m_index_scan_type) { case partition_index_read: - error= file->index_read_map(rec_buf_ptr, - m_start_key.key, - m_start_key.keypart_map, - m_start_key.flag); + error= file->ha_index_read_map(rec_buf_ptr, + m_start_key.key, + m_start_key.keypart_map, + m_start_key.flag); break; case partition_index_first: - /* MyISAM engine can fail if we call index_first() when indexes disabled */ - /* that happens if the table is empty. */ - /* Here we use file->stats.records instead of file->records() because */ - /* file->records() is supposed to return an EXACT count, and it can be */ - /* possibly slow. We don't need an exact number, an approximate one- from*/ - /* the last ::info() call - is sufficient. */ + /* + MyISAM engine can fail if we call index_first() when indexes disabled + that happens if the table is empty. + Here we use file->stats.records instead of file->records() because + file->records() is supposed to return an EXACT count, and it can be + possibly slow. We don't need an exact number, an approximate one- from + the last ::info() call - is sufficient. + */ if (file->stats.records == 0) { error= HA_ERR_END_OF_FILE; break; } - error= file->index_first(rec_buf_ptr); + error= file->ha_index_first(rec_buf_ptr); reverse_order= FALSE; break; case partition_index_last: - /* MyISAM engine can fail if we call index_last() when indexes disabled */ - /* that happens if the table is empty. */ - /* Here we use file->stats.records instead of file->records() because */ - /* file->records() is supposed to return an EXACT count, and it can be */ - /* possibly slow. We don't need an exact number, an approximate one- from*/ - /* the last ::info() call - is sufficient. */ + /* + MyISAM engine can fail if we call index_last() when indexes disabled + that happens if the table is empty. + Here we use file->stats.records instead of file->records() because + file->records() is supposed to return an EXACT count, and it can be + possibly slow. We don't need an exact number, an approximate one- from + the last ::info() call - is sufficient. + */ if (file->stats.records == 0) { error= HA_ERR_END_OF_FILE; break; } - error= file->index_last(rec_buf_ptr); + error= file->ha_index_last(rec_buf_ptr); reverse_order= TRUE; break; case partition_index_read_last: - error= file->index_read_last_map(rec_buf_ptr, - m_start_key.key, - m_start_key.keypart_map); + error= file->ha_index_read_last_map(rec_buf_ptr, + m_start_key.key, + m_start_key.keypart_map); reverse_order= TRUE; break; case partition_read_range: @@ -4647,10 +4653,10 @@ int ha_partition::handle_ordered_next(uc memcpy(rec_buf(part_id), table->record[0], m_rec_length); } else if (!is_next_same) - error= file->index_next(rec_buf(part_id)); + error= file->ha_index_next(rec_buf(part_id)); else - error= file->index_next_same(rec_buf(part_id), m_start_key.key, - m_start_key.length); + error= file->ha_index_next_same(rec_buf(part_id), m_start_key.key, + m_start_key.length); if (error) { if (error == HA_ERR_END_OF_FILE) @@ -4695,7 +4701,7 @@ int ha_partition::handle_ordered_prev(uc handler *file= m_file[part_id]; DBUG_ENTER("ha_partition::handle_ordered_prev"); - if ((error= file->index_prev(rec_buf(part_id)))) + if ((error= file->ha_index_prev(rec_buf(part_id)))) { if (error == HA_ERR_END_OF_FILE) { === modified file 'sql/handler.cc' --- sql/handler.cc 2009-09-09 21:06:57 +0000 +++ sql/handler.cc 2009-10-19 01:38:34 +0000 @@ -1195,6 +1195,7 @@ int ha_commit_trans(THD *thd, bool all) if (cookie) tc_log->unlog(cookie, xid); DBUG_EXECUTE_IF("crash_commit_after", DBUG_ABORT();); + end: if (rw_trans) start_waiting_global_read_lock(thd); @@ -1236,6 +1237,7 @@ int ha_commit_one_phase(THD *thd, bool a my_error(ER_ERROR_DURING_COMMIT, MYF(0), err); error=1; } + /* Should this be done only if is_real_trans is set ? */ status_var_increment(thd->status_var.ha_commit_count); ha_info_next= ha_info->next(); ha_info->reset(); /* keep it conveniently zero-filled */ @@ -2092,6 +2094,8 @@ int handler::ha_open(TABLE *table_arg, c dup_ref=ref+ALIGN_SIZE(ref_length); cached_table_flags= table_flags(); } + rows_read= rows_changed= 0; + memset(index_rows_read, 0, sizeof(index_rows_read)); DBUG_RETURN(error); } @@ -2513,9 +2517,10 @@ void handler::get_auto_increment(ulonglo key_copy(key, table->record[0], table->key_info + table->s->next_number_index, table->s->next_number_key_offset); - error= index_read_map(table->record[1], key, - make_prev_keypart_map(table->s->next_number_keypart), - HA_READ_PREFIX_LAST); + error= ha_index_read_map(table->record[1], key, + make_prev_keypart_map(table->s-> + next_number_keypart), + HA_READ_PREFIX_LAST); /* MySQL needs to call us for next row: assume we are inserting ("a",null) here, we return 3, and next this statement will want to insert @@ -3549,6 +3554,122 @@ void handler::get_dynamic_partition_info } +/* + Updates the global table stats with the TABLE this handler represents +*/ + +void handler::update_global_table_stats() +{ + TABLE_STATS * table_stats; + + status_var_add(table->in_use->status_var.rows_read, rows_read); + + if (!opt_userstat_running) + { + rows_read= rows_changed= 0; + return; + } + + if (rows_read + rows_changed == 0) + return; // Nothing to update. + + DBUG_ASSERT(table->s && table->s->table_cache_key.str); + + pthread_mutex_lock(&LOCK_global_table_stats); + /* Gets the global table stats, creating one if necessary. */ + if (!(table_stats= (TABLE_STATS*) + hash_search(&global_table_stats, + (uchar*) table->s->table_cache_key.str, + table->s->table_cache_key.length))) + { + if (!(table_stats = ((TABLE_STATS*) + my_malloc(sizeof(TABLE_STATS), + MYF(MY_WME | MY_ZEROFILL))))) + { + /* Out of memory error already given */ + goto end; + } + memcpy(table_stats->table, table->s->table_cache_key.str, + table->s->table_cache_key.length); + table_stats->table_name_length= table->s->table_cache_key.length; + table_stats->engine_type= ht->db_type; + /* No need to set variables to 0, as we use MY_ZEROFILL above */ + + if (my_hash_insert(&global_table_stats, (uchar*) table_stats)) + { + /* Out of memory error is already given */ + my_free(table_stats, 0); + goto end; + } + } + // Updates the global table stats. + table_stats->rows_read+= rows_read; + table_stats->rows_changed+= rows_changed; + table_stats->rows_changed_x_indexes+= (rows_changed * + (table->s->keys ? table->s->keys : + 1)); + rows_read= rows_changed= 0; +end: + pthread_mutex_unlock(&LOCK_global_table_stats); +} + + +/* + Updates the global index stats with this handler's accumulated index reads. +*/ + +void handler::update_global_index_stats() +{ + DBUG_ASSERT(table->s); + + if (!table->in_use->userstat_running) + { + /* Reset all index read values */ + bzero(index_rows_read, sizeof(index_rows_read[0]) * table->s->keys); + return; + } + + for (uint index = 0; index < table->s->keys; index++) + { + if (index_rows_read[index]) + { + INDEX_STATS* index_stats; + uint key_length; + KEY *key_info = &table->key_info[index]; // Rows were read using this + + DBUG_ASSERT(key_info->cache_name); + if (!key_info->cache_name) + continue; + key_length= table->s->table_cache_key.length + key_info->name_length + 1; + pthread_mutex_lock(&LOCK_global_index_stats); + // Gets the global index stats, creating one if necessary. + if (!(index_stats= (INDEX_STATS*) hash_search(&global_index_stats, + key_info->cache_name, + key_length))) + { + if (!(index_stats = ((INDEX_STATS*) + my_malloc(sizeof(INDEX_STATS), + MYF(MY_WME | MY_ZEROFILL))))) + goto end; // Error is already given + + memcpy(index_stats->index, key_info->cache_name, key_length); + index_stats->index_name_length= key_length; + if (my_hash_insert(&global_index_stats, (uchar*) index_stats)) + { + my_free(index_stats, 0); + goto end; + } + } + /* Updates the global index stats. */ + index_stats->rows_read+= index_rows_read[index]; + index_rows_read[index]= 0; +end: + pthread_mutex_unlock(&LOCK_global_index_stats); + } + } +} + + /**************************************************************************** ** Some general functions that isn't in the handler class ****************************************************************************/ @@ -4207,17 +4328,16 @@ int handler::read_range_first(const key_ range_key_part= table->key_info[active_index].key_part; if (!start_key) // Read first record - result= index_first(table->record[0]); + result= ha_index_first(table->record[0]); else - result= index_read_map(table->record[0], - start_key->key, - start_key->keypart_map, - start_key->flag); + result= ha_index_read_map(table->record[0], + start_key->key, + start_key->keypart_map, + start_key->flag); if (result) DBUG_RETURN((result == HA_ERR_KEY_NOT_FOUND) ? HA_ERR_END_OF_FILE : result); - DBUG_RETURN (compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE); } @@ -4243,11 +4363,11 @@ int handler::read_range_next() if (eq_range) { /* We trust that index_next_same always gives a row in range */ - DBUG_RETURN(index_next_same(table->record[0], - end_range->key, - end_range->length)); + DBUG_RETURN(ha_index_next_same(table->record[0], + end_range->key, + end_range->length)); } - result= index_next(table->record[0]); + result= ha_index_next(table->record[0]); if (result) DBUG_RETURN(result); DBUG_RETURN(compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE); @@ -4629,6 +4749,7 @@ int handler::ha_write_row(uchar *buf) if (unlikely(error= write_row(buf))) DBUG_RETURN(error); + rows_changed++; if (unlikely(error= binlog_log_row(table, 0, buf, log_func))) DBUG_RETURN(error); /* purecov: inspected */ DBUG_RETURN(0); @@ -4650,6 +4771,7 @@ int handler::ha_update_row(const uchar * if (unlikely(error= update_row(old_data, new_data))) return error; + rows_changed++; if (unlikely(error= binlog_log_row(table, old_data, new_data, log_func))) return error; return 0; @@ -4664,6 +4786,7 @@ int handler::ha_delete_row(const uchar * if (unlikely(error= delete_row(buf))) return error; + rows_changed++; if (unlikely(error= binlog_log_row(table, buf, 0, log_func))) return error; return 0; === modified file 'sql/handler.h' --- sql/handler.h 2009-09-07 20:50:10 +0000 +++ sql/handler.h 2009-10-18 20:53:48 +0000 @@ -30,6 +30,10 @@ #define USING_TRANSACTIONS +#if MAX_KEY > 128 +#error MAX_KEY is too large. Values up to 128 are supported. +#endif + // the following is for checking tables #define HA_ADMIN_ALREADY_DONE 1 @@ -601,8 +605,9 @@ struct handlerton SHOW_COMP_OPTION state; /* - Historical number used for frm file to determine the correct storage engine. - This is going away and new engines will just use "name" for this. + Historical number used for frm file to determine the correct + storage engine. This is going away and new engines will just use + "name" for this. */ enum legacy_db_type db_type; /* @@ -1138,6 +1143,11 @@ public: Interval returned by get_auto_increment() and being consumed by the inserter. */ + /* Statistics variables */ + ulonglong rows_read; + ulonglong rows_changed; + ulonglong index_rows_read[MAX_KEY]; + Discrete_interval auto_inc_interval_for_cur_row; /** Number of reserved auto-increment intervals. Serves as a heuristic @@ -1156,7 +1166,10 @@ public: locked(FALSE), implicit_emptied(0), pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0), auto_inc_intervals_count(0) - {} + { + reset_statistics(); + } + virtual ~handler(void) { DBUG_ASSERT(locked == FALSE); @@ -1278,10 +1291,16 @@ public: virtual void print_error(int error, myf errflag); virtual bool get_error_message(int error, String *buf); uint get_dup_key(int error); + void reset_statistics() + { + rows_read= rows_changed= 0; + bzero(index_rows_read, sizeof(index_rows_read)); + } virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share) { table= table_arg; table_share= share; + reset_statistics(); } virtual double scan_time() { return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; } @@ -1390,22 +1409,23 @@ public: } /** @brief - Positions an index cursor to the index specified in the handle. Fetches the - row if available. If the key value is null, begin at the first key of the - index. + Positions an index cursor to the index specified in the + handle. Fetches the row if available. If the key value is null, + begin at the first key of the index. */ +protected: virtual int index_read_map(uchar * buf, const uchar * key, key_part_map keypart_map, enum ha_rkey_function find_flag) { uint key_len= calculate_key_len(table, active_index, key, keypart_map); - return index_read(buf, key, key_len, find_flag); + return index_read(buf, key, key_len, find_flag); } /** @brief - Positions an index cursor to the index specified in the handle. Fetches the - row if available. If the key value is null, begin at the first key of the - index. + Positions an index cursor to the index specified in the + handle. Fetches the row if available. If the key value is null, + begin at the first key of the index. */ virtual int index_read_idx_map(uchar * buf, uint index, const uchar * key, key_part_map keypart_map, @@ -1430,6 +1450,81 @@ public: uint key_len= calculate_key_len(table, active_index, key, keypart_map); return index_read_last(buf, key, key_len); } + inline void update_index_statistics() + { + if (active_index != MAX_KEY) + index_rows_read[active_index]++; + rows_read++; + } +public: + + /* Similar functions like the above, but does statistics counting */ + inline int ha_index_read_map(uchar * buf, const uchar * key, + key_part_map keypart_map, + enum ha_rkey_function find_flag) + { + int error= index_read_map(buf, key, keypart_map, find_flag); + if (!error) + update_index_statistics(); + return error; + } + inline int ha_index_read_idx_map(uchar * buf, uint index, const uchar * key, + key_part_map keypart_map, + enum ha_rkey_function find_flag) + { + int error= index_read_idx_map(buf, index, key, keypart_map, find_flag); + if (!error) + { + rows_read++; + if (index != MAX_KEY) + index_rows_read[index]++; + } + return error; + } + inline int ha_index_next(uchar * buf) + { + int error= index_next(buf); + if (!error) + update_index_statistics(); + return error; + } + inline int ha_index_prev(uchar * buf) + { + int error= index_prev(buf); + if (!error) + update_index_statistics(); + return error; + } + inline int ha_index_first(uchar * buf) + { + int error= index_first(buf); + if (!error) + update_index_statistics(); + return error; + } + inline int ha_index_last(uchar * buf) + { + int error= index_last(buf); + if (!error) + update_index_statistics(); + return error; + } + inline int ha_index_next_same(uchar *buf, const uchar *key, uint keylen) + { + int error= index_next_same(buf, key, keylen); + if (!error) + update_index_statistics(); + return error; + } + inline int ha_index_read_last_map(uchar * buf, const uchar * key, + key_part_map keypart_map) + { + int error= index_read_last_map(buf, key, keypart_map); + if (!error) + update_index_statistics(); + return error; + } + virtual int read_multi_range_first(KEY_MULTI_RANGE **found_range_p, KEY_MULTI_RANGE *ranges, uint range_count, bool sorted, HANDLER_BUFFER *buffer); @@ -1443,6 +1538,7 @@ public: void ft_end() { ft_handler=NULL; } virtual FT_INFO *ft_init_ext(uint flags, uint inx,String *key) { return NULL; } +private: virtual int ft_read(uchar *buf) { return HA_ERR_WRONG_COMMAND; } virtual int rnd_next(uchar *buf)=0; virtual int rnd_pos(uchar * buf, uchar *pos)=0; @@ -1453,11 +1549,50 @@ public: handlers for random position. */ virtual int rnd_pos_by_record(uchar *record) - { - position(record); - return rnd_pos(record, ref); - } + { + position(record); + return rnd_pos(record, ref); + } virtual int read_first_row(uchar *buf, uint primary_key); +public: + + /* Same as above, but with statistics */ + inline int ha_ft_read(uchar *buf) + { + int error= ft_read(buf); + if (!error) + rows_read++; + return error; + } + inline int ha_rnd_next(uchar *buf) + { + int error= rnd_next(buf); + if (!error) + rows_read++; + return error; + } + inline int ha_rnd_pos(uchar *buf, uchar *pos) + { + int error= rnd_pos(buf, pos); + if (!error) + rows_read++; + return error; + } + inline int ha_rnd_pos_by_record(uchar *buf) + { + int error= rnd_pos_by_record(buf); + if (!error) + rows_read++; + return error; + } + inline int ha_read_first_row(uchar *buf, uint primary_key) + { + int error= read_first_row(buf, primary_key); + if (!error) + rows_read++; + return error; + } + /** The following 3 function is only needed for tables that may be internal temporary tables during joins. @@ -1626,6 +1761,9 @@ public: virtual bool is_crashed() const { return 0; } virtual bool auto_repair() const { return 0; } + void update_global_table_stats(); + void update_global_index_stats(); + #define CHF_CREATE_FLAG 0 #define CHF_DELETE_FLAG 1 #define CHF_RENAME_FLAG 2 @@ -1944,6 +2082,7 @@ private: { return HA_ERR_WRONG_COMMAND; } virtual int rename_partitions(const char *path) { return HA_ERR_WRONG_COMMAND; } + friend class ha_partition; }; === modified file 'sql/item_subselect.cc' --- sql/item_subselect.cc 2009-09-15 10:46:35 +0000 +++ sql/item_subselect.cc 2009-10-17 08:13:46 +0000 @@ -2048,7 +2048,7 @@ int subselect_uniquesubquery_engine::sca table->null_row= 0; for (;;) { - error=table->file->rnd_next(table->record[0]); + error=table->file->ha_rnd_next(table->record[0]); if (error && error != HA_ERR_END_OF_FILE) { error= report_error(table, error); @@ -2222,10 +2222,11 @@ int subselect_uniquesubquery_engine::exe if (!table->file->inited) table->file->ha_index_init(tab->ref.key, 0); - error= table->file->index_read_map(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT); + error= table->file->ha_index_read_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab-> + ref.key_parts), + HA_READ_KEY_EXACT); if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) error= report_error(table, error); @@ -2343,10 +2344,11 @@ int subselect_indexsubquery_engine::exec if (!table->file->inited) table->file->ha_index_init(tab->ref.key, 1); - error= table->file->index_read_map(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT); + error= table->file->ha_index_read_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab-> + ref.key_parts), + HA_READ_KEY_EXACT); if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) error= report_error(table, error); @@ -2367,9 +2369,9 @@ int subselect_indexsubquery_engine::exec ((Item_in_subselect *) item)->value= 1; break; } - error= table->file->index_next_same(table->record[0], - tab->ref.key_buff, - tab->ref.key_length); + error= table->file->ha_index_next_same(table->record[0], + tab->ref.key_buff, + tab->ref.key_length); if (error && error != HA_ERR_END_OF_FILE) { error= report_error(table, error); === modified file 'sql/lex.h' --- sql/lex.h 2009-09-07 20:50:10 +0000 +++ sql/lex.h 2009-10-16 08:46:41 +0000 @@ -106,6 +106,7 @@ static SYMBOL symbols[] = { { "CHECKSUM", SYM(CHECKSUM_SYM)}, { "CIPHER", SYM(CIPHER_SYM)}, { "CLIENT", SYM(CLIENT_SYM)}, + { "CLIENT_STATISTICS", SYM(CLIENT_STATS_SYM)}, { "CLOSE", SYM(CLOSE_SYM)}, { "COALESCE", SYM(COALESCE)}, { "CODE", SYM(CODE_SYM)}, @@ -245,6 +246,7 @@ static SYMBOL symbols[] = { { "IN", SYM(IN_SYM)}, { "INDEX", SYM(INDEX_SYM)}, { "INDEXES", SYM(INDEXES)}, + { "INDEX_STATISTICS", SYM(INDEX_STATS_SYM)}, { "INFILE", SYM(INFILE)}, { "INITIAL_SIZE", SYM(INITIAL_SIZE_SYM)}, { "INNER", SYM(INNER_SYM)}, @@ -478,6 +480,7 @@ static SYMBOL symbols[] = { { "SIGNED", SYM(SIGNED_SYM)}, { "SIMPLE", SYM(SIMPLE_SYM)}, { "SLAVE", SYM(SLAVE)}, + { "SLOW", SYM(SLOW_SYM)}, { "SNAPSHOT", SYM(SNAPSHOT_SYM)}, { "SMALLINT", SYM(SMALLINT)}, { "SOCKET", SYM(SOCKET_SYM)}, @@ -526,6 +529,7 @@ static SYMBOL symbols[] = { { "TABLE", SYM(TABLE_SYM)}, { "TABLES", SYM(TABLES)}, { "TABLESPACE", SYM(TABLESPACE)}, + { "TABLE_STATISTICS", SYM(TABLE_STATS_SYM)}, { "TABLE_CHECKSUM", SYM(TABLE_CHECKSUM_SYM)}, { "TEMPORARY", SYM(TEMPORARY)}, { "TEMPTABLE", SYM(TEMPTABLE_SYM)}, @@ -569,6 +573,7 @@ static SYMBOL symbols[] = { { "USE", SYM(USE_SYM)}, { "USER", SYM(USER)}, { "USER_RESOURCES", SYM(RESOURCES)}, + { "USER_STATISTICS", SYM(USER_STATS_SYM)}, { "USE_FRM", SYM(USE_FRM)}, { "USING", SYM(USING)}, { "UTC_DATE", SYM(UTC_DATE_SYM)}, === modified file 'sql/log.cc' --- sql/log.cc 2009-09-15 10:46:35 +0000 +++ sql/log.cc 2009-10-17 14:18:58 +0000 @@ -821,6 +821,13 @@ void Log_to_file_event_handler::flush() mysql_slow_log.reopen_file(); } +void Log_to_file_event_handler::flush_slow_log() +{ + /* reopen slow log file */ + if (opt_slow_log) + mysql_slow_log.reopen_file(); +} + /* Log error with all enabled log event handlers @@ -916,8 +923,6 @@ void LOGGER::init_log_tables() bool LOGGER::flush_logs(THD *thd) { - int rc= 0; - /* Now we lock logger, as nobody should be able to use logging routines while log tables are closed @@ -929,7 +934,24 @@ bool LOGGER::flush_logs(THD *thd) /* end of log flush */ logger.unlock(); - return rc; + return 0; +} + + +bool LOGGER::flush_slow_log(THD *thd) +{ + /* + Now we lock logger, as nobody should be able to use logging routines while + log tables are closed + */ + logger.lock_exclusive(); + + /* reopen log files */ + file_log_handler->flush_slow_log(); + + /* end of log flush */ + logger.unlock(); + return 0; } @@ -4070,6 +4092,7 @@ bool MYSQL_BIN_LOG::write(Log_event *eve if (likely(is_open())) { IO_CACHE *file= &log_file; + my_off_t my_org_b_tell; #ifdef HAVE_REPLICATION /* In the future we need to add to the following if tests like @@ -4077,7 +4100,7 @@ bool MYSQL_BIN_LOG::write(Log_event *eve binlog_[wild_]{do|ignore}_table?" (WL#1049)" */ const char *local_db= event_info->get_db(); - if ((thd && !(thd->options & OPTION_BIN_LOG)) || + if ((!(thd->options & OPTION_BIN_LOG)) || (!binlog_filter->db_ok(local_db))) { VOID(pthread_mutex_unlock(&LOCK_log)); @@ -4085,6 +4108,8 @@ bool MYSQL_BIN_LOG::write(Log_event *eve } #endif /* HAVE_REPLICATION */ + my_org_b_tell= my_b_tell(file); + #if defined(USING_TRANSACTIONS) /* Should we write to the binlog cache or to the binlog on disk? @@ -4095,7 +4120,7 @@ bool MYSQL_BIN_LOG::write(Log_event *eve trans/non-trans table types the best possible in binlogging) - or if the event asks for it (cache_stmt == TRUE). */ - if (opt_using_transactions && thd) + if (opt_using_transactions) { if (thd->binlog_setup_trx_data()) goto err; @@ -4136,7 +4161,6 @@ bool MYSQL_BIN_LOG::write(Log_event *eve If row-based binlogging, Insert_id, Rand and other kind of "setting context" events are not needed. */ - if (thd) { if (!thd->current_stmt_binlog_row_based) { @@ -4183,16 +4207,16 @@ bool MYSQL_BIN_LOG::write(Log_event *eve } } - /* - Write the SQL command - */ - + /* Write the SQL command */ if (event_info->write(file) || DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0)) goto err; if (file == &log_file) // we are writing to the real log (disk) { + ulonglong data_written= (my_b_tell(file) - my_org_b_tell); + status_var_add(thd->status_var.binlog_bytes_written, data_written); + if (flush_and_sync()) goto err; signal_update(); @@ -4318,6 +4342,7 @@ uint MYSQL_BIN_LOG::next_file_id() SYNOPSIS write_cache() + thd Current_thread cache Cache to write to the binary log lock_log True if the LOCK_log mutex should be aquired, false otherwise sync_log True if the log should be flushed and sync:ed @@ -4327,7 +4352,8 @@ uint MYSQL_BIN_LOG::next_file_id() be reset as a READ_CACHE to be able to read the contents from it. */ -int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log) +int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache, bool lock_log, + bool sync_log) { Mutex_sentry sentry(lock_log ? &LOCK_log : NULL); @@ -4375,6 +4401,7 @@ int MYSQL_BIN_LOG::write_cache(IO_CACHE /* write the first half of the split header */ if (my_b_write(&log_file, header, carry)) return ER_ERROR_ON_WRITE; + status_var_add(thd->status_var.binlog_bytes_written, carry); /* copy fixed second half of header to cache so the correct @@ -4443,6 +4470,8 @@ int MYSQL_BIN_LOG::write_cache(IO_CACHE /* Write data to the binary log file */ if (my_b_write(&log_file, cache->read_pos, length)) return ER_ERROR_ON_WRITE; + status_var_add(thd->status_var.binlog_bytes_written, length); + cache->read_pos=cache->read_end; // Mark buffer used up } while ((length= my_b_fill(cache))); @@ -4494,6 +4523,8 @@ bool MYSQL_BIN_LOG::write_incident(THD * if (lock) pthread_mutex_lock(&LOCK_log); ev.write(&log_file); + status_var_add(thd->status_var.binlog_bytes_written, ev.data_written); + if (lock) { if (!error && !(error= flush_and_sync())) @@ -4565,21 +4596,28 @@ bool MYSQL_BIN_LOG::write(THD *thd, IO_C */ if (qinfo.write(&log_file)) goto err; + status_var_add(thd->status_var.binlog_bytes_written, qinfo.data_written); DBUG_EXECUTE_IF("crash_before_writing_xid", { - if ((write_error= write_cache(cache, false, true))) + if ((write_error= write_cache(thd, cache, FALSE, + TRUE))) DBUG_PRINT("info", ("error writing binlog cache: %d", write_error)); DBUG_PRINT("info", ("crashing before writing xid")); abort(); }); - if ((write_error= write_cache(cache, false, false))) + if ((write_error= write_cache(thd, cache, FALSE, FALSE))) goto err; - if (commit_event && commit_event->write(&log_file)) - goto err; + if (commit_event) + { + if (commit_event->write(&log_file)) + goto err; + status_var_add(thd->status_var.binlog_bytes_written, + commit_event->data_written); + } if (incident && write_incident(thd, FALSE)) goto err; === modified file 'sql/log.h' --- sql/log.h 2009-06-18 13:52:46 +0000 +++ sql/log.h 2009-10-17 14:15:43 +0000 @@ -359,7 +359,8 @@ public: bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event, bool incident); bool write_incident(THD *thd, bool lock); - int write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync); + int write_cache(THD *thd, IO_CACHE *cache, bool lock_log, + bool flush_and_sync); void set_write_error(THD *thd); bool check_write_error(THD *thd); @@ -487,6 +488,7 @@ public: const char *sql_text, uint sql_text_len, CHARSET_INFO *client_cs); void flush(); + void flush_slow_log(); void init_pthread_objects(); MYSQL_QUERY_LOG *get_mysql_slow_log() { return &mysql_slow_log; } MYSQL_QUERY_LOG *get_mysql_log() { return &mysql_log; } @@ -531,6 +533,7 @@ public: void init_base(); void init_log_tables(); bool flush_logs(THD *thd); + bool flush_slow_log(THD *thd); /* Perform basic logger cleanup. this will leave e.g. error log open. */ void cleanup_base(); /* Free memory. Nothing could be logged after this function is called */ === modified file 'sql/log_event.cc' --- sql/log_event.cc 2009-09-07 20:50:10 +0000 +++ sql/log_event.cc 2009-10-18 14:27:40 +0000 @@ -4465,7 +4465,7 @@ int Load_log_event::do_apply_event(NET* as the present method does not call mysql_parse(). */ lex_start(thd); - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, 0); if (!use_rli_only_for_errors) { @@ -6262,7 +6262,7 @@ int Append_block_log_event::do_apply_eve as the present method does not call mysql_parse(). */ lex_start(thd); - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, 0); my_delete(fname, MYF(0)); // old copy may exist already if ((fd= my_create(fname, CREATE_MODE, O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW, @@ -7202,7 +7202,7 @@ int Rows_log_event::do_apply_event(Relay we need to do any changes to that value after this function. */ lex_start(thd); - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, 0); /* The current statement is just about to begin and has not yet modified anything. Note, all.modified is reset @@ -8465,7 +8465,7 @@ Rows_log_event::write_row(const Relay_lo if (table->file->ha_table_flags() & HA_DUPLICATE_POS) { DBUG_PRINT("info",("Locating offending record using rnd_pos()")); - error= table->file->rnd_pos(table->record[1], table->file->dup_ref); + error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref); if (error) { DBUG_PRINT("info",("rnd_pos() returns error %d",error)); @@ -8497,10 +8497,10 @@ Rows_log_event::write_row(const Relay_lo key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum, 0); - error= table->file->index_read_idx_map(table->record[1], keynum, - (const uchar*)key.get(), - HA_WHOLE_KEY, - HA_READ_KEY_EXACT); + error= table->file->ha_index_read_idx_map(table->record[1], keynum, + (const uchar*)key.get(), + HA_WHOLE_KEY, + HA_READ_KEY_EXACT); if (error) { DBUG_PRINT("info",("index_read_idx() returns %s", HA_ERR(error))); @@ -8768,13 +8768,14 @@ int Rows_log_event::find_row(const Relay length. Something along these lines should work: ADD>>> store_record(table,record[1]); - int error= table->file->rnd_pos(table->record[0], table->file->ref); + int error= table->file->ha_rnd_pos(table->record[0], + table->file->ref); ADD>>> DBUG_ASSERT(memcmp(table->record[1], table->record[0], table->s->reclength) == 0); */ DBUG_PRINT("info",("locating record using primary key (position)")); - int error= table->file->rnd_pos_by_record(table->record[0]); + int error= table->file->ha_rnd_pos_by_record(table->record[0]); if (error) { DBUG_PRINT("info",("rnd_pos returns error %d",error)); @@ -8834,9 +8835,9 @@ int Rows_log_event::find_row(const Relay table->record[0][table->s->null_bytes - 1]|= 256U - (1U << table->s->last_null_bit_pos); - if ((error= table->file->index_read_map(table->record[0], m_key, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT))) + if ((error= table->file->ha_index_read_map(table->record[0], m_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT))) { DBUG_PRINT("info",("no record matching the key found in the table")); if (error == HA_ERR_RECORD_DELETED) @@ -8898,7 +8899,7 @@ int Rows_log_event::find_row(const Relay 256U - (1U << table->s->last_null_bit_pos); } - while ((error= table->file->index_next(table->record[0]))) + while ((error= table->file->ha_index_next(table->record[0]))) { /* We just skip records that has already been deleted */ if (error == HA_ERR_RECORD_DELETED) @@ -8934,7 +8935,7 @@ int Rows_log_event::find_row(const Relay do { restart_rnd_next: - error= table->file->rnd_next(table->record[0]); + error= table->file->ha_rnd_next(table->record[0]); DBUG_PRINT("info", ("error: %s", HA_ERR(error))); switch (error) { === modified file 'sql/log_event_old.cc' --- sql/log_event_old.cc 2009-05-19 09:28:05 +0000 +++ sql/log_event_old.cc 2009-10-18 14:27:39 +0000 @@ -63,7 +63,7 @@ Old_rows_log_event::do_apply_event(Old_r we need to do any changes to that value after this function. */ lex_start(thd); - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, 0); /* Check if the slave is set to use SBR. If so, it should switch @@ -553,7 +553,7 @@ replace_record(THD *thd, TABLE *table, */ if (table->file->ha_table_flags() & HA_DUPLICATE_POS) { - error= table->file->rnd_pos(table->record[1], table->file->dup_ref); + error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref); if (error) { DBUG_PRINT("info",("rnd_pos() returns error %d",error)); @@ -579,10 +579,10 @@ replace_record(THD *thd, TABLE *table, key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum, 0); - error= table->file->index_read_idx_map(table->record[1], keynum, - (const uchar*)key.get(), - HA_WHOLE_KEY, - HA_READ_KEY_EXACT); + error= table->file->ha_index_read_idx_map(table->record[1], keynum, + (const uchar*)key.get(), + HA_WHOLE_KEY, + HA_READ_KEY_EXACT); if (error) { DBUG_PRINT("info", ("index_read_idx() returns error %d", error)); @@ -694,13 +694,13 @@ static int find_and_fetch_row(TABLE *tab length. Something along these lines should work: ADD>>> store_record(table,record[1]); - int error= table->file->rnd_pos(table->record[0], table->file->ref); + int error= table->file->ha_rnd_pos(table->record[0], table->file->ref); ADD>>> DBUG_ASSERT(memcmp(table->record[1], table->record[0], table->s->reclength) == 0); */ table->file->position(table->record[0]); - int error= table->file->rnd_pos(table->record[0], table->file->ref); + int error= table->file->ha_rnd_pos(table->record[0], table->file->ref); /* rnd_pos() returns the record in table->record[0], so we have to move it to table->record[1]. @@ -738,8 +738,9 @@ static int find_and_fetch_row(TABLE *tab my_ptrdiff_t const pos= table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0; table->record[1][pos]= 0xFF; - if ((error= table->file->index_read_map(table->record[1], key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT))) + if ((error= table->file->ha_index_read_map(table->record[1], key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT))) { table->file->print_error(error, MYF(0)); table->file->ha_index_end(); @@ -793,7 +794,7 @@ static int find_and_fetch_row(TABLE *tab 256U - (1U << table->s->last_null_bit_pos); } - while ((error= table->file->index_next(table->record[1]))) + while ((error= table->file->ha_index_next(table->record[1]))) { /* We just skip records that has already been deleted */ if (error == HA_ERR_RECORD_DELETED) @@ -822,7 +823,7 @@ static int find_and_fetch_row(TABLE *tab do { restart_rnd_next: - error= table->file->rnd_next(table->record[1]); + error= table->file->ha_rnd_next(table->record[1]); DBUG_DUMP("record[0]", table->record[0], table->s->reclength); DBUG_DUMP("record[1]", table->record[1], table->s->reclength); @@ -2115,7 +2116,7 @@ Old_rows_log_event::write_row(const Rela if (table->file->ha_table_flags() & HA_DUPLICATE_POS) { DBUG_PRINT("info",("Locating offending record using rnd_pos()")); - error= table->file->rnd_pos(table->record[1], table->file->dup_ref); + error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref); if (error) { DBUG_PRINT("info",("rnd_pos() returns error %d",error)); @@ -2147,10 +2148,10 @@ Old_rows_log_event::write_row(const Rela key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum, 0); - error= table->file->index_read_idx_map(table->record[1], keynum, - (const uchar*)key.get(), - HA_WHOLE_KEY, - HA_READ_KEY_EXACT); + error= table->file->ha_index_read_idx_map(table->record[1], keynum, + (const uchar*)key.get(), + HA_WHOLE_KEY, + HA_READ_KEY_EXACT); if (error) { DBUG_PRINT("info",("index_read_idx() returns error %d", error)); @@ -2301,13 +2302,13 @@ int Old_rows_log_event::find_row(const R length. Something along these lines should work: ADD>>> store_record(table,record[1]); - int error= table->file->rnd_pos(table->record[0], table->file->ref); + int error= table->file->ha_rnd_pos(table->record[0], table->file->ref); ADD>>> DBUG_ASSERT(memcmp(table->record[1], table->record[0], table->s->reclength) == 0); */ DBUG_PRINT("info",("locating record using primary key (position)")); - int error= table->file->rnd_pos_by_record(table->record[0]); + int error= table->file->ha_rnd_pos_by_record(table->record[0]); if (error) { DBUG_PRINT("info",("rnd_pos returns error %d",error)); @@ -2367,9 +2368,9 @@ int Old_rows_log_event::find_row(const R table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0; table->record[0][pos]= 0xFF; - if ((error= table->file->index_read_map(table->record[0], m_key, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT))) + if ((error= table->file->ha_index_read_map(table->record[0], m_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT))) { DBUG_PRINT("info",("no record matching the key found in the table")); if (error == HA_ERR_RECORD_DELETED) @@ -2431,7 +2432,7 @@ int Old_rows_log_event::find_row(const R 256U - (1U << table->s->last_null_bit_pos); } - while ((error= table->file->index_next(table->record[0]))) + while ((error= table->file->ha_index_next(table->record[0]))) { /* We just skip records that has already been deleted */ if (error == HA_ERR_RECORD_DELETED) @@ -2467,7 +2468,7 @@ int Old_rows_log_event::find_row(const R do { restart_rnd_next: - error= table->file->rnd_next(table->record[0]); + error= table->file->ha_rnd_next(table->record[0]); switch (error) { === modified file 'sql/mysql_priv.h' --- sql/mysql_priv.h 2009-10-06 14:53:46 +0000 +++ sql/mysql_priv.h 2009-10-18 12:46:24 +0000 @@ -1063,6 +1063,7 @@ bool setup_connection_thread_globals(THD bool login_connection(THD *thd); void end_connection(THD *thd); void prepare_new_connection_state(THD* thd); +void update_global_user_stats(THD* thd, bool create_user, time_t now); int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent); bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create); @@ -1099,14 +1100,22 @@ bool is_update_query(enum enum_sql_comma bool is_log_table_write_query(enum enum_sql_command command); bool alloc_query(THD *thd, const char *packet, uint packet_length); void mysql_init_select(LEX *lex); -void mysql_reset_thd_for_next_command(THD *thd); +void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat); bool mysql_new_select(LEX *lex, bool move_down); void create_select_for_variable(const char *var_name); void mysql_init_multi_delete(LEX *lex); bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); void init_max_user_conn(void); void init_update_queries(void); +void init_global_user_stats(void); +void init_global_table_stats(void); +void init_global_index_stats(void); +void init_global_client_stats(void); void free_max_user_conn(void); +void free_global_user_stats(void); +void free_global_table_stats(void); +void free_global_index_stats(void); +void free_global_client_stats(void); pthread_handler_t handle_bootstrap(void *arg); int mysql_execute_command(THD *thd); bool do_command(THD *thd); @@ -1967,6 +1976,7 @@ extern ulong max_connect_errors, connect extern ulong extra_max_connections; extern ulong slave_net_timeout, slave_trans_retries; extern uint max_user_connections; +extern ulonglong denied_connections; extern ulong what_to_log,flush_time; extern ulong query_buff_size; extern ulong max_prepared_stmt_count, prepared_stmt_count; @@ -2020,6 +2030,7 @@ extern my_bool opt_safe_show_db, opt_loc extern my_bool opt_slave_compressed_protocol, use_temp_pool; extern ulong slave_exec_mode_options; extern my_bool opt_readonly, lower_case_file_system; +extern my_bool opt_userstat_running; extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs; extern my_bool opt_secure_auth; extern char* opt_secure_file_priv; @@ -2060,6 +2071,11 @@ extern pthread_mutex_t LOCK_des_key_file #endif extern pthread_mutex_t LOCK_server_started; extern pthread_cond_t COND_server_started; +extern pthread_mutex_t LOCK_global_user_client_stats; +extern pthread_mutex_t LOCK_global_table_stats; +extern pthread_mutex_t LOCK_global_index_stats; +extern pthread_mutex_t LOCK_stats; + extern int mysqld_server_started; extern rw_lock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave; extern rw_lock_t LOCK_system_variables_hash; @@ -2086,6 +2102,11 @@ extern KNOWN_DATE_TIME_FORMAT known_date extern String null_string; extern HASH open_cache, lock_db_cache; +extern HASH global_user_stats; +extern HASH global_client_stats; +extern HASH global_table_stats; +extern HASH global_index_stats; + extern TABLE *unused_tables; extern const char* any_db; extern struct my_option my_long_options[]; === modified file 'sql/mysqld.cc' --- sql/mysqld.cc 2009-10-07 13:07:10 +0000 +++ sql/mysqld.cc 2009-10-18 18:48:55 +0000 @@ -416,6 +416,7 @@ static pthread_cond_t COND_thread_cache, bool opt_update_log, opt_bin_log, opt_ignore_builtin_innodb= 0; my_bool opt_log, opt_slow_log; +my_bool opt_userstat_running; ulong log_output_options; my_bool opt_log_queries_not_using_indexes= 0; bool opt_error_log= IF_WIN(1,0); @@ -548,6 +549,7 @@ ulong binlog_cache_use= 0, binlog_cache_ ulong max_connections, max_connect_errors; ulong extra_max_connections; uint max_user_connections= 0; +ulonglong denied_connections; /** Limit of the total number of prepared statements in the server. Is necessary to protect the server against out-of-memory attacks. @@ -649,6 +651,9 @@ pthread_mutex_t LOCK_mysql_create_db, LO LOCK_global_system_variables, LOCK_user_conn, LOCK_slave_list, LOCK_active_mi, LOCK_connection_count, LOCK_uuid_generator; +pthread_mutex_t LOCK_stats, LOCK_global_user_client_stats; +pthread_mutex_t LOCK_global_table_stats, LOCK_global_index_stats; + /** The below lock protects access to two global server variables: max_prepared_stmt_count and prepared_stmt_count. These variables @@ -1342,6 +1347,10 @@ void clean_up(bool print_message) x_free(opt_secure_file_priv); bitmap_free(&temp_pool); free_max_user_conn(); + free_global_user_stats(); + free_global_client_stats(); + free_global_table_stats(); + free_global_index_stats(); #ifdef HAVE_REPLICATION end_slave_list(); #endif @@ -1428,6 +1437,11 @@ static void clean_up_mutexes() (void) pthread_mutex_destroy(&LOCK_bytes_received); (void) pthread_mutex_destroy(&LOCK_user_conn); (void) pthread_mutex_destroy(&LOCK_connection_count); + (void) pthread_mutex_destroy(&LOCK_stats); + (void) pthread_mutex_destroy(&LOCK_global_user_client_stats); + (void) pthread_mutex_destroy(&LOCK_global_table_stats); + (void) pthread_mutex_destroy(&LOCK_global_index_stats); + Events::destroy_mutexes(); #ifdef HAVE_OPENSSL (void) pthread_mutex_destroy(&LOCK_des_key_file); @@ -3203,6 +3217,7 @@ SHOW_VAR com_status_vars[]= { {"show_binlog_events", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOG_EVENTS]), SHOW_LONG_STATUS}, {"show_binlogs", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOGS]), SHOW_LONG_STATUS}, {"show_charsets", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CHARSETS]), SHOW_LONG_STATUS}, + {"show_client_stats", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CLIENT_STATS]), SHOW_LONG_STATUS}, {"show_collations", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLLATIONS]), SHOW_LONG_STATUS}, {"show_column_types", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLUMN_TYPES]), SHOW_LONG_STATUS}, {"show_contributors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CONTRIBUTORS]), SHOW_LONG_STATUS}, @@ -3225,6 +3240,7 @@ SHOW_VAR com_status_vars[]= { {"show_function_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS_FUNC]), SHOW_LONG_STATUS}, {"show_grants", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS]), SHOW_LONG_STATUS}, {"show_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS}, + {"show_index_stats", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_INDEX_STATS]), SHOW_LONG_STATUS}, {"show_master_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_MASTER_STAT]), SHOW_LONG_STATUS}, {"show_new_master", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_NEW_MASTER]), SHOW_LONG_STATUS}, {"show_open_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_OPEN_TABLES]), SHOW_LONG_STATUS}, @@ -3241,9 +3257,11 @@ SHOW_VAR com_status_vars[]= { {"show_slave_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_STAT]), SHOW_LONG_STATUS}, {"show_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS]), SHOW_LONG_STATUS}, {"show_storage_engines", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STORAGE_ENGINES]), SHOW_LONG_STATUS}, + {"show_table_stats", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATS]), SHOW_LONG_STATUS}, {"show_table_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATUS]), SHOW_LONG_STATUS}, {"show_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLES]), SHOW_LONG_STATUS}, {"show_triggers", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TRIGGERS]), SHOW_LONG_STATUS}, + {"show_user_stats", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_USER_STATS]), SHOW_LONG_STATUS}, {"show_variables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_VARIABLES]), SHOW_LONG_STATUS}, {"show_warnings", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_WARNS]), SHOW_LONG_STATUS}, {"slave_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS}, @@ -3642,6 +3660,12 @@ static int init_thread_environment() (void) pthread_mutex_init(&LOCK_prepared_stmt_count, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_uuid_generator, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_connection_count, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_stats, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_global_user_client_stats, + MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_global_table_stats, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_global_index_stats, MY_MUTEX_INIT_FAST); + #ifdef HAVE_OPENSSL (void) pthread_mutex_init(&LOCK_des_key_file,MY_MUTEX_INIT_FAST); #ifndef HAVE_YASSL @@ -4005,6 +4029,9 @@ server."); /* call ha_init_key_cache() on all key caches to init them */ process_key_caches(&ha_init_key_cache); + init_global_table_stats(); + init_global_index_stats(); + /* Allow storage engine to give real error messages */ if (ha_init_errors()) DBUG_RETURN(1); @@ -4210,6 +4237,8 @@ server."); init_max_user_conn(); init_update_queries(); + init_global_user_stats(); + init_global_client_stats(); DBUG_RETURN(0); } @@ -5019,6 +5048,7 @@ static void create_new_thread(THD *thd) DBUG_PRINT("error",("Too many connections")); close_connection(thd, ER_CON_COUNT_ERROR, 1); + statistic_increment(denied_connections, &LOCK_status); delete thd; DBUG_VOID_RETURN; } @@ -5810,6 +5840,7 @@ enum options_mysqld OPT_LOG_SLOW_RATE_LIMIT, OPT_LOG_SLOW_VERBOSITY, OPT_LOG_SLOW_FILTER, + OPT_USERSTAT, OPT_GENERAL_LOG_FILE, OPT_SLOW_QUERY_LOG_FILE, OPT_IGNORE_BUILTIN_INNODB @@ -7209,6 +7240,10 @@ The minimum value for this variable is 4 (uchar**) &max_system_variables.net_wait_timeout, 0, GET_ULONG, REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT), 0, 1, 0}, + {"userstat", OPT_USERSTAT, + "Control USER_STATISTICS, CLIENT_STATISTICS, INDEX_STATISTICS and TABLE_STATISTICS running", + (uchar**) &opt_userstat_running, (uchar**) &opt_userstat_running, + 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; @@ -7579,19 +7614,24 @@ static int show_ssl_get_cipher_list(THD SHOW_VAR status_vars[]= { {"Aborted_clients", (char*) &aborted_threads, SHOW_LONG}, {"Aborted_connects", (char*) &aborted_connects, SHOW_LONG}, + {"Access_denied_errors", (char*) offsetof(STATUS_VAR, access_denied_errors), SHOW_LONG_STATUS}, {"Binlog_cache_disk_use", (char*) &binlog_cache_disk_use, SHOW_LONG}, {"Binlog_cache_use", (char*) &binlog_cache_use, SHOW_LONG}, + {"Busy_time", (char*) offsetof(STATUS_VAR, busy_time), SHOW_DOUBLE_STATUS}, {"Bytes_received", (char*) offsetof(STATUS_VAR, bytes_received), SHOW_LONGLONG_STATUS}, {"Bytes_sent", (char*) offsetof(STATUS_VAR, bytes_sent), SHOW_LONGLONG_STATUS}, + {"Binlog_bytes_written", (char*) offsetof(STATUS_VAR, binlog_bytes_written), SHOW_LONGLONG_STATUS}, {"Com", (char*) com_status_vars, SHOW_ARRAY}, {"Compression", (char*) &show_net_compression, SHOW_FUNC}, {"Connections", (char*) &thread_id, SHOW_LONG_NOFLUSH}, + {"Cpu_time", (char*) offsetof(STATUS_VAR, cpu_time), SHOW_DOUBLE_STATUS}, {"Created_tmp_disk_tables", (char*) offsetof(STATUS_VAR, created_tmp_disk_tables), SHOW_LONG_STATUS}, {"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG}, {"Created_tmp_tables", (char*) offsetof(STATUS_VAR, created_tmp_tables), SHOW_LONG_STATUS}, {"Delayed_errors", (char*) &delayed_insert_errors, SHOW_LONG}, {"Delayed_insert_threads", (char*) &delayed_insert_threads, SHOW_LONG_NOFLUSH}, {"Delayed_writes", (char*) &delayed_insert_writes, SHOW_LONG}, + {"Empty_queries", (char*) offsetof(STATUS_VAR, empty_queries), SHOW_LONG_STATUS}, {"Flush_commands", (char*) &refresh_version, SHOW_LONG_NOFLUSH}, {"Handler_commit", (char*) offsetof(STATUS_VAR, ha_commit_count), SHOW_LONG_STATUS}, {"Handler_delete", (char*) offsetof(STATUS_VAR, ha_delete_count), SHOW_LONG_STATUS}, @@ -7626,6 +7666,8 @@ SHOW_VAR status_vars[]= { {"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS}, {"Opened_table_definitions", (char*) offsetof(STATUS_VAR, opened_shares), SHOW_LONG_STATUS}, {"Prepared_stmt_count", (char*) &show_prepared_stmt_count, SHOW_FUNC}, + {"Rows_sent", (char*) offsetof(STATUS_VAR, rows_sent), SHOW_LONG_STATUS}, + {"Rows_read", (char*) offsetof(STATUS_VAR, rows_read), SHOW_LONG_STATUS}, #ifdef HAVE_QUERY_CACHE {"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, SHOW_LONG_NOFLUSH}, {"Qcache_free_memory", (char*) &query_cache.free_memory, SHOW_LONG_NOFLUSH}, @@ -9110,6 +9152,8 @@ void refresh_status(THD *thd) /* Reset thread's status variables */ bzero((uchar*) &thd->status_var, sizeof(thd->status_var)); + bzero((uchar*) &thd->org_status_var, sizeof(thd->org_status_var)); + thd->start_bytes_received= 0; /* Reset some global variables */ reset_status_vars(); === modified file 'sql/opt_range.cc' --- sql/opt_range.cc 2009-09-09 21:59:28 +0000 +++ sql/opt_range.cc 2009-10-17 08:13:46 +0000 @@ -8230,7 +8230,7 @@ int QUICK_ROR_INTERSECT_SELECT::get_next /* We get here if we got the same row ref in all scans. */ if (need_to_fetch_row) - error= head->file->rnd_pos(head->record[0], last_rowid); + error= head->file->ha_rnd_pos(head->record[0], last_rowid); } while (error == HA_ERR_RECORD_DELETED); DBUG_RETURN(error); } @@ -8296,7 +8296,7 @@ int QUICK_ROR_UNION_SELECT::get_next() cur_rowid= prev_rowid; prev_rowid= tmp; - error= head->file->rnd_pos(quick->record, prev_rowid); + error= head->file->ha_rnd_pos(quick->record, prev_rowid); } while (error == HA_ERR_RECORD_DELETED); DBUG_RETURN(error); } @@ -8521,10 +8521,12 @@ int QUICK_RANGE_SELECT::get_next_prefix( key_range start_key, end_key; if (last_range) { - /* Read the next record in the same range with prefix after cur_prefix. */ + /* + Read the next record in the same range with prefix after cur_prefix. + */ DBUG_ASSERT(cur_prefix != 0); - result= file->index_read_map(record, cur_prefix, keypart_map, - HA_READ_AFTER_KEY); + result= file->ha_index_read_map(record, cur_prefix, keypart_map, + HA_READ_AFTER_KEY); if (result || (file->compare_key(file->end_range) <= 0)) DBUG_RETURN(result); } @@ -8580,8 +8582,8 @@ int QUICK_RANGE_SELECT_GEOM::get_next() if (last_range) { // Already read through key - result= file->index_next_same(record, last_range->min_key, - last_range->min_length); + result= file->ha_index_next_same(record, last_range->min_key, + last_range->min_length); if (result != HA_ERR_END_OF_FILE) DBUG_RETURN(result); } @@ -8595,10 +8597,10 @@ int QUICK_RANGE_SELECT_GEOM::get_next() } last_range= *(cur_range++); - result= file->index_read_map(record, last_range->min_key, - last_range->min_keypart_map, - (ha_rkey_function)(last_range->flag ^ - GEOM_FLAG)); + result= file->ha_index_read_map(record, last_range->min_key, + last_range->min_keypart_map, + (ha_rkey_function)(last_range->flag ^ + GEOM_FLAG)); if (result != HA_ERR_KEY_NOT_FOUND && result != HA_ERR_END_OF_FILE) DBUG_RETURN(result); last_range= 0; // Not found, to next range @@ -8710,9 +8712,9 @@ int QUICK_SELECT_DESC::get_next() { // Already read through key result = ((last_range->flag & EQ_RANGE && used_key_parts <= head->key_info[index].key_parts) ? - file->index_next_same(record, last_range->min_key, + file->ha_index_next_same(record, last_range->min_key, last_range->min_length) : - file->index_prev(record)); + file->ha_index_prev(record)); if (!result) { if (cmp_prev(*rev_it.ref()) == 0) @@ -8728,7 +8730,7 @@ int QUICK_SELECT_DESC::get_next() if (last_range->flag & NO_MAX_RANGE) // Read last record { int local_error; - if ((local_error=file->index_last(record))) + if ((local_error=file->ha_index_last(record))) DBUG_RETURN(local_error); // Empty table if (cmp_prev(last_range) == 0) DBUG_RETURN(0); @@ -8740,9 +8742,9 @@ int QUICK_SELECT_DESC::get_next() used_key_parts <= head->key_info[index].key_parts) { - result = file->index_read_map(record, last_range->max_key, - last_range->max_keypart_map, - HA_READ_KEY_EXACT); + result= file->ha_index_read_map(record, last_range->max_key, + last_range->max_keypart_map, + HA_READ_KEY_EXACT); } else { @@ -8750,11 +8752,11 @@ int QUICK_SELECT_DESC::get_next() (last_range->flag & EQ_RANGE && used_key_parts > head->key_info[index].key_parts) || range_reads_after_key(last_range)); - result=file->index_read_map(record, last_range->max_key, - last_range->max_keypart_map, - ((last_range->flag & NEAR_MAX) ? - HA_READ_BEFORE_KEY : - HA_READ_PREFIX_LAST_OR_PREV)); + result= file->ha_index_read_map(record, last_range->max_key, + last_range->max_keypart_map, + ((last_range->flag & NEAR_MAX) ? + HA_READ_BEFORE_KEY : + HA_READ_PREFIX_LAST_OR_PREV)); } if (result) { @@ -10467,7 +10469,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::reset(vo DBUG_RETURN(result); if (quick_prefix_select && quick_prefix_select->reset()) DBUG_RETURN(1); - result= file->index_last(record); + result= file->ha_index_last(record); if (result == HA_ERR_END_OF_FILE) DBUG_RETURN(0); /* Save the prefix of the last group. */ @@ -10569,9 +10571,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::get_next first sub-group with the extended prefix. */ if (!have_min && !have_max && key_infix_len > 0) - result= file->index_read_map(record, group_prefix, - make_prev_keypart_map(real_key_parts), - HA_READ_KEY_EXACT); + result= file->ha_index_read_map(record, group_prefix, + make_prev_keypart_map(real_key_parts), + HA_READ_KEY_EXACT); result= have_min ? min_res : have_max ? max_res : result; } while ((result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE) && @@ -10633,9 +10635,10 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min /* Apply the constant equality conditions to the non-group select fields */ if (key_infix_len > 0) { - if ((result= file->index_read_map(record, group_prefix, - make_prev_keypart_map(real_key_parts), - HA_READ_KEY_EXACT))) + if ((result= + file->ha_index_read_map(record, group_prefix, + make_prev_keypart_map(real_key_parts), + HA_READ_KEY_EXACT))) DBUG_RETURN(result); } @@ -10650,9 +10653,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min { /* Find the first subsequent record without NULL in the MIN/MAX field. */ key_copy(tmp_record, record, index_info, 0); - result= file->index_read_map(record, tmp_record, - make_keypart_map(real_key_parts), - HA_READ_AFTER_KEY); + result= file->ha_index_read_map(record, tmp_record, + make_keypart_map(real_key_parts), + HA_READ_AFTER_KEY); /* Check if the new record belongs to the current group by comparing its prefix with the group's prefix. If it is from the next group, then the @@ -10707,9 +10710,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max if (min_max_ranges.elements > 0) result= next_max_in_range(); else - result= file->index_read_map(record, group_prefix, - make_prev_keypart_map(real_key_parts), - HA_READ_PREFIX_LAST); + result= file->ha_index_read_map(record, group_prefix, + make_prev_keypart_map(real_key_parts), + HA_READ_PREFIX_LAST); DBUG_RETURN(result); } @@ -10752,7 +10755,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_pre { if (!seen_first_key) { - result= file->index_first(record); + result= file->ha_index_first(record); if (result) DBUG_RETURN(result); seen_first_key= TRUE; @@ -10760,9 +10763,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_pre else { /* Load the first key in this group into record. */ - result= file->index_read_map(record, group_prefix, - make_prev_keypart_map(group_key_parts), - HA_READ_AFTER_KEY); + result= file->ha_index_read_map(record, group_prefix, + make_prev_keypart_map(group_key_parts), + HA_READ_AFTER_KEY); if (result) DBUG_RETURN(result); } @@ -10839,7 +10842,8 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min HA_READ_AFTER_KEY : HA_READ_KEY_OR_NEXT; } - result= file->index_read_map(record, group_prefix, keypart_map, find_flag); + result= file->ha_index_read_map(record, group_prefix, keypart_map, + find_flag); if (result) { if ((result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE) && @@ -10978,7 +10982,8 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max HA_READ_BEFORE_KEY : HA_READ_PREFIX_LAST_OR_PREV; } - result= file->index_read_map(record, group_prefix, keypart_map, find_flag); + result= file->ha_index_read_map(record, group_prefix, keypart_map, + find_flag); if (result) { === modified file 'sql/opt_range.h' --- sql/opt_range.h 2009-09-02 08:40:18 +0000 +++ sql/opt_range.h 2009-10-17 14:09:21 +0000 @@ -727,7 +727,7 @@ public: ~FT_SELECT() { file->ft_end(); } int init() { return error=file->ft_init(); } int reset() { return 0; } - int get_next() { return error=file->ft_read(record); } + int get_next() { return error= file->ha_ft_read(record); } int get_type() { return QS_TYPE_FULLTEXT; } }; === modified file 'sql/opt_sum.cc' --- sql/opt_sum.cc 2009-09-07 20:50:10 +0000 +++ sql/opt_sum.cc 2009-10-17 08:09:24 +0000 @@ -254,7 +254,7 @@ int opt_sum_query(TABLE_LIST *tables, Li error= table->file->ha_index_init((uint) ref.key, 1); if (!ref.key_length) - error= table->file->index_first(table->record[0]); + error= table->file->ha_index_first(table->record[0]); else { /* @@ -276,10 +276,10 @@ int opt_sum_query(TABLE_LIST *tables, Li Closed interval: Either The MIN argument is non-nullable, or we have a >= predicate for the MIN argument. */ - error= table->file->index_read_map(table->record[0], - ref.key_buff, - make_prev_keypart_map(ref.key_parts), - HA_READ_KEY_OR_NEXT); + error= table->file->ha_index_read_map(table->record[0], + ref.key_buff, + make_prev_keypart_map(ref.key_parts), + HA_READ_KEY_OR_NEXT); else { /* @@ -288,10 +288,10 @@ int opt_sum_query(TABLE_LIST *tables, Li 2) there is a > predicate on it, nullability is irrelevant. We need to scan the next bigger record first. */ - error= table->file->index_read_map(table->record[0], - ref.key_buff, - make_prev_keypart_map(ref.key_parts), - HA_READ_AFTER_KEY); + error= table->file->ha_index_read_map(table->record[0], + ref.key_buff, + make_prev_keypart_map(ref.key_parts), + HA_READ_AFTER_KEY); /* If the found record is outside the group formed by the search prefix, or there is no such record at all, check if all @@ -314,10 +314,10 @@ int opt_sum_query(TABLE_LIST *tables, Li key_cmp_if_same(table, ref.key_buff, ref.key, prefix_len))) { DBUG_ASSERT(item_field->field->real_maybe_null()); - error= table->file->index_read_map(table->record[0], - ref.key_buff, - make_prev_keypart_map(ref.key_parts), - HA_READ_KEY_EXACT); + error= table->file->ha_index_read_map(table->record[0], + ref.key_buff, + make_prev_keypart_map(ref.key_parts), + HA_READ_KEY_EXACT); } } } @@ -402,13 +402,13 @@ int opt_sum_query(TABLE_LIST *tables, Li error= table->file->ha_index_init((uint) ref.key, 1); if (!ref.key_length) - error= table->file->index_last(table->record[0]); + error= table->file->ha_index_last(table->record[0]); else - error= table->file->index_read_map(table->record[0], key_buff, - make_prev_keypart_map(ref.key_parts), - range_fl & NEAR_MAX ? - HA_READ_BEFORE_KEY : - HA_READ_PREFIX_LAST_OR_PREV); + error= table->file->ha_index_read_map(table->record[0], key_buff, + make_prev_keypart_map(ref.key_parts), + range_fl & NEAR_MAX ? + HA_READ_BEFORE_KEY : + HA_READ_PREFIX_LAST_OR_PREV); if (!error && reckey_in_range(1, &ref, item_field->field, conds, range_fl, prefix_len)) error= HA_ERR_KEY_NOT_FOUND; === modified file 'sql/records.cc' --- sql/records.cc 2009-05-06 12:03:24 +0000 +++ sql/records.cc 2009-10-17 08:13:46 +0000 @@ -342,7 +342,7 @@ static int rr_quick(READ_RECORD *info) static int rr_index_first(READ_RECORD *info) { - int tmp= info->file->index_first(info->record); + int tmp= info->file->ha_index_first(info->record); info->read_record= rr_index; if (tmp) tmp= rr_handle_error(info, tmp); @@ -368,7 +368,7 @@ static int rr_index_first(READ_RECORD *i static int rr_index(READ_RECORD *info) { - int tmp= info->file->index_next(info->record); + int tmp= info->file->ha_index_next(info->record); if (tmp) tmp= rr_handle_error(info, tmp); return tmp; @@ -378,7 +378,7 @@ static int rr_index(READ_RECORD *info) int rr_sequential(READ_RECORD *info) { int tmp; - while ((tmp=info->file->rnd_next(info->record))) + while ((tmp=info->file->ha_rnd_next(info->record))) { if (info->thd->killed) { @@ -406,7 +406,7 @@ static int rr_from_tempfile(READ_RECORD { if (my_b_read(info->io_cache,info->ref_pos,info->ref_length)) return -1; /* End of file */ - if (!(tmp=info->file->rnd_pos(info->record,info->ref_pos))) + if (!(tmp=info->file->ha_rnd_pos(info->record,info->ref_pos))) break; /* The following is extremely unlikely to happen */ if (tmp == HA_ERR_RECORD_DELETED || @@ -457,7 +457,7 @@ static int rr_from_pointers(READ_RECORD cache_pos= info->cache_pos; info->cache_pos+= info->ref_length; - if (!(tmp=info->file->rnd_pos(info->record,cache_pos))) + if (!(tmp=info->file->ha_rnd_pos(info->record,cache_pos))) break; /* The following is extremely unlikely to happen */ @@ -590,7 +590,7 @@ static int rr_from_cache(READ_RECORD *in record=uint3korr(position); position+=3; record_pos=info->cache+record*info->reclength; - if ((error=(int16) info->file->rnd_pos(record_pos,info->ref_pos))) + if ((error=(int16) info->file->ha_rnd_pos(record_pos,info->ref_pos))) { record_pos[info->error_offset]=1; shortstore(record_pos,error); === modified file 'sql/set_var.cc' --- sql/set_var.cc 2009-09-15 10:46:35 +0000 +++ sql/set_var.cc 2009-10-18 16:39:56 +0000 @@ -511,6 +511,9 @@ static sys_var_const sys_prot static sys_var_thd_ulong sys_read_buff_size(&vars, "read_buffer_size", &SV::read_buff_size); static sys_var_opt_readonly sys_readonly(&vars, "read_only", &opt_readonly); +static sys_var_bool_ptr sys_userstat(&vars, "userstat", + &opt_userstat_running); + static sys_var_thd_ulong sys_read_rnd_buff_size(&vars, "read_rnd_buffer_size", &SV::read_rnd_buff_size); static sys_var_thd_ulong sys_div_precincrement(&vars, "div_precision_increment", === modified file 'sql/sp.cc' --- sql/sp.cc 2009-07-28 22:39:58 +0000 +++ sql/sp.cc 2009-10-17 08:09:24 +0000 @@ -344,8 +344,9 @@ db_find_routine_aux(THD *thd, int type, key_copy(key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx_map(table->record[0], 0, key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_idx_map(table->record[0], 0, key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) DBUG_RETURN(SP_KEY_NOT_FOUND); DBUG_RETURN(SP_OK); @@ -1101,9 +1102,9 @@ sp_drop_db_routines(THD *thd, char *db) ret= SP_OK; table->file->ha_index_init(0, 1); - if (! table->file->index_read_map(table->record[0], - (uchar *)table->field[MYSQL_PROC_FIELD_DB]->ptr, - (key_part_map)1, HA_READ_KEY_EXACT)) + if (! table->file->ha_index_read_map(table->record[0], + (uchar *)table->field[MYSQL_PROC_FIELD_DB]->ptr, + (key_part_map)1, HA_READ_KEY_EXACT)) { int nxtres; bool deleted= FALSE; @@ -1118,9 +1119,11 @@ sp_drop_db_routines(THD *thd, char *db) nxtres= 0; break; } - } while (! (nxtres= table->file->index_next_same(table->record[0], - (uchar *)table->field[MYSQL_PROC_FIELD_DB]->ptr, - key_len))); + } while (!(nxtres= table->file-> + ha_index_next_same(table->record[0], + (uchar *)table->field[MYSQL_PROC_FIELD_DB]-> + ptr, + key_len))); if (nxtres != HA_ERR_END_OF_FILE) ret= SP_KEY_NOT_FOUND; if (deleted) === modified file 'sql/sql_acl.cc' --- sql/sql_acl.cc 2009-09-07 20:50:10 +0000 +++ sql/sql_acl.cc 2009-10-17 08:13:46 +0000 @@ -1834,9 +1834,9 @@ static bool update_user_table(THD *thd, key_copy((uchar *) user_key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx_map(table->record[0], 0, - (uchar *) user_key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar *) user_key, HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0)); /* purecov: deadcode */ @@ -1927,9 +1927,9 @@ static int replace_user_table(THD *thd, key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx_map(table->record[0], 0, user_key, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { /* what == 'N' means revoke */ if (what == 'N') @@ -2151,9 +2151,9 @@ static int replace_db_table(TABLE *table key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx_map(table->record[0],0, user_key, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_idx_map(table->record[0],0, user_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { if (what == 'N') { // no row, no revoke @@ -2369,8 +2369,9 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TA col_privs->field[4]->store("",0, &my_charset_latin1); col_privs->file->ha_index_init(0, 1); - if (col_privs->file->index_read_map(col_privs->record[0], (uchar*) key, - (key_part_map)15, HA_READ_KEY_EXACT)) + if (col_privs->file->ha_index_read_map(col_privs->record[0], (uchar*) key, + (key_part_map)15, + HA_READ_KEY_EXACT)) { cols = 0; /* purecov: deadcode */ col_privs->file->ha_index_end(); @@ -2391,7 +2392,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TA return; /* purecov: deadcode */ } my_hash_insert(&hash_columns, (uchar *) mem_check); - } while (!col_privs->file->index_next(col_privs->record[0]) && + } while (!col_privs->file->ha_index_next(col_privs->record[0]) && !key_cmp_if_same(col_privs,key,0,key_prefix_len)); col_privs->file->ha_index_end(); } @@ -2532,8 +2533,8 @@ static int replace_column_table(GRANT_TA key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_map(table->record[0], user_key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_map(table->record[0], user_key, + HA_WHOLE_KEY, HA_READ_KEY_EXACT)) { if (revoke_grant) { @@ -2610,9 +2611,9 @@ static int replace_column_table(GRANT_TA key_copy(user_key, table->record[0], table->key_info, key_prefix_length); - if (table->file->index_read_map(table->record[0], user_key, - (key_part_map)15, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_map(table->record[0], user_key, + (key_part_map)15, + HA_READ_KEY_EXACT)) goto end; /* Scan through all rows with the same host,db,user and table */ @@ -2663,7 +2664,7 @@ static int replace_column_table(GRANT_TA hash_delete(&g_t->hash_columns,(uchar*) grant_column); } } - } while (!table->file->index_next(table->record[0]) && + } while (!table->file->ha_index_next(table->record[0]) && !key_cmp_if_same(table, key, 0, key_prefix_length)); } @@ -2713,9 +2714,9 @@ static int replace_table_table(THD *thd, key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx_map(table->record[0], 0, user_key, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { /* The following should never happen as we first check the in memory @@ -2840,10 +2841,10 @@ static int replace_routine_table(THD *th TRUE); store_record(table,record[1]); // store at pos 1 - if (table->file->index_read_idx_map(table->record[0], 0, - (uchar*) table->field[0]->ptr, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar*) table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { /* The following should never happen as we first check the in memory @@ -3548,7 +3549,7 @@ static my_bool grant_load_procs_priv(TAB p_table->file->ha_index_init(0, 1); p_table->use_all_columns(); - if (!p_table->file->index_first(p_table->record[0])) + if (!p_table->file->ha_index_first(p_table->record[0])) { memex_ptr= &memex; my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr); @@ -3600,7 +3601,7 @@ static my_bool grant_load_procs_priv(TAB goto end_unlock; } } - while (!p_table->file->index_next(p_table->record[0])); + while (!p_table->file->ha_index_next(p_table->record[0])); } /* Return ok */ return_val= 0; @@ -3650,7 +3651,7 @@ static my_bool grant_load(THD *thd, TABL t_table->use_all_columns(); c_table->use_all_columns(); - if (!t_table->file->index_first(t_table->record[0])) + if (!t_table->file->ha_index_first(t_table->record[0])) { memex_ptr= &memex; my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr); @@ -3685,7 +3686,7 @@ static my_bool grant_load(THD *thd, TABL goto end_unlock; } } - while (!t_table->file->index_next(t_table->record[0])); + while (!t_table->file->ha_index_next(t_table->record[0])); } return_val=0; // Return ok @@ -3957,6 +3958,8 @@ err: { char command[128]; get_privilege_desc(command, sizeof(command), want_access); + status_var_increment(thd->status_var.access_denied_errors); + my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), command, sctx->priv_user, @@ -5203,9 +5206,9 @@ static int handle_grant_table(TABLE_LIST table->key_info->key_part[1].store_length); key_copy(user_key, table->record[0], table->key_info, key_prefix_length); - if ((error= table->file->index_read_idx_map(table->record[0], 0, - user_key, (key_part_map)3, - HA_READ_KEY_EXACT))) + if ((error= table->file->ha_index_read_idx_map(table->record[0], 0, + user_key, (key_part_map)3, + HA_READ_KEY_EXACT))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) { @@ -5240,7 +5243,7 @@ static int handle_grant_table(TABLE_LIST DBUG_PRINT("info",("scan table: '%s' search: '%s'@'%s'", table->s->table_name.str, user_str, host_str)); #endif - while ((error= table->file->rnd_next(table->record[0])) != + while ((error= table->file->ha_rnd_next(table->record[0])) != HA_ERR_END_OF_FILE) { if (error) === modified file 'sql/sql_base.cc' --- sql/sql_base.cc 2009-09-07 20:50:10 +0000 +++ sql/sql_base.cc 2009-10-19 02:07:41 +0000 @@ -1373,6 +1373,12 @@ bool close_thread_table(THD *thd, TABLE DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str, table->s->table_name.str, (long) table)); + if (table->file) + { + table->file->update_global_table_stats(); + table->file->update_global_index_stats(); + } + *table_ptr=table->next; /* When closing a MERGE parent or child table, detach the children first. @@ -1902,6 +1908,13 @@ void close_temporary(TABLE *table, bool DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'", table->s->db.str, table->s->table_name.str)); + /* in_use is not set for replication temporary tables during shutdown */ + if (table->in_use) + { + table->file->update_global_table_stats(); + table->file->update_global_index_stats(); + } + free_io_cache(table); closefrm(table, 0); if (delete_table) === modified file 'sql/sql_class.cc' --- sql/sql_class.cc 2009-09-15 10:46:35 +0000 +++ sql/sql_class.cc 2009-10-18 17:24:57 +0000 @@ -615,6 +615,7 @@ THD::THD() mysys_var=0; binlog_evt_union.do_union= FALSE; enable_slow_log= 0; + #ifndef DBUG_OFF dbug_sentry=THD_SENTRY_MAGIC; #endif @@ -817,7 +818,63 @@ void THD::init(void) update_charset(); reset_current_stmt_binlog_row_based(); bzero((char *) &status_var, sizeof(status_var)); + bzero((char *) &org_status_var, sizeof(org_status_var)); sql_log_bin_toplevel= options & OPTION_BIN_LOG; + select_commands= update_commands= other_commands= 0; + /* Set to handle counting of aborted connections */ + userstat_running= opt_userstat_running; + last_global_update_time= current_connect_time= time(NULL); +} + + +/* Updates some status variables to be used by update_global_user_stats */ + +void THD::update_stats(void) +{ + /* sql_command == SQLCOM_END in case of parse errors or quit */ + if (lex->sql_command != SQLCOM_END) + { + /* The replication thread has the COM_CONNECT command */ + DBUG_ASSERT(command == COM_QUERY || command == COM_CONNECT); + + /* A SQL query. */ + if (lex->sql_command == SQLCOM_SELECT) + select_commands++; + else if (! sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) + { + /* Ignore 'SHOW ' commands */ + } + else if (is_update_query(lex->sql_command)) + update_commands++; + else + other_commands++; + } +} + + +void THD::update_all_stats() +{ + time_t save_time; + ulonglong end_cpu_time, end_utime; + double busy_time, cpu_time; + + /* This is set at start of query if opt_userstat_running was set */ + if (!userstat_running) + return; + + end_cpu_time= my_getrealtime(); + end_utime= my_micro_time_and_time(&save_time); + busy_time= (end_utime - start_utime) / 1000000.0; + cpu_time= (end_cpu_time - start_cpu_time) / 10000000.0; + /* In case there are bad values, 2629743 is the #seconds in a month. */ + if (cpu_time > 2629743.0) + cpu_time= 0; + status_var_add(status_var.cpu_time, cpu_time); + status_var_add(status_var.busy_time, busy_time); + + /* Updates THD stats and the global user stats. */ + update_stats(); + update_global_user_stats(this, TRUE, save_time); } @@ -984,9 +1041,8 @@ THD::~THD() from_var from this array NOTES - This function assumes that all variables are long/ulong. - If this assumption will change, then we have to explictely add - the other variables after the while loop + This function assumes that all variables at start are long/ulong and + other types are handled explicitely */ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var) @@ -998,6 +1054,13 @@ void add_to_status(STATUS_VAR *to_var, S while (to != end) *(to++)+= *(from++); + + /* Handle the not ulong variables. See end of system_status_var */ + to_var->bytes_received= from_var->bytes_received; + to_var->bytes_sent+= from_var->bytes_sent; + to_var->binlog_bytes_written= from_var->binlog_bytes_written; + to_var->cpu_time+= from_var->cpu_time; + to_var->busy_time+= from_var->busy_time; } /* @@ -1010,7 +1073,8 @@ void add_to_status(STATUS_VAR *to_var, S dec_var minus this array NOTE - This function assumes that all variables are long/ulong. + This function assumes that all variables at start are long/ulong and + other types are handled explicitely */ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, @@ -1023,6 +1087,14 @@ void add_diff_to_status(STATUS_VAR *to_v while (to != end) *(to++)+= *(from++) - *(dec++); + + to_var->bytes_received= (from_var->bytes_received - + dec_var->bytes_received); + to_var->bytes_sent+= from_var->bytes_sent - dec_var->bytes_sent; + to_var->binlog_bytes_written= (from_var->binlog_bytes_written - + dec_var->binlog_bytes_written); + to_var->cpu_time+= from_var->cpu_time - dec_var->cpu_time; + to_var->busy_time+= from_var->busy_time - dec_var->busy_time; } #define SECONDS_TO_WAIT_FOR_KILL 2 @@ -2773,7 +2845,8 @@ void thd_increment_bytes_sent(ulong leng { THD *thd=current_thd; if (likely(thd != 0)) - { /* current_thd==0 when close_connection() calls net_send_error() */ + { + /* current_thd == 0 when close_connection() calls net_send_error() */ thd->status_var.bytes_sent+= length; } } === modified file 'sql/sql_class.h' --- sql/sql_class.h 2009-09-15 10:46:35 +0000 +++ sql/sql_class.h 2009-10-18 17:18:46 +0000 @@ -415,8 +415,6 @@ struct system_variables typedef struct system_status_var { - ulonglong bytes_received; - ulonglong bytes_sent; ulong com_other; ulong com_stat[(uint) SQLCOM_END]; ulong created_tmp_disk_tables; @@ -455,6 +453,9 @@ typedef struct system_status_var ulong select_range_count; ulong select_range_check_count; ulong select_scan_count; + ulong rows_examined; + ulong rows_read; + ulong rows_sent; ulong long_query_count; ulong filesort_merge_passes; ulong filesort_range_count; @@ -472,6 +473,9 @@ typedef struct system_status_var Number of statements sent from the client */ ulong questions; + ulong empty_queries; + ulong access_denied_errors; /* Can only be 0 or 1 */ + ulong lost_connections; /* IMPORTANT! SEE last_system_status_var DEFINITION BELOW. @@ -480,12 +484,16 @@ typedef struct system_status_var Status variables which it does not make sense to add to global status variable counter */ + ulonglong bytes_received; + ulonglong bytes_sent; + ulonglong binlog_bytes_written; double last_query_cost; + double cpu_time, busy_time; } STATUS_VAR; /* This is used for 'SHOW STATUS'. It must be updated to the last ulong - variable in system_status_var which is makes sens to add to the global + variable in system_status_var which is makes sense to add to the global counter */ @@ -1299,6 +1307,7 @@ public: struct my_rnd_struct rand; // used for authentication struct system_variables variables; // Changeable local variables struct system_status_var status_var; // Per thread statistic vars + struct system_status_var org_status_var; // For user statistics struct system_status_var *initial_status_var; /* used by show status */ THR_LOCK_INFO lock_info; // Locking info of this thread THR_LOCK_OWNER main_lock_id; // To use for conventional queries @@ -1399,6 +1408,8 @@ public: uint in_sub_stmt; /* TRUE when the current top has SQL_LOG_BIN ON */ bool sql_log_bin_toplevel; + /* True when opt_userstat_running is set at start of query */ + bool userstat_running; /* container for handler's private per-connection data */ Ha_data ha_data[MAX_HA]; @@ -1752,6 +1763,7 @@ public: /* variables.transaction_isolation is reset to this after each commit */ enum_tx_isolation session_tx_isolation; enum_check_fields count_cuted_fields; + ha_rows updated_row_count; DYNAMIC_ARRAY user_var_events; /* For user variables replication */ MEM_ROOT *user_var_events_alloc; /* Allocate above array elements here */ @@ -1842,6 +1854,26 @@ public: */ LOG_INFO* current_linfo; NET* slave_net; // network connection from slave -> m. + + /* + Used to update global user stats. The global user stats are updated + occasionally with the 'diff' variables. After the update, the 'diff' + variables are reset to 0. + */ + /* Time when the current thread connected to MySQL. */ + time_t current_connect_time; + /* Last time when THD stats were updated in global_user_stats. */ + time_t last_global_update_time; + /* Number of commands not reflected in global_user_stats yet. */ + uint select_commands, update_commands, other_commands; + ulonglong start_cpu_time; + ulonglong start_bytes_received; + /* + Per account query delay in miliseconds. When not 0, sleep this number of + milliseconds before every SQL command. + */ + ulonglong query_delay_millis; + /* Used by the sys_var class to store temporary values */ union { @@ -1902,6 +1934,12 @@ public: alloc_root. */ void init_for_queries(); + void update_all_stats(); + void reset_stats(void); + void reset_diff_stats(void); + // ran_command is true when this is called immediately after a + // command has been run. + void update_stats(void); void change_user(void); void cleanup(void); void cleanup_after_query(); @@ -2319,7 +2357,6 @@ private: MEM_ROOT main_mem_root; }; - /** A short cut for thd->main_da.set_ok_status(). */ inline void === modified file 'sql/sql_connect.cc' --- sql/sql_connect.cc 2009-09-07 20:50:10 +0000 +++ sql/sql_connect.cc 2009-10-18 19:37:12 +0000 @@ -20,6 +20,13 @@ #include "mysql_priv.h" +HASH global_user_stats, global_client_stats, global_table_stats; +HASH global_index_stats; +/* Protects the above global stats */ +extern pthread_mutex_t LOCK_global_user_client_stats; +extern pthread_mutex_t LOCK_global_table_stats; +extern pthread_mutex_t LOCK_global_index_stats; + #ifdef HAVE_OPENSSL /* Without SSL the handshake consists of one packet. This packet @@ -459,6 +466,7 @@ check_user(THD *thd, enum enum_server_co check_for_max_user_connections(thd, thd->user_connect)) { /* The error is set in check_for_max_user_connections(). */ + status_var_increment(denied_connections); DBUG_RETURN(1); } @@ -470,6 +478,7 @@ check_user(THD *thd, enum enum_server_co /* mysql_change_db() has pushed the error message. */ if (thd->user_connect) decrease_user_connections(thd->user_connect); + status_var_increment(thd->status_var.access_denied_errors); DBUG_RETURN(1); } } @@ -493,6 +502,8 @@ check_user(THD *thd, enum enum_server_co thd->main_security_ctx.user, thd->main_security_ctx.host_or_ip, passwd_len ? ER(ER_YES) : ER(ER_NO)); + status_var_increment(thd->status_var.access_denied_errors); + DBUG_RETURN(1); #endif /* NO_EMBEDDED_ACCESS_CHECKS */ } @@ -520,10 +531,14 @@ extern "C" void free_user(struct user_co void init_max_user_conn(void) { #ifndef NO_EMBEDDED_ACCESS_CHECKS - (void) hash_init(&hash_user_connections,system_charset_info,max_connections, - 0,0, - (hash_get_key) get_key_conn, (hash_free_key) free_user, - 0); + if (hash_init(&hash_user_connections,system_charset_info,max_connections, + 0,0, + (hash_get_key) get_key_conn, (hash_free_key) free_user, + 0)) + { + sql_print_error("Initializing hash_user_connections failed."); + exit(1); + } #endif } @@ -576,6 +591,449 @@ void reset_mqh(LEX_USER *lu, bool get_th #endif /* NO_EMBEDDED_ACCESS_CHECKS */ } +/***************************************************************************** + Handle users statistics +*****************************************************************************/ + +/* 'mysql_system_user' is used for when the user is not defined for a THD. */ +static const char mysql_system_user[]= "#mysql_system#"; + +// Returns 'user' if it's not NULL. Returns 'mysql_system_user' otherwise. +static const char * get_valid_user_string(char* user) +{ + return user ? user : mysql_system_user; +} + +/* + Returns string as 'IP' for the client-side of the connection represented by + 'client'. Does not allocate memory. May return "". +*/ + +static const char *get_client_host(THD *client) +{ + return client->security_ctx->host_or_ip[0] ? + client->security_ctx->host_or_ip : + client->security_ctx->host ? client->security_ctx->host : ""; +} + +extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length, + my_bool not_used __attribute__((unused))) +{ + *length= user_stats->user_name_length; + return (uchar*) user_stats->user; +} + +void free_user_stats(USER_STATS* user_stats) +{ + my_free(user_stats, MYF(0)); +} + +void init_user_stats(USER_STATS *user_stats, + const char *user, + size_t user_length, + const char *priv_user, + uint total_connections, + uint concurrent_connections, + time_t connected_time, + double busy_time, + double cpu_time, + ulonglong bytes_received, + ulonglong bytes_sent, + ulonglong binlog_bytes_written, + ha_rows rows_sent, + ha_rows rows_read, + ha_rows rows_inserted, + ha_rows rows_deleted, + ha_rows rows_updated, + ulonglong select_commands, + ulonglong update_commands, + ulonglong other_commands, + ulonglong commit_trans, + ulonglong rollback_trans, + ulonglong denied_connections, + ulonglong lost_connections, + ulonglong access_denied_errors, + ulonglong empty_queries) +{ + DBUG_ENTER("init_user_stats"); + DBUG_PRINT("info", + ("Add user_stats entry for user %s - priv_user %s", + user, priv_user)); + user_length= min(user_length, sizeof(user_stats->user)-1); + memcpy(user_stats->user, user, user_length); + user_stats->user[user_length]= 0; + user_stats->user_name_length= user_length; + strmake(user_stats->priv_user, priv_user, sizeof(user_stats->priv_user)-1); + + user_stats->total_connections= total_connections; + user_stats->concurrent_connections= concurrent_connections; + user_stats->connected_time= connected_time; + user_stats->busy_time= busy_time; + user_stats->cpu_time= cpu_time; + user_stats->bytes_received= bytes_received; + user_stats->bytes_sent= bytes_sent; + user_stats->binlog_bytes_written= binlog_bytes_written; + user_stats->rows_sent= rows_sent; + user_stats->rows_updated= rows_updated; + user_stats->rows_read= rows_read; + user_stats->select_commands= select_commands; + user_stats->update_commands= update_commands; + user_stats->other_commands= other_commands; + user_stats->commit_trans= commit_trans; + user_stats->rollback_trans= rollback_trans; + user_stats->denied_connections= denied_connections; + user_stats->lost_connections= lost_connections; + user_stats->access_denied_errors= access_denied_errors; + user_stats->empty_queries= empty_queries; + DBUG_VOID_RETURN; +} + + +#ifdef COMPLEAT_PATCH_NOT_ADDED_YET + +void add_user_stats(USER_STATS *user_stats, + uint total_connections, + uint concurrent_connections, + time_t connected_time, + double busy_time, + double cpu_time, + ulonglong bytes_received, + ulonglong bytes_sent, + ulonglong binlog_bytes_written, + ha_rows rows_sent, + ha_rows rows_read, + ha_rows rows_inserted, + ha_rows rows_deleted, + ha_rows rows_updated, + ulonglong select_commands, + ulonglong update_commands, + ulonglong other_commands, + ulonglong commit_trans, + ulonglong rollback_trans, + ulonglong denied_connections, + ulonglong lost_connections, + ulonglong access_denied_errors, + ulonglong empty_queries) +{ + user_stats->total_connections+= total_connections; + user_stats->concurrent_connections+= concurrent_connections; + user_stats->connected_time+= connected_time; + user_stats->busy_time+= busy_time; + user_stats->cpu_time+= cpu_time; + user_stats->bytes_received+= bytes_received; + user_stats->bytes_sent+= bytes_sent; + user_stats->binlog_bytes_written+= binlog_bytes_written; + user_stats->rows_sent+= rows_sent; + user_stats->rows_inserted+= rows_inserted; + user_stats->rows_deleted+= rows_deleted; + user_stats->rows_updated+= rows_updated; + user_stats->rows_read+= rows_read; + user_stats->select_commands+= select_commands; + user_stats->update_commands+= update_commands; + user_stats->other_commands+= other_commands; + user_stats->commit_trans+= commit_trans; + user_stats->rollback_trans+= rollback_trans; + user_stats->denied_connections+= denied_connections; + user_stats->lost_connections+= lost_connections; + user_stats->access_denied_errors+= access_denied_errors; + user_stats->empty_queries+= empty_queries; +} +#endif + + +void init_global_user_stats(void) +{ + if (hash_init(&global_user_stats, system_charset_info, max_connections, + 0, 0, (hash_get_key) get_key_user_stats, + (hash_free_key)free_user_stats, 0)) + { + sql_print_error("Initializing global_user_stats failed."); + exit(1); + } +} + +void init_global_client_stats(void) +{ + if (hash_init(&global_client_stats, system_charset_info, max_connections, + 0, 0, (hash_get_key) get_key_user_stats, + (hash_free_key)free_user_stats, 0)) + { + sql_print_error("Initializing global_client_stats failed."); + exit(1); + } +} + +extern "C" uchar *get_key_table_stats(TABLE_STATS *table_stats, size_t *length, + my_bool not_used __attribute__((unused))) +{ + *length= table_stats->table_name_length; + return (uchar*) table_stats->table; +} + +extern "C" void free_table_stats(TABLE_STATS* table_stats) +{ + my_free(table_stats, MYF(0)); +} + +void init_global_table_stats(void) +{ + if (hash_init(&global_table_stats, system_charset_info, max_connections, + 0, 0, (hash_get_key) get_key_table_stats, + (hash_free_key)free_table_stats, 0)) { + sql_print_error("Initializing global_table_stats failed."); + exit(1); + } +} + +extern "C" uchar *get_key_index_stats(INDEX_STATS *index_stats, size_t *length, + my_bool not_used __attribute__((unused))) +{ + *length= index_stats->index_name_length; + return (uchar*) index_stats->index; +} + +extern "C" void free_index_stats(INDEX_STATS* index_stats) +{ + my_free(index_stats, MYF(0)); +} + +void init_global_index_stats(void) +{ + if (hash_init(&global_index_stats, system_charset_info, max_connections, + 0, 0, (hash_get_key) get_key_index_stats, + (hash_free_key)free_index_stats, 0)) + { + sql_print_error("Initializing global_index_stats failed."); + exit(1); + } +} + + +void free_global_user_stats(void) +{ + hash_free(&global_user_stats); +} + +void free_global_table_stats(void) +{ + hash_free(&global_table_stats); +} + +void free_global_index_stats(void) +{ + hash_free(&global_index_stats); +} + +void free_global_client_stats(void) +{ + hash_free(&global_client_stats); +} + +/* + Increments the global stats connection count for an entry from + global_client_stats or global_user_stats. Returns 0 on success + and 1 on error. +*/ + +static int increment_count_by_name(const char *name, size_t name_length, + const char *role_name, + HASH *users_or_clients, THD *thd) +{ + USER_STATS *user_stats; + + if (!(user_stats= (USER_STATS*) hash_search(users_or_clients, (uchar*) name, + name_length))) + { + // First connection for this user or client + if (!(user_stats= ((USER_STATS*) + my_malloc(sizeof(USER_STATS), + MYF(MY_WME | MY_ZEROFILL))))) + { + return 1; // Out of memory + } + + init_user_stats(user_stats, name, name_length, role_name, + 0, 0, // connections + 0, 0, 0, // time + 0, 0, 0, // bytes sent, received and written + 0, 0, // Rows sent and read + 0, 0, 0, // rows inserted, deleted and updated + 0, 0, 0, // select, update and other commands + 0, 0, // commit and rollback trans + thd->status_var.access_denied_errors, + 0, // lost connections + 0, // access denied errors + 0); // empty queries + + if (my_hash_insert(users_or_clients, (uchar*)user_stats)) + { + my_free(user_stats, 0); + return 1; // Out of memory + } + } + user_stats->total_connections++; + return 0; +} + + +/* + Increments the global user and client stats connection count. + + @param use_lock if true, LOCK_global_user_client_stats will be locked + + @retval 0 ok + @retval 1 error. +*/ + +static int increment_connection_count(THD* thd, bool use_lock) +{ + const char *user_string= get_valid_user_string(thd->main_security_ctx.user); + const char *client_string= get_client_host(thd); + int return_value= 0; + + if (!thd->userstat_running) + return 0; + + if (use_lock) + pthread_mutex_lock(&LOCK_global_user_client_stats); + + if (increment_count_by_name(user_string, strlen(user_string), user_string, + &global_user_stats, thd)) + { + return_value= 1; + goto end; + } + if (increment_count_by_name(client_string, strlen(client_string), + user_string, &global_client_stats, thd)) + { + return_value= 1; + goto end; + } + +end: + if (use_lock) + pthread_mutex_unlock(&LOCK_global_user_client_stats); + return return_value; +} + + +/* + Used to update the global user and client stats +*/ + +static void update_global_user_stats_with_user(THD *thd, + USER_STATS *user_stats, + time_t now) +{ + DBUG_ASSERT(thd->userstat_running); + + user_stats->connected_time+= now - thd->last_global_update_time; + user_stats->busy_time+= (thd->status_var.busy_time - + thd->org_status_var.busy_time); + user_stats->cpu_time+= (thd->status_var.cpu_time - + thd->org_status_var.cpu_time); + /* + This is handle specially as bytes_recieved is incremented BEFORE + org_status_var is copied. + */ + user_stats->bytes_received+= (thd->org_status_var.bytes_received- + thd->start_bytes_received); + user_stats->bytes_sent+= (thd->status_var.bytes_sent - + thd->org_status_var.bytes_sent); + user_stats->binlog_bytes_written+= + (thd->status_var.binlog_bytes_written - + thd->org_status_var.binlog_bytes_written); + user_stats->rows_read+= (thd->status_var.rows_read - + thd->org_status_var.rows_read); + user_stats->rows_sent+= (thd->status_var.rows_sent - + thd->org_status_var.rows_sent); + user_stats->rows_inserted+= (thd->status_var.ha_write_count - + thd->org_status_var.ha_write_count); + user_stats->rows_deleted+= (thd->status_var.ha_delete_count - + thd->org_status_var.ha_delete_count); + user_stats->rows_updated+= (thd->status_var.ha_update_count - + thd->org_status_var.ha_update_count); + user_stats->select_commands+= thd->select_commands; + user_stats->update_commands+= thd->update_commands; + user_stats->other_commands+= thd->other_commands; + user_stats->commit_trans+= (thd->status_var.ha_commit_count - + thd->org_status_var.ha_commit_count); + user_stats->rollback_trans+= (thd->status_var.ha_rollback_count + + thd->status_var.ha_savepoint_rollback_count - + thd->org_status_var.ha_rollback_count - + thd->org_status_var. + ha_savepoint_rollback_count); + user_stats->access_denied_errors+= + (thd->status_var.access_denied_errors - + thd->org_status_var.access_denied_errors); + user_stats->empty_queries+= (thd->status_var.empty_queries - + thd->org_status_var.empty_queries); + + /* The following can only contain 0 or 1 and then connection ends */ + user_stats->denied_connections+= thd->status_var.access_denied_errors; + user_stats->lost_connections+= thd->status_var.lost_connections; +} + + +/* Updates the global stats of a user or client */ +void update_global_user_stats(THD *thd, bool create_user, time_t now) +{ + const char *user_string, *client_string; + USER_STATS *user_stats; + size_t user_string_length, client_string_length; + + if (!thd->userstat_running) + return; + + user_string= get_valid_user_string(thd->main_security_ctx.user); + user_string_length= strlen(user_string); + client_string= get_client_host(thd); + client_string_length= strlen(client_string); + + pthread_mutex_lock(&LOCK_global_user_client_stats); + + // Update by user name + if ((user_stats= (USER_STATS*) hash_search(&global_user_stats, + (uchar*) user_string, + user_string_length))) + { + /* Found user. */ + update_global_user_stats_with_user(thd, user_stats, now); + } + else + { + /* Create the entry */ + if (create_user) + { + increment_count_by_name(user_string, user_string_length, user_string, + &global_user_stats, thd); + } + } + + /* Update by client IP */ + if ((user_stats= (USER_STATS*)hash_search(&global_client_stats, + (uchar*) client_string, + client_string_length))) + { + // Found by client IP + update_global_user_stats_with_user(thd, user_stats, now); + } + else + { + // Create the entry + if (create_user) + { + increment_count_by_name(client_string, client_string_length, + user_string, &global_client_stats, thd); + } + } + /* Reset variables only used for counting */ + thd->select_commands= thd->update_commands= thd->other_commands= 0; + thd->last_global_update_time= now; + + pthread_mutex_unlock(&LOCK_global_user_client_stats); +} + void thd_init_client_charset(THD *thd, uint cs_number) { @@ -970,6 +1428,14 @@ bool login_connection(THD *thd) /* Connect completed, set read/write timeouts back to default */ my_net_set_read_timeout(net, thd->variables.net_read_timeout); my_net_set_write_timeout(net, thd->variables.net_write_timeout); + + /* Updates global user connection stats. */ + if (increment_connection_count(thd, TRUE)) + { + net_send_error(thd, ER_OUTOFMEMORY); // Out of memory + DBUG_RETURN(1); + } + DBUG_RETURN(0); } @@ -991,6 +1457,7 @@ void end_connection(THD *thd) if (thd->killed || (net->error && net->vio != 0)) { statistic_increment(aborted_threads,&LOCK_status); + status_var_increment(thd->status_var.lost_connections); } if (net->error && net->vio != 0) @@ -1117,10 +1584,14 @@ pthread_handler_t handle_one_connection( for (;;) { NET *net= &thd->net; + bool create_user= TRUE; lex_start(thd); if (login_connection(thd)) + { + create_user= FALSE; goto end_thread; + } prepare_new_connection_state(thd); @@ -1134,12 +1605,13 @@ pthread_handler_t handle_one_connection( end_thread: close_connection(thd, 0, 1); + update_global_user_stats(thd, create_user, time(NULL)); + if (thd->scheduler->end_thread(thd,1)) return 0; // Probably no-threads /* - If end_thread() returns, we are either running with - thread-handler=no-threads or this thread has been schedule to + If end_thread() returns, this thread has been schedule to handle the next connection. */ thd= current_thd; === modified file 'sql/sql_cursor.cc' --- sql/sql_cursor.cc 2008-12-10 14:16:21 +0000 +++ sql/sql_cursor.cc 2009-10-17 08:13:46 +0000 @@ -655,7 +655,7 @@ void Materialized_cursor::fetch(ulong nu result->begin_dataset(); for (fetch_limit+= num_rows; fetch_count < fetch_limit; fetch_count++) { - if ((res= table->file->rnd_next(table->record[0]))) + if ((res= table->file->ha_rnd_next(table->record[0]))) break; /* Send data only if the read was successful. */ result->send_data(item_list); === modified file 'sql/sql_handler.cc' --- sql/sql_handler.cc 2009-07-15 23:23:57 +0000 +++ sql/sql_handler.cc 2009-10-17 08:09:24 +0000 @@ -565,8 +565,8 @@ retry: if (table->file->inited != handler::NONE) { error=keyname ? - table->file->index_next(table->record[0]) : - table->file->rnd_next(table->record[0]); + table->file->ha_index_next(table->record[0]) : + table->file->ha_rnd_next(table->record[0]); break; } /* else fall through */ @@ -575,13 +575,13 @@ retry: { table->file->ha_index_or_rnd_end(); table->file->ha_index_init(keyno, 1); - error= table->file->index_first(table->record[0]); + error= table->file->ha_index_first(table->record[0]); } else { table->file->ha_index_or_rnd_end(); if (!(error= table->file->ha_rnd_init(1))) - error= table->file->rnd_next(table->record[0]); + error= table->file->ha_rnd_next(table->record[0]); } mode=RNEXT; break; @@ -589,7 +589,7 @@ retry: DBUG_ASSERT(keyname != 0); if (table->file->inited != handler::NONE) { - error=table->file->index_prev(table->record[0]); + error=table->file->ha_index_prev(table->record[0]); break; } /* else fall through */ @@ -597,13 +597,13 @@ retry: DBUG_ASSERT(keyname != 0); table->file->ha_index_or_rnd_end(); table->file->ha_index_init(keyno, 1); - error= table->file->index_last(table->record[0]); + error= table->file->ha_index_last(table->record[0]); mode=RPREV; break; case RNEXT_SAME: /* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...) */ DBUG_ASSERT(keyname != 0); - error= table->file->index_next_same(table->record[0], key, key_len); + error= table->file->ha_index_next_same(table->record[0], key, key_len); break; case RKEY: { @@ -643,8 +643,8 @@ retry: table->file->ha_index_or_rnd_end(); table->file->ha_index_init(keyno, 1); key_copy(key, table->record[0], table->key_info + keyno, key_len); - error= table->file->index_read_map(table->record[0], - key, keypart_map, ha_rkey_mode); + error= table->file->ha_index_read_map(table->record[0], + key, keypart_map, ha_rkey_mode); mode=rkey_to_rnext[(int)ha_rkey_mode]; break; } === modified file 'sql/sql_help.cc' --- sql/sql_help.cc 2009-09-07 20:50:10 +0000 +++ sql/sql_help.cc 2009-10-17 08:09:23 +0000 @@ -294,13 +294,13 @@ int get_topics_for_keyword(THD *thd, TAB rkey_id->store((longlong) key_id, TRUE); rkey_id->get_key_image(buff, rkey_id->pack_length(), Field::itRAW); - int key_res= relations->file->index_read_map(relations->record[0], - buff, (key_part_map) 1, - HA_READ_KEY_EXACT); + int key_res= relations->file->ha_index_read_map(relations->record[0], + buff, (key_part_map) 1, + HA_READ_KEY_EXACT); for ( ; !key_res && key_id == (int16) rkey_id->val_int() ; - key_res= relations->file->index_next(relations->record[0])) + key_res= relations->file->ha_index_next(relations->record[0])) { uchar topic_id_buff[8]; longlong topic_id= rtopic_id->val_int(); @@ -308,8 +308,8 @@ int get_topics_for_keyword(THD *thd, TAB field->store((longlong) topic_id, TRUE); field->get_key_image(topic_id_buff, field->pack_length(), Field::itRAW); - if (!topics->file->index_read_map(topics->record[0], topic_id_buff, - (key_part_map)1, HA_READ_KEY_EXACT)) + if (!topics->file->ha_index_read_map(topics->record[0], topic_id_buff, + (key_part_map)1, HA_READ_KEY_EXACT)) { memorize_variant_topic(thd,topics,count,find_fields, names,name,description,example); === modified file 'sql/sql_insert.cc' --- sql/sql_insert.cc 2009-09-07 20:50:10 +0000 +++ sql/sql_insert.cc 2009-10-17 14:12:45 +0000 @@ -1425,7 +1425,7 @@ int write_record(THD *thd, TABLE *table, goto err; if (table->file->ha_table_flags() & HA_DUPLICATE_POS) { - if (table->file->rnd_pos(table->record[1],table->file->dup_ref)) + if (table->file->ha_rnd_pos(table->record[1],table->file->dup_ref)) goto err; } else @@ -1446,9 +1446,10 @@ int write_record(THD *thd, TABLE *table, } } key_copy((uchar*) key,table->record[0],table->key_info+key_nr,0); - if ((error=(table->file->index_read_idx_map(table->record[1],key_nr, - (uchar*) key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)))) + if ((error=(table->file->ha_index_read_idx_map(table->record[1],key_nr, + (uchar*) key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)))) goto err; } if (info->handle_duplicates == DUP_UPDATE) === modified file 'sql/sql_lex.h' --- sql/sql_lex.h 2009-09-15 10:46:35 +0000 +++ sql/sql_lex.h 2009-10-16 10:20:13 +0000 @@ -118,6 +118,8 @@ enum enum_sql_command { SQLCOM_SHOW_CREATE_TRIGGER, SQLCOM_ALTER_DB_UPGRADE, SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES, + SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS, + SQLCOM_SHOW_CLIENT_STATS, /* When a command is added here, be sure it's also added in mysqld.cc === modified file 'sql/sql_parse.cc' --- sql/sql_parse.cc 2009-09-15 10:46:35 +0000 +++ sql/sql_parse.cc 2009-10-18 17:19:36 +0000 @@ -549,7 +549,6 @@ end: DBUG_RETURN(0); } - /** @brief Check access privs for a MERGE table and fix children lock types. @@ -801,6 +800,8 @@ bool do_command(THD *thd) net_new_transaction(net); + /* Save for user statistics */ + thd->start_bytes_received= thd->status_var.bytes_received; packet_length= my_net_read(net); #if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) thd->profiling.start_new_query(); @@ -1324,7 +1325,7 @@ bool dispatch_command(enum enum_server_c table_list.select_lex= &(thd->lex->select_lex); lex_start(thd); - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, opt_userstat_running); thd->lex-> select_lex.table_list.link_in_list((uchar*) &table_list, @@ -1609,6 +1610,9 @@ bool dispatch_command(enum enum_server_c /* Free tables */ close_thread_tables(thd); + /* Update status; Must be done after close_thread_tables */ + thd->update_all_stats(); + log_slow_statement(thd); thd_proc_info(thd, "cleaning up"); @@ -1777,6 +1781,11 @@ int prepare_schema_table(THD *thd, LEX * thd->profiling.discard_current_query(); #endif break; + case SCH_USER_STATS: + case SCH_CLIENT_STATS: + return check_global_access(thd, SUPER_ACL | PROCESS_ACL); + case SCH_TABLE_STATS: + case SCH_INDEX_STATS: case SCH_OPEN_TABLES: case SCH_VARIABLES: case SCH_STATUS: @@ -5059,6 +5068,10 @@ static bool execute_sqlcom_select(THD *t delete result; } } + /* Count number of empty select queries */ + if (!thd->sent_row_count) + status_var_increment(thd->status_var.empty_queries); + status_var_add(thd->status_var.rows_sent, thd->sent_row_count); return res; } @@ -5220,6 +5233,7 @@ check_access(THD *thd, ulong want_access if (!no_errors) { const char *db_name= db ? db : thd->db; + status_var_increment(thd->status_var.access_denied_errors); my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, db_name); } @@ -5252,12 +5266,15 @@ check_access(THD *thd, ulong want_access { // We can never grant this DBUG_PRINT("error",("No possible access")); if (!no_errors) + { + status_var_increment(thd->status_var.access_denied_errors); my_error(ER_ACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, (thd->password ? ER(ER_YES) : ER(ER_NO))); /* purecov: tested */ + } DBUG_RETURN(TRUE); /* purecov: tested */ } @@ -5283,11 +5300,14 @@ check_access(THD *thd, ulong want_access DBUG_PRINT("error",("Access denied")); if (!no_errors) + { + status_var_increment(thd->status_var.access_denied_errors); my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, (db ? db : (thd->db ? thd->db : "unknown"))); /* purecov: tested */ + } DBUG_RETURN(TRUE); /* purecov: tested */ } @@ -5316,6 +5336,7 @@ static bool check_show_access(THD *thd, if (!thd->col_access && check_grant_db(thd, dst_db_name)) { + status_var_increment(thd->status_var.access_denied_errors); my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), thd->security_ctx->priv_user, thd->security_ctx->priv_host, @@ -5378,14 +5399,14 @@ check_table_access(THD *thd, ulong want_ { TABLE_LIST *org_tables= tables; TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); - uint i= 0; Security_context *sctx= thd->security_ctx, *backup_ctx= thd->security_ctx; + uint i; /* The check that first_not_own_table is not reached is for the case when the given table list refers to the list for prelocking (contains tables of other queries). For simple queries first_not_own_table is 0. */ - for (; i < number && tables != first_not_own_table; + for (i=0; i < number && tables != first_not_own_table; tables= tables->next_global, i++) { if (tables->security_ctx) @@ -5397,9 +5418,12 @@ check_table_access(THD *thd, ulong want_ (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL))) { if (!no_errors) + { + status_var_increment(thd->status_var.access_denied_errors); my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, INFORMATION_SCHEMA_NAME.str); + } return TRUE; } /* @@ -5563,6 +5587,7 @@ bool check_global_access(THD *thd, ulong return 0; get_privilege_desc(command, sizeof(command), want_access); my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command); + status_var_increment(thd->status_var.access_denied_errors); return 1; #else return 0; @@ -5666,7 +5691,7 @@ bool my_yyoverflow(short **yyss, YYSTYPE Call it after we use THD for queries, not before. */ -void mysql_reset_thd_for_next_command(THD *thd) +void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat) { DBUG_ENTER("mysql_reset_thd_for_next_command"); DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */ @@ -5711,6 +5736,16 @@ void mysql_reset_thd_for_next_command(TH thd->total_warn_count=0; // Warnings for this query thd->rand_used= 0; thd->sent_row_count= thd->examined_row_count= 0; + thd->updated_row_count=0; + + /* Copy data for user stats */ + if ((thd->userstat_running= calculate_userstat)) + { + thd->start_cpu_time= my_getrealtime(); + memcpy(&thd->org_status_var, &thd->status_var, sizeof(thd->status_var)); + thd->select_commands= thd->update_commands= thd->other_commands= 0; + } + thd->query_plan_flags= QPLAN_INIT; thd->query_plan_fsort_passes= 0; @@ -5909,7 +5944,6 @@ void mysql_parse(THD *thd, const char *i const char ** found_semicolon) { DBUG_ENTER("mysql_parse"); - DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on();); /* @@ -5929,7 +5963,7 @@ void mysql_parse(THD *thd, const char *i FIXME: cleanup the dependencies in the code to simplify this. */ lex_start(thd); - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, opt_userstat_running); if (query_cache_send_result_to_client(thd, (char*) inBuf, length) <= 0) { @@ -6001,10 +6035,11 @@ void mysql_parse(THD *thd, const char *i } else { + /* Update statistics for getting the query from the cache */ + thd->lex->sql_command= SQLCOM_SELECT; /* There are no multi queries in the cache. */ *found_semicolon= NULL; } - DBUG_VOID_RETURN; } @@ -6028,7 +6063,7 @@ bool mysql_test_parse_for_slave(THD *thd Parser_state parser_state(thd, inBuf, length); lex_start(thd); - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, 0); if (!parse_sql(thd, & parser_state, NULL) && all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first)) @@ -6867,6 +6902,12 @@ bool reload_acl_and_cache(THD *thd, ulon if (flush_error_log()) result=1; } + if (options & REFRESH_SLOW_QUERY_LOG) + { + /* We are only flushing slow query log */ + logger.flush_slow_log(thd); + } + #ifdef HAVE_QUERY_CACHE if (options & REFRESH_QUERY_CACHE_FREE) { @@ -6949,26 +6990,55 @@ bool reload_acl_and_cache(THD *thd, ulon } #endif #ifdef OPENSSL - if (options & REFRESH_DES_KEY_FILE) - { - if (des_key_file && load_des_key_file(des_key_file)) - result= 1; - } + if (options & REFRESH_DES_KEY_FILE) + { + if (des_key_file && load_des_key_file(des_key_file)) + result= 1; + } #endif #ifdef HAVE_REPLICATION - if (options & REFRESH_SLAVE) - { - tmp_write_to_binlog= 0; - pthread_mutex_lock(&LOCK_active_mi); - if (reset_slave(thd, active_mi)) - result=1; - pthread_mutex_unlock(&LOCK_active_mi); - } -#endif - if (options & REFRESH_USER_RESOURCES) - reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */ - *write_to_binlog= tmp_write_to_binlog; - return result; + if (options & REFRESH_SLAVE) + { + tmp_write_to_binlog= 0; + pthread_mutex_lock(&LOCK_active_mi); + if (reset_slave(thd, active_mi)) + result=1; + pthread_mutex_unlock(&LOCK_active_mi); + } +#endif + if (options & REFRESH_USER_RESOURCES) + reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */ + if (options & REFRESH_TABLE_STATS) + { + pthread_mutex_lock(&LOCK_global_table_stats); + free_global_table_stats(); + init_global_table_stats(); + pthread_mutex_unlock(&LOCK_global_table_stats); + } + if (options & REFRESH_INDEX_STATS) + { + pthread_mutex_lock(&LOCK_global_index_stats); + free_global_index_stats(); + init_global_index_stats(); + pthread_mutex_unlock(&LOCK_global_index_stats); + } + if (options & (REFRESH_USER_STATS | REFRESH_CLIENT_STATS)) + { + pthread_mutex_lock(&LOCK_global_user_client_stats); + if (options & REFRESH_USER_STATS) + { + free_global_user_stats(); + init_global_user_stats(); + } + if (options & REFRESH_CLIENT_STATS) + { + free_global_client_stats(); + init_global_client_stats(); + } + pthread_mutex_unlock(&LOCK_global_user_client_stats); + } + *write_to_binlog= tmp_write_to_binlog; + return result; } @@ -7004,7 +7074,6 @@ uint kill_one_thread(THD *thd, ulong id, VOID(pthread_mutex_unlock(&LOCK_thread_count)); if (tmp) { - /* If we're SUPER, we can KILL anything, including system-threads. No further checks. === modified file 'sql/sql_plugin.cc' --- sql/sql_plugin.cc 2009-10-01 21:27:39 +0000 +++ sql/sql_plugin.cc 2009-10-17 08:09:23 +0000 @@ -1790,10 +1790,10 @@ bool mysql_uninstall_plugin(THD *thd, co table->use_all_columns(); table->field[0]->store(name->str, name->length, system_charset_info); - if (! table->file->index_read_idx_map(table->record[0], 0, - (uchar *)table->field[0]->ptr, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (! table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar *)table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { int error; /* === modified file 'sql/sql_prepare.cc' --- sql/sql_prepare.cc 2009-09-07 20:50:10 +0000 +++ sql/sql_prepare.cc 2009-10-18 16:38:12 +0000 @@ -2067,14 +2067,13 @@ void mysqld_stmt_prepare(THD *thd, const Prepared_statement *stmt; bool error; DBUG_ENTER("mysqld_stmt_prepare"); - DBUG_PRINT("prep_query", ("%s", packet)); /* First of all clear possible warnings from the previous command */ - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, opt_userstat_running); if (! (stmt= new Prepared_statement(thd))) - DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */ + goto end; /* out of memory: error is set in Sql_alloc */ if (thd->stmt_map.insert(thd, stmt)) { @@ -2082,7 +2081,7 @@ void mysqld_stmt_prepare(THD *thd, const The error is set in the insert. The statement itself will be also deleted there (this is how the hash works). */ - DBUG_VOID_RETURN; + goto end; } /* Reset warnings from previous command */ @@ -2109,6 +2108,7 @@ void mysqld_stmt_prepare(THD *thd, const thd->protocol= save_protocol; /* check_prepared_statemnt sends the metadata packet in case of success */ +end: DBUG_VOID_RETURN; } @@ -2450,7 +2450,7 @@ void mysqld_stmt_execute(THD *thd, char packet+= 9; /* stmt_id + 5 bytes of flags */ /* First of all clear possible warnings from the previous command */ - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, opt_userstat_running); if (!(stmt= find_prepared_statement(thd, stmt_id))) { @@ -2549,7 +2549,8 @@ void mysqld_stmt_fetch(THD *thd, char *p DBUG_ENTER("mysqld_stmt_fetch"); /* First of all clear possible warnings from the previous command */ - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, opt_userstat_running); + status_var_increment(thd->status_var.com_stmt_fetch); if (!(stmt= find_prepared_statement(thd, stmt_id))) { @@ -2615,7 +2616,7 @@ void mysqld_stmt_reset(THD *thd, char *p DBUG_ENTER("mysqld_stmt_reset"); /* First of all clear possible warnings from the previous command */ - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, opt_userstat_running); status_var_increment(thd->status_var.com_stmt_reset); if (!(stmt= find_prepared_statement(thd, stmt_id))) === modified file 'sql/sql_select.cc' --- sql/sql_select.cc 2009-09-15 10:46:35 +0000 +++ sql/sql_select.cc 2009-10-17 14:09:17 +0000 @@ -10949,7 +10949,7 @@ create_internal_tmp_table_from_heap2(THD is safe as this is a temporary MyISAM table without timestamp/autoincrement or partitioning. */ - while (!table->file->rnd_next(new_table.record[1])) + while (!table->file->ha_rnd_next(new_table.record[1])) { write_err= new_table.file->ha_write_row(new_table.record[1]); DBUG_EXECUTE_IF("raise_error", write_err= HA_ERR_FOUND_DUPP_KEY ;); @@ -11746,10 +11746,10 @@ int safe_index_read(JOIN_TAB *tab) { int error; TABLE *table= tab->table; - if ((error=table->file->index_read_map(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT))) + if ((error=table->file->ha_index_read_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts), + HA_READ_KEY_EXACT))) return report_error(table, error); return 0; } @@ -11858,8 +11858,8 @@ join_read_system(JOIN_TAB *tab) int error; if (table->status & STATUS_GARBAGE) // If first read { - if ((error=table->file->read_first_row(table->record[0], - table->s->primary_key))) + if ((error=table->file->ha_read_first_row(table->record[0], + table->s->primary_key))) { if (error != HA_ERR_END_OF_FILE) return report_error(table, error); @@ -11901,10 +11901,10 @@ join_read_const(JOIN_TAB *tab) error=HA_ERR_KEY_NOT_FOUND; else { - error=table->file->index_read_idx_map(table->record[0],tab->ref.key, - (uchar*) tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT); + error=table->file->ha_index_read_idx_map(table->record[0],tab->ref.key, + (uchar*) tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts), + HA_READ_KEY_EXACT); } if (error) { @@ -11949,10 +11949,10 @@ join_read_key(JOIN_TAB *tab) table->status=STATUS_NOT_FOUND; return -1; } - error=table->file->index_read_map(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT); + error=table->file->ha_index_read_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts), + HA_READ_KEY_EXACT); if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) return report_error(table, error); } @@ -12005,10 +12005,10 @@ join_read_always_key(JOIN_TAB *tab) if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref)) return -1; - if ((error=table->file->index_read_map(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT))) + if ((error=table->file->ha_index_read_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts), + HA_READ_KEY_EXACT))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) return report_error(table, error); @@ -12039,9 +12039,9 @@ join_read_last_key(JOIN_TAB *tab) } if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref)) return -1; - if ((error=table->file->index_read_last_map(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts)))) + if ((error= table->file->ha_index_read_last_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts)))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) return report_error(table, error); @@ -12066,9 +12066,9 @@ join_read_next_same(READ_RECORD *info) TABLE *table= info->table; JOIN_TAB *tab=table->reginfo.join_tab; - if ((error=table->file->index_next_same(table->record[0], - tab->ref.key_buff, - tab->ref.key_length))) + if ((error= table->file->ha_index_next_same(table->record[0], + tab->ref.key_buff, + tab->ref.key_length))) { if (error != HA_ERR_END_OF_FILE) return report_error(table, error); @@ -12086,7 +12086,7 @@ join_read_prev_same(READ_RECORD *info) TABLE *table= info->table; JOIN_TAB *tab=table->reginfo.join_tab; - if ((error=table->file->index_prev(table->record[0]))) + if ((error= table->file->ha_index_prev(table->record[0]))) return report_error(table, error); if (key_cmp_if_same(table, tab->ref.key_buff, tab->ref.key, tab->ref.key_length)) @@ -12158,7 +12158,7 @@ join_read_first(JOIN_TAB *tab) error= table->file->ha_index_init(tab->index, tab->sorted); if (!error) error= table->file->prepare_index_scan(); - if (error || (error=tab->table->file->index_first(tab->table->record[0]))) + if (error || (error=tab->table->file->ha_index_first(tab->table->record[0]))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) report_error(table, error); @@ -12172,7 +12172,7 @@ static int join_read_next(READ_RECORD *info) { int error; - if ((error=info->file->index_next(info->record))) + if ((error= info->file->ha_index_next(info->record))) return report_error(info->table, error); return 0; } @@ -12199,7 +12199,7 @@ join_read_last(JOIN_TAB *tab) error= table->file->ha_index_init(tab->index, 1); if (!error) error= table->file->prepare_index_scan(); - if (error || (error= tab->table->file->index_last(tab->table->record[0]))) + if (error || (error= tab->table->file->ha_index_last(tab->table->record[0]))) return report_error(table, error); return 0; } @@ -12209,7 +12209,7 @@ static int join_read_prev(READ_RECORD *info) { int error; - if ((error= info->file->index_prev(info->record))) + if ((error= info->file->ha_index_prev(info->record))) return report_error(info->table, error); return 0; } @@ -12234,7 +12234,7 @@ join_ft_read_first(JOIN_TAB *tab) #endif table->file->ft_init(); - if ((error= table->file->ft_read(table->record[0]))) + if ((error= table->file->ha_ft_read(table->record[0]))) return report_error(table, error); return 0; } @@ -12243,7 +12243,7 @@ static int join_ft_read_next(READ_RECORD *info) { int error; - if ((error= info->file->ft_read(info->table->record[0]))) + if ((error= info->file->ha_ft_read(info->table->record[0]))) return report_error(info->table, error); return 0; } @@ -12590,10 +12590,10 @@ end_update(JOIN *join, JOIN_TAB *join_ta if (item->maybe_null) group->buff[-1]= (char) group->field->is_null(); } - if (!table->file->index_read_map(table->record[1], - join->tmp_table_param.group_buff, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (!table->file->ha_index_read_map(table->record[1], + join->tmp_table_param.group_buff, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { /* Update old record */ restore_record(table,record[1]); update_tmptable_sum_func(join->sum_funcs,table); @@ -12671,7 +12671,7 @@ end_unique_update(JOIN *join, JOIN_TAB * table->file->print_error(error,MYF(0)); /* purecov: inspected */ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ } - if (table->file->rnd_pos(table->record[1],table->file->dup_ref)) + if (table->file->ha_rnd_pos(table->record[1],table->file->dup_ref)) { table->file->print_error(error,MYF(0)); /* purecov: inspected */ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ @@ -14016,7 +14016,7 @@ static int remove_dup_with_compare(THD * new_record=(char*) table->record[1]+offset; file->ha_rnd_init(1); - error=file->rnd_next(record); + error=file->ha_rnd_next(record); for (;;) { if (thd->killed) @@ -14037,7 +14037,7 @@ static int remove_dup_with_compare(THD * { if ((error=file->ha_delete_row(record))) goto err; - error=file->rnd_next(record); + error=file->ha_rnd_next(record); continue; } if (copy_blobs(first_field)) @@ -14052,7 +14052,7 @@ static int remove_dup_with_compare(THD * bool found=0; for (;;) { - if ((error=file->rnd_next(record))) + if ((error=file->ha_rnd_next(record))) { if (error == HA_ERR_RECORD_DELETED) continue; @@ -14152,7 +14152,7 @@ static int remove_dup_with_hash_index(TH error=0; goto err; } - if ((error=file->rnd_next(record))) + if ((error= file->ha_rnd_next(record))) { if (error == HA_ERR_RECORD_DELETED) continue; === modified file 'sql/sql_servers.cc' --- sql/sql_servers.cc 2009-03-20 14:27:53 +0000 +++ sql/sql_servers.cc 2009-10-17 08:09:23 +0000 @@ -520,10 +520,10 @@ int insert_server_record(TABLE *table, F system_charset_info); /* read index until record is that specified in server_name */ - if ((error= table->file->index_read_idx_map(table->record[0], 0, - (uchar *)table->field[0]->ptr, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT))) + if ((error= table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar *)table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT))) { /* if not found, err */ if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) @@ -863,10 +863,10 @@ update_server_record(TABLE *table, FOREI server->server_name_length, system_charset_info); - if ((error= table->file->index_read_idx_map(table->record[0], 0, - (uchar *)table->field[0]->ptr, - ~(longlong)0, - HA_READ_KEY_EXACT))) + if ((error= table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar *)table->field[0]->ptr, + ~(longlong)0, + HA_READ_KEY_EXACT))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) table->file->print_error(error, MYF(0)); @@ -920,10 +920,10 @@ delete_server_record(TABLE *table, /* set the field that's the PK to the value we're looking for */ table->field[0]->store(server_name, server_name_length, system_charset_info); - if ((error= table->file->index_read_idx_map(table->record[0], 0, - (uchar *)table->field[0]->ptr, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT))) + if ((error= table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar *)table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) table->file->print_error(error, MYF(0)); === modified file 'sql/sql_show.cc' --- sql/sql_show.cc 2009-09-23 11:03:47 +0000 +++ sql/sql_show.cc 2009-10-18 19:28:52 +0000 @@ -714,6 +714,7 @@ bool mysqld_show_create_db(THD *thd, cha sctx->master_access); if (!(db_access & DB_ACLS) && check_grant_db(thd,dbname)) { + status_var_increment(thd->status_var.access_denied_errors); my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->host_or_ip, dbname); general_log_print(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR), @@ -2100,11 +2101,6 @@ void remove_status_vars(SHOW_VAR *list) } } -inline void make_upper(char *buf) -{ - for (; *buf; buf++) - *buf= my_toupper(system_charset_info, *buf); -} static bool show_status_array(THD *thd, const char *wild, SHOW_VAR *variables, @@ -2143,7 +2139,7 @@ static bool show_status_array(THD *thd, strnmov(prefix_end, variables->name, len); name_buffer[sizeof(name_buffer)-1]=0; /* Safety */ if (ucase_names) - make_upper(name_buffer); + my_caseup_str(system_charset_info, name_buffer); restore_record(table, s->default_values); table->field[0]->store(name_buffer, strlen(name_buffer), @@ -2270,6 +2266,323 @@ end: DBUG_RETURN(res); } +#ifdef COMPLEAT_PATCH_NOT_ADDED_YET +/* + Aggregate values for mapped_user entries by their role. + + SYNOPSIS + aggregate_user_stats + all_user_stats - input to aggregate + agg_user_stats - returns aggregated values + + RETURN + 0 - OK + 1 - error +*/ + +static int aggregate_user_stats(HASH *all_user_stats, HASH *agg_user_stats) +{ + DBUG_ENTER("aggregate_user_stats"); + if (hash_init(agg_user_stats, system_charset_info, + max(all_user_stats->records, 1), + 0, 0, (hash_get_key)get_key_user_stats, + (hash_free_key)free_user_stats, 0)) + { + sql_print_error("Malloc in aggregate_user_stats failed"); + DBUG_RETURN(1); + } + + for (uint i= 0; i < all_user_stats->records; i++) + { + USER_STATS *user= (USER_STATS*)hash_element(all_user_stats, i); + USER_STATS *agg_user; + uint name_length= strlen(user->priv_user); + + if (!(agg_user= (USER_STATS*) hash_search(agg_user_stats, + (uchar*)user->priv_user, + name_length))) + { + // First entry for this role. + if (!(agg_user= (USER_STATS*) my_malloc(sizeof(USER_STATS), + MYF(MY_WME | MY_ZEROFILL)))) + { + sql_print_error("Malloc in aggregate_user_stats failed"); + DBUG_RETURN(1); + } + + init_user_stats(agg_user, user->priv_user, name_length, + user->priv_user, + user->total_connections, user->concurrent_connections, + user->connected_time, user->busy_time, user->cpu_time, + user->bytes_received, user->bytes_sent, + user->binlog_bytes_written, + user->rows_sent, user->rows_read, + user->rows_inserted, user->rows_deleted, + user->rows_updated, + user->select_commands, user->update_commands, + user->other_commands, + user->commit_trans, user->rollback_trans, + user->denied_connections, user->lost_connections, + user->access_denied_errors, user->empty_queries); + + if (my_hash_insert(agg_user_stats, (uchar*) agg_user)) + { + /* Out of memory */ + my_free(agg_user, 0); + sql_print_error("Malloc in aggregate_user_stats failed"); + DBUG_RETURN(1); + } + } + else + { + /* Aggregate with existing values for this role. */ + add_user_stats(agg_user, + user->total_connections, user->concurrent_connections, + user->connected_time, user->busy_time, user->cpu_time, + user->bytes_received, user->bytes_sent, + user->binlog_bytes_written, + user->rows_sent, user->rows_read, + user->rows_inserted, user->rows_deleted, + user->rows_updated, + user->select_commands, user->update_commands, + user->other_commands, + user->commit_trans, user->rollback_trans, + user->denied_connections, user->lost_connections, + user->access_denied_errors, user->empty_queries); + } + } + DBUG_PRINT("exit", ("aggregated %lu input into %lu output entries", + all_user_stats->records, agg_user_stats->records)); + DBUG_RETURN(0); +} +#endif + +/* + Write result to network for SHOW USER_STATISTICS + + SYNOPSIS + send_user_stats + all_user_stats - values to return + table - I_S table + + RETURN + 0 - OK + 1 - error +*/ + +int send_user_stats(THD* thd, HASH *all_user_stats, TABLE *table) +{ + DBUG_ENTER("send_user_stats"); + + for (uint i= 0; i < all_user_stats->records; i++) + { + uint j= 0; + USER_STATS *user_stats= (USER_STATS*) hash_element(all_user_stats, i); + + table->field[j++]->store(user_stats->user, user_stats->user_name_length, + system_charset_info); + table->field[j++]->store((longlong)user_stats->total_connections,TRUE); + table->field[j++]->store((longlong)user_stats->concurrent_connections); + table->field[j++]->store((longlong)user_stats->connected_time); + table->field[j++]->store((double)user_stats->busy_time); + table->field[j++]->store((double)user_stats->cpu_time); + table->field[j++]->store((longlong)user_stats->bytes_received, TRUE); + table->field[j++]->store((longlong)user_stats->bytes_sent, TRUE); + table->field[j++]->store((longlong)user_stats->binlog_bytes_written, TRUE); + table->field[j++]->store((longlong)user_stats->rows_read, TRUE); + table->field[j++]->store((longlong)user_stats->rows_sent, TRUE); + table->field[j++]->store((longlong)user_stats->rows_deleted, TRUE); + table->field[j++]->store((longlong)user_stats->rows_inserted, TRUE); + table->field[j++]->store((longlong)user_stats->rows_updated, TRUE); + table->field[j++]->store((longlong)user_stats->select_commands, TRUE); + table->field[j++]->store((longlong)user_stats->update_commands, TRUE); + table->field[j++]->store((longlong)user_stats->other_commands, TRUE); + table->field[j++]->store((longlong)user_stats->commit_trans, TRUE); + table->field[j++]->store((longlong)user_stats->rollback_trans, TRUE); + table->field[j++]->store((longlong)user_stats->denied_connections, TRUE); + table->field[j++]->store((longlong)user_stats->lost_connections, TRUE); + table->field[j++]->store((longlong)user_stats->access_denied_errors, TRUE); + table->field[j++]->store((longlong)user_stats->empty_queries, TRUE); + if (schema_table_store_record(thd, table)) + { + DBUG_PRINT("error", ("store record error")); + DBUG_RETURN(1); + } + } + DBUG_RETURN(0); +} + +/* + Process SHOW USER_STATISTICS + + SYNOPSIS + mysqld_show_user_stats + thd - current thread + wild - limit results to the entry for this user + with_roles - when true, display role for mapped users + + RETURN + 0 - OK + 1 - error +*/ + +int fill_schema_user_stats(THD* thd, TABLE_LIST* tables, COND* cond) +{ + TABLE *table= tables->table; + int result; + DBUG_ENTER("fill_schema_user_stats"); + + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL)) + DBUG_RETURN(1); + + /* + Iterates through all the global stats and sends them to the client. + Pattern matching on the client IP is supported. + */ + + pthread_mutex_lock(&LOCK_global_user_client_stats); + result= send_user_stats(thd, &global_user_stats, table) != 0; + pthread_mutex_unlock(&LOCK_global_user_client_stats); + + DBUG_PRINT("exit", ("result: %d", result)); + DBUG_RETURN(result); +} + +/* + Process SHOW CLIENT_STATISTICS + + SYNOPSIS + mysqld_show_client_stats + thd - current thread + wild - limit results to the entry for this client + + RETURN + 0 - OK + 1 - error +*/ + +int fill_schema_client_stats(THD* thd, TABLE_LIST* tables, COND* cond) +{ + TABLE *table= tables->table; + int result; + DBUG_ENTER("fill_schema_client_stats"); + + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL)) + DBUG_RETURN(1); + + /* + Iterates through all the global stats and sends them to the client. + Pattern matching on the client IP is supported. + */ + + pthread_mutex_lock(&LOCK_global_user_client_stats); + result= send_user_stats(thd, &global_client_stats, table) != 0; + pthread_mutex_unlock(&LOCK_global_user_client_stats); + + DBUG_PRINT("exit", ("result: %d", result)); + DBUG_RETURN(result); +} + + +/* Sends the global table stats back to the client. */ + +int fill_schema_table_stats(THD *thd, TABLE_LIST *tables, COND *cond) +{ + TABLE *table= tables->table; + DBUG_ENTER("fill_schema_table_stats"); + + pthread_mutex_lock(&LOCK_global_table_stats); + for (uint i= 0; i < global_table_stats.records; i++) + { + char *end_of_schema; + TABLE_STATS *table_stats= + (TABLE_STATS*)hash_element(&global_table_stats, i); + TABLE_LIST tmp_table; + size_t schema_length, table_name_length; + + end_of_schema= strend(table_stats->table); + schema_length= (size_t) (end_of_schema - table_stats->table); + table_name_length= strlen(table_stats->table + schema_length + 1); + + bzero((char*) &tmp_table,sizeof(tmp_table)); + tmp_table.db= table_stats->table; + tmp_table.table_name= end_of_schema+1; + tmp_table.grant.privilege= 0; + if (check_access(thd, SELECT_ACL | EXTRA_ACL, tmp_table.db, + &tmp_table.grant.privilege, 0, 0, + is_schema_db(tmp_table.db)) || + check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, + 1)) + continue; + + table->field[0]->store(table_stats->table, schema_length, + system_charset_info); + table->field[1]->store(table_stats->table + schema_length+1, + table_name_length, system_charset_info); + table->field[2]->store((longlong)table_stats->rows_read, TRUE); + table->field[3]->store((longlong)table_stats->rows_changed, TRUE); + table->field[4]->store((longlong)table_stats->rows_changed_x_indexes, + TRUE); + if (schema_table_store_record(thd, table)) + { + VOID(pthread_mutex_unlock(&LOCK_global_table_stats)); + DBUG_RETURN(1); + } + } + pthread_mutex_unlock(&LOCK_global_table_stats); + DBUG_RETURN(0); +} + + +/* Sends the global index stats back to the client */ + +int fill_schema_index_stats(THD *thd, TABLE_LIST *tables, COND *cond) +{ + TABLE *table= tables->table; + DBUG_ENTER("fill_schema_index_stats"); + + pthread_mutex_lock(&LOCK_global_index_stats); + for (uint i= 0; i < global_index_stats.records; i++) + { + INDEX_STATS *index_stats = + (INDEX_STATS*) hash_element(&global_index_stats, i); + TABLE_LIST tmp_table; + char *index_name; + size_t schema_name_length, table_name_length, index_name_length; + + bzero((char*) &tmp_table,sizeof(tmp_table)); + tmp_table.db= index_stats->index; + tmp_table.table_name= strend(index_stats->index)+1; + tmp_table.grant.privilege= 0; + if (check_access(thd, SELECT_ACL | EXTRA_ACL, tmp_table.db, + &tmp_table.grant.privilege, 0, 0, + is_schema_db(tmp_table.db)) || + check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, 1)) + continue; + + index_name= strend(tmp_table.table_name)+1; + schema_name_length= (tmp_table.table_name - index_stats->index) -1; + table_name_length= (index_name - tmp_table.table_name)-1; + index_name_length= (index_stats->index_name_length - schema_name_length - + table_name_length - 3); + + table->field[0]->store(tmp_table.db, schema_name_length, + system_charset_info); + table->field[1]->store(tmp_table.table_name, table_name_length, + system_charset_info); + table->field[2]->store(index_name, index_name_length, system_charset_info); + table->field[3]->store((longlong)index_stats->rows_read, TRUE); + + if (schema_table_store_record(thd, table)) + { + VOID(pthread_mutex_unlock(&LOCK_global_index_stats)); + DBUG_RETURN(1); + } + } + pthread_mutex_unlock(&LOCK_global_index_stats); + DBUG_RETURN(0); +} + /* collect status for all running threads */ @@ -4206,7 +4519,7 @@ int fill_schema_proc(THD *thd, TABLE_LIS DBUG_RETURN(1); } proc_table->file->ha_index_init(0, 1); - if ((res= proc_table->file->index_first(proc_table->record[0]))) + if ((res= proc_table->file->ha_index_first(proc_table->record[0]))) { res= (res == HA_ERR_END_OF_FILE) ? 0 : 1; goto err; @@ -4216,7 +4529,7 @@ int fill_schema_proc(THD *thd, TABLE_LIS res= 1; goto err; } - while (!proc_table->file->index_next(proc_table->record[0])) + while (!proc_table->file->ha_index_next(proc_table->record[0])) { if (store_schema_proc(thd, table, proc_table, wild, full_access, definer)) { @@ -5462,6 +5775,81 @@ struct schema_table_ref ST_SCHEMA_TABLE *schema_table; }; +ST_FIELD_INFO user_stats_fields_info[]= +{ + {"USER", USERNAME_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User", SKIP_OPEN_TABLE}, + {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections",SKIP_OPEN_TABLE}, + {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections",SKIP_OPEN_TABLE}, + {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time",SKIP_OPEN_TABLE}, + {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_DOUBLE, 0, 0, "Busy_time",SKIP_OPEN_TABLE}, + {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_DOUBLE, 0, 0, "Cpu_time",SKIP_OPEN_TABLE}, + {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_received",SKIP_OPEN_TABLE}, + {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_sent",SKIP_OPEN_TABLE}, + {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Binlog_bytes_written",SKIP_OPEN_TABLE}, + {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read",SKIP_OPEN_TABLE}, + {"ROWS_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_sent",SKIP_OPEN_TABLE}, + {"ROWS_DELETED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_deleted",SKIP_OPEN_TABLE}, + {"ROWS_INSERTED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_inserted",SKIP_OPEN_TABLE}, + {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated",SKIP_OPEN_TABLE}, + {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Select_commands",SKIP_OPEN_TABLE}, + {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Update_commands",SKIP_OPEN_TABLE}, + {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Other_commands",SKIP_OPEN_TABLE}, + {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Commit_transactions",SKIP_OPEN_TABLE}, + {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rollback_transactions",SKIP_OPEN_TABLE}, + {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Denied_connections",SKIP_OPEN_TABLE}, + {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Lost_connections",SKIP_OPEN_TABLE}, + {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Access_denied",SKIP_OPEN_TABLE}, + {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Empty_queries",SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0} +}; + +ST_FIELD_INFO client_stats_fields_info[]= +{ + {"CLIENT", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Client",SKIP_OPEN_TABLE}, + {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections",SKIP_OPEN_TABLE}, + {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections",SKIP_OPEN_TABLE}, + {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time",SKIP_OPEN_TABLE}, + {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_DOUBLE, 0, 0, "Busy_time",SKIP_OPEN_TABLE}, + {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_DOUBLE, 0, 0, "Cpu_time",SKIP_OPEN_TABLE}, + {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_received",SKIP_OPEN_TABLE}, + {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_sent",SKIP_OPEN_TABLE}, + {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Binlog_bytes_written",SKIP_OPEN_TABLE}, + {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read",SKIP_OPEN_TABLE}, + {"ROWS_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_sent",SKIP_OPEN_TABLE}, + {"ROWS_DELETED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_deleted",SKIP_OPEN_TABLE}, + {"ROWS_INSERTED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_inserted",SKIP_OPEN_TABLE}, + {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated",SKIP_OPEN_TABLE}, + {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Select_commands",SKIP_OPEN_TABLE}, + {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Update_commands",SKIP_OPEN_TABLE}, + {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Other_commands",SKIP_OPEN_TABLE}, + {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Commit_transactions",SKIP_OPEN_TABLE}, + {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rollback_transactions",SKIP_OPEN_TABLE}, + {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Denied_connections",SKIP_OPEN_TABLE}, + {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Lost_connections",SKIP_OPEN_TABLE}, + {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Access_denied",SKIP_OPEN_TABLE}, + {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Empty_queries",SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0} +}; + + +ST_FIELD_INFO table_stats_fields_info[]= +{ + {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_schema",SKIP_OPEN_TABLE}, + {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name",SKIP_OPEN_TABLE}, + {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read",SKIP_OPEN_TABLE}, + {"ROWS_CHANGED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_changed",SKIP_OPEN_TABLE}, + {"ROWS_CHANGED_X_INDEXES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_changed_x_#indexes",SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0} +}; + +ST_FIELD_INFO index_stats_fields_info[]= +{ + {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_schema",SKIP_OPEN_TABLE}, + {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name",SKIP_OPEN_TABLE}, + {"INDEX_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Index_name",SKIP_OPEN_TABLE}, + {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read",SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0,0} +}; /* Find schema_tables elment by name @@ -6683,6 +7071,8 @@ ST_SCHEMA_TABLE schema_tables[]= { {"CHARACTER_SETS", charsets_fields_info, create_schema_table, fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0, 0}, + {"CLIENT_STATISTICS", client_stats_fields_info, create_schema_table, + fill_schema_client_stats, make_old_format, 0, -1, -1, 0, 0}, {"COLLATIONS", collation_fields_info, create_schema_table, fill_schema_collation, make_old_format, 0, -1, -1, 0, 0}, {"COLLATION_CHARACTER_SET_APPLICABILITY", coll_charset_app_fields_info, @@ -6707,6 +7097,8 @@ ST_SCHEMA_TABLE schema_tables[]= fill_status, make_old_format, 0, 0, -1, 0, 0}, {"GLOBAL_VARIABLES", variables_fields_info, create_schema_table, fill_variables, make_old_format, 0, 0, -1, 0, 0}, + {"INDEX_STATISTICS", index_stats_fields_info, create_schema_table, + fill_schema_index_stats, make_old_format, 0, -1, -1, 0, 0}, {"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table, get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0, OPEN_TABLE_ONLY}, @@ -6748,11 +7140,15 @@ ST_SCHEMA_TABLE schema_tables[]= get_all_tables, make_table_names_old_format, 0, 1, 2, 1, 0}, {"TABLE_PRIVILEGES", table_privileges_fields_info, create_schema_table, fill_schema_table_privileges, 0, 0, -1, -1, 0, 0}, + {"TABLE_STATISTICS", table_stats_fields_info, create_schema_table, + fill_schema_table_stats, make_old_format, 0, -1, -1, 0, 0}, {"TRIGGERS", triggers_fields_info, create_schema_table, get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0, OPEN_TABLE_ONLY}, {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table, fill_schema_user_privileges, 0, 0, -1, -1, 0, 0}, + {"USER_STATISTICS", user_stats_fields_info, create_schema_table, + fill_schema_user_stats, make_old_format, 0, -1, -1, 0, 0}, {"VARIABLES", variables_fields_info, create_schema_table, fill_variables, make_old_format, 0, 0, -1, 1, 0}, {"VIEWS", view_fields_info, create_schema_table, === modified file 'sql/sql_table.cc' --- sql/sql_table.cc 2009-09-18 01:04:43 +0000 +++ sql/sql_table.cc 2009-10-17 08:13:46 +0000 @@ -7815,7 +7815,7 @@ bool mysql_checksum_table(THD *thd, TABL goto err; } ha_checksum row_crc= 0; - int error= t->file->rnd_next(t->record[0]); + int error= t->file->ha_rnd_next(t->record[0]); if (unlikely(error)) { if (error == HA_ERR_RECORD_DELETED) === modified file 'sql/sql_udf.cc' --- sql/sql_udf.cc 2009-05-15 12:57:51 +0000 +++ sql/sql_udf.cc 2009-10-17 08:09:23 +0000 @@ -567,10 +567,10 @@ int mysql_drop_function(THD *thd,const L goto err; table->use_all_columns(); table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin); - if (!table->file->index_read_idx_map(table->record[0], 0, - (uchar*) table->field[0]->ptr, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (!table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar*) table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { int error; if ((error = table->file->ha_delete_row(table->record[0]))) === modified file 'sql/sql_update.cc' --- sql/sql_update.cc 2009-09-07 20:50:10 +0000 +++ sql/sql_update.cc 2009-10-17 14:13:49 +0000 @@ -142,7 +142,7 @@ static void prepare_record_for_error_mes /* Tell the engine about the new set. */ table->file->column_bitmaps_signal(); /* Read record that is identified by table->file->ref. */ - (void) table->file->rnd_pos(table->record[1], table->file->ref); + (void) table->file->ha_rnd_pos(table->record[1], table->file->ref); /* Copy the newly read columns into the new record. */ for (field_p= table->field; (field= *field_p); field_p++) if (bitmap_is_set(&unique_map, field->field_index)) @@ -1928,7 +1928,7 @@ int multi_update::do_updates() { if (thd->killed && trans_safe) goto err; - if ((local_error=tmp_table->file->rnd_next(tmp_table->record[0]))) + if ((local_error= tmp_table->file->ha_rnd_next(tmp_table->record[0]))) { if (local_error == HA_ERR_END_OF_FILE) break; @@ -1943,12 +1943,12 @@ int multi_update::do_updates() uint field_num= 0; do { - if((local_error= - tbl->file->rnd_pos(tbl->record[0], - (uchar *) tmp_table->field[field_num]->ptr))) + if ((local_error= + tbl->file->ha_rnd_pos(tbl->record[0], + (uchar*) tmp_table->field[field_num]->ptr))) goto err; field_num++; - } while((tbl= check_opt_it++)); + } while ((tbl= check_opt_it++)); table->status|= STATUS_UPDATED; store_record(table,record[1]); === modified file 'sql/sql_yacc.yy' --- sql/sql_yacc.yy 2009-09-07 20:50:10 +0000 +++ sql/sql_yacc.yy 2009-10-17 07:26:29 +0000 @@ -598,6 +598,7 @@ bool my_yyoverflow(short **a, YYSTYPE ** %token CHECK_SYM /* SQL-2003-R */ %token CIPHER_SYM %token CLIENT_SYM +%token CLIENT_STATS_SYM %token CLOSE_SYM /* SQL-2003-R */ %token COALESCE /* SQL-2003-N */ %token CODE_SYM @@ -744,6 +745,7 @@ bool my_yyoverflow(short **a, YYSTYPE ** %token IMPORT %token INDEXES %token INDEX_SYM +%token INDEX_STATS_SYM %token INFILE %token INITIAL_SIZE_SYM %token INNER_SYM /* SQL-2003-R */ @@ -985,6 +987,7 @@ bool my_yyoverflow(short **a, YYSTYPE ** %token SIGNED_SYM %token SIMPLE_SYM /* SQL-2003-N */ %token SLAVE +%token SLOW_SYM %token SMALLINT /* SQL-2003-R */ %token SNAPSHOT_SYM %token SOCKET_SYM @@ -1029,6 +1032,7 @@ bool my_yyoverflow(short **a, YYSTYPE ** %token TABLES %token TABLESPACE %token TABLE_REF_PRIORITY +%token TABLE_STATS_SYM %token TABLE_SYM /* SQL-2003-R */ %token TABLE_CHECKSUM_SYM %token TEMPORARY /* SQL-2003-N */ @@ -1076,6 +1080,7 @@ bool my_yyoverflow(short **a, YYSTYPE ** %token UPGRADE_SYM %token USAGE /* SQL-2003-N */ %token USER /* SQL-2003-R */ +%token USER_STATS_SYM %token USE_FRM %token USE_SYM %token USING /* SQL-2003-R */ @@ -9995,12 +10000,12 @@ show_param: if (prepare_schema_table(YYTHD, lex, 0, SCH_ENGINES)) MYSQL_YYABORT; } - | AUTHORS_SYM + | AUTHORS_SYM wild_and_where { LEX *lex=Lex; lex->sql_command= SQLCOM_SHOW_AUTHORS; } - | CONTRIBUTORS_SYM + | CONTRIBUTORS_SYM wild_and_where { LEX *lex=Lex; lex->sql_command= SQLCOM_SHOW_CONTRIBUTORS; @@ -10131,6 +10136,34 @@ show_param: { Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; } + | CLIENT_STATS_SYM wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_CLIENT_STATS; + if (prepare_schema_table(YYTHD, lex, 0, SCH_CLIENT_STATS)) + MYSQL_YYABORT; + } + | USER_STATS_SYM wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_USER_STATS; + if (prepare_schema_table(YYTHD, lex, 0, SCH_USER_STATS)) + MYSQL_YYABORT; + } + | TABLE_STATS_SYM wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_TABLE_STATS; + if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_STATS)) + MYSQL_YYABORT; + } + | INDEX_STATS_SYM wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_INDEX_STATS; + if (prepare_schema_table(YYTHD, lex, 0, SCH_INDEX_STATS)) + MYSQL_YYABORT; + } | CREATE PROCEDURE sp_name { LEX *lex= Lex; @@ -10339,6 +10372,16 @@ flush_option: { Lex->type|= REFRESH_STATUS; } | SLAVE { Lex->type|= REFRESH_SLAVE; } + | SLOW_SYM QUERY_SYM LOGS_SYM + { Lex->type |= REFRESH_SLOW_QUERY_LOG; } + | CLIENT_STATS_SYM + { Lex->type|= REFRESH_CLIENT_STATS; } + | USER_STATS_SYM + { Lex->type|= REFRESH_USER_STATS; } + | TABLE_STATS_SYM + { Lex->type|= REFRESH_TABLE_STATS; } + | INDEX_STATS_SYM + { Lex->type|= REFRESH_INDEX_STATS; } | MASTER_SYM { Lex->type|= REFRESH_MASTER; } | DES_KEY_FILE @@ -11447,6 +11490,7 @@ keyword_sp: | CHAIN_SYM {} | CHANGED {} | CIPHER_SYM {} + | CLIENT_STATS_SYM {} | CLIENT_SYM {} | COALESCE {} | CODE_SYM {} @@ -11508,6 +11552,7 @@ keyword_sp: | HOSTS_SYM {} | HOUR_SYM {} | IDENTIFIED_SYM {} + | INDEX_STATS_SYM {} | INVOKER_SYM {} | IMPORT {} | INDEXES {} @@ -11631,6 +11676,7 @@ keyword_sp: | SIMPLE_SYM {} | SHARE_SYM {} | SHUTDOWN {} + | SLOW_SYM {} | SNAPSHOT_SYM {} | SOUNDS_SYM {} | SOURCE_SYM {} @@ -11650,6 +11696,7 @@ keyword_sp: | SUSPEND_SYM {} | SWAPS_SYM {} | SWITCHES_SYM {} + | TABLE_STATS_SYM {} | TABLES {} | TABLE_CHECKSUM_SYM {} | TABLESPACE {} @@ -11675,6 +11722,7 @@ keyword_sp: | UNKNOWN_SYM {} | UNTIL_SYM {} | USER {} + | USER_STATS_SYM {} | USE_FRM {} | VARIABLES {} | VIEW_SYM {} === modified file 'sql/structs.h' --- sql/structs.h 2009-06-26 19:57:42 +0000 +++ sql/structs.h 2009-10-18 15:39:18 +0000 @@ -76,6 +76,7 @@ typedef struct st_key { uint extra_length; uint usable_key_parts; /* Should normally be = key_parts */ uint block_size; + uint name_length; enum ha_key_alg algorithm; /* Note that parser is used when the table is opened for use, and @@ -88,6 +89,8 @@ typedef struct st_key { }; KEY_PART_INFO *key_part; char *name; /* Name of key */ + /* Unique name for cache; db + \0 + table_name + \0 + key_name + \0 */ + uchar *cache_name; /* Array of AVG(#records with the same field value) for 1st ... Nth key part. 0 means 'not known'. @@ -231,6 +234,111 @@ typedef struct user_conn { USER_RESOURCES user_resources; } USER_CONN; +typedef struct st_user_stats +{ + char user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1]; + // Account name the user is mapped to when this is a user from mapped_user. + // Otherwise, the same value as user. + char priv_user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1]; + uint user_name_length; + uint total_connections; + uint concurrent_connections; + time_t connected_time; // in seconds + double busy_time; // in seconds + double cpu_time; // in seconds + ulonglong bytes_received; + ulonglong bytes_sent; + ulonglong binlog_bytes_written; + ha_rows rows_examined, rows_read, rows_sent; + ha_rows rows_updated, rows_deleted, rows_inserted; + ulonglong select_commands, update_commands, other_commands; + ulonglong commit_trans, rollback_trans; + ulonglong denied_connections, lost_connections; + ulonglong access_denied_errors; + ulonglong empty_queries; +} USER_STATS; + +/* Lookup function for hash tables with USER_STATS entries */ +extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length, + my_bool not_used __attribute__((unused))); + +/* Free all memory for a hash table with USER_STATS entries */ +extern void free_user_stats(USER_STATS* user_stats); + +/* Intialize an instance of USER_STATS */ +extern void +init_user_stats(USER_STATS *user_stats, + const char *user, + size_t user_length, + const char *priv_user, + uint total_connections, + uint concurrent_connections, + time_t connected_time, + double busy_time, + double cpu_time, + ulonglong bytes_received, + ulonglong bytes_sent, + ulonglong binlog_bytes_written, + ha_rows rows_sent, + ha_rows rows_read, + ha_rows rows_inserted, + ha_rows rows_deleted, + ha_rows rows_updated, + ulonglong select_commands, + ulonglong update_commands, + ulonglong other_commands, + ulonglong commit_trans, + ulonglong rollback_trans, + ulonglong denied_connections, + ulonglong lost_connections, + ulonglong access_denied_errors, + ulonglong empty_queries); + +/* Increment values of an instance of USER_STATS */ +extern void +add_user_stats(USER_STATS *user_stats, + uint total_connections, + uint concurrent_connections, + time_t connected_time, + double busy_time, + double cpu_time, + ulonglong bytes_received, + ulonglong bytes_sent, + ulonglong binlog_bytes_written, + ha_rows rows_sent, + ha_rows rows_read, + ha_rows rows_inserted, + ha_rows rows_deleted, + ha_rows rows_updated, + ulonglong select_commands, + ulonglong update_commands, + ulonglong other_commands, + ulonglong commit_trans, + ulonglong rollback_trans, + ulonglong denied_connections, + ulonglong lost_connections, + ulonglong access_denied_errors, + ulonglong empty_queries); + +typedef struct st_table_stats +{ + char table[NAME_LEN * 2 + 2]; // [db] + '\0' + [table] + '\0' + uint table_name_length; + ulonglong rows_read, rows_changed; + ulonglong rows_changed_x_indexes; + /* Stores enum db_type, but forward declarations cannot be done */ + int engine_type; +} TABLE_STATS; + +typedef struct st_index_stats +{ + // [db] + '\0' + [table] + '\0' + [index] + '\0' + char index[NAME_LEN * 3 + 3]; + uint index_name_length; /* Length of 'index' */ + ulonglong rows_read; +} INDEX_STATS; + + /* Bits in form->update */ #define REG_MAKE_DUPP 1 /* Make a copy of record when read */ #define REG_NEW_RECORD 2 /* Write a new record if not found */ === modified file 'sql/table.cc' --- sql/table.cc 2009-09-09 21:06:57 +0000 +++ sql/table.cc 2009-10-17 14:00:24 +0000 @@ -1325,6 +1325,19 @@ static int open_binary_frm(THD *thd, TAB { uint usable_parts= 0; keyinfo->name=(char*) share->keynames.type_names[key]; + keyinfo->name_length= strlen(keyinfo->name); + keyinfo->cache_name= + (uchar*) alloc_root(&share->mem_root, + share->table_cache_key.length+ + keyinfo->name_length + 1); + if (keyinfo->cache_name) // If not out of memory + { + uchar *pos= keyinfo->cache_name; + memcpy(pos, share->table_cache_key.str, share->table_cache_key.length); + memcpy(pos + share->table_cache_key.length, keyinfo->name, + keyinfo->name_length+1); + } + /* Fix fulltext keys for old .frm files */ if (share->key_info[key].flags & HA_FULLTEXT) share->key_info[key].algorithm= HA_KEY_ALG_FULLTEXT; === modified file 'sql/table.h' --- sql/table.h 2009-09-15 10:46:35 +0000 +++ sql/table.h 2009-10-17 07:32:14 +0000 @@ -878,6 +878,7 @@ typedef struct st_foreign_key_info enum enum_schema_tables { SCH_CHARSETS= 0, + SCH_CLIENT_STATS, SCH_COLLATIONS, SCH_COLLATION_CHARACTER_SET_APPLICABILITY, SCH_COLUMNS, @@ -887,6 +888,7 @@ enum enum_schema_tables SCH_FILES, SCH_GLOBAL_STATUS, SCH_GLOBAL_VARIABLES, + SCH_INDEX_STATS, SCH_KEY_COLUMN_USAGE, SCH_OPEN_TABLES, SCH_PARTITIONS, @@ -905,8 +907,10 @@ enum enum_schema_tables SCH_TABLE_CONSTRAINTS, SCH_TABLE_NAMES, SCH_TABLE_PRIVILEGES, + SCH_TABLE_STATS, SCH_TRIGGERS, SCH_USER_PRIVILEGES, + SCH_USER_STATS, SCH_VARIABLES, SCH_VIEWS }; === modified file 'sql/tztime.cc' --- sql/tztime.cc 2009-09-07 20:50:10 +0000 +++ sql/tztime.cc 2009-10-17 08:09:23 +0000 @@ -1676,7 +1676,7 @@ my_tz_init(THD *org_thd, const char *def tz_leapcnt= 0; - res= table->file->index_first(table->record[0]); + res= table->file->ha_index_first(table->record[0]); while (!res) { @@ -1698,7 +1698,7 @@ my_tz_init(THD *org_thd, const char *def tz_leapcnt, (ulong) tz_lsis[tz_leapcnt-1].ls_trans, tz_lsis[tz_leapcnt-1].ls_corr)); - res= table->file->index_next(table->record[0]); + res= table->file->ha_index_next(table->record[0]); } (void)table->file->ha_index_end(); @@ -1865,8 +1865,8 @@ tz_load_from_open_tables(const String *t */ (void)table->file->ha_index_init(0, 1); - if (table->file->index_read_map(table->record[0], table->field[0]->ptr, - HA_WHOLE_KEY, HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_map(table->record[0], table->field[0]->ptr, + HA_WHOLE_KEY, HA_READ_KEY_EXACT)) { #ifdef EXTRA_DEBUG /* @@ -1893,8 +1893,8 @@ tz_load_from_open_tables(const String *t table->field[0]->store((longlong) tzid, TRUE); (void)table->file->ha_index_init(0, 1); - if (table->file->index_read_map(table->record[0], table->field[0]->ptr, - HA_WHOLE_KEY, HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_map(table->record[0], table->field[0]->ptr, + HA_WHOLE_KEY, HA_READ_KEY_EXACT)) { sql_print_error("Can't find description of time zone '%u'", tzid); goto end; @@ -1920,8 +1920,8 @@ tz_load_from_open_tables(const String *t table->field[0]->store((longlong) tzid, TRUE); (void)table->file->ha_index_init(0, 1); - res= table->file->index_read_map(table->record[0], table->field[0]->ptr, - (key_part_map)1, HA_READ_KEY_EXACT); + res= table->file->ha_index_read_map(table->record[0], table->field[0]->ptr, + (key_part_map)1, HA_READ_KEY_EXACT); while (!res) { ttid= (uint)table->field[1]->val_int(); @@ -1968,8 +1968,8 @@ tz_load_from_open_tables(const String *t tmp_tz_info.typecnt= ttid + 1; - res= table->file->index_next_same(table->record[0], - table->field[0]->ptr, 4); + res= table->file->ha_index_next_same(table->record[0], + table->field[0]->ptr, 4); } if (res != HA_ERR_END_OF_FILE) @@ -1991,8 +1991,8 @@ tz_load_from_open_tables(const String *t table->field[0]->store((longlong) tzid, TRUE); (void)table->file->ha_index_init(0, 1); - res= table->file->index_read_map(table->record[0], table->field[0]->ptr, - (key_part_map)1, HA_READ_KEY_EXACT); + res= table->file->ha_index_read_map(table->record[0], table->field[0]->ptr, + (key_part_map)1, HA_READ_KEY_EXACT); while (!res) { ttime= (my_time_t)table->field[1]->val_int(); @@ -2021,8 +2021,8 @@ tz_load_from_open_tables(const String *t ("time_zone_transition table: tz_id: %u tt_time: %lu tt_id: %u", tzid, (ulong) ttime, ttid)); - res= table->file->index_next_same(table->record[0], - table->field[0]->ptr, 4); + res= table->file->ha_index_next_same(table->record[0], + table->field[0]->ptr, 4); } /* ----------- Regards, Monty