summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <cmiller@zippy.cornsilk.net>2007-04-26 11:51:37 -0400
committerunknown <cmiller@zippy.cornsilk.net>2007-04-26 11:51:37 -0400
commit808beaa51df9178e13bde87429ef699c75d51445 (patch)
treeeb53ce8e1bbaa9420298b4d71fd9100db7d63374 /sql
parent0d3c4483530469670b05acc77b853aad6b78a6e7 (diff)
parenta318c75af76c8676cfe45dc154685a6e0e283cfa (diff)
downloadmariadb-git-808beaa51df9178e13bde87429ef699c75d51445.tar.gz
Merge mysqldev@production.mysql.com:my/mysql-5.0-release
into zippy.cornsilk.net:/home/cmiller/work/mysql/mysql-5.0-community include/config-win.h: Auto merged myisam/mi_open.c: Auto merged mysql-test/r/func_in.result: Auto merged mysql-test/r/information_schema.result: Auto merged mysql-test/t/func_in.test: Auto merged ndb/src/ndbapi/DictCache.cpp: Auto merged sql/ha_archive.cc: Auto merged sql/ha_myisam.cc: Auto merged sql/ha_ndbcluster.cc: Auto merged sql/item_cmpfunc.cc: Auto merged sql/item_func.cc: Auto merged sql/lex.h: Auto merged sql/lock.cc: Auto merged sql/log_event.cc: Auto merged sql/mysql_priv.h: Auto merged sql/mysqld.cc: Auto merged sql/slave.cc: Auto merged sql/sp_head.cc: Auto merged sql/sql_base.cc: Auto merged sql/sql_class.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_delete.cc: Auto merged sql/sql_insert.cc: Auto merged sql/sql_lex.cc: Auto merged sql/sql_parse.cc: Auto merged sql/sql_prepare.cc: Auto merged sql/sql_select.cc: Auto merged sql/sql_show.cc: Auto merged sql/sql_table.cc: Auto merged sql/sql_update.cc: Auto merged sql/sql_view.cc: Auto merged sql/sql_yacc.yy: Auto merged sql/table.h: Auto merged configure.in: Version increment. support-files/mysql.spec.sh: Include enterprise advert.
Diffstat (limited to 'sql')
-rwxr-xr-xsql/CMakeLists.txt1
-rw-r--r--sql/Makefile.am2
-rw-r--r--sql/ha_archive.cc4
-rw-r--r--sql/ha_berkeley.cc4
-rw-r--r--sql/ha_myisam.cc24
-rw-r--r--sql/ha_myisammrg.cc2
-rw-r--r--sql/ha_ndbcluster.cc4
-rw-r--r--sql/item_cmpfunc.cc2
-rw-r--r--sql/item_func.cc8
-rw-r--r--sql/lex.h13
-rw-r--r--sql/lock.cc8
-rw-r--r--sql/log_event.cc8
-rw-r--r--sql/mysql_priv.h8
-rw-r--r--sql/mysqld.cc15
-rw-r--r--sql/repl_failsafe.cc10
-rw-r--r--sql/set_var.cc18
-rw-r--r--sql/slave.cc40
-rw-r--r--sql/sp_head.cc4
-rw-r--r--sql/sql_base.cc30
-rw-r--r--sql/sql_cache.cc18
-rw-r--r--sql/sql_class.cc3
-rw-r--r--sql/sql_class.h8
-rw-r--r--sql/sql_delete.cc14
-rw-r--r--sql/sql_insert.cc38
-rw-r--r--sql/sql_lex.cc2
-rw-r--r--sql/sql_lex.h10
-rw-r--r--sql/sql_parse.cc81
-rw-r--r--sql/sql_prepare.cc3
-rw-r--r--sql/sql_profile.cc804
-rw-r--r--sql/sql_profile.h346
-rw-r--r--sql/sql_repl.cc18
-rw-r--r--sql/sql_select.cc73
-rw-r--r--sql/sql_show.cc12
-rw-r--r--sql/sql_table.cc20
-rw-r--r--sql/sql_update.cc14
-rw-r--r--sql/sql_view.cc2
-rw-r--r--sql/sql_yacc.yy88
-rw-r--r--sql/structs.h1
-rw-r--r--sql/table.h1
39 files changed, 1572 insertions, 189 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 7e26f62b5f7..9058f9077d9 100755
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -73,6 +73,7 @@ ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.cc
time.cc tztime.cc uniques.cc unireg.cc
../sql-common/my_user.c
sql_locale.cc
+ sql_profile.cc
${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
${PROJECT_SOURCE_DIR}/include/mysqld_error.h
diff --git a/sql/Makefile.am b/sql/Makefile.am
index 4f84023724f..1808eaa773c 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -52,6 +52,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
procedure.h sql_class.h sql_lex.h sql_list.h \
sql_manager.h sql_map.h sql_string.h unireg.h \
sql_error.h field.h handler.h mysqld_suffix.h \
+ sql_profile.h \
ha_myisammrg.h\
ha_heap.h ha_myisam.h ha_berkeley.h ha_innodb.h \
ha_ndbcluster.h opt_range.h protocol.h \
@@ -80,6 +81,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \
set_var.cc sql_parse.cc sql_yacc.yy \
sql_base.cc table.cc sql_select.cc sql_insert.cc \
sql_prepare.cc sql_error.cc sql_locale.cc \
+ sql_profile.cc \
sql_update.cc sql_delete.cc uniques.cc sql_do.cc \
procedure.cc item_uniq.cc sql_test.cc \
log.cc log_event.cc init.cc derror.cc sql_acl.cc \
diff --git a/sql/ha_archive.cc b/sql/ha_archive.cc
index e2a2211259f..1ff9ec0130e 100644
--- a/sql/ha_archive.cc
+++ b/sql/ha_archive.cc
@@ -1209,7 +1209,7 @@ int ha_archive::check(THD* thd, HA_CHECK_OPT* check_opt)
ha_rows count= share->rows_recorded;
DBUG_ENTER("ha_archive::check");
- thd->proc_info= "Checking table";
+ thd_proc_info(thd, "Checking table");
/* Flush any waiting data */
gzflush(share->archive_write, Z_SYNC_FLUSH);
@@ -1233,7 +1233,7 @@ int ha_archive::check(THD* thd, HA_CHECK_OPT* check_opt)
my_free((char*)buf, MYF(0));
- thd->proc_info= old_proc_info;
+ thd_proc_info(thd, old_proc_info);
if ((rc && rc != HA_ERR_END_OF_FILE) || count)
{
diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc
index 2a5fe775ca6..5e0c2ff51fd 100644
--- a/sql/ha_berkeley.cc
+++ b/sql/ha_berkeley.cc
@@ -1865,8 +1865,8 @@ int ha_berkeley::external_lock(THD *thd, int lock_type)
OPTION_TABLE_LOCK)) && !trx->all)
{
/* We have to start a master transaction */
- DBUG_PRINT("trans",("starting transaction all: options: 0x%lx",
- (ulong) thd->options));
+ DBUG_PRINT("trans",("starting transaction all: options: 0x%llx",
+ thd->options));
if ((error=txn_begin(db_env, 0, &trx->all, 0)))
{
trx->bdb_lock_count--; // We didn't get the lock
diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc
index 6d8a770175d..58ed111763b 100644
--- a/sql/ha_myisam.cc
+++ b/sql/ha_myisam.cc
@@ -673,7 +673,7 @@ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
MYISAM_SHARE* share = file->s;
const char *old_proc_info=thd->proc_info;
- thd->proc_info="Checking table";
+ thd_proc_info(thd, "Checking table");
myisamchk_init(&param);
param.thd = thd;
param.op_name = "check";
@@ -747,7 +747,7 @@ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
}
- thd->proc_info=old_proc_info;
+ thd_proc_info(thd, old_proc_info);
return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK;
}
@@ -1029,22 +1029,22 @@ int ha_myisam::repair(THD *thd, MI_CHECK &param, bool do_optimize)
char buf[40];
/* TODO: respect myisam_repair_threads variable */
my_snprintf(buf, 40, "Repair with %d threads", my_count_bits(key_map));
- thd->proc_info=buf;
+ thd_proc_info(thd, buf);
error = mi_repair_parallel(&param, file, fixed_name,
param.testflag & T_QUICK);
- thd->proc_info="Repair done"; // to reset proc_info, as
+ thd_proc_info(thd, "Repair done"); // to reset proc_info, as
// it was pointing to local buffer
}
else
{
- thd->proc_info="Repair by sorting";
+ thd_proc_info(thd, "Repair by sorting");
error = mi_repair_by_sort(&param, file, fixed_name,
param.testflag & T_QUICK);
}
}
else
{
- thd->proc_info="Repair with keycache";
+ thd_proc_info(thd, "Repair with keycache");
param.testflag &= ~T_REP_BY_SORT;
error= mi_repair(&param, file, fixed_name,
param.testflag & T_QUICK);
@@ -1058,7 +1058,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK &param, bool do_optimize)
(share->state.changed & STATE_NOT_SORTED_PAGES))
{
optimize_done=1;
- thd->proc_info="Sorting index";
+ thd_proc_info(thd, "Sorting index");
error=mi_sort_index(&param,file,fixed_name);
}
if (!statistics_done && (local_testflag & T_STATISTICS))
@@ -1066,14 +1066,14 @@ int ha_myisam::repair(THD *thd, MI_CHECK &param, bool do_optimize)
if (share->state.changed & STATE_NOT_ANALYZED)
{
optimize_done=1;
- thd->proc_info="Analyzing";
+ thd_proc_info(thd, "Analyzing");
error = chk_key(&param, file);
}
else
local_testflag&= ~T_STATISTICS; // Don't update statistics
}
}
- thd->proc_info="Saving state";
+ thd_proc_info(thd, "Saving state");
if (!error)
{
if ((share->state.changed & STATE_CHANGED) || mi_is_crashed(file))
@@ -1111,7 +1111,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK &param, bool do_optimize)
file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
update_state_info(&param, file, 0);
}
- thd->proc_info=old_proc_info;
+ thd_proc_info(thd, old_proc_info);
if (!thd->locked_tables)
mi_lock_database(file,F_UNLCK);
DBUG_RETURN(error ? HA_ADMIN_FAILED :
@@ -1336,7 +1336,7 @@ int ha_myisam::enable_indexes(uint mode)
THD *thd=current_thd;
MI_CHECK param;
const char *save_proc_info=thd->proc_info;
- thd->proc_info="Creating index";
+ thd_proc_info(thd, "Creating index");
myisamchk_init(&param);
param.op_name= "recreating_index";
param.testflag= (T_SILENT | T_REP_BY_SORT | T_QUICK |
@@ -1361,7 +1361,7 @@ int ha_myisam::enable_indexes(uint mode)
thd->clear_error();
}
info(HA_STATUS_CONST);
- thd->proc_info=save_proc_info;
+ thd_proc_info(thd, save_proc_info);
}
else
{
diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc
index 1202a733a16..389df51f693 100644
--- a/sql/ha_myisammrg.cc
+++ b/sql/ha_myisammrg.cc
@@ -108,7 +108,7 @@ int ha_myisammrg::open(const char *name, int mode, uint test_if_locked)
DBUG_PRINT("info", ("ha_myisammrg::open exit %d", my_errno));
return (my_errno ? my_errno : -1);
}
- DBUG_PRINT("info", ("ha_myisammrg::open myrg_extrafunc..."))
+ DBUG_PRINT("info", ("ha_myisammrg::open myrg_extrafunc..."));
myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref);
if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED ||
test_if_locked == HA_OPEN_ABORT_IF_LOCKED))
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index 350133221ab..b2ad09f34f9 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -5971,14 +5971,14 @@ ha_ndbcluster::register_query_cache_table(THD *thd,
if (!is_autocommit)
{
- DBUG_PRINT("exit", ("Can't register table during transaction"))
+ DBUG_PRINT("exit", ("Can't register table during transaction"));
DBUG_RETURN(FALSE);
}
if (ndb_get_commitcount(thd, m_dbname, m_tabname, &commit_count))
{
*engine_data= 0;
- DBUG_PRINT("exit", ("Error, could not get commitcount"))
+ DBUG_PRINT("exit", ("Error, could not get commitcount"));
DBUG_RETURN(FALSE);
}
*engine_data= commit_count;
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index f9844f6d36e..aab648f52a5 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -3365,7 +3365,7 @@ longlong Item_is_not_null_test::val_int()
}
if (args[0]->is_null())
{
- DBUG_PRINT("info", ("null"))
+ DBUG_PRINT("info", ("null"));
owner->was_null|= 1;
DBUG_RETURN(0);
}
diff --git a/sql/item_func.cc b/sql/item_func.cc
index c4279d1bd4e..2007a6eb4a0 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -3215,7 +3215,7 @@ void debug_sync_point(const char* lock_name, uint lock_timeout)
Structure is now initialized. Try to get the lock.
Set up control struct to allow others to abort locks
*/
- thd->proc_info="User lock";
+ thd_proc_info(thd, "User lock");
thd->mysys_var->current_mutex= &LOCK_user_locks;
thd->mysys_var->current_cond= &ull->cond;
@@ -3240,7 +3240,7 @@ void debug_sync_point(const char* lock_name, uint lock_timeout)
}
pthread_mutex_unlock(&LOCK_user_locks);
pthread_mutex_lock(&thd->mysys_var->mutex);
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
thd->mysys_var->current_mutex= 0;
thd->mysys_var->current_cond= 0;
pthread_mutex_unlock(&thd->mysys_var->mutex);
@@ -3321,7 +3321,7 @@ longlong Item_func_get_lock::val_int()
Structure is now initialized. Try to get the lock.
Set up control struct to allow others to abort locks.
*/
- thd->proc_info="User lock";
+ thd_proc_info(thd, "User lock");
thd->mysys_var->current_mutex= &LOCK_user_locks;
thd->mysys_var->current_cond= &ull->cond;
@@ -3359,7 +3359,7 @@ longlong Item_func_get_lock::val_int()
pthread_mutex_unlock(&LOCK_user_locks);
pthread_mutex_lock(&thd->mysys_var->mutex);
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
thd->mysys_var->current_mutex= 0;
thd->mysys_var->current_cond= 0;
pthread_mutex_unlock(&thd->mysys_var->mutex);
diff --git a/sql/lex.h b/sql/lex.h
index 352d80da5c6..33217ee4bc4 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -87,6 +87,7 @@ static SYMBOL symbols[] = {
{ "BINLOG", SYM(BINLOG_SYM)},
{ "BIT", SYM(BIT_SYM)},
{ "BLOB", SYM(BLOB_SYM)},
+ { "BLOCK", SYM(BLOCK_SYM)},
{ "BOOL", SYM(BOOL_SYM)},
{ "BOOLEAN", SYM(BOOLEAN_SYM)},
{ "BOTH", SYM(BOTH)},
@@ -125,8 +126,10 @@ static SYMBOL symbols[] = {
{ "CONSISTENT", SYM(CONSISTENT_SYM)},
{ "CONSTRAINT", SYM(CONSTRAINT)},
{ "CONTAINS", SYM(CONTAINS_SYM)},
+ { "CONTEXT", SYM(CONTEXT_SYM)},
{ "CONTINUE", SYM(CONTINUE_SYM)},
{ "CONVERT", SYM(CONVERT_SYM)},
+ { "CPU", SYM(CPU_SYM)},
{ "CREATE", SYM(CREATE)},
{ "CROSS", SYM(CROSS)},
{ "CUBE", SYM(CUBE_SYM)},
@@ -192,6 +195,7 @@ static SYMBOL symbols[] = {
{ "EXTENDED", SYM(EXTENDED_SYM)},
{ "FALSE", SYM(FALSE_SYM)},
{ "FAST", SYM(FAST_SYM)},
+ { "FAULTS", SYM(FAULTS_SYM)},
{ "FETCH", SYM(FETCH_SYM)},
{ "FIELDS", SYM(COLUMNS)},
{ "FILE", SYM(FILE_SYM)},
@@ -251,7 +255,9 @@ static SYMBOL symbols[] = {
{ "INTEGER", SYM(INT_SYM)},
{ "INTERVAL", SYM(INTERVAL_SYM)},
{ "INTO", SYM(INTO)},
+ { "IO", SYM(IO_SYM)},
{ "IO_THREAD", SYM(RELAY_THREAD)},
+ { "IPC", SYM(IPC_SYM)},
{ "IS", SYM(IS)},
{ "ISOLATION", SYM(ISOLATION)},
{ "ISSUER", SYM(ISSUER_SYM)},
@@ -309,6 +315,7 @@ static SYMBOL symbols[] = {
{ "MEDIUMBLOB", SYM(MEDIUMBLOB)},
{ "MEDIUMINT", SYM(MEDIUMINT)},
{ "MEDIUMTEXT", SYM(MEDIUMTEXT)},
+ { "MEMORY", SYM(MEMORY_SYM)},
{ "MERGE", SYM(MERGE_SYM)},
{ "MICROSECOND", SYM(MICROSECOND_SYM)},
{ "MIDDLEINT", SYM(MEDIUMINT)}, /* For powerbuilder */
@@ -356,6 +363,7 @@ static SYMBOL symbols[] = {
{ "OUT", SYM(OUT_SYM)},
{ "OUTER", SYM(OUTER)},
{ "OUTFILE", SYM(OUTFILE)},
+ { "PAGE", SYM(PAGE_SYM)},
{ "PACK_KEYS", SYM(PACK_KEYS_SYM)},
{ "PARTIAL", SYM(PARTIAL)},
{ "PASSWORD", SYM(PASSWORD)},
@@ -370,6 +378,8 @@ static SYMBOL symbols[] = {
{ "PROCEDURE", SYM(PROCEDURE)},
{ "PROCESS" , SYM(PROCESS)},
{ "PROCESSLIST", SYM(PROCESSLIST_SYM)},
+ { "PROFILE", SYM(PROFILE_SYM)},
+ { "PROFILES", SYM(PROFILES_SYM)},
{ "PURGE", SYM(PURGE)},
{ "QUARTER", SYM(QUARTER_SYM)},
{ "QUERY", SYM(QUERY_SYM)},
@@ -437,6 +447,7 @@ static SYMBOL symbols[] = {
{ "SOME", SYM(ANY_SYM)},
{ "SONAME", SYM(UDF_SONAME_SYM)},
{ "SOUNDS", SYM(SOUNDS_SYM)},
+ { "SOURCE", SYM(SOURCE_SYM)},
{ "SPATIAL", SYM(SPATIAL_SYM)},
{ "SPECIFIC", SYM(SPECIFIC_SYM)},
{ "SQL", SYM(SQL_SYM)},
@@ -471,6 +482,8 @@ static SYMBOL symbols[] = {
{ "SUBJECT", SYM(SUBJECT_SYM)},
{ "SUPER", SYM(SUPER_SYM)},
{ "SUSPEND", SYM(SUSPEND_SYM)},
+ { "SWAPS", SYM(SWAPS_SYM)},
+ { "SWITCHES", SYM(SWITCHES_SYM)},
{ "TABLE", SYM(TABLE_SYM)},
{ "TABLES", SYM(TABLES)},
{ "TABLESPACE", SYM(TABLESPACE)},
diff --git a/sql/lock.cc b/sql/lock.cc
index 233d12d9cc4..a2e8da42363 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -150,7 +150,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
}
}
- thd->proc_info="System lock";
+ thd_proc_info(thd, "System lock");
if (lock_external(thd, tables, count))
{
/* Clear the lock type of all lock data to avoid reusage. */
@@ -159,7 +159,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
sql_lock=0;
break;
}
- thd->proc_info="Table lock";
+ thd_proc_info(thd, "Table lock");
thd->locked=1;
/* Copy the lock data array. thr_multi_lock() reorders its contens. */
memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
@@ -193,7 +193,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
thd->locked=0;
break;
}
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
/* some table was altered or deleted. reopen tables marked deleted */
mysql_unlock_tables(thd,sql_lock);
@@ -208,7 +208,7 @@ retry:
if (wait_for_tables(thd))
break; // Couldn't open tables
}
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
if (thd->killed)
{
thd->send_kill_message();
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 8bb63e72bde..0c0a542baa7 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -4412,7 +4412,7 @@ int Create_file_log_event::exec_event(struct st_relay_log_info* rli)
bzero((char*)&file, sizeof(file));
fname_buf= strmov(proc_info, "Making temp file ");
ext= slave_load_file_stem(fname_buf, file_id, server_id, ".info");
- thd->proc_info= proc_info;
+ thd_proc_info(thd, proc_info);
my_delete(fname_buf, MYF(0)); // old copy may exist already
if ((fd= my_create(fname_buf, CREATE_MODE,
O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
@@ -4466,7 +4466,7 @@ err:
end_io_cache(&file);
if (fd >= 0)
my_close(fd, MYF(0));
- thd->proc_info= 0;
+ thd_proc_info(thd, 0);
return error ? 1 : Log_event::exec_event(rli);
}
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
@@ -4586,7 +4586,7 @@ int Append_block_log_event::exec_event(struct st_relay_log_info* rli)
fname= strmov(proc_info, "Making temp file ");
slave_load_file_stem(fname, file_id, server_id, ".data");
- thd->proc_info= proc_info;
+ thd_proc_info(thd, proc_info);
if (get_create_or_append())
{
my_delete(fname, MYF(0)); // old copy may exist already
@@ -4620,7 +4620,7 @@ int Append_block_log_event::exec_event(struct st_relay_log_info* rli)
err:
if (fd >= 0)
my_close(fd, MYF(0));
- thd->proc_info= 0;
+ thd_proc_info(thd, 0);
DBUG_RETURN(error ? error : Log_event::exec_event(rli));
}
#endif
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 39f115f6fd5..7fabfe8ee12 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -195,6 +195,8 @@ MY_LOCALE *my_locale_by_number(uint number);
#define BDB_LOG_ALLOC_BLOCK_SIZE 1024
#define WARN_ALLOC_BLOCK_SIZE 2048
#define WARN_ALLOC_PREALLOC_SIZE 1024
+#define PROFILE_ALLOC_BLOCK_SIZE 2048
+#define PROFILE_ALLOC_PREALLOC_SIZE 1024
/*
The following parameters is to decide when to use an extra cache to
@@ -367,6 +369,8 @@ MY_LOCALE *my_locale_by_number(uint number);
fulltext functions when reading from it.
*/
#define TMP_TABLE_FORCE_MYISAM (ULL(1) << 32)
+#define OPTION_PROFILING (ULL(1) << 33)
+
/*
@@ -569,6 +573,8 @@ typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key,
#include "field.h" /* Field definitions */
#include "protocol.h"
#include "sql_udf.h"
+#include "sql_profile.h"
+
class user_var_entry;
class Security_context;
enum enum_var_type
@@ -1240,7 +1246,7 @@ void my_dbopt_free(void);
External variables
*/
-extern time_t server_start_time;
+extern time_t server_start_time, flush_status_time;
extern char *mysql_data_home,server_version[SERVER_VERSION_LENGTH],
mysql_real_data_home[], *opt_mysql_tmpdir, mysql_charsets_dir[],
def_ft_boolean_syntax[sizeof(ft_boolean_syntax)];
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index b0fc5a30ff5..fbf0998a64f 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -435,7 +435,7 @@ ulong rpl_recovery_rank=0;
double log_10[32]; /* 10 potences */
double log_01[32];
-time_t server_start_time;
+time_t server_start_time, flush_status_time;
char mysql_home[FN_REFLEN], pidfile_name[FN_REFLEN], system_time_zone[30];
char *default_tz_name;
@@ -1667,7 +1667,7 @@ void end_thread(THD *thd, bool put_in_cache)
! abort_loop && !kill_cached_threads)
{
/* Don't kill the thread, just put it in cache for reuse */
- DBUG_PRINT("info", ("Adding thread to cache"))
+ DBUG_PRINT("info", ("Adding thread to cache"));
cached_thread_count++;
while (!abort_loop && ! wake_thread && ! kill_cached_threads)
(void) pthread_cond_wait(&COND_thread_cache, &LOCK_thread_count);
@@ -2633,7 +2633,7 @@ static int init_common_variables(const char *conf_file_name, int argc,
tzset(); // Set tzname
max_system_variables.pseudo_thread_id= (ulong)~0;
- server_start_time= time((time_t*) 0);
+ server_start_time= flush_status_time= time((time_t*) 0);
if (init_thread_environment())
return 1;
mysql_init_variables();
@@ -4771,6 +4771,7 @@ enum options_mysqld
OPT_TABLE_LOCK_WAIT_TIMEOUT,
OPT_PORT_OPEN_TIMEOUT,
OPT_MERGE,
+ OPT_PROFILING,
OPT_INNODB_ROLLBACK_ON_TIMEOUT,
OPT_SECURE_FILE_PRIV
};
@@ -5344,6 +5345,12 @@ Disable with --skip-ndbcluster (will save memory).",
"Maximum time in seconds to wait for the port to become free. "
"(Default: no wait)", (gptr*) &mysqld_port_timeout,
(gptr*) &mysqld_port_timeout, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#ifdef ENABLED_PROFILING
+ {"profiling_history_size", OPT_PROFILING, "Limit of query profiling memory",
+ (gptr*) &global_system_variables.profiling_history_size,
+ (gptr*) &max_system_variables.profiling_history_size,
+ 0, GET_ULONG, REQUIRED_ARG, 15, 0, 100, 0, 0, 0},
+#endif
{"relay-log", OPT_RELAY_LOG,
"The location and name to use for relay logs.",
(gptr*) &opt_relay_logname, (gptr*) &opt_relay_logname, 0,
@@ -6370,6 +6377,7 @@ struct show_var_st status_vars[]= {
{"Threads_created", (char*) &thread_created, SHOW_LONG_CONST},
{"Threads_running", (char*) &thread_running, SHOW_INT_CONST},
{"Uptime", (char*) 0, SHOW_STARTTIME},
+ {"Uptime_since_flush_status",(char*) 0, SHOW_FLUSHTIME},
{NullS, NullS, SHOW_LONG}
};
@@ -7676,6 +7684,7 @@ void refresh_status(THD *thd)
/* Reset the counters of all key caches (default and named). */
process_key_caches(reset_key_cache_counters);
+ flush_status_time= time((time_t*) 0);
pthread_mutex_unlock(&LOCK_status);
/*
diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc
index 1dc16b6e566..6c403065d19 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -92,7 +92,7 @@ static int init_failsafe_rpl_thread(THD* thd)
if (thd->variables.max_join_size == HA_POS_ERROR)
thd->options|= OPTION_BIG_SELECTS;
- thd->proc_info="Thread initialized";
+ thd_proc_info(thd, "Thread initialized");
thd->version=refresh_version;
thd->set_time();
DBUG_RETURN(0);
@@ -600,7 +600,7 @@ pthread_handler_t handle_failsafe_rpl(void *arg)
{
bool break_req_chain = 0;
pthread_cond_wait(&COND_rpl_status, &LOCK_rpl_status);
- thd->proc_info="Processing request";
+ thd_proc_info(thd, "Processing request");
while (!break_req_chain)
{
switch (rpl_status) {
@@ -944,7 +944,7 @@ bool load_master_data(THD* thd)
goto err;
}
}
- thd->proc_info="purging old relay logs";
+ thd_proc_info(thd, "purging old relay logs");
if (purge_relay_logs(&active_mi->rli,thd,
0 /* not only reset, but also reinit */,
&errmsg))
@@ -971,7 +971,7 @@ bool load_master_data(THD* thd)
flush_relay_log_info(&active_mi->rli);
pthread_cond_broadcast(&active_mi->rli.data_cond);
pthread_mutex_unlock(&active_mi->rli.data_lock);
- thd->proc_info = "starting slave";
+ thd_proc_info(thd, "starting slave");
if (restart_thread_mask)
{
error=start_slave_threads(0 /* mutex not needed */,
@@ -983,7 +983,7 @@ bool load_master_data(THD* thd)
err:
unlock_slave_threads(active_mi);
pthread_mutex_unlock(&LOCK_active_mi);
- thd->proc_info = 0;
+ thd_proc_info(thd, 0);
mysql_close(&mysql); // safe to call since we always do mysql_init()
if (!error)
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 46c2a775d8a..49d224595c5 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -544,6 +544,12 @@ static sys_var_thd_bit sys_unique_checks("unique_checks", 0,
set_option_bit,
OPTION_RELAXED_UNIQUE_CHECKS,
1);
+#ifdef ENABLED_PROFILING
+static sys_var_thd_bit sys_profiling("profiling", NULL, set_option_bit,
+ ulonglong(OPTION_PROFILING));
+static sys_var_thd_ulong sys_profiling_history_size("profiling_history_size",
+ &SV::profiling_history_size);
+#endif
/* Local state variables */
@@ -700,6 +706,10 @@ sys_var *sys_variables[]=
&sys_optimizer_prune_level,
&sys_optimizer_search_depth,
&sys_preload_buff_size,
+#ifdef ENABLED_PROFILING
+ &sys_profiling,
+ &sys_profiling_history_size,
+#endif
&sys_pseudo_thread_id,
&sys_query_alloc_block_size,
&sys_query_cache_size,
@@ -1011,6 +1021,10 @@ struct show_var_st init_vars[]= {
{"pid_file", (char*) pidfile_name, SHOW_CHAR},
{"port", (char*) &mysqld_port, SHOW_INT},
{sys_preload_buff_size.name, (char*) &sys_preload_buff_size, SHOW_SYS},
+#ifdef ENABLED_PROFILING
+ {sys_profiling.name, (char*) &sys_profiling, SHOW_SYS},
+ {sys_profiling_history_size.name, (char*) &sys_profiling_history_size, SHOW_SYS},
+#endif
{"protocol_version", (char*) &protocol_version, SHOW_INT},
{sys_query_alloc_block_size.name, (char*) &sys_query_alloc_block_size,
SHOW_SYS},
@@ -2873,14 +2887,14 @@ static bool set_option_autocommit(THD *thd, set_var *var)
if ((org_options & OPTION_NOT_AUTOCOMMIT))
{
/* We changed to auto_commit mode */
- thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
+ thd->options&= ~(OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
thd->server_status|= SERVER_STATUS_AUTOCOMMIT;
if (ha_commit(thd))
return 1;
}
else
{
- thd->options&= ~(ulong) (OPTION_STATUS_NO_TRANS_UPDATE);
+ thd->options&= ~(OPTION_STATUS_NO_TRANS_UPDATE);
thd->server_status&= ~SERVER_STATUS_AUTOCOMMIT;
}
}
diff --git a/sql/slave.cc b/sql/slave.cc
index ba8c3ff902a..1700bd9326b 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -1560,8 +1560,8 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
/* Create the table. We do not want to log the "create table" statement */
save_options = thd->options;
- thd->options &= ~(ulong) (OPTION_BIN_LOG);
- thd->proc_info = "Creating table from master dump";
+ thd->options &= ~ (OPTION_BIN_LOG);
+ thd_proc_info(thd, "Creating table from master dump");
// save old db in case we are creating in a different database
save_db = thd->db;
save_db_length= thd->db_length;
@@ -1575,7 +1575,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
if (thd->query_error)
goto err; // mysql_parse took care of the error send
- thd->proc_info = "Opening master dump table";
+ thd_proc_info(thd, "Opening master dump table");
tables.lock_type = TL_WRITE;
if (!open_ltable(thd, &tables, TL_WRITE))
{
@@ -1584,7 +1584,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
}
file = tables.table->file;
- thd->proc_info = "Reading master dump table data";
+ thd_proc_info(thd, "Reading master dump table data");
/* Copy the data file */
if (file->net_read_dump(net))
{
@@ -1596,7 +1596,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
check_opt.init();
check_opt.flags|= T_VERY_SILENT | T_CALC_CHECKSUM | T_QUICK;
- thd->proc_info = "Rebuilding the index on master dump table";
+ thd_proc_info(thd, "Rebuilding the index on master dump table");
/*
We do not want repair() to spam us with messages
just send them to the error log, and report the failure in case of
@@ -2926,9 +2926,9 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
#endif
if (thd_type == SLAVE_THD_SQL)
- thd->proc_info= "Waiting for the next event in relay log";
+ thd_proc_info(thd, "Waiting for the next event in relay log");
else
- thd->proc_info= "Waiting for master update";
+ thd_proc_info(thd, "Waiting for master update");
thd->version=refresh_version;
thd->set_time();
DBUG_RETURN(0);
@@ -3488,7 +3488,7 @@ slave_begin:
goto err;
}
- thd->proc_info = "Connecting to master";
+ thd_proc_info(thd, "Connecting to master");
// we can get killed during safe_connect
if (!safe_connect(thd, mysql, mi))
{
@@ -3515,7 +3515,7 @@ connected:
// TODO: the assignment below should be under mutex (5.0)
mi->slave_running= MYSQL_SLAVE_RUN_CONNECT;
thd->slave_net = &mysql->net;
- thd->proc_info = "Checking master version";
+ thd_proc_info(thd, "Checking master version");
if (get_master_version_and_clock(mysql, mi))
goto err;
@@ -3526,7 +3526,7 @@ connected:
If fails, this is not fatal - we just print the error message and go
on with life.
*/
- thd->proc_info = "Registering slave on master";
+ thd_proc_info(thd, "Registering slave on master");
if (register_slave_on_master(mysql) || update_slave_list(mysql, mi))
goto err;
}
@@ -3535,7 +3535,7 @@ connected:
while (!io_slave_killed(thd,mi))
{
bool suppress_warnings= 0;
- thd->proc_info = "Requesting binlog dump";
+ thd_proc_info(thd, "Requesting binlog dump");
if (request_dump(mysql, mi, &suppress_warnings))
{
sql_print_error("Failed on request_dump()");
@@ -3547,7 +3547,7 @@ dump");
}
mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT;
- thd->proc_info= "Waiting to reconnect after a failed binlog dump request";
+ thd_proc_info(thd, "Waiting to reconnect after a failed binlog dump request");
#ifdef SIGNAL_WITH_VIO_CLOSE
thd->clear_active_vio();
#endif
@@ -3571,7 +3571,7 @@ dump");
goto err;
}
- thd->proc_info = "Reconnecting after a failed binlog dump request";
+ thd_proc_info(thd, "Reconnecting after a failed binlog dump request");
if (!suppress_warnings)
sql_print_error("Slave I/O thread: failed dump request, \
reconnecting to try again, log '%s' at postion %s", IO_RPL_LOG_NAME,
@@ -3598,7 +3598,7 @@ after reconnect");
important thing is to not confuse users by saying "reading" whereas
we're in fact receiving nothing.
*/
- thd->proc_info= "Waiting for master to send event";
+ thd_proc_info(thd, "Waiting for master to send event");
event_len= read_event(mysql, mi, &suppress_warnings);
if (io_slave_killed(thd,mi))
{
@@ -3626,7 +3626,7 @@ max_allowed_packet",
goto err;
}
mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT;
- thd->proc_info = "Waiting to reconnect after a failed master event read";
+ thd_proc_info(thd, "Waiting to reconnect after a failed master event read");
#ifdef SIGNAL_WITH_VIO_CLOSE
thd->clear_active_vio();
#endif
@@ -3645,7 +3645,7 @@ max_allowed_packet",
reconnect after a failed read");
goto err;
}
- thd->proc_info = "Reconnecting after a failed master event read";
+ thd_proc_info(thd, "Reconnecting after a failed master event read");
if (!suppress_warnings)
sql_print_information("Slave I/O thread: Failed reading log event, \
reconnecting to retry, log '%s' position %s", IO_RPL_LOG_NAME,
@@ -3662,7 +3662,7 @@ reconnect done to recover from failed read");
} // if (event_len == packet_error)
retry_count=0; // ok event, reset retry counter
- thd->proc_info = "Queueing master event to the relay log";
+ thd_proc_info(thd, "Queueing master event to the relay log");
if (queue_event(mi,(const char*)mysql->net.read_pos + 1,
event_len))
{
@@ -3744,7 +3744,7 @@ err:
mi->mysql=0;
}
write_ignored_events_info_to_relay_log(thd, mi);
- thd->proc_info = "Waiting for slave mutex on exit";
+ thd_proc_info(thd, "Waiting for slave mutex on exit");
pthread_mutex_lock(&mi->run_lock);
/* Forget the relay log's format */
@@ -3921,7 +3921,7 @@ Slave SQL thread aborted. Can't execute init_slave query");
while (!sql_slave_killed(thd,rli))
{
- thd->proc_info = "Reading event from the relay log";
+ thd_proc_info(thd, "Reading event from the relay log");
DBUG_ASSERT(rli->sql_thd == thd);
THD_CHECK_SENTRY(thd);
if (exec_relay_log_event(thd,rli))
@@ -3953,7 +3953,7 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \
thd->query= 0;
thd->query_length= 0;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
- thd->proc_info = "Waiting for slave mutex on exit";
+ thd_proc_info(thd, "Waiting for slave mutex on exit");
pthread_mutex_lock(&rli->run_lock);
/* We need data_lock, at least to wake up any waiting master_pos_wait() */
pthread_mutex_lock(&rli->data_lock);
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 21225d82188..843f19e1ffe 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -2408,9 +2408,9 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
m_lex->unit.cleanup();
- thd->proc_info="closing tables";
+ thd_proc_info(thd, "closing tables");
close_thread_tables(thd);
- thd->proc_info= 0;
+ thd_proc_info(thd, 0);
if (m_lex->query_tables_own_last)
{
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 1100a452b15..c5d1fd188c4 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -359,7 +359,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
*/
thd->mysys_var->current_mutex= &LOCK_open;
thd->mysys_var->current_cond= &COND_refresh;
- thd->proc_info="Flushing tables";
+ thd_proc_info(thd, "Flushing tables");
close_old_data_files(thd,thd->open_tables,1,1);
mysql_ha_flush(thd, tables, MYSQL_HA_REOPEN_ON_USAGE | MYSQL_HA_FLUSH_ALL,
@@ -401,7 +401,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
pthread_mutex_lock(&thd->mysys_var->mutex);
thd->mysys_var->current_mutex= 0;
thd->mysys_var->current_cond= 0;
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
pthread_mutex_unlock(&thd->mysys_var->mutex);
}
DBUG_RETURN(result);
@@ -602,7 +602,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
good idea to turn off OPTION_TABLE_LOCK flag.
*/
DBUG_ASSERT(thd->lex->requires_prelocking());
- thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
+ thd->options&= ~(OPTION_TABLE_LOCK);
}
DBUG_VOID_RETURN;
@@ -1129,7 +1129,7 @@ void wait_for_refresh(THD *thd)
thd->mysys_var->current_mutex= &LOCK_open;
thd->mysys_var->current_cond= &COND_refresh;
proc_info=thd->proc_info;
- thd->proc_info="Waiting for table";
+ thd_proc_info(thd, "Waiting for table");
if (!thd->killed)
(void) pthread_cond_wait(&COND_refresh,&LOCK_open);
@@ -1137,7 +1137,7 @@ void wait_for_refresh(THD *thd)
pthread_mutex_lock(&thd->mysys_var->mutex);
thd->mysys_var->current_mutex= 0;
thd->mysys_var->current_cond= 0;
- thd->proc_info= proc_info;
+ thd_proc_info(thd, proc_info);
pthread_mutex_unlock(&thd->mysys_var->mutex);
DBUG_VOID_RETURN;
}
@@ -1940,7 +1940,7 @@ bool wait_for_tables(THD *thd)
bool result;
DBUG_ENTER("wait_for_tables");
- thd->proc_info="Waiting for tables";
+ thd_proc_info(thd, "Waiting for tables");
pthread_mutex_lock(&LOCK_open);
while (!thd->killed)
{
@@ -1956,12 +1956,12 @@ bool wait_for_tables(THD *thd)
else
{
/* Now we can open all tables without any interference */
- thd->proc_info="Reopen tables";
+ thd_proc_info(thd, "Reopen tables");
thd->version= refresh_version;
result=reopen_tables(thd,0,0);
}
pthread_mutex_unlock(&LOCK_open);
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
DBUG_RETURN(result);
}
@@ -2262,7 +2262,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
restart:
*counter= 0;
query_tables_last_own= 0;
- thd->proc_info="Opening tables";
+ thd_proc_info(thd, "Opening tables");
/*
If we are not already executing prelocked statement and don't have
@@ -2484,7 +2484,7 @@ process_view_routines:
}
err:
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block
if (query_tables_last_own)
@@ -2558,7 +2558,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type)
bool refresh;
DBUG_ENTER("open_ltable");
- thd->proc_info="Opening table";
+ thd_proc_info(thd, "Opening table");
thd->current_tablenr= 0;
/* open_ltable can be used only for BASIC TABLEs */
table_list->required_type= FRMTYPE_TABLE;
@@ -2592,7 +2592,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type)
table= 0;
}
}
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
DBUG_RETURN(table);
}
@@ -2812,7 +2812,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
{
if (thd->lex->requires_prelocking())
{
- thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
+ thd->options&= ~(OPTION_TABLE_LOCK);
thd->in_lock_tables=0;
}
DBUG_RETURN(-1);
@@ -2845,7 +2845,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
ha_rollback_stmt(thd);
mysql_unlock_tables(thd, thd->locked_tables);
thd->locked_tables= 0;
- thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
+ thd->options&= ~(OPTION_TABLE_LOCK);
DBUG_RETURN(-1);
}
}
@@ -5739,7 +5739,7 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order)
List_iterator<Item_func_match> li(*(select_lex->ftfunc_list));
Item_func_match *ifm;
DBUG_PRINT("info",("Performing FULLTEXT search"));
- thd->proc_info="FULLTEXT initialization";
+ thd_proc_info(thd, "FULLTEXT initialization");
while ((ifm=li++))
ifm->init_search(no_order);
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index d06ac7824fd..dab701cda95 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -315,13 +315,13 @@ TODO list:
#define MUTEX_UNLOCK(M) {DBUG_PRINT("lock", ("mutex unlock 0x%lx",\
(ulong)(M))); pthread_mutex_unlock(M);}
#define RW_WLOCK(M) {DBUG_PRINT("lock", ("rwlock wlock 0x%lx",(ulong)(M))); \
- if (!rw_wrlock(M)) DBUG_PRINT("lock", ("rwlock wlock ok")) \
+ if (!rw_wrlock(M)) DBUG_PRINT("lock", ("rwlock wlock ok")); \
else DBUG_PRINT("lock", ("rwlock wlock FAILED %d", errno)); }
#define RW_RLOCK(M) {DBUG_PRINT("lock", ("rwlock rlock 0x%lx", (ulong)(M))); \
- if (!rw_rdlock(M)) DBUG_PRINT("lock", ("rwlock rlock ok")) \
+ if (!rw_rdlock(M)) DBUG_PRINT("lock", ("rwlock rlock ok")); \
else DBUG_PRINT("lock", ("rwlock wlock FAILED %d", errno)); }
#define RW_UNLOCK(M) {DBUG_PRINT("lock", ("rwlock unlock 0x%lx",(ulong)(M))); \
- if (!rw_unlock(M)) DBUG_PRINT("lock", ("rwlock unlock ok")) \
+ if (!rw_unlock(M)) DBUG_PRINT("lock", ("rwlock unlock ok")); \
else DBUG_PRINT("lock", ("rwlock unlock FAILED %d", errno)); }
#define STRUCT_LOCK(M) {DBUG_PRINT("lock", ("%d struct lock...",__LINE__)); \
pthread_mutex_lock(M);DBUG_PRINT("lock", ("struct lock OK"));}
@@ -669,6 +669,7 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
void query_cache_abort(NET *net)
{
DBUG_ENTER("query_cache_abort");
+ THD *thd= current_thd;
/* See the comment on double-check locking usage above. */
if (net->query_cache_query == 0)
@@ -687,6 +688,7 @@ void query_cache_abort(NET *net)
net->query_cache_query);
if (query_block) // Test if changed by other thread
{
+ thd_proc_info(thd, "storing result in query cache");
DUMP(&query_cache);
BLOCK_LOCK_WR(query_block);
// The following call will remove the lock on query_block
@@ -724,6 +726,7 @@ void query_cache_end_of_result(THD *thd)
query_block= ((Query_cache_block*) thd->net.query_cache_query);
if (query_block)
{
+ thd_proc_info(thd, "storing result in query cache");
DUMP(&query_cache);
BLOCK_LOCK_WR(query_block);
Query_cache_query *header= query_block->query();
@@ -1088,6 +1091,8 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
DBUG_PRINT("qcache", ("No active database"));
}
+ thd_proc_info(thd, "checking query cache for query");
+
// fill all gaps between fields with 0 to get repeatable key
bzero(&flags, QUERY_CACHE_FLAGS_SIZE);
flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG);
@@ -1162,6 +1167,7 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu",
}
// Check access;
+ thd_proc_info(thd, "checking privileges on cached query");
block_table= query_block->table(0);
block_table_end= block_table+query_block->n_tables;
for (; block_table != block_table_end; block_table++)
@@ -1254,6 +1260,7 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu",
Send cached result to client
*/
#ifndef EMBEDDED_LIBRARY
+ thd_proc_info(thd, "sending cached result to client");
do
{
DBUG_PRINT("qcache", ("Results (len: %lu used: %lu headers: %lu)",
@@ -1331,9 +1338,11 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
{
+ THD *thd= current_thd;
DBUG_ENTER("Query_cache::invalidate (changed table list)");
if (tables_used)
{
+ thd_proc_info(thd, "invalidating query cache entries (table list)");
STRUCT_LOCK(&structure_guard_mutex);
if (query_cache_size > 0 && !flush_in_progress)
{
@@ -1364,9 +1373,11 @@ void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
*/
void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used)
{
+ THD *thd= current_thd;
DBUG_ENTER("Query_cache::invalidate_locked_for_write");
if (tables_used)
{
+ thd_proc_info(thd, "invalidating query cache entries (table)");
STRUCT_LOCK(&structure_guard_mutex);
if (query_cache_size > 0 && !flush_in_progress)
{
@@ -1416,6 +1427,7 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
STRUCT_LOCK(&structure_guard_mutex);
if (query_cache_size > 0 && !flush_in_progress)
{
+ thd_proc_info(thd, "invalidating query cache entries (key)");
using_transactions= using_transactions &&
(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
if (using_transactions) // used for innodb => has_transactions() is TRUE
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index a94f903a47b..05a064ad966 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -253,6 +253,9 @@ THD::THD()
init();
/* Initialize sub structures */
init_sql_alloc(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE);
+#ifdef ENABLED_PROFILING
+ profiling.set_thd(this);
+#endif
user_connect=(USER_CONN *)0;
hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
(hash_get_key) get_var_key,
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 99803802001..173e77883a6 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -533,6 +533,7 @@ struct system_variables
ulong optimizer_prune_level;
ulong optimizer_search_depth;
ulong preload_buff_size;
+ ulong profiling_history_size;
ulong query_cache_type;
ulong read_buff_size;
ulong read_rnd_buff_size;
@@ -1214,6 +1215,9 @@ public:
Points to info-string that we show in SHOW PROCESSLIST
You are supposed to update thd->proc_info only if you have coded
a time-consuming piece that MySQL can get stuck in for a long time.
+
+ Set it using the thd_proc_info(THD *thread, const char *message)
+ macro/function.
*/
const char *proc_info;
@@ -1368,6 +1372,10 @@ public:
List <MYSQL_ERROR> warn_list;
uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END];
uint total_warn_count;
+#ifdef ENABLED_PROFILING
+ PROFILING profiling;
+#endif
+
/*
Id of current query. Statement can be reused to execute several queries
query_id is global in context of the whole MySQL server.
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 19f3135c594..bbbb810abf6 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -54,7 +54,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
}
- thd->proc_info="init";
+ thd_proc_info(thd, "init");
table->map=1;
if (mysql_prepare_delete(thd, table_list, &conds))
@@ -214,7 +214,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
deleted=0L;
init_ftfuncs(thd, select_lex, 1);
- thd->proc_info="updating";
+ thd_proc_info(thd, "updating");
if (table->triggers)
{
@@ -282,7 +282,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
}
if (thd->killed && !error)
error= 1; // Aborted
- thd->proc_info="end";
+ thd_proc_info(thd, "end");
end_read_record(&info);
free_io_cache(table); // Will not do any harm
if (options & OPTION_QUICK)
@@ -512,7 +512,7 @@ multi_delete::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
DBUG_ENTER("multi_delete::prepare");
unit= u;
do_delete= 1;
- thd->proc_info="deleting from main table";
+ thd_proc_info(thd, "deleting from main table");
DBUG_RETURN(0);
}
@@ -788,7 +788,7 @@ int multi_delete::do_deletes()
bool multi_delete::send_eof()
{
- thd->proc_info="deleting from reference tables";
+ thd_proc_info(thd, "deleting from reference tables");
/* Does deletes for the last n - 1 tables, returns 0 if ok */
int local_error= do_deletes(); // returns 0 if success
@@ -797,7 +797,7 @@ bool multi_delete::send_eof()
local_error= local_error || error;
/* reset used flags */
- thd->proc_info="end";
+ thd_proc_info(thd, "end");
/*
We must invalidate the query cache before binlog writing and
@@ -942,7 +942,7 @@ end:
/* Probably InnoDB table */
ulonglong save_options= thd->options;
table_list->lock_type= TL_WRITE;
- thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT);
+ thd->options&= ~(OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT);
ha_enable_transaction(thd, FALSE);
mysql_init_select(thd->lex);
error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0,
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 955f83de25e..7cfdce150a8 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -515,7 +515,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
if (res || thd->is_fatal_error)
DBUG_RETURN(TRUE);
- thd->proc_info="init";
+ thd_proc_info(thd, "init");
thd->used_tables=0;
values= its++;
value_count= values->elements;
@@ -600,7 +600,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
error=0;
id=0;
- thd->proc_info="update";
+ thd_proc_info(thd, "update");
if (duplic != DUP_ERROR || ignore)
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
if (duplic == DUP_REPLACE)
@@ -798,7 +798,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
thd->lock=0;
}
}
- thd->proc_info="end";
+ thd_proc_info(thd, "end");
table->next_number_field=0;
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
thd->next_insert_id=0; // Reset this if wrongly used
@@ -1553,7 +1553,7 @@ I_List<delayed_insert> delayed_threads;
delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list)
{
- thd->proc_info="waiting for delay_list";
+ thd_proc_info(thd, "waiting for delay_list");
pthread_mutex_lock(&LOCK_delayed_insert); // Protect master list
I_List_iterator<delayed_insert> it(delayed_threads);
delayed_insert *tmp;
@@ -1590,7 +1590,7 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list)
*/
if (delayed_insert_threads >= thd->variables.max_insert_delayed_threads)
DBUG_RETURN(0);
- thd->proc_info="Creating delayed handler";
+ thd_proc_info(thd, "Creating delayed handler");
pthread_mutex_lock(&LOCK_delayed_create);
/*
The first search above was done without LOCK_delayed_create.
@@ -1632,13 +1632,13 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list)
}
/* Wait until table is open */
- thd->proc_info="waiting for handler open";
+ thd_proc_info(thd, "waiting for handler open");
while (!tmp->thd.killed && !tmp->table && !thd->killed)
{
pthread_cond_wait(&tmp->cond_client,&tmp->mutex);
}
pthread_mutex_unlock(&tmp->mutex);
- thd->proc_info="got old table";
+ thd_proc_info(thd, "got old table");
if (tmp->thd.killed)
{
if (tmp->thd.is_fatal_error)
@@ -1698,13 +1698,13 @@ TABLE *delayed_insert::get_local_table(THD* client_thd)
tables_in_use++;
if (!thd.lock) // Table is not locked
{
- client_thd->proc_info="waiting for handler lock";
+ thd_proc_info(client_thd, "waiting for handler lock");
pthread_cond_signal(&cond); // Tell handler to lock table
while (!dead && !thd.lock && ! client_thd->killed)
{
pthread_cond_wait(&cond_client,&mutex);
}
- client_thd->proc_info="got handler lock";
+ thd_proc_info(client_thd, "got handler lock");
if (client_thd->killed)
goto error;
if (dead)
@@ -1722,7 +1722,7 @@ TABLE *delayed_insert::get_local_table(THD* client_thd)
bytes. Since the table copy is used for creating one record only,
the other record buffers and alignment are unnecessary.
*/
- client_thd->proc_info="allocating local table";
+ thd_proc_info(client_thd, "allocating local table");
copy= (TABLE*) client_thd->alloc(sizeof(*copy)+
(table->s->fields+1)*sizeof(Field**)+
table->s->reclength);
@@ -1802,11 +1802,11 @@ static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic, bool igno
delayed_insert *di=thd->di;
DBUG_ENTER("write_delayed");
- thd->proc_info="waiting for handler insert";
+ thd_proc_info(thd, "waiting for handler insert");
pthread_mutex_lock(&di->mutex);
while (di->stacked_inserts >= delayed_queue_size && !thd->killed)
pthread_cond_wait(&di->cond_client,&di->mutex);
- thd->proc_info="storing row into queue";
+ thd_proc_info(thd, "storing row into queue");
if (thd->killed || !(row= new delayed_row(duplic, ignore, log_on)))
goto err;
@@ -2015,7 +2015,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
/* Information for pthread_kill */
di->thd.mysys_var->current_mutex= &di->mutex;
di->thd.mysys_var->current_cond= &di->cond;
- di->thd.proc_info="Waiting for INSERT";
+ thd_proc_info(&(di->thd), "Waiting for INSERT");
DBUG_PRINT("info",("Waiting for someone to insert rows"));
while (!thd->killed)
@@ -2050,7 +2050,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
pthread_mutex_unlock(&di->thd.mysys_var->mutex);
pthread_mutex_lock(&di->mutex);
}
- di->thd.proc_info=0;
+ thd_proc_info(&(di->thd), 0);
if (di->tables_in_use && ! thd->lock)
{
@@ -2169,7 +2169,7 @@ bool delayed_insert::handle_inserts(void)
table->next_number_field=table->found_next_number_field;
- thd.proc_info="upgrading lock";
+ thd_proc_info(&thd, "upgrading lock");
if (thr_upgrade_write_delay_lock(*thd.lock->locks))
{
/* This can only happen if thread is killed by shutdown */
@@ -2177,7 +2177,7 @@ bool delayed_insert::handle_inserts(void)
goto err;
}
- thd.proc_info="insert";
+ thd_proc_info(&thd, "insert");
max_rows= delayed_insert_limit;
if (thd.killed || table->s->version != refresh_version)
{
@@ -2295,7 +2295,7 @@ bool delayed_insert::handle_inserts(void)
{
if (tables_in_use)
pthread_cond_broadcast(&cond_client); // If waiting clients
- thd.proc_info="reschedule";
+ thd_proc_info(&thd, "reschedule");
pthread_mutex_unlock(&mutex);
if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
{
@@ -2314,14 +2314,14 @@ bool delayed_insert::handle_inserts(void)
if (!using_bin_log)
table->file->extra(HA_EXTRA_WRITE_CACHE);
pthread_mutex_lock(&mutex);
- thd.proc_info="insert";
+ thd_proc_info(&thd, "insert");
}
if (tables_in_use)
pthread_cond_broadcast(&cond_client); // If waiting clients
}
}
- thd.proc_info=0;
+ thd_proc_info(&thd, 0);
table->next_number_field=0;
pthread_mutex_unlock(&mutex);
if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 3be844b6761..5338e72fc73 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -184,7 +184,7 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->proc_list.first= 0;
lex->escape_used= FALSE;
lex->reset_query_tables_list(FALSE);
-
+ lex->profile_options= PROFILE_NONE;
lex->nest_level=0 ;
lex->allow_sum_func= 0;
lex->in_sum_func= NULL;
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index de7de0d46e9..56465872301 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -94,8 +94,13 @@ enum enum_sql_command {
SQLCOM_XA_START, SQLCOM_XA_END, SQLCOM_XA_PREPARE,
SQLCOM_XA_COMMIT, SQLCOM_XA_ROLLBACK, SQLCOM_XA_RECOVER,
SQLCOM_SHOW_PROC_CODE, SQLCOM_SHOW_FUNC_CODE,
- /* This should be the last !!! */
+ SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES,
+ /*
+ When a command is added here, be sure it's also added in mysqld.cc
+ in "struct show_var_st status_vars[]= {" ...
+ */
+ /* This should be the last !!! */
SQLCOM_END
};
@@ -1001,6 +1006,9 @@ typedef struct st_lex : public Query_tables_list
enum enum_var_type option_type;
enum enum_view_create_mode create_view_mode;
enum enum_drop_mode drop_mode;
+
+ uint profile_query_id;
+ uint profile_options;
uint uint_geom_type;
uint grant, grant_tot_col, which_columns;
uint fk_delete_opt, fk_update_opt, fk_match_option;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 1b8bfd38fc4..34702dce957 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -140,14 +140,14 @@ static bool end_active_trans(THD *thd)
if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN |
OPTION_TABLE_LOCK))
{
- DBUG_PRINT("info",("options: 0x%lx", (ulong) thd->options));
+ DBUG_PRINT("info",("options: 0x%llx", thd->options));
/* Safety if one did "drop table" on locked tables */
if (!thd->locked_tables)
thd->options&= ~OPTION_TABLE_LOCK;
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
if (ha_commit(thd))
error=1;
- thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
+ thd->options&= ~(OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
}
DBUG_RETURN(error);
}
@@ -171,7 +171,7 @@ static bool begin_trans(THD *thd)
else
{
LEX *lex= thd->lex;
- thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
+ thd->options= ((thd->options & ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
OPTION_BEGIN);
thd->server_status|= SERVER_STATUS_IN_TRANS;
if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT)
@@ -1056,7 +1056,7 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var,
Vio* save_vio;
ulong save_client_capabilities;
- thd->proc_info= "Execution of init_command";
+ thd_proc_info(thd, "Execution of init_command");
/*
We need to lock init_command_var because
during execution of init_command_var query
@@ -1160,7 +1160,7 @@ pthread_handler_t handle_one_connection(void *arg)
net->compress=1; // Use compression
thd->version= refresh_version;
- thd->proc_info= 0;
+ thd_proc_info(thd, 0);
thd->command= COM_SLEEP;
thd->init_for_queries();
@@ -1176,7 +1176,7 @@ pthread_handler_t handle_one_connection(void *arg)
sctx->host_or_ip, "init_connect command failed");
sql_print_warning("%s", net->last_error);
}
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
thd->init_for_queries();
}
@@ -1262,7 +1262,7 @@ pthread_handler_t handle_bootstrap(void *arg)
if (thd->variables.max_join_size == HA_POS_ERROR)
thd->options |= OPTION_BIG_SELECTS;
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
thd->version=refresh_version;
thd->security_ctx->priv_user=
thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
@@ -1463,7 +1463,7 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion)
*/
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
res= ha_commit(thd);
- thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
+ thd->options&= ~(OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
break;
case COMMIT_RELEASE:
do_release= 1; /* fall through */
@@ -1480,7 +1480,7 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion)
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
if (ha_rollback(thd))
res= -1;
- thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
+ thd->options&= ~(OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
if (!res && (completion == ROLLBACK_AND_CHAIN))
res= begin_trans(thd);
break;
@@ -2126,7 +2126,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (thd->lock || thd->open_tables || thd->derived_tables ||
thd->prelocked_mode)
{
- thd->proc_info="closing tables";
+ thd_proc_info(thd, "closing tables");
close_thread_tables(thd); /* Free tables */
}
/*
@@ -2149,9 +2149,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
log_slow_statement(thd);
- thd->proc_info="cleaning up";
+ thd_proc_info(thd, "cleaning up");
VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
thd->command=COM_SLEEP;
thd->query=0;
thd->query_length=0;
@@ -2184,7 +2184,7 @@ void log_slow_statement(THD *thd)
*/
if (thd->enable_slow_log && !thd->user_time)
{
- thd->proc_info="logging slow query";
+ thd_proc_info(thd, "logging slow query");
if ((ulong) (thd->start_time - thd->time_after_lock) >
thd->variables.long_query_time ||
@@ -2194,6 +2194,7 @@ void log_slow_statement(THD *thd)
/* == SQLCOM_END unless this is a SHOW command */
thd->lex->orig_sql_command == SQLCOM_END)
{
+ thd_proc_info(thd, "logging slow query");
thd->status_var.long_query_count++;
mysql_slow_log.write(thd, thd->query, thd->query_length, start_of_query);
}
@@ -2743,6 +2744,37 @@ mysql_execute_command(THD *thd)
(1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR));
break;
}
+ case SQLCOM_SHOW_PROFILES:
+ {
+#ifdef ENABLED_PROFILING
+ thd->profiling.store();
+ thd->profiling.discard();
+ res= thd->profiling.show_profiles();
+ if (res)
+ goto error;
+#else
+ my_error(ER_FEATURE_DISABLED, MYF(0), "SHOW PROFILES", "enable-profiling");
+ goto error;
+#endif
+ break;
+ }
+ case SQLCOM_SHOW_PROFILE:
+ {
+#ifdef ENABLED_PROFILING
+ thd->profiling.store();
+ thd->profiling.discard(); // will get re-enabled by reset()
+ if (lex->profile_query_id != 0)
+ res= thd->profiling.show(lex->profile_options, lex->profile_query_id);
+ else
+ res= thd->profiling.show_last(lex->profile_options);
+ if (res)
+ goto error;
+#else
+ my_error(ER_FEATURE_DISABLED, MYF(0), "SHOW PROFILE", "enable-profiling");
+ goto error;
+#endif
+ break;
+ }
case SQLCOM_SHOW_NEW_MASTER:
{
if (check_global_access(thd, REPL_SLAVE_ACL))
@@ -3659,7 +3691,7 @@ end_with_restore_list:
if (add_item_to_list(thd, new Item_null()))
goto error;
- thd->proc_info="init";
+ thd_proc_info(thd, "init");
if ((res= open_and_lock_tables(thd, all_tables)))
break;
@@ -3814,7 +3846,7 @@ end_with_restore_list:
if (thd->options & OPTION_TABLE_LOCK)
{
end_active_trans(thd);
- thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
+ thd->options&= ~(OPTION_TABLE_LOCK);
}
if (thd->global_read_lock)
unlock_global_read_lock(thd);
@@ -3840,7 +3872,7 @@ end_with_restore_list:
send_ok(thd);
}
else
- thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
+ thd->options&= ~(OPTION_TABLE_LOCK);
thd->in_lock_tables=0;
break;
case SQLCOM_CREATE_DB:
@@ -4939,7 +4971,7 @@ create_sp_error:
thd->transaction.xid_state.xa_state=XA_ACTIVE;
thd->transaction.xid_state.xid.set(thd->lex->xid);
xid_cache_insert(&thd->transaction.xid_state);
- thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
+ thd->options= ((thd->options & ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
OPTION_BEGIN);
thd->server_status|= SERVER_STATUS_IN_TRANS;
send_ok(thd);
@@ -5033,7 +5065,7 @@ create_sp_error:
xa_state_names[thd->transaction.xid_state.xa_state]);
break;
}
- thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
+ thd->options&= ~(OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
xid_cache_delete(&thd->transaction.xid_state);
thd->transaction.xid_state.xa_state=XA_NOTR;
@@ -5063,7 +5095,7 @@ create_sp_error:
my_error(ER_XAER_RMERR, MYF(0));
else
send_ok(thd);
- thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
+ thd->options&= ~(OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
xid_cache_delete(&thd->transaction.xid_state);
thd->transaction.xid_state.xa_state=XA_NOTR;
@@ -5078,7 +5110,7 @@ create_sp_error:
send_ok(thd);
break;
}
- thd->proc_info="query end";
+ thd_proc_info(thd, "query end");
/* Two binlog-related cleanups: */
/*
@@ -5249,6 +5281,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
else
save_priv= &dummy;
+ thd_proc_info(thd, "checking permissions");
if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
{
DBUG_PRINT("error",("No database"));
@@ -5697,6 +5730,9 @@ mysql_init_query(THD *thd, uchar *buf, uint length)
DBUG_ENTER("mysql_init_query");
lex_start(thd, buf, length);
mysql_reset_thd_for_next_command(thd);
+#ifdef ENABLED_PROFILING
+ thd->profiling.reset();
+#endif
DBUG_VOID_RETURN;
}
@@ -5738,6 +5774,9 @@ void mysql_reset_thd_for_next_command(THD *thd)
thd->total_warn_count=0; // Warnings for this query
thd->rand_used= 0;
thd->sent_row_count= thd->examined_row_count= 0;
+#ifdef ENABLED_PROFILING
+ thd->profiling.reset();
+#endif
}
DBUG_VOID_RETURN;
}
@@ -5947,7 +5986,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
thd->lex->sphead= 0;
}
lex->unit.cleanup();
- thd->proc_info="freeing items";
+ thd_proc_info(thd, "freeing items");
thd->end_statement();
thd->cleanup_after_query();
DBUG_ASSERT(thd->change_list.is_empty());
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 85092f14624..124d41062f2 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2231,6 +2231,9 @@ void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_execute")))
DBUG_VOID_RETURN;
+#ifdef ENABLED_PROFILING
+ thd->profiling.set_query_source(stmt->query, stmt->query_length);
+#endif
DBUG_PRINT("exec_query", ("%s", stmt->query));
DBUG_PRINT("info",("stmt: %p", stmt));
diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc
new file mode 100644
index 00000000000..91b0b062e4f
--- /dev/null
+++ b/sql/sql_profile.cc
@@ -0,0 +1,804 @@
+/* Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#include "mysql_priv.h"
+#include "my_sys.h"
+
+#define TIME_FLOAT_DIGITS 9
+#define MAX_QUERY_LENGTH 300
+
+bool schema_table_store_record(THD *thd, TABLE *table);
+
+/* Reserved for systems that can't record the function name in source. */
+const char * const _unknown_func_ = "<unknown>";
+
+/**
+ Connects Information_Schema and Profiling.
+*/
+int fill_query_profile_statistics_info(THD *thd, struct st_table_list *tables,
+ Item *cond)
+{
+#ifdef ENABLED_PROFILING
+ return(thd->profiling.fill_statistics_info(thd, tables, cond));
+#else
+ return(1);
+#endif
+}
+
+ST_FIELD_INFO query_profile_statistics_info[]=
+{
+ /* name, length, type, value, maybe_null, old_name */
+ {"QUERY_ID", 20, MYSQL_TYPE_LONG, 0, false, NULL},
+ {"SEQ", 20, MYSQL_TYPE_LONG, 0, false, NULL},
+ {"STATE", 30, MYSQL_TYPE_STRING, 0, false, NULL},
+ {"DURATION", TIME_FLOAT_DIGITS, MYSQL_TYPE_DOUBLE, 0, false, NULL},
+ {"CPU_USER", TIME_FLOAT_DIGITS, MYSQL_TYPE_DOUBLE, 0, true, NULL},
+ {"CPU_SYSTEM", TIME_FLOAT_DIGITS, MYSQL_TYPE_DOUBLE, 0, true, NULL},
+ {"CONTEXT_VOLUNTARY", 20, MYSQL_TYPE_LONG, 0, true, NULL},
+ {"CONTEXT_INVOLUNTARY", 20, MYSQL_TYPE_LONG, 0, true, NULL},
+ {"BLOCK_OPS_IN", 20, MYSQL_TYPE_LONG, 0, true, NULL},
+ {"BLOCK_OPS_OUT", 20, MYSQL_TYPE_LONG, 0, true, NULL},
+ {"MESSAGES_SENT", 20, MYSQL_TYPE_LONG, 0, true, NULL},
+ {"MESSAGES_RECEIVED", 20, MYSQL_TYPE_LONG, 0, true, NULL},
+ {"PAGE_FAULTS_MAJOR", 20, MYSQL_TYPE_LONG, 0, true, NULL},
+ {"PAGE_FAULTS_MINOR", 20, MYSQL_TYPE_LONG, 0, true, NULL},
+ {"SWAPS", 20, MYSQL_TYPE_LONG, 0, true, NULL},
+ {"SOURCE_FUNCTION", 30, MYSQL_TYPE_STRING, 0, true, NULL},
+ {"SOURCE_FILE", 20, MYSQL_TYPE_STRING, 0, true, NULL},
+ {"SOURCE_LINE", 20, MYSQL_TYPE_LONG, 0, true, NULL},
+ {NULL, 0, MYSQL_TYPE_STRING, 0, true, NULL}
+};
+
+#ifdef ENABLED_PROFILING
+
+#define RUSAGE_USEC(tv) ((tv).tv_sec*1000*1000 + (tv).tv_usec)
+#define RUSAGE_DIFF_USEC(tv1, tv2) (RUSAGE_USEC((tv1))-RUSAGE_USEC((tv2)))
+
+PROFILE_ENTRY::PROFILE_ENTRY()
+ :profile(NULL), status(NULL), function(NULL), file(NULL), line(0),
+ time_usecs(0.0), allocated_status_memory(NULL)
+{
+ collect();
+
+ /* The beginning of the query, before any state is set. */
+ set_status("(initialization)", NULL, NULL, 0);
+}
+
+PROFILE_ENTRY::PROFILE_ENTRY(QUERY_PROFILE *profile_arg, const char *status_arg)
+ :profile(profile_arg)
+{
+ collect();
+ set_status(status_arg, NULL, NULL, 0);
+}
+
+PROFILE_ENTRY::PROFILE_ENTRY(QUERY_PROFILE *profile_arg, const char *status_arg,
+ const char *function_arg,
+ const char *file_arg, unsigned int line_arg)
+ :profile(profile_arg)
+{
+ collect();
+ set_status(status_arg, function_arg, file_arg, line_arg);
+}
+
+PROFILE_ENTRY::~PROFILE_ENTRY()
+{
+ if (allocated_status_memory != NULL)
+ my_free(allocated_status_memory, MYF(0));
+ status= function= file= NULL;
+}
+
+void PROFILE_ENTRY::set_status(const char *status_arg, const char *function_arg, const char *file_arg, unsigned int line_arg)
+{
+ size_t sizes[3]; /* 3 == status+function+file */
+ char *cursor;
+
+ /*
+ Compute all the space we'll need to allocate one block for everything
+ we'll need, instead of N mallocs.
+ */
+ sizes[0]= (status_arg == NULL) ? 0 : strlen(status_arg) + 1;
+ sizes[1]= (function_arg == NULL) ? 0 : strlen(function_arg) + 1;
+ sizes[2]= (file_arg == NULL) ? 0 : strlen(file_arg) + 1;
+
+ allocated_status_memory= (char *) my_malloc(sizes[0] + sizes[1] + sizes[2], MYF(0));
+ DBUG_ASSERT(allocated_status_memory != NULL);
+
+ cursor= allocated_status_memory;
+
+ if (status_arg != NULL)
+ {
+ strcpy(cursor, status_arg);
+ status= cursor;
+ cursor+= sizes[0];
+ }
+ else
+ status= NULL;
+
+ if (function_arg != NULL)
+ {
+ strcpy(cursor, function_arg);
+ function= cursor;
+ cursor+= sizes[1];
+ }
+ else
+ function= NULL;
+
+ if (file_arg != NULL)
+ {
+ strcpy(cursor, file_arg);
+ file= cursor;
+ cursor+= sizes[2];
+ }
+ else
+ file= NULL;
+
+ line= line_arg;
+}
+
+void PROFILE_ENTRY::collect()
+{
+ time_usecs= (double) my_getsystime() / 10.0; /* 1 sec was 1e7, now is 1e6 */
+#ifdef HAVE_GETRUSAGE
+ getrusage(RUSAGE_SELF, &rusage);
+#endif
+}
+
+QUERY_PROFILE::QUERY_PROFILE(PROFILING *profiling_arg, char *query_source_arg,
+ uint query_length_arg)
+ :profiling(profiling_arg), server_query_id(0), profiling_query_id(0),
+ query_source(NULL)
+{
+ profile_end= &profile_start;
+ set_query_source(query_source_arg, query_length_arg);
+}
+
+void QUERY_PROFILE::set_query_source(char *query_source_arg,
+ uint query_length_arg)
+{
+ if (! profiling->enabled)
+ return;
+
+ /* Truncate to avoid DoS attacks. */
+ uint length= min(MAX_QUERY_LENGTH, query_length_arg);
+ /* TODO?: Provide a way to include the full text, as in SHOW PROCESSLIST. */
+
+ DBUG_ASSERT(query_source == NULL);
+ if (query_source_arg != NULL)
+ query_source= my_strdup_with_length(query_source_arg, length, MYF(0));
+}
+
+QUERY_PROFILE::~QUERY_PROFILE()
+{
+ while (! entries.is_empty())
+ delete entries.pop();
+
+ if (query_source != NULL)
+ my_free(query_source, MYF(0));
+}
+
+void QUERY_PROFILE::status(const char *status_arg,
+ const char *function_arg= NULL,
+ const char *file_arg= NULL, unsigned int line_arg= 0)
+{
+ THD *thd= profiling->thd;
+ PROFILE_ENTRY *prof;
+ DBUG_ENTER("QUERY_PROFILE::status");
+
+ /* Blank status. Just return, and thd->proc_info will be set blank later. */
+ if (unlikely(status_arg == NULL))
+ DBUG_VOID_RETURN;
+
+ /* If thd->proc_info is currently set to status_arg, don't profile twice. */
+ if (likely((thd->proc_info != NULL) &&
+ ((thd->proc_info == status_arg) ||
+ (strcmp(thd->proc_info, status_arg) == 0))))
+ {
+ DBUG_VOID_RETURN;
+ }
+
+ /* Is this the same query as our profile currently contains? */
+ if (unlikely((thd->query_id != server_query_id) && !thd->spcont))
+ reset();
+
+ if (function_arg && file_arg)
+ {
+ if ((profile_end= prof= new PROFILE_ENTRY(this, status_arg, function_arg,
+ file_arg, line_arg)))
+ entries.push_back(prof);
+ }
+ else
+ {
+ if ((profile_end= prof= new PROFILE_ENTRY(this, status_arg)))
+ entries.push_back(prof);
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+void QUERY_PROFILE::reset()
+{
+ DBUG_ENTER("QUERY_PROFILE::reset");
+ if (likely(profiling->thd->query_id != server_query_id))
+ {
+ server_query_id= profiling->thd->query_id; /* despite name, is global */
+ profile_start.collect();
+
+ while (! entries.is_empty())
+ delete entries.pop();
+ }
+ DBUG_VOID_RETURN;
+}
+
+bool QUERY_PROFILE::show(uint options)
+{
+ THD *thd= profiling->thd;
+ List<Item> field_list;
+ DBUG_ENTER("QUERY_PROFILE::show");
+
+ field_list.push_back(new Item_empty_string("Status", MYSQL_ERRMSG_SIZE));
+ field_list.push_back(new Item_return_int("Duration", TIME_FLOAT_DIGITS,
+ MYSQL_TYPE_DOUBLE));
+
+ if (options & PROFILE_CPU)
+ {
+ field_list.push_back(new Item_return_int("CPU_user", TIME_FLOAT_DIGITS,
+ MYSQL_TYPE_DOUBLE));
+ field_list.push_back(new Item_return_int("CPU_system", TIME_FLOAT_DIGITS,
+ MYSQL_TYPE_DOUBLE));
+ }
+
+ if (options & PROFILE_MEMORY)
+ {
+ }
+
+ if (options & PROFILE_CONTEXT)
+ {
+ field_list.push_back(new Item_return_int("Context_voluntary", 10,
+ MYSQL_TYPE_LONG));
+ field_list.push_back(new Item_return_int("Context_involuntary", 10,
+ MYSQL_TYPE_LONG));
+ }
+
+ if (options & PROFILE_BLOCK_IO)
+ {
+ field_list.push_back(new Item_return_int("Block_ops_in", 10,
+ MYSQL_TYPE_LONG));
+ field_list.push_back(new Item_return_int("Block_ops_out", 10,
+ MYSQL_TYPE_LONG));
+ }
+
+ if (options & PROFILE_IPC)
+ {
+ field_list.push_back(new Item_return_int("Messages_sent", 10,
+ MYSQL_TYPE_LONG));
+ field_list.push_back(new Item_return_int("Messages_received", 10,
+ MYSQL_TYPE_LONG));
+ }
+
+ if (options & PROFILE_PAGE_FAULTS)
+ {
+ field_list.push_back(new Item_return_int("Page_faults_major", 10,
+ MYSQL_TYPE_LONG));
+ field_list.push_back(new Item_return_int("Page_faults_minor", 10,
+ MYSQL_TYPE_LONG));
+ }
+
+ if (options & PROFILE_SWAPS)
+ {
+ field_list.push_back(new Item_return_int("Swaps", 10, MYSQL_TYPE_LONG));
+ }
+
+ if (options & PROFILE_SOURCE)
+ {
+ field_list.push_back(new Item_empty_string("Source_function",
+ MYSQL_ERRMSG_SIZE));
+ field_list.push_back(new Item_empty_string("Source_file",
+ MYSQL_ERRMSG_SIZE));
+ field_list.push_back(new Item_return_int("Source_line", 10,
+ MYSQL_TYPE_LONG));
+ }
+
+ if (thd->protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ DBUG_RETURN(TRUE);
+
+ Protocol *protocol= thd->protocol;
+ SELECT_LEX *sel= &thd->lex->select_lex;
+ SELECT_LEX_UNIT *unit= &thd->lex->unit;
+ ha_rows idx= 0;
+ unit->set_limit(sel);
+ PROFILE_ENTRY *previous= &profile_start;
+
+ PROFILE_ENTRY *entry;
+ void *iterator;
+ for (iterator= entries.new_iterator();
+ iterator != NULL;
+ iterator= entries.iterator_next(iterator))
+ {
+ entry= entries.iterator_value(iterator);
+
+#ifdef HAVE_GETRUSAGE
+ struct rusage *rusage= &(entry->rusage);
+#endif
+ String elapsed;
+
+ if (++idx <= unit->offset_limit_cnt)
+ continue;
+ if (idx > unit->select_limit_cnt)
+ break;
+
+ protocol->prepare_for_resend();
+
+ /*
+ This entry, n, has a point in time, T(n), and a status phrase, S(n). The
+ status phrase S(n) describes the period of time that begins at T(n). The
+ previous status phrase S(n-1) describes the period of time that starts at
+ T(n-1) and ends at T(n). Since we want to describe the time that a status
+ phrase took T(n)-T(n-1), this line must describe the previous status.
+ */
+ protocol->store(previous->status, strlen(previous->status),
+ system_charset_info);
+ protocol->store((double)(entry->time_usecs -
+ previous->time_usecs)/(1000.0*1000),
+ (uint32) TIME_FLOAT_DIGITS-1, &elapsed);
+
+ if (options & PROFILE_CPU)
+ {
+#ifdef HAVE_GETRUSAGE
+ String cpu_utime, cpu_stime;
+ protocol->store((double)(RUSAGE_DIFF_USEC(rusage->ru_utime,
+ previous->rusage.ru_utime))/(1000.0*1000),
+ (uint32) TIME_FLOAT_DIGITS-1, &cpu_utime);
+ protocol->store((double)(RUSAGE_DIFF_USEC(rusage->ru_stime,
+ previous->rusage.ru_stime))/(1000.0*1000),
+ (uint32) TIME_FLOAT_DIGITS-1, &cpu_stime);
+#else
+ protocol->store_null();
+ protocol->store_null();
+#endif
+ }
+
+ if (options & PROFILE_CONTEXT)
+ {
+#ifdef HAVE_GETRUSAGE
+ protocol->store((uint32)(rusage->ru_nvcsw - previous->rusage.ru_nvcsw));
+ protocol->store((uint32)(rusage->ru_nivcsw - previous->rusage.ru_nivcsw));
+#else
+ protocol->store_null();
+ protocol->store_null();
+#endif
+ }
+
+ if (options & PROFILE_BLOCK_IO)
+ {
+#ifdef HAVE_GETRUSAGE
+ protocol->store((uint32)(rusage->ru_inblock - previous->rusage.ru_inblock));
+ protocol->store((uint32)(rusage->ru_oublock - previous->rusage.ru_oublock));
+#else
+ protocol->store_null();
+ protocol->store_null();
+#endif
+ }
+
+ if (options & PROFILE_IPC)
+ {
+#ifdef HAVE_GETRUSAGE
+ protocol->store((uint32)(rusage->ru_msgsnd - previous->rusage.ru_msgsnd));
+ protocol->store((uint32)(rusage->ru_msgrcv - previous->rusage.ru_msgrcv));
+#else
+ protocol->store_null();
+ protocol->store_null();
+#endif
+ }
+
+ if (options & PROFILE_PAGE_FAULTS)
+ {
+#ifdef HAVE_GETRUSAGE
+ protocol->store((uint32)(rusage->ru_majflt - previous->rusage.ru_majflt));
+ protocol->store((uint32)(rusage->ru_minflt - previous->rusage.ru_minflt));
+#else
+ protocol->store_null();
+ protocol->store_null();
+#endif
+ }
+
+ if (options & PROFILE_SWAPS)
+ {
+#ifdef HAVE_GETRUSAGE
+ protocol->store((uint32)(rusage->ru_nswap - previous->rusage.ru_nswap));
+#else
+ protocol->store_null();
+#endif
+ }
+
+ if (options & PROFILE_SOURCE)
+ {
+ if ((entry->function != NULL) && (entry->file != NULL))
+ {
+ protocol->store(entry->function, strlen(entry->function),
+ system_charset_info);
+ protocol->store(entry->file, strlen(entry->file), system_charset_info);
+ protocol->store((uint32) entry->line);
+ } else {
+ protocol->store_null();
+ protocol->store_null();
+ protocol->store_null();
+ }
+ }
+
+ if (protocol->write())
+ DBUG_RETURN(TRUE);
+
+ previous= entry;
+ }
+ send_eof(thd);
+ DBUG_RETURN(FALSE);
+}
+
+PROFILING::PROFILING()
+ :profile_id_counter(1), keeping(TRUE), enabled(FALSE), current(NULL), last(NULL)
+{
+}
+
+PROFILING::~PROFILING()
+{
+ while (! history.is_empty())
+ delete history.pop();
+
+ if (current != NULL)
+ delete current;
+}
+
+void PROFILING::status_change(const char *status_arg,
+ const char *function_arg,
+ const char *file_arg, unsigned int line_arg)
+{
+ DBUG_ENTER("PROFILING::status_change");
+
+ if (unlikely(enabled))
+ {
+ if (unlikely(current == NULL))
+ reset();
+
+ DBUG_ASSERT(current != NULL);
+
+ current->status(status_arg, function_arg, file_arg, line_arg);
+ }
+
+ thd->proc_info= status_arg;
+ DBUG_VOID_RETURN;
+}
+
+void PROFILING::store()
+{
+ DBUG_ENTER("PROFILING::store");
+
+ /* Already stored */
+ if (unlikely((last != NULL) &&
+ (current != NULL) &&
+ (last->server_query_id == current->server_query_id)))
+ {
+ DBUG_VOID_RETURN;
+ }
+
+ while (history.elements > thd->variables.profiling_history_size)
+ delete history.pop();
+
+ if (likely(((thd)->options & OPTION_PROFILING) == 0))
+ DBUG_VOID_RETURN;
+
+ if (current != NULL)
+ {
+ if (keeping &&
+ (enabled) && /* ON at start? */
+ (((thd)->options & OPTION_PROFILING) != 0) && /* and ON at end? */
+ (current->query_source != NULL) &&
+ (current->query_source[0] != '\0') &&
+ (!current->entries.is_empty()))
+ {
+ current->profiling_query_id= next_profile_id(); /* assign an id */
+
+ last= current; /* never contains something that is not in the history. */
+ history.push_back(current);
+ current= NULL;
+ }
+ else
+ {
+ delete current;
+ current= NULL;
+ }
+ }
+
+ DBUG_ASSERT(current == NULL);
+ if (enabled)
+ current= new QUERY_PROFILE(this, thd->query, thd->query_length);
+
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Store and clean up the old information and get ready to hold info about this
+ new query. This is called very often so it must be very lightweight if
+ profiling is not active.
+*/
+void PROFILING::reset()
+{
+ DBUG_ENTER("PROFILING::reset");
+
+ store();
+
+ if (likely(((thd)->options & OPTION_PROFILING) == 0))
+ {
+ enabled= FALSE;
+ DBUG_VOID_RETURN;
+ }
+ else
+ enabled= TRUE;
+
+ if (current != NULL)
+ current->reset();
+ keep();
+
+ DBUG_VOID_RETURN;
+}
+
+bool PROFILING::show_profiles()
+{
+ DBUG_ENTER("PROFILING::show_profiles");
+ QUERY_PROFILE *prof;
+ List<Item> field_list;
+
+ field_list.push_back(new Item_return_int("Query_ID", 10,
+ MYSQL_TYPE_LONG));
+ field_list.push_back(new Item_return_int("Duration", TIME_FLOAT_DIGITS-1,
+ MYSQL_TYPE_DOUBLE));
+ field_list.push_back(new Item_empty_string("Query", 40));
+
+ if (thd->protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ DBUG_RETURN(TRUE);
+
+ SELECT_LEX *sel= &thd->lex->select_lex;
+ SELECT_LEX_UNIT *unit= &thd->lex->unit;
+ ha_rows idx= 0;
+ Protocol *protocol= thd->protocol;
+
+ unit->set_limit(sel);
+
+ void *iterator;
+ for (iterator= history.new_iterator();
+ iterator != NULL;
+ iterator= history.iterator_next(iterator))
+ {
+ prof= history.iterator_value(iterator);
+
+ String elapsed;
+
+ PROFILE_ENTRY *ps= &prof->profile_start;
+ PROFILE_ENTRY *pe= prof->profile_end;
+
+ if (++idx <= unit->offset_limit_cnt)
+ continue;
+ if (idx > unit->select_limit_cnt)
+ break;
+
+ protocol->prepare_for_resend();
+ protocol->store((uint32)(prof->profiling_query_id));
+ protocol->store((double)(pe->time_usecs - ps->time_usecs)/(1000.0*1000),
+ (uint32) TIME_FLOAT_DIGITS-1, &elapsed);
+ if (prof->query_source != NULL)
+ protocol->store(prof->query_source, strlen(prof->query_source),
+ system_charset_info);
+ else
+ protocol->store_null();
+
+ if (protocol->write())
+ DBUG_RETURN(TRUE);
+ }
+ send_eof(thd);
+ DBUG_RETURN(FALSE);
+}
+
+/*
+ This is an awful hack to let prepared statements tell us the query
+ that they're executing.
+*/
+void PROFILING::set_query_source(char *query_source_arg, uint query_length_arg)
+{
+ DBUG_ENTER("PROFILING::set_query_source");
+
+ /* We can't get this query source through normal means. */
+ DBUG_ASSERT((thd->query == NULL) || (thd->query_length == 0));
+
+ if (current != NULL)
+ current->set_query_source(query_source_arg, query_length_arg);
+ else
+ DBUG_PRINT("info", ("no current profile to send query source to"));
+ DBUG_VOID_RETURN;
+}
+
+bool PROFILING::show(uint options, uint profiling_query_id)
+{
+ DBUG_ENTER("PROFILING::show");
+ QUERY_PROFILE *prof;
+
+ void *iterator;
+ for (iterator= history.new_iterator();
+ iterator != NULL;
+ iterator= history.iterator_next(iterator))
+ {
+ prof= history.iterator_value(iterator);
+
+ if(prof->profiling_query_id == profiling_query_id)
+ DBUG_RETURN(prof->show(options));
+ }
+
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "SHOW PROFILE");
+ DBUG_RETURN(TRUE);
+}
+
+bool PROFILING::show_last(uint options)
+{
+ DBUG_ENTER("PROFILING::show_last");
+ if (!history.is_empty()) {
+ DBUG_RETURN(last->show(options));
+ }
+ DBUG_RETURN(TRUE);
+}
+
+
+/**
+ Fill the information schema table, "query_profile", as defined in show.cc .
+*/
+int PROFILING::fill_statistics_info(THD *thd, struct st_table_list *tables, Item *cond)
+{
+ DBUG_ENTER("PROFILING::fill_statistics_info");
+ TABLE *table= tables->table;
+ ulonglong row_number= 0;
+
+ QUERY_PROFILE *query;
+ /* Go through each query in this thread's stored history... */
+ void *history_iterator;
+ for (history_iterator= history.new_iterator();
+ history_iterator != NULL;
+ history_iterator= history.iterator_next(history_iterator))
+ {
+ query= history.iterator_value(history_iterator);
+ PROFILE_ENTRY *previous= &(query->profile_start);
+
+ /*
+ Because we put all profiling info into a table that may be reordered, let
+ us also include a numbering of each state per query. The query_id and
+ the "seq" together are unique.
+ */
+ ulonglong seq;
+
+ void *entry_iterator;
+ PROFILE_ENTRY *entry;
+ /* ...and for each query, go through all its state-change steps. */
+ for (seq= 0, entry_iterator= query->entries.new_iterator();
+ entry_iterator != NULL;
+ entry_iterator= query->entries.iterator_next(entry_iterator),
+ seq++, previous=entry, row_number++)
+ {
+ entry= query->entries.iterator_value(entry_iterator);
+
+ /* Set default values for this row. */
+ restore_record(table, s->default_values);
+
+ /*
+ The order of these fields is set by the query_profile_statistics_info
+ array.
+ */
+ table->field[0]->store((ulonglong) query->profiling_query_id);
+ table->field[1]->store((ulonglong) seq); /* the step in the sequence */
+ /*
+ This entry, n, has a point in time, T(n), and a status phrase, S(n).
+ The status phrase S(n) describes the period of time that begins at
+ T(n). The previous status phrase S(n-1) describes the period of time
+ that starts at T(n-1) and ends at T(n). Since we want to describe the
+ time that a status phrase took T(n)-T(n-1), this line must describe the
+ previous status.
+ */
+ table->field[2]->store(previous->status, strlen(previous->status),
+ system_charset_info);
+ table->field[3]->store((double)(entry->time_usecs -
+ previous->time_usecs)/(1000*1000));
+
+#ifdef HAVE_GETRUSAGE
+ table->field[4]->store((double)RUSAGE_DIFF_USEC(entry->rusage.ru_utime,
+ previous->rusage.ru_utime)/(1000.0*1000));
+ table->field[4]->set_notnull();
+ table->field[5]->store((double)RUSAGE_DIFF_USEC(entry->rusage.ru_stime,
+ previous->rusage.ru_stime)/(1000.0*1000));
+
+ table->field[5]->set_notnull();
+#else
+ /* TODO: Add CPU-usage info for non-BSD systems */
+#endif
+
+#ifdef HAVE_GETRUSAGE
+ table->field[6]->store((uint32)(entry->rusage.ru_nvcsw -
+ previous->rusage.ru_nvcsw));
+ table->field[6]->set_notnull();
+ table->field[7]->store((uint32)(entry->rusage.ru_nivcsw -
+ previous->rusage.ru_nivcsw));
+ table->field[7]->set_notnull();
+#else
+ /* TODO: Add context switch info for non-BSD systems */
+#endif
+
+#ifdef HAVE_GETRUSAGE
+ table->field[8]->store((uint32)(entry->rusage.ru_inblock -
+ previous->rusage.ru_inblock));
+ table->field[8]->set_notnull();
+ table->field[9]->store((uint32)(entry->rusage.ru_oublock -
+ previous->rusage.ru_oublock));
+ table->field[9]->set_notnull();
+#else
+ /* TODO: Add block IO info for non-BSD systems */
+#endif
+
+#ifdef HAVE_GETRUSAGE
+ table->field[10]->store((uint32)(entry->rusage.ru_msgsnd -
+ previous->rusage.ru_msgsnd), true);
+ table->field[10]->set_notnull();
+ table->field[11]->store((uint32)(entry->rusage.ru_msgrcv -
+ previous->rusage.ru_msgrcv), true);
+ table->field[11]->set_notnull();
+#else
+ /* TODO: Add message info for non-BSD systems */
+#endif
+
+#ifdef HAVE_GETRUSAGE
+ table->field[12]->store((uint32)(entry->rusage.ru_majflt -
+ previous->rusage.ru_majflt), true);
+ table->field[12]->set_notnull();
+ table->field[13]->store((uint32)(entry->rusage.ru_minflt -
+ previous->rusage.ru_minflt), true);
+ table->field[13]->set_notnull();
+#else
+ /* TODO: Add page fault info for non-BSD systems */
+#endif
+
+#ifdef HAVE_GETRUSAGE
+ table->field[14]->store((uint32)(entry->rusage.ru_nswap -
+ previous->rusage.ru_nswap), true);
+ table->field[14]->set_notnull();
+#else
+ /* TODO: Add swap info for non-BSD systems */
+#endif
+
+ if ((entry->function != NULL) && (entry->file != NULL))
+ {
+ table->field[15]->store(entry->function, strlen(entry->function),
+ system_charset_info);
+ table->field[15]->set_notnull();
+ table->field[16]->store(entry->file, strlen(entry->file), system_charset_info);
+ table->field[16]->set_notnull();
+ table->field[17]->store(entry->line, true);
+ table->field[17]->set_notnull();
+ }
+
+ if (schema_table_store_record(thd, table))
+ DBUG_RETURN(1);
+
+ }
+ }
+
+ DBUG_RETURN(0);
+}
+#endif /* ENABLED_PROFILING */
diff --git a/sql/sql_profile.h b/sql/sql_profile.h
new file mode 100644
index 00000000000..b82b5ce090c
--- /dev/null
+++ b/sql/sql_profile.h
@@ -0,0 +1,346 @@
+/* Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _SQL_PROFILE_H
+#define _SQL_PROFILE_H
+
+#if __STDC_VERSION__ < 199901L
+# if __GNUC__ >= 2
+# define __func__ __FUNCTION__
+# else
+# define __func__ _unknown_func_
+extern const char * const _unknown_func_;
+# endif
+#elif defined(_MSC_VER)
+# if _MSC_VER < 1300
+# define __func__ _unknown_func_
+extern const char * const _unknown_func_;
+# else
+# define __func__ __FUNCTION__
+# endif
+#elif defined(__BORLANDC__)
+# define __func__ __FUNC__
+#else
+# define __func__ _unknown_func_
+extern const char * const _unknown_func_;
+#endif
+
+extern ST_FIELD_INFO query_profile_statistics_info[];
+int fill_query_profile_statistics_info(THD *thd, struct st_table_list *tables, Item *cond);
+
+
+#define PROFILE_NONE 0
+#define PROFILE_CPU (1<<0)
+#define PROFILE_MEMORY (1<<1)
+#define PROFILE_BLOCK_IO (1<<2)
+#define PROFILE_CONTEXT (1<<3)
+#define PROFILE_PAGE_FAULTS (1<<4)
+#define PROFILE_IPC (1<<5)
+#define PROFILE_SWAPS (1<<6)
+#define PROFILE_SOURCE (1<<16)
+#define PROFILE_ALL (~0)
+
+
+#ifndef ENABLED_PROFILING
+
+# define thd_proc_info(thd, msg) do { (thd)->proc_info= (msg); } while (0)
+
+#else
+
+# define thd_proc_info(thd, msg) \
+ do { \
+ if (unlikely(((thd)->options & OPTION_PROFILING) != 0)) \
+ { \
+ (thd)->profiling.status_change((msg), __func__, __FILE__, __LINE__); \
+ } \
+ else \
+ { \
+ (thd)->proc_info= (msg); \
+ } \
+ } while (0)
+
+#include "mysql_priv.h"
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+
+class PROFILE_ENTRY;
+class QUERY_PROFILE;
+class PROFILING;
+
+
+/**
+ Implements a persistent FIFO using server List method names. Not
+ thread-safe. Intended to be used on thread-local data only.
+*/
+template <class T> class Queue
+{
+private:
+
+ struct queue_item
+ {
+ T *payload;
+ struct queue_item *next, *previous;
+ };
+
+ struct queue_item *first, *last;
+
+public:
+ Queue()
+ {
+ elements= 0;
+ first= last= NULL;
+ }
+
+ void empty()
+ {
+ struct queue_item *i, *after_i;
+ for (i= first; i != NULL; i= after_i)
+ {
+ after_i= i->next;
+ my_free((char *) i, MYF(0));
+ }
+ elements= 0;
+ }
+
+ ulong elements; /* The count of items in the Queue */
+
+ void push_back(T *payload)
+ {
+ struct queue_item *new_item;
+
+ new_item= (struct queue_item *) my_malloc(sizeof(struct queue_item), MYF(0));
+
+ new_item->payload= payload;
+
+ if (first == NULL)
+ first= new_item;
+ if (last != NULL)
+ {
+ DBUG_ASSERT(last->next == NULL);
+ last->next= new_item;
+ }
+ new_item->previous= last;
+ new_item->next= NULL;
+ last= new_item;
+
+ elements++;
+ }
+
+ T *pop()
+ {
+ struct queue_item *old_item= first;
+ T *ret= NULL;
+
+ if (first == NULL)
+ {
+ DBUG_PRINT("warning", ("tried to pop nonexistent item from Queue"));
+ return NULL;
+ }
+
+ ret= old_item->payload;
+ if (first->next != NULL)
+ first->next->previous= NULL;
+ else
+ last= NULL;
+ first= first->next;
+
+ my_free((char *)old_item, MYF(0));
+ elements--;
+
+ return ret;
+ }
+
+ bool is_empty()
+ {
+ DBUG_ASSERT(((elements > 0) && (first != NULL)) || ((elements == 0) || (first == NULL)));
+ return (elements == 0);
+ }
+
+ void *new_iterator()
+ {
+ return first;
+ }
+
+ void *iterator_next(void *current)
+ {
+ return ((struct queue_item *) current)->next;
+ }
+
+ T *iterator_value(void *current)
+ {
+ return ((struct queue_item *) current)->payload;
+ }
+
+};
+
+
+/**
+ A single entry in a single profile.
+*/
+class PROFILE_ENTRY
+{
+private:
+ friend class QUERY_PROFILE;
+ friend class PROFILING;
+
+ QUERY_PROFILE *profile;
+ char *status;
+#ifdef HAVE_GETRUSAGE
+ struct rusage rusage;
+#endif
+
+ char *function;
+ char *file;
+ unsigned int line;
+
+ double time_usecs;
+ char *allocated_status_memory;
+
+ void set_status(const char *status_arg, const char *function_arg,
+ const char *file_arg, unsigned int line_arg);
+ void clean_up();
+
+ PROFILE_ENTRY();
+ PROFILE_ENTRY(QUERY_PROFILE *profile_arg, const char *status_arg);
+ PROFILE_ENTRY(QUERY_PROFILE *profile_arg, const char *status_arg,
+ const char *function_arg,
+ const char *file_arg, unsigned int line_arg);
+ ~PROFILE_ENTRY();
+ void collect();
+};
+
+
+/**
+ The full profile for a single query, and includes multiple PROFILE_ENTRY
+ objects.
+*/
+class QUERY_PROFILE
+{
+private:
+ friend class PROFILING;
+
+ PROFILING *profiling;
+
+ query_id_t server_query_id; /* Global id. */
+ query_id_t profiling_query_id; /* Session-specific id. */
+ char *query_source;
+ PROFILE_ENTRY profile_start;
+ PROFILE_ENTRY *profile_end;
+ Queue<PROFILE_ENTRY> entries;
+
+
+ QUERY_PROFILE(PROFILING *profiling_arg, char *query_source_arg, uint query_length_arg);
+ ~QUERY_PROFILE();
+
+ void set_query_source(char *query_source_arg, uint query_length_arg);
+
+ /* Add a profile status change to the current profile. */
+ void status(const char *status_arg,
+ const char *function_arg,
+ const char *file_arg, unsigned int line_arg);
+
+ /* Reset the contents of this profile entry. */
+ void reset();
+
+ /* Show this profile. This is called by PROFILING. */
+ bool show(uint options);
+};
+
+
+/**
+ Profiling state for a single THD; contains multiple QUERY_PROFILE objects.
+*/
+class PROFILING
+{
+private:
+ friend class PROFILE_ENTRY;
+ friend class QUERY_PROFILE;
+
+ /*
+ Not the system query_id, but a counter unique to profiling.
+ */
+ query_id_t profile_id_counter;
+ THD *thd;
+ bool keeping;
+ bool enabled;
+
+ QUERY_PROFILE *current;
+ QUERY_PROFILE *last;
+ Queue<QUERY_PROFILE> history;
+
+ query_id_t next_profile_id() { return(profile_id_counter++); }
+
+public:
+ PROFILING();
+ ~PROFILING();
+ void set_query_source(char *query_source_arg, uint query_length_arg);
+
+ /** Reset the current profile and state of profiling for the next query. */
+ void reset();
+
+ /**
+ Do we intend to keep the currently collected profile?
+
+ We don't keep profiles for some commands, such as SHOW PROFILE, SHOW
+ PROFILES, and some SQLCOM commands which aren't useful to profile. The
+ keep() and discard() functions can be called many times, only the final
+ setting when the query finishes is used to decide whether to discard the
+ profile.
+
+ The default is to keep the profile for all queries.
+ */
+ inline void keep() { keeping= true; };
+
+ /**
+ Do we intend to keep the currently collected profile?
+ @see keep()
+ */
+ inline void discard() { keeping= false; };
+
+ /**
+ Stash this profile in the profile history and remove the oldest
+ profile if the history queue is full, as defined by the
+ profiling_history_size system variable.
+ */
+ void store();
+
+ /**
+ Called with every update of the status via thd_proc_info() , and is
+ therefore the main hook into the profiling code.
+ */
+ void status_change(const char *status_arg,
+ const char *function_arg,
+ const char *file_arg, unsigned int line_arg);
+
+ inline void set_thd(THD *thd_arg) { thd= thd_arg; };
+
+ /* SHOW PROFILES */
+ bool show_profiles();
+
+ /* SHOW PROFILE FOR QUERY query_id */
+ bool show(uint options, uint profiling_query_id);
+
+ /* SHOW PROFILE */
+ bool show_last(uint options);
+
+ /* ... from INFORMATION_SCHEMA.PROFILING ... */
+ int fill_statistics_info(THD *thd, struct st_table_list *tables, Item *cond);
+};
+
+# endif /* HAVE_PROFILING */
+#endif /* _SQL_PROFILE_H */
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index b451c612398..0f3b64c7a83 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -647,7 +647,7 @@ impossible position";
if (read_packet)
{
- thd->proc_info = "Sending binlog event to slave";
+ thd_proc_info(thd, "Sending binlog event to slave");
if (my_net_write(net, (char*)packet->ptr(), packet->length()) )
{
errmsg = "Failed on my_net_write()";
@@ -685,7 +685,7 @@ impossible position";
bool loop_breaker = 0;
/* need this to break out of the for loop from switch */
- thd->proc_info = "Finished reading one binlog; switching to next binlog";
+ thd_proc_info(thd, "Finished reading one binlog; switching to next binlog");
switch (mysql_bin_log.find_next_log(&linfo, 1)) {
case LOG_INFO_EOF:
loop_breaker = (flags & BINLOG_DUMP_NON_BLOCK);
@@ -731,14 +731,14 @@ end:
(void)my_close(file, MYF(MY_WME));
send_eof(thd);
- thd->proc_info = "Waiting to finalize termination";
+ thd_proc_info(thd, "Waiting to finalize termination");
pthread_mutex_lock(&LOCK_thread_count);
thd->current_linfo = 0;
pthread_mutex_unlock(&LOCK_thread_count);
DBUG_VOID_RETURN;
err:
- thd->proc_info = "Waiting to finalize termination";
+ thd_proc_info(thd, "Waiting to finalize termination");
end_io_cache(&log);
/*
Exclude iteration through thread list
@@ -892,7 +892,7 @@ int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report )
if (check_access(thd, SUPER_ACL, any_db,0,0,0,0))
DBUG_RETURN(1);
- thd->proc_info = "Killing slave";
+ thd_proc_info(thd, "Killing slave");
int thread_mask;
lock_slave_threads(mi);
// Get a mask of _running_ threads
@@ -919,7 +919,7 @@ int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report )
ER(ER_SLAVE_WAS_NOT_RUNNING));
}
unlock_slave_threads(mi);
- thd->proc_info = 0;
+ thd_proc_info(thd, 0);
if (slave_errno)
{
@@ -1075,7 +1075,7 @@ bool change_master(THD* thd, MASTER_INFO* mi)
DBUG_RETURN(TRUE);
}
- thd->proc_info = "Changing master";
+ thd_proc_info(thd, "Changing master");
LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
// TODO: see if needs re-write
if (init_master_info(mi, master_info_file, relay_log_info_file, 0,
@@ -1200,7 +1200,7 @@ bool change_master(THD* thd, MASTER_INFO* mi)
if (need_relay_log_purge)
{
relay_log_purge= 1;
- thd->proc_info="Purging old relay logs";
+ thd_proc_info(thd, "Purging old relay logs");
if (purge_relay_logs(&mi->rli, thd,
0 /* not only reset, but also reinit */,
&errmsg))
@@ -1263,7 +1263,7 @@ bool change_master(THD* thd, MASTER_INFO* mi)
pthread_mutex_unlock(&mi->rli.data_lock);
unlock_slave_threads(mi);
- thd->proc_info = 0;
+ thd_proc_info(thd, 0);
send_ok(thd);
DBUG_RETURN(FALSE);
}
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index dc018dd43bb..7a2f6c8b23a 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -742,6 +742,7 @@ JOIN::optimize()
if (thd->lex->orig_sql_command != SQLCOM_SHOW_STATUS)
thd->status_var.last_query_cost= 0.0;
+ thd_proc_info(thd, "optimizing");
row_limit= ((select_distinct || order || group_list) ? HA_POS_ERROR :
unit->select_limit_cnt);
/* select_limit is used to decide if we are likely to scan the whole table */
@@ -857,7 +858,14 @@ JOIN::optimize()
thd->fatal_error();
error= res;
DBUG_PRINT("error",("Error from opt_sum_query"));
- DBUG_RETURN(1);
+ DBUG_RETURN(1);
+ }
+ if (res < 0)
+ {
+ DBUG_PRINT("info",("No matching min/max row"));
+ zero_result_cause= "No matching min/max row";
+ error=0;
+ DBUG_RETURN(0);
}
DBUG_PRINT("info",("Select tables optimized away"));
zero_result_cause= "Select tables optimized away";
@@ -893,7 +901,7 @@ JOIN::optimize()
sort_by_table= get_sort_by_table(order, group_list, select_lex->leaf_tables);
/* Calculate how to do the join */
- thd->proc_info= "statistics";
+ thd_proc_info(thd, "statistics");
if (make_join_statistics(this, select_lex->leaf_tables, conds, &keyuse) ||
thd->is_fatal_error)
{
@@ -903,7 +911,7 @@ JOIN::optimize()
/* Remove distinct if only const tables */
select_distinct= select_distinct && (const_tables != tables);
- thd->proc_info= "preparing";
+ thd_proc_info(thd, "preparing");
if (result->initialize_tables(this))
{
DBUG_PRINT("error",("Error: initialize_tables() failed"));
@@ -1267,8 +1275,9 @@ JOIN::optimize()
join_tab[const_tables].type != JT_REF_OR_NULL &&
(order && simple_order || group_list && simple_group))
{
- if (add_ref_to_table_cond(thd,&join_tab[const_tables]))
+ if (add_ref_to_table_cond(thd,&join_tab[const_tables])) {
DBUG_RETURN(1);
+ }
}
if (!(select_options & SELECT_BIG_RESULT) &&
@@ -1326,7 +1335,7 @@ JOIN::optimize()
if (need_tmp)
{
DBUG_PRINT("info",("Creating tmp table"));
- thd->proc_info="Creating tmp table";
+ thd_proc_info(thd, "Creating tmp table");
init_items_ref_array();
@@ -1355,7 +1364,9 @@ JOIN::optimize()
select_options,
tmp_rows_limit,
(char *) "")))
+ {
DBUG_RETURN(1);
+ }
/*
We don't have to store rows in temp table that doesn't match HAVING if:
@@ -1375,28 +1386,34 @@ JOIN::optimize()
if (group_list && simple_group)
{
DBUG_PRINT("info",("Sorting for group"));
- thd->proc_info="Sorting for group";
+ thd_proc_info(thd, "Sorting for group");
if (create_sort_index(thd, this, group_list,
HA_POS_ERROR, HA_POS_ERROR) ||
alloc_group_fields(this, group_list) ||
make_sum_func_list(all_fields, fields_list, 1) ||
setup_sum_funcs(thd, sum_funcs))
- DBUG_RETURN(1);
+ {
+ DBUG_RETURN(1);
+ }
group_list=0;
}
else
{
if (make_sum_func_list(all_fields, fields_list, 0) ||
setup_sum_funcs(thd, sum_funcs))
- DBUG_RETURN(1);
+ {
+ DBUG_RETURN(1);
+ }
+
if (!group_list && ! exec_tmp_table1->distinct && order && simple_order)
{
- DBUG_PRINT("info",("Sorting for order"));
- thd->proc_info="Sorting for order";
- if (create_sort_index(thd, this, order,
+ thd_proc_info(thd, "Sorting for order");
+ if (create_sort_index(thd, this, order,
HA_POS_ERROR, HA_POS_ERROR))
- DBUG_RETURN(1);
- order=0;
+ {
+ DBUG_RETURN(1);
+ }
+ order=0;
}
}
@@ -1519,6 +1536,7 @@ JOIN::exec()
int tmp_error;
DBUG_ENTER("JOIN::exec");
+ thd_proc_info(thd, "executing");
error= 0;
if (procedure)
{
@@ -1658,7 +1676,7 @@ JOIN::exec()
curr_tmp_table= exec_tmp_table1;
/* Copy data to the temporary table */
- thd->proc_info= "Copying to tmp table";
+ thd_proc_info(thd, "Copying to tmp table");
DBUG_PRINT("info", ("%s", thd->proc_info));
if ((tmp_error= do_select(curr_join, (List<Item> *) 0, curr_tmp_table, 0)))
{
@@ -1781,7 +1799,7 @@ JOIN::exec()
}
if (curr_join->group_list)
{
- thd->proc_info= "Creating sort index";
+ thd_proc_info(thd, "Creating sort index");
if (curr_join->join_tab == join_tab && save_join_tab())
{
DBUG_VOID_RETURN;
@@ -1795,7 +1813,7 @@ JOIN::exec()
sortorder= curr_join->sortorder;
}
- thd->proc_info="Copying to group table";
+ thd_proc_info(thd, "Copying to group table");
DBUG_PRINT("info", ("%s", thd->proc_info));
tmp_error= -1;
if (curr_join != this)
@@ -1851,7 +1869,7 @@ JOIN::exec()
curr_join->join_free(); /* Free quick selects */
if (curr_join->select_distinct && ! curr_join->group_list)
{
- thd->proc_info="Removing duplicates";
+ thd_proc_info(thd, "Removing duplicates");
if (curr_join->tmp_having)
curr_join->tmp_having->update_used_tables();
if (remove_duplicates(curr_join, curr_tmp_table,
@@ -1912,7 +1930,7 @@ JOIN::exec()
if (curr_join->group_list || curr_join->order)
{
DBUG_PRINT("info",("Sorting for send_fields"));
- thd->proc_info="Sorting result";
+ thd_proc_info(thd, "Sorting result");
/* If we have already done the group, add HAVING to sorted table */
if (curr_join->tmp_having && ! curr_join->group_list &&
! curr_join->sort_and_group)
@@ -2036,7 +2054,7 @@ JOIN::exec()
}
else
{
- thd->proc_info="Sending data";
+ thd_proc_info(thd, "Sending data");
DBUG_PRINT("info", ("%s", thd->proc_info));
result->send_fields((procedure ? curr_join->procedure_fields_list :
*curr_fields_list),
@@ -2184,7 +2202,7 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
{
if (!(join= new JOIN(thd, fields, select_options, result)))
DBUG_RETURN(TRUE);
- thd->proc_info="init";
+ thd_proc_info(thd, "init");
thd->used_tables=0; // Updated by setup_fields
if (err= join->prepare(rref_pointer_array, tables, wild_num,
conds, og_num, order, group, having, proc_param,
@@ -2229,8 +2247,9 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
err:
if (free_join)
{
- thd->proc_info="end";
+ thd_proc_info(thd, "end");
err|= select_lex->cleanup();
+ thd_proc_info(thd, "end");
DBUG_RETURN(err || thd->net.report_error);
}
DBUG_RETURN(join->error);
@@ -10001,7 +10020,7 @@ free_tmp_table(THD *thd, TABLE *entry)
DBUG_PRINT("enter",("table: %s",entry->alias));
save_proc_info=thd->proc_info;
- thd->proc_info="removing tmp table";
+ thd_proc_info(thd, "removing tmp table");
if (entry->file)
{
@@ -10029,7 +10048,7 @@ free_tmp_table(THD *thd, TABLE *entry)
bitmap_clear_bit(&temp_pool, entry->temp_pool_slot);
free_root(&own_root, MYF(0)); /* the table is allocated in its own root */
- thd->proc_info=save_proc_info;
+ thd_proc_info(thd, save_proc_info);
DBUG_VOID_RETURN;
}
@@ -10059,7 +10078,7 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
DBUG_RETURN(1); // End of memory
save_proc_info=thd->proc_info;
- thd->proc_info="converting HEAP to MyISAM";
+ thd_proc_info(thd, "converting HEAP to MyISAM");
if (create_myisam_tmp_table(&new_table,param,
thd->lex->select_lex.options | thd->options))
@@ -10112,8 +10131,8 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
table->s= &table->share_not_to_be_used;
table->file->change_table_ptr(table);
if (save_proc_info)
- thd->proc_info= (!strcmp(save_proc_info,"Copying to tmp table") ?
- "Copying to tmp table on disk" : save_proc_info);
+ thd_proc_info(thd, (!strcmp(save_proc_info,"Copying to tmp table") ?
+ "Copying to tmp table on disk" : save_proc_info));
DBUG_RETURN(0);
err:
@@ -10125,7 +10144,7 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
new_table.file->delete_table(new_table.s->table_name);
delete new_table.file;
err2:
- thd->proc_info=save_proc_info;
+ thd_proc_info(thd, save_proc_info);
DBUG_RETURN(1);
}
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 0c9ea7b0bbf..c59e2845c14 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -44,7 +44,7 @@ static void
append_algorithm(TABLE_LIST *table, String *buff);
static int
view_store_create_info(THD *thd, TABLE_LIST *table, String *buff);
-static bool schema_table_store_record(THD *thd, TABLE *table);
+bool schema_table_store_record(THD *thd, TABLE *table);
/***************************************************************************
@@ -1354,7 +1354,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
#if !defined(DONT_USE_THR_ALARM) && ! defined(SCO)
if (pthread_kill(tmp->real_id,0))
- tmp->proc_info="*** DEAD ***"; // This shouldn't happen
+ thd_proc_info(tmp, "*** DEAD ***"); // This shouldn't happen
#endif
#ifdef EXTRA_DEBUG
thd_info->start_time= tmp->time_after_lock;
@@ -1497,6 +1497,10 @@ static bool show_status_array(THD *thd, const char *wild,
nr= (long) (thd->query_start() - server_start_time);
end= int10_to_str(nr, buff, 10);
break;
+ case SHOW_FLUSHTIME:
+ nr= (long) (thd->query_start() - flush_status_time);
+ end= int10_to_str(nr, buff, 10);
+ break;
case SHOW_QUESTION:
end= int10_to_str((long) thd->query_id, buff, 10);
break;
@@ -1848,7 +1852,7 @@ typedef struct st_index_field_values
1 error
*/
-static bool schema_table_store_record(THD *thd, TABLE *table)
+bool schema_table_store_record(THD *thd, TABLE *table)
{
int error;
if ((error= table->file->write_row(table->record[0])))
@@ -4339,6 +4343,8 @@ ST_SCHEMA_TABLE schema_tables[]=
get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0},
{"OPEN_TABLES", open_tables_fields_info, create_schema_table,
fill_open_tables, make_old_format, 0, -1, -1, 1},
+ {"PROFILING", query_profile_statistics_info, create_schema_table,
+ fill_query_profile_statistics_info, NULL, NULL, -1, -1, false},
{"ROUTINES", proc_fields_info, create_schema_table,
fill_schema_proc, make_proc_old_format, 0, -1, -1, 0},
{"SCHEMATA", schema_fields_info, create_schema_table,
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index efbb7db4bcb..03544c16752 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1770,7 +1770,7 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name,
}
}
- thd->proc_info="creating table";
+ thd_proc_info(thd, "creating table");
create_info->table_existed= 0; // Mark that table is created
if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
@@ -1801,7 +1801,7 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name,
end:
VOID(pthread_mutex_unlock(&LOCK_open));
- thd->proc_info="After create";
+ thd_proc_info(thd, "After create");
DBUG_RETURN(error);
warn:
@@ -2931,7 +2931,7 @@ mysql_discard_or_import_tablespace(THD *thd,
ALTER TABLE
*/
- thd->proc_info="discard_or_import_tablespace";
+ thd_proc_info(thd, "discard_or_import_tablespace");
discard= test(tablespace_op == DISCARD_TABLESPACE);
@@ -2948,7 +2948,7 @@ mysql_discard_or_import_tablespace(THD *thd,
error=table->file->discard_or_import_tablespace(discard);
- thd->proc_info="end";
+ thd_proc_info(thd, "end");
if (error)
goto err;
@@ -3065,7 +3065,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
frm_type_enum frm_type;
DBUG_ENTER("mysql_alter_table");
- thd->proc_info="init";
+ thd_proc_info(thd, "init");
table_name=table_list->table_name;
alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
@@ -3202,7 +3202,7 @@ view_err:
DBUG_RETURN(TRUE);
}
- thd->proc_info="setup";
+ thd_proc_info(thd, "setup");
if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
!table->s->tmp_table) // no need to touch frm
{
@@ -3253,7 +3253,7 @@ view_err:
if (!error && (new_name != table_name || new_db != db))
{
- thd->proc_info="rename";
+ thd_proc_info(thd, "rename");
/* Then do a 'simple' rename of the table */
if (!access(new_name_buff,F_OK))
{
@@ -3718,7 +3718,7 @@ view_err:
/* We don't want update TIMESTAMP fields during ALTER TABLE. */
thd->count_cuted_fields= CHECK_FIELD_WARN; // calc cuted fields
thd->cuted_fields=0L;
- thd->proc_info="copy to tmp table";
+ thd_proc_info(thd, "copy to tmp table");
next_insert_id=thd->next_insert_id; // Remember for logging
copied=deleted=0;
if (new_table && !new_table->s->is_view)
@@ -3802,7 +3802,7 @@ view_err:
from the cache, free all locks, close the old table and remove it.
*/
- thd->proc_info="rename result table";
+ thd_proc_info(thd, "rename result table");
my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
current_pid, thd->thread_id);
if (lower_case_table_names)
@@ -3913,7 +3913,7 @@ view_err:
broadcast_refresh();
goto err;
}
- thd->proc_info="end";
+ thd_proc_info(thd, "end");
if (mysql_bin_log.is_open())
{
thd->clear_error();
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index e7e54d8ca1d..1a6752abcda 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -164,7 +164,7 @@ int mysql_update(THD *thd,
mysql_handle_derived(thd->lex, &mysql_derived_filling)))
DBUG_RETURN(1);
- thd->proc_info="init";
+ thd_proc_info(thd, "init");
table= table_list->table;
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
@@ -360,7 +360,7 @@ int mysql_update(THD *thd,
else
init_read_record_idx(&info, thd, table, 1, used_index);
- thd->proc_info="Searching rows for update";
+ thd_proc_info(thd, "Searching rows for update");
uint tmp_limit= limit;
while (!(error=info.read_record(&info)) && !thd->killed)
@@ -425,7 +425,7 @@ int mysql_update(THD *thd,
updated= found= 0;
thd->count_cuted_fields= CHECK_FIELD_WARN; /* calc cuted fields */
thd->cuted_fields=0L;
- thd->proc_info="Updating";
+ thd_proc_info(thd, "Updating");
query_id=thd->query_id;
transactional_table= table->file->has_transactions();
@@ -525,7 +525,7 @@ int mysql_update(THD *thd,
end_read_record(&info);
free_io_cache(table); // If ORDER BY
delete select;
- thd->proc_info="end";
+ thd_proc_info(thd, "end");
VOID(table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY));
/*
@@ -973,7 +973,7 @@ int multi_update::prepare(List<Item> &not_used_values,
thd->count_cuted_fields= CHECK_FIELD_WARN;
thd->cuted_fields=0L;
- thd->proc_info="updating main table";
+ thd_proc_info(thd, "updating main table");
tables_to_update= get_table_map(fields);
@@ -1538,11 +1538,11 @@ err2:
bool multi_update::send_eof()
{
char buff[STRING_BUFFER_USUAL_SIZE];
- thd->proc_info="updating reference tables";
+ thd_proc_info(thd, "updating reference tables");
/* Does updates for the last n - 1 tables, returns 0 if ok */
int local_error = (table_count) ? do_updates(0) : 0;
- thd->proc_info= "end";
+ thd_proc_info(thd, "end");
/* We must invalidate the query cache before binlog writing and
ha_autocommit_... */
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 83beec3d1be..9e88b391030 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -592,7 +592,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
DBUG_RETURN(0);
err:
- thd->proc_info= "end";
+ thd_proc_info(thd, "end");
lex->link_first_table_back(view, link_to_local);
unit->cleanup();
DBUG_RETURN(res || thd->net.report_error);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 4a50a602121..54a6a6dc462 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -500,6 +500,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token BIT_SYM
%token BIT_XOR
%token BLOB_SYM
+%token BLOCK_SYM
%token BOOLEAN_SYM
%token BOOL_SYM
%token BOTH
@@ -540,10 +541,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token CONSISTENT_SYM
%token CONSTRAINT
%token CONTAINS_SYM
+%token CONTEXT_SYM
%token CONTINUE_SYM
%token CONVERT_SYM
%token CONVERT_TZ_SYM
%token COUNT_SYM
+%token CPU_SYM
%token CREATE
%token CROSS
%token CUBE_SYM
@@ -617,6 +620,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token EXTRACT_SYM
%token FALSE_SYM
%token FAST_SYM
+%token FAULTS_SYM
%token FETCH_SYM
%token FIELD_FUNC
%token FILE_SYM
@@ -686,6 +690,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token INT_SYM
%token INVOKER_SYM
%token IN_SYM
+%token IO_SYM
+%token IPC_SYM
%token IS
%token ISOLATION
%token ISSUER_SYM
@@ -754,6 +760,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token MEDIUMINT
%token MEDIUMTEXT
%token MEDIUM_SYM
+%token MEMORY_SYM
%token MERGE_SYM
%token MICROSECOND_SYM
%token MIGRATE_SYM
@@ -812,6 +819,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token OUTFILE
%token OUT_SYM
%token PACK_KEYS_SYM
+%token PAGE_SYM
%token PARTIAL
%token PASSWORD
%token PARAM_MARKER
@@ -829,6 +837,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token PROCEDURE
%token PROCESS
%token PROCESSLIST_SYM
+%token PROFILE_SYM
+%token PROFILES_SYM
%token PURGE
%token QUARTER_SYM
%token QUERY_SYM
@@ -899,6 +909,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token SMALLINT
%token SNAPSHOT_SYM
%token SOUNDS_SYM
+%token SOURCE_SYM
%token SPATIAL_SYM
%token SPECIFIC_SYM
%token SQLEXCEPTION_SYM
@@ -929,6 +940,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token SUM_SYM
%token SUPER_SYM
%token SUSPEND_SYM
+%token SWAPS_SYM
+%token SWITCHES_SYM
%token SYSDATE
%token TABLES
%token TABLESPACE
@@ -6835,6 +6848,64 @@ opt_table_sym:
/* empty */
| TABLE_SYM;
+opt_profile_defs:
+ /* empty */
+ | profile_defs
+
+profile_defs:
+ profile_def
+ | profile_defs ',' profile_def
+
+profile_def:
+ CPU_SYM
+ {
+ Lex->profile_options|= PROFILE_CPU;
+ }
+ | MEMORY_SYM
+ {
+ Lex->profile_options|= PROFILE_MEMORY;
+ }
+ | BLOCK_SYM IO_SYM
+ {
+ Lex->profile_options|= PROFILE_BLOCK_IO;
+ }
+ | CONTEXT_SYM SWITCHES_SYM
+ {
+ Lex->profile_options|= PROFILE_CONTEXT;
+ }
+ | PAGE_SYM FAULTS_SYM
+ {
+ Lex->profile_options|= PROFILE_PAGE_FAULTS;
+ }
+ | IPC_SYM
+ {
+ Lex->profile_options|= PROFILE_IPC;
+ }
+ | SWAPS_SYM
+ {
+ Lex->profile_options|= PROFILE_SWAPS;
+ }
+ | SOURCE_SYM
+ {
+ Lex->profile_options|= PROFILE_SOURCE;
+ }
+ | ALL
+ {
+ Lex->profile_options|= PROFILE_ALL;
+ }
+ ;
+
+opt_profile_args:
+ /* empty */
+ {
+ Lex->profile_query_id= 0;
+ }
+ | FOR_SYM QUERY_SYM NUM
+ {
+ Lex->profile_query_id= atoi($3.str);
+ }
+ ;
+
/* Show things */
show: SHOW
@@ -6970,6 +7041,10 @@ show_param:
{ Lex->sql_command = SQLCOM_SHOW_WARNS;}
| ERRORS opt_limit_clause_init
{ Lex->sql_command = SQLCOM_SHOW_ERRORS;}
+ | PROFILES_SYM
+ { Lex->sql_command = SQLCOM_SHOW_PROFILES; }
+ | PROFILE_SYM opt_profile_defs opt_profile_args opt_limit_clause_init
+ { Lex->sql_command = SQLCOM_SHOW_PROFILE; }
| opt_var_type STATUS_SYM wild_and_where
{
LEX *lex= Lex;
@@ -8063,6 +8138,7 @@ keyword_sp:
| BERKELEY_DB_SYM {}
| BINLOG_SYM {}
| BIT_SYM {}
+ | BLOCK_SYM {}
| BOOL_SYM {}
| BOOLEAN_SYM {}
| BTREE_SYM {}
@@ -8080,6 +8156,8 @@ keyword_sp:
| CONCURRENT {}
| CONNECTION_SYM {}
| CONSISTENT_SYM {}
+ | CONTEXT_SYM {}
+ | CPU_SYM {}
| CUBE_SYM {}
| DATA_SYM {}
| DATETIME {}
@@ -8102,6 +8180,7 @@ keyword_sp:
| EXPANSION_SYM {}
| EXTENDED_SYM {}
| FAST_SYM {}
+ | FAULTS_SYM {}
| FOUND_SYM {}
| DISABLE_SYM {}
| ENABLE_SYM {}
@@ -8126,6 +8205,8 @@ keyword_sp:
| ISSUER_SYM {}
| INNOBASE_SYM {}
| INSERT_METHOD {}
+ | IO_SYM {}
+ | IPC_SYM {}
| RELAY_THREAD {}
| LAST_SYM {}
| LEAVES {}
@@ -8155,6 +8236,7 @@ keyword_sp:
| MAX_UPDATES_PER_HOUR {}
| MAX_USER_CONNECTIONS_SYM {}
| MEDIUM_SYM {}
+ | MEMORY_SYM {}
| MERGE_SYM {}
| MICROSECOND_SYM {}
| MIGRATE_SYM {}
@@ -8181,6 +8263,7 @@ keyword_sp:
| ONE_SHOT_SYM {}
| ONE_SYM {}
| PACK_KEYS_SYM {}
+ | PAGE_SYM {}
| PARTIAL {}
| PASSWORD {}
| PHASE_SYM {}
@@ -8190,6 +8273,8 @@ keyword_sp:
| PRIVILEGES {}
| PROCESS {}
| PROCESSLIST_SYM {}
+ | PROFILE_SYM {}
+ | PROFILES_SYM {}
| QUARTER_SYM {}
| QUERY_SYM {}
| QUICK {}
@@ -8223,6 +8308,7 @@ keyword_sp:
| SHUTDOWN {}
| SNAPSHOT_SYM {}
| SOUNDS_SYM {}
+ | SOURCE_SYM {}
| SQL_CACHE_SYM {}
| SQL_BUFFER_RESULT {}
| SQL_NO_CACHE_SYM {}
@@ -8234,6 +8320,8 @@ keyword_sp:
| SUBJECT_SYM {}
| SUPER_SYM {}
| SUSPEND_SYM {}
+ | SWAPS_SYM {}
+ | SWITCHES_SYM {}
| TABLES {}
| TABLESPACE {}
| TEMPORARY {}
diff --git a/sql/structs.h b/sql/structs.h
index 2dcafdef615..4bb4d625cb4 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -174,6 +174,7 @@ enum SHOW_TYPE
SHOW_BOOL, SHOW_MY_BOOL, SHOW_OPENTABLES, SHOW_STARTTIME, SHOW_QUESTION,
SHOW_LONG_CONST, SHOW_INT_CONST, SHOW_HAVE, SHOW_SYS, SHOW_HA_ROWS,
SHOW_VARS,
+ SHOW_FLUSHTIME,
#ifdef HAVE_OPENSSL
SHOW_SSL_CTX_SESS_ACCEPT, SHOW_SSL_CTX_SESS_ACCEPT_GOOD,
SHOW_SSL_GET_VERSION, SHOW_SSL_CTX_GET_SESSION_CACHE_MODE,
diff --git a/sql/table.h b/sql/table.h
index 61232a39f0e..9dbdbeb7264 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -320,6 +320,7 @@ enum enum_schema_tables
SCH_COLUMN_PRIVILEGES,
SCH_KEY_COLUMN_USAGE,
SCH_OPEN_TABLES,
+ SCH_PROFILING,
SCH_PROCEDURES,
SCH_SCHEMATA,
SCH_SCHEMA_PRIVILEGES,