diff options
author | unknown <cmiller@zippy.cornsilk.net> | 2007-01-03 17:15:10 -0500 |
---|---|---|
committer | unknown <cmiller@zippy.cornsilk.net> | 2007-01-03 17:15:10 -0500 |
commit | 66dfd85cf4d55e6d87e8bb81b8fc8450eb9b3f67 (patch) | |
tree | 3ccc1d229c4d2704fc85d800e206032a74a7a2e0 | |
parent | 96fa010c66bffd1f904d5d71bd2d0a0087ad287b (diff) | |
download | mariadb-git-66dfd85cf4d55e6d87e8bb81b8fc8450eb9b3f67.tar.gz |
Bug#24795: Add SHOW PROFILE
Patch contributed by Jeremy Cole. CLA received Oct 2006 by Kaj Arnö
Add rudimentary query profiling support.
libmysqld/Makefile.am:
Add profile file to source list.
sql/Makefile.am:
Add profiling files to source and header lists.
sql/ha_archive.cc:
Macro-ized other discovered instances of setting proc_info.
sql/ha_myisam.cc:
Macroize setting thread-state info
sql/item_func.cc:
Macro-ized other discovered instances of setting proc_info.
sql/lex.h:
Add lexer info for profiling.
sql/lock.cc:
Macroize setting thread-state info
sql/log_event.cc:
Macro-ized other discovered instances of setting proc_info.
sql/mysql_priv.h:
Set constants for profiling.
sql/repl_failsafe.cc:
Macro-ized other discovered instances of setting proc_info.
sql/slave.cc:
Macro-ized other discovered instances of setting proc_info.
sql/sp_head.cc:
Macro-ized other discovered instances of setting proc_info.
sql/sql_base.cc:
Macroize setting thread-state info
---
Macro-ized other discovered instances of setting proc_info.
sql/sql_cache.cc:
Macroize setting thread-state info
sql/sql_class.cc:
Integrate profiling.
sql/sql_class.h:
Instantiate profiling object.
sql/sql_delete.cc:
Macroize setting thread-state info
sql/sql_insert.cc:
Macroize setting thread-state info
---
Macro-ized other discovered instances of setting proc_info.
sql/sql_lex.cc:
Initialize profiling.
sql/sql_lex.h:
Define lex tokens and allocate space for profiling options.
sql/sql_parse.cc:
Integrate profiling.
---
Macro-ized other discovered instances of setting proc_info.
sql/sql_repl.cc:
Macro-ized other discovered instances of setting proc_info.
sql/sql_select.cc:
Macroize setting thread-state info.
Clean up some lines.
sql/sql_show.cc:
Macro-ized other discovered instances of setting proc_info.
---
Revert bad use of macro.
sql/sql_table.cc:
Macroize setting thread-state info
sql/sql_update.cc:
Macroize setting thread-state info
sql/sql_view.cc:
Macro-ized other discovered instances of setting proc_info.
sql/sql_yacc.yy:
Add parser info for profiling.
---
Fix new YACC shift/reduce conflict. (Now at 249.)
mysql-test/r/profile.result:
Test profiling code.
---
A not-very-useful result.
mysql-test/t/profile.test:
Test profiling code.
---
Test syntax, but not values of profiles code.
sql/sql_profile.cc:
Add profiling code.
---
Add wishlist comment.
sql/sql_profile.h:
Add profiling code.
---
Changed the value of the macro so that it's syntactically equivalent to a
single statement.
-rw-r--r-- | BitKeeper/etc/collapsed | 3 | ||||
-rw-r--r-- | libmysqld/Makefile.am | 1 | ||||
-rw-r--r-- | mysql-test/r/profile.result | 30 | ||||
-rw-r--r-- | mysql-test/t/profile.test | 39 | ||||
-rw-r--r-- | sql/Makefile.am | 2 | ||||
-rw-r--r-- | sql/ha_archive.cc | 4 | ||||
-rw-r--r-- | sql/ha_myisam.cc | 24 | ||||
-rw-r--r-- | sql/item_func.cc | 8 | ||||
-rw-r--r-- | sql/lex.h | 13 | ||||
-rw-r--r-- | sql/lock.cc | 8 | ||||
-rw-r--r-- | sql/log_event.cc | 8 | ||||
-rw-r--r-- | sql/mysql_priv.h | 4 | ||||
-rw-r--r-- | sql/repl_failsafe.cc | 6 | ||||
-rw-r--r-- | sql/slave.cc | 6 | ||||
-rw-r--r-- | sql/sp_head.cc | 4 | ||||
-rw-r--r-- | sql/sql_base.cc | 24 | ||||
-rw-r--r-- | sql/sql_cache.cc | 12 | ||||
-rw-r--r-- | sql/sql_class.cc | 2 | ||||
-rw-r--r-- | sql/sql_class.h | 3 | ||||
-rw-r--r-- | sql/sql_delete.cc | 12 | ||||
-rw-r--r-- | sql/sql_insert.cc | 38 | ||||
-rw-r--r-- | sql/sql_lex.cc | 1 | ||||
-rw-r--r-- | sql/sql_lex.h | 3 | ||||
-rw-r--r-- | sql/sql_parse.cc | 40 | ||||
-rw-r--r-- | sql/sql_profile.cc | 441 | ||||
-rw-r--r-- | sql/sql_profile.h | 187 | ||||
-rw-r--r-- | sql/sql_repl.cc | 2 | ||||
-rw-r--r-- | sql/sql_select.cc | 73 | ||||
-rw-r--r-- | sql/sql_show.cc | 2 | ||||
-rw-r--r-- | sql/sql_table.cc | 20 | ||||
-rw-r--r-- | sql/sql_update.cc | 14 | ||||
-rw-r--r-- | sql/sql_view.cc | 2 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 74 |
33 files changed, 976 insertions, 134 deletions
diff --git a/BitKeeper/etc/collapsed b/BitKeeper/etc/collapsed index 10bc7a2182a..63e4bff0331 100644 --- a/BitKeeper/etc/collapsed +++ b/BitKeeper/etc/collapsed @@ -19,3 +19,6 @@ 4538a7b0EbDHHkWPbIwxO6ZIDdg6Dg 454a7ef8gdvE_ddMlJyghvOAkKPNOQ 454f8960jsVT_kMKJtZ9OCgXoba0xQ +459c03b9N_mqF2XJKK6DwSrIt7e6_g +459c1965_BQMBzBO8S_gVqjTHYQrmw +459c2098XoAUsUn8N07IVRDD6CTM-A diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index cdd71724166..2b54bf6a386 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -53,6 +53,7 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \ protocol.cc net_serv.cc opt_range.cc \ opt_sum.cc procedure.cc records.cc sql_acl.cc \ sql_load.cc discover.cc sql_locale.cc \ + sql_profile.cc \ sql_analyse.cc sql_base.cc sql_cache.cc sql_class.cc \ sql_crypt.cc sql_db.cc sql_delete.cc sql_error.cc sql_insert.cc \ sql_lex.cc sql_list.cc sql_manager.cc sql_map.cc sql_parse.cc \ diff --git a/mysql-test/r/profile.result b/mysql-test/r/profile.result new file mode 100644 index 00000000000..8a4db348445 --- /dev/null +++ b/mysql-test/r/profile.result @@ -0,0 +1,30 @@ +create table t1 ( +a int, +b int +); +insert into t1 values (1,1), (2,null), (3, 4); +insert into t1 values (5,1), (6,null), (7, 4); +insert into t1 values (1,1), (2,null), (3, 4); +insert into t1 values (5,1), (6,null), (7, 4); +insert into t1 values (5,1), (6,null), (7, 4); +select sum(a) from t1; +select sum(a) from t1 group by b; +select sum(a) + sum(b) from t1 group by b; +select max(x) from (select sum(a) as x from t1 group by b) as teeone; +show profiles; +show profile for query 8; +show profile cpu, block io for query 8; +show profile cpu for query 8; +show profile cpu for query 9 limit 2 offset 2; +show profile cpu for query 10 limit 0; +show profile cpu for query 65534; +show profile memory; +show profile block io; +show profile context switches; +show profile page faults; +show profile ipc; +show profile swaps limit 1 offset 2; +show profile source; +show profile all for query 0 limit 0; +drop table t1; +End of 5.0 tests diff --git a/mysql-test/t/profile.test b/mysql-test/t/profile.test new file mode 100644 index 00000000000..4cf2a3aa92f --- /dev/null +++ b/mysql-test/t/profile.test @@ -0,0 +1,39 @@ + +create table t1 ( + a int, + b int +); +--disable_result_log +insert into t1 values (1,1), (2,null), (3, 4); +insert into t1 values (5,1), (6,null), (7, 4); +insert into t1 values (1,1), (2,null), (3, 4); +insert into t1 values (5,1), (6,null), (7, 4); +insert into t1 values (5,1), (6,null), (7, 4); +select sum(a) from t1; +select sum(a) from t1 group by b; +select sum(a) + sum(b) from t1 group by b; +select max(x) from (select sum(a) as x from t1 group by b) as teeone; + +# Merely verify that commands work. Checking values is impossible, right? +show profiles; +show profile for query 8; +show profile cpu, block io for query 8; +show profile cpu for query 8; +show profile cpu for query 9 limit 2 offset 2; +show profile cpu for query 10 limit 0; +show profile cpu for query 65534; +show profile memory; +show profile block io; +show profile context switches; +show profile page faults; +show profile ipc; +show profile swaps limit 1 offset 2; +show profile source; +show profile all for query 0 limit 0; +--enable_result_log + +drop table t1; + + +## +--echo End of 5.0 tests diff --git a/sql/Makefile.am b/sql/Makefile.am index cbb87f16d80..597ab49a135 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 e3f979952e0..da879915d72 100644 --- a/sql/ha_archive.cc +++ b/sql/ha_archive.cc @@ -1206,7 +1206,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); @@ -1230,7 +1230,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_myisam.cc b/sql/ha_myisam.cc index 19ec1b29da3..a09682cc0ab 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -340,7 +340,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(¶m); param.thd = thd; param.op_name = "check"; @@ -414,7 +414,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; } @@ -680,22 +680,22 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool 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(¶m, 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(¶m, 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(¶m, file, fixed_name, param.testflag & T_QUICK); @@ -709,7 +709,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool 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(¶m,file,fixed_name); } if (!statistics_done && (local_testflag & T_STATISTICS)) @@ -717,14 +717,14 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) if (share->state.changed & STATE_NOT_ANALYZED) { optimize_done=1; - thd->proc_info="Analyzing"; + THD_PROC_INFO(thd, "Analyzing"); error = chk_key(¶m, 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)) @@ -762,7 +762,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED; update_state_info(¶m, 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 : @@ -987,7 +987,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(¶m); param.op_name= "recreating_index"; param.testflag= (T_SILENT | T_REP_BY_SORT | T_QUICK | @@ -1012,7 +1012,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/item_func.cc b/sql/item_func.cc index 659a214e9c2..ddd2f28c5a5 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3193,7 +3193,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; @@ -3218,7 +3218,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); @@ -3299,7 +3299,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; @@ -3337,7 +3337,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 68f34d8de93..11791457b8d 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -88,6 +88,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)}, @@ -126,8 +127,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)}, @@ -193,6 +196,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)}, @@ -252,7 +256,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)}, @@ -310,6 +316,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 */ @@ -357,6 +364,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)}, @@ -371,6 +379,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)}, @@ -438,6 +448,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)}, @@ -472,6 +483,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 90ddcc957a2..71c0b753b2a 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -151,7 +151,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. */ @@ -160,7 +160,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, @@ -194,7 +194,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); @@ -209,7 +209,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 0d7ef7e8f2a..e6f4665d49b 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -4266,7 +4266,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, @@ -4320,7 +4320,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) */ @@ -4440,7 +4440,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 @@ -4474,7 +4474,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 c5ec6f02cdd..06762516f76 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -174,6 +174,8 @@ MY_LOCALE *my_locale_by_name(const char *name); #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 @@ -542,6 +544,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 diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 21e46e71825..e085ebc77b2 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); @@ -598,7 +598,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) { @@ -942,7 +942,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)) diff --git a/sql/slave.cc b/sql/slave.cc index d0396444ace..eaa53a15ac3 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2925,9 +2925,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); @@ -3546,7 +3546,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 diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 152bc87aead..badd8c3f9cf 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2372,9 +2372,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 90043e45052..54af1064088 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -309,7 +309,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, @@ -351,7 +351,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); @@ -1065,7 +1065,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); @@ -1073,7 +1073,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; } @@ -1778,7 +1778,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) { @@ -1794,12 +1794,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); } @@ -2098,7 +2098,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 @@ -2278,7 +2278,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) @@ -2352,7 +2352,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; @@ -2386,7 +2386,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); } @@ -5387,7 +5387,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 2c77e0ef230..3f9a41fecd9 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -670,6 +670,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) @@ -688,6 +689,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 @@ -725,6 +727,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(); @@ -1089,6 +1092,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); @@ -1163,6 +1168,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++) @@ -1255,6 +1261,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)", @@ -1332,9 +1339,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) { @@ -1365,9 +1374,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) { @@ -1417,6 +1428,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 d2f1e9ed0d9..3745f537612 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -248,6 +248,7 @@ THD::THD() init(); /* Initialize sub structures */ init_sql_alloc(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE); + profiling.set_thd(this); user_connect=(USER_CONN *)0; hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, (hash_get_key) get_var_key, @@ -330,6 +331,7 @@ void THD::init_for_queries() variables.trans_alloc_block_size, variables.trans_prealloc_size); #endif + profiling.reset(); transaction.xid_state.xid.null(); transaction.xid_state.in_thd=1; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 9ad67524998..cbccaf884b0 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1309,6 +1309,9 @@ public: List <MYSQL_ERROR> warn_list; uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END]; uint total_warn_count; + + PROFILING profiling; + /* 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 b665113dd18..ecb0f498812 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -55,7 +55,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)) @@ -206,7 +206,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) table->triggers->mark_fields_used(thd, TRG_EVENT_DELETE); @@ -262,7 +262,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) @@ -486,7 +486,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); } @@ -750,7 +750,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 @@ -759,7 +759,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 diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 2ce83caa369..48bb8305e02 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -398,7 +398,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++; @@ -471,7 +471,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) @@ -677,7 +677,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 @@ -1408,7 +1408,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; @@ -1445,7 +1445,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. @@ -1487,13 +1487,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) @@ -1553,13 +1553,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) @@ -1577,7 +1577,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); @@ -1657,11 +1657,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; @@ -1873,7 +1873,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) @@ -1908,7 +1908,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) { @@ -2027,7 +2027,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 */ @@ -2035,7 +2035,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) { @@ -2153,7 +2153,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))) { @@ -2172,14 +2172,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 3de842c8551..7e775cfffdd 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -174,6 +174,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; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index be12467d097..a918188a4bc 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -95,6 +95,7 @@ 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, + SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES, /* This should be the last !!! */ SQLCOM_END @@ -940,6 +941,8 @@ 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_options; + uint profile_query_id; 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 fec7e5ab20d..4124db36b7f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1059,7 +1059,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 @@ -1159,7 +1159,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->set_time(); 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->set_time(); thd->init_for_queries(); } @@ -1259,7 +1259,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)); @@ -2106,7 +2106,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 */ } /* @@ -2129,9 +2129,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; @@ -2164,8 +2164,6 @@ void log_slow_statement(THD *thd) */ if (thd->enable_slow_log && !thd->user_time) { - thd->proc_info="logging slow query"; - if ((ulong) (thd->start_time - thd->time_after_lock) > thd->variables.long_query_time || (thd->server_status & @@ -2174,6 +2172,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); } @@ -2697,6 +2696,20 @@ mysql_execute_command(THD *thd) (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR)); break; } + case SQLCOM_SHOW_PROFILES: + { + thd->profiling.store(); + thd->profiling.discard(); + res= thd->profiling.show_profiles(); + break; + } + case SQLCOM_SHOW_PROFILE: + { + thd->profiling.store(); + thd->profiling.discard(); // will get re-enabled by reset() + res= thd->profiling.show_last(lex->profile_options); + break; + } case SQLCOM_SHOW_NEW_MASTER: { if (check_global_access(thd, REPL_SLAVE_ACL)) @@ -3544,7 +3557,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; @@ -4972,7 +4985,7 @@ end_with_restore_list: send_ok(thd); break; } - thd->proc_info="query end"; + THD_PROC_INFO(thd, "query end"); /* Two binlog-related cleanups: */ /* @@ -5143,6 +5156,8 @@ 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")); @@ -5630,6 +5645,7 @@ 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; + thd->profiling.reset(); } DBUG_VOID_RETURN; } @@ -5853,7 +5869,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length) query_cache_abort(&thd->net); 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_profile.cc b/sql/sql_profile.cc new file mode 100644 index 00000000000..98a5f8d7576 --- /dev/null +++ b/sql/sql_profile.cc @@ -0,0 +1,441 @@ +/* Copyright (C) 2005 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; either version 2 of the License, or + (at your option) any later version. + + 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 "sp_rcontext.h" + +#define RUSAGE_USEC(tv) ((tv).tv_sec*1000000 + (tv).tv_usec) +#define RUSAGE_DIFF_USEC(tv1, tv2) (RUSAGE_USEC((tv1))-RUSAGE_USEC((tv2))) + +PROFILE_ENTRY::PROFILE_ENTRY() + :status(NULL), time(0), function(NULL), file(NULL), line(0) +{ + collect(); +} + +PROFILE_ENTRY::PROFILE_ENTRY(PROFILE *profile_arg, const char *status_arg) + :profile(profile_arg), function(NULL), file(NULL), line(0) +{ + collect(); + if (status_arg) + set_status(status_arg); +} + +PROFILE_ENTRY::PROFILE_ENTRY(PROFILE *profile_arg, const char *status_arg, + const char *function_arg, + const char *file_arg, unsigned int line_arg) + :profile(profile_arg) +{ + collect(); + if (status_arg) + set_status(status_arg); + if (function_arg) + function= strdup_root(&profile->profiling->root, function_arg); + if (file_arg) + file= strdup_root(&profile->profiling->root, file_arg); + line= line_arg; +} + +PROFILE_ENTRY::~PROFILE_ENTRY() +{ + if (status) + free(status); + if (function) + free(function); + if (file) + free(file); +} + +void PROFILE_ENTRY::set_status(const char *status_arg) +{ + status= strdup_root(&profile->profiling->root, status_arg); +} + +void PROFILE_ENTRY::collect() +{ + time= my_getsystime(); + getrusage(RUSAGE_SELF, &rusage); +} + +PROFILE::PROFILE(PROFILING *profiling_arg) + :profiling(profiling_arg) +{ + profile_end= &profile_start; +} + +PROFILE::~PROFILE() +{ + entries.empty(); +} + +void PROFILE::status(const char *status_arg, + const char *function_arg=NULL, + const char *file_arg=NULL, unsigned int line_arg=0) +{ + PROFILE_ENTRY *prof= NULL; + MEM_ROOT *old_root= NULL; + THD *thd= profiling->thd; + + DBUG_ENTER("PROFILE::status"); + + /* Blank status. Just return, and thd->proc_info will be set blank later. */ + if (unlikely(!status_arg)) + DBUG_VOID_RETURN; + + /* If thd->proc_info is currently set to status_arg, don't profile twice. */ + if (unlikely(thd->proc_info && !(strcmp(thd->proc_info, status_arg)))) + DBUG_VOID_RETURN; + + /* Is this the same query as our profile currently contains? */ + if (unlikely(thd->query_id != query_id && !thd->spcont)) + reset(); + + /* + In order to keep the profile information between queries (i.e. from + SELECT to the following SHOW PROFILE command) the following code is + necessary to keep the profile from getting freed automatically when + mysqld cleans up after the query. This code is shamelessly stolen + from SHOW WARNINGS. + + The thd->mem_root structure is freed after each query is completed, + so temporarily override it. + */ + old_root= thd->mem_root; + thd->mem_root= &profiling->root; + 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); + } + thd->mem_root= old_root; + + DBUG_VOID_RETURN; +} + +void PROFILE::reset() +{ + DBUG_ENTER("PROFILE::reset"); + if (profiling->thd->query_id != query_id) + { + query_id= profiling->thd->query_id; + profile_start.collect(); + entries.empty(); + } + DBUG_VOID_RETURN; +} + +bool PROFILE::show(uint options) +{ + PROFILE_ENTRY *prof; + THD *thd= profiling->thd; + PROFILE_ENTRY *ps= &profile_start; + List<Item> field_list; + DBUG_ENTER("PROFILE::show"); + + field_list.push_back(new Item_empty_string("Status", MYSQL_ERRMSG_SIZE)); + field_list.push_back(new Item_return_int("Time_elapsed", 20, + MYSQL_TYPE_LONGLONG)); + + if (options & PROFILE_CPU) + { + field_list.push_back(new Item_return_int("CPU_user", 20, + MYSQL_TYPE_LONGLONG)); + field_list.push_back(new Item_return_int("CPU_system", 20, + MYSQL_TYPE_LONGLONG)); + } + + 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); + + 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); + + List_iterator<PROFILE_ENTRY> it(entries); + ulonglong last_time= ps->time; + while ((prof= it++)) + { + if (++idx <= unit->offset_limit_cnt) + continue; + if (idx > unit->select_limit_cnt) + break; + + protocol->prepare_for_resend(); + protocol->store(prof->status, strlen(prof->status), system_charset_info); + protocol->store((ulonglong)(prof->time - ps->time)/10); + + if (options & PROFILE_CPU) + { + protocol->store((ulonglong)RUSAGE_DIFF_USEC(prof->rusage.ru_utime, + ps->rusage.ru_utime)); + protocol->store((ulonglong)RUSAGE_DIFF_USEC(prof->rusage.ru_stime, + ps->rusage.ru_stime)); + } + + if (options & PROFILE_CONTEXT) + { + protocol->store((uint32)(prof->rusage.ru_nvcsw - ps->rusage.ru_nvcsw)); + protocol->store((uint32)(prof->rusage.ru_nivcsw - ps->rusage.ru_nivcsw)); + } + + if (options & PROFILE_BLOCK_IO) + { + protocol->store((uint32)(prof->rusage.ru_inblock-ps->rusage.ru_inblock)); + protocol->store((uint32)(prof->rusage.ru_oublock-ps->rusage.ru_oublock)); + } + + if (options & PROFILE_IPC) + { + protocol->store((uint32)(prof->rusage.ru_msgsnd - ps->rusage.ru_msgsnd)); + protocol->store((uint32)(prof->rusage.ru_msgrcv - ps->rusage.ru_msgrcv)); + } + + if (options & PROFILE_PAGE_FAULTS) + { + protocol->store((uint32)(prof->rusage.ru_majflt - ps->rusage.ru_majflt)); + protocol->store((uint32)(prof->rusage.ru_minflt - ps->rusage.ru_minflt)); + } + + if (options & PROFILE_SWAPS) + { + protocol->store((uint32)(prof->rusage.ru_nswap - ps->rusage.ru_nswap)); + } + + if (options & PROFILE_SOURCE) + { + if(prof->function && prof->file) + { + protocol->store(prof->function, strlen(prof->function), system_charset_info); + protocol->store(prof->file, strlen(prof->file), system_charset_info); + protocol->store(prof->line); + } else { + protocol->store("(unknown)", 10, system_charset_info); + protocol->store("(unknown)", 10, system_charset_info); + protocol->store((uint32) 0); + } + } + + if (protocol->write()) + DBUG_RETURN(TRUE); + last_time= prof->time; + } + send_eof(thd); + DBUG_RETURN(FALSE); +} + +/* XXX: enabled should be set to the current global profiling setting */ +PROFILING::PROFILING() + :enabled(1), keeping(1), current(NULL), last(NULL) +{ + init_sql_alloc(&root, + PROFILE_ALLOC_BLOCK_SIZE, + PROFILE_ALLOC_PREALLOC_SIZE); +} + +PROFILING::~PROFILING() +{ + free_root(&root, MYF(0)); +} + +void PROFILING::status(const char *status_arg, + const char *function_arg, + const char *file_arg, unsigned int line_arg) +{ + DBUG_ENTER("PROFILING::status"); + + if(!current) + reset(); + + if(unlikely(enabled)) + current->status(status_arg, function_arg, file_arg, line_arg); + + thd->proc_info= status_arg; + + DBUG_VOID_RETURN; +} + +void PROFILING::store() +{ + MEM_ROOT *old_root; + DBUG_ENTER("PROFILING::store"); + + if (last && current && (last->query_id == current->query_id)) + DBUG_VOID_RETURN; + + if (history.elements > 10) /* XXX: global/session var */ + { + PROFILE *tmp= history.pop(); + delete tmp; + } + + old_root= thd->mem_root; + thd->mem_root= &root; + + if (current) + { + if (keeping && (!current->entries.is_empty())) { + last= current; + history.push_back(current); + current= NULL; + } else { + delete current; + } + } + + current= new PROFILE(this); + thd->mem_root= old_root; + + DBUG_VOID_RETURN; +} + +void PROFILING::reset() +{ + DBUG_ENTER("PROFILING::reset"); + + store(); + + current->reset(); + /*free_root(&root, MYF(0));*/ + keep(); + + DBUG_VOID_RETURN; +} + +bool PROFILING::show_profiles() +{ + PROFILE *prof; + List<Item> field_list; + DBUG_ENTER("PROFILING::list_all"); + + field_list.push_back(new Item_return_int("Query_ID", 10, + MYSQL_TYPE_LONG)); + field_list.push_back(new Item_return_int("Time", 20, + MYSQL_TYPE_LONGLONG)); + /* TODO: Add another field that lists the query. */ + + 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); + + List_iterator<PROFILE> it(history); + while ((prof= it++)) + { + 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->query_id)); + protocol->store((ulonglong)((pe->time - ps->time)/10)); + + if (protocol->write()) + DBUG_RETURN(TRUE); + } + send_eof(thd); + DBUG_RETURN(FALSE); +} + +bool PROFILING::show(uint options, uint query_id) +{ + DBUG_ENTER("PROFILING::show"); + PROFILE *prof; + + List_iterator<PROFILE> it(history); + while ((prof= it++)) + { + if(prof->query_id == query_id) + prof->show(options); + } + + 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); +} diff --git a/sql/sql_profile.h b/sql/sql_profile.h new file mode 100644 index 00000000000..698a80f07e4 --- /dev/null +++ b/sql/sql_profile.h @@ -0,0 +1,187 @@ +/* Copyright (C) 2005 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; either version 2 of the License, or + (at your option) any later version. + + 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 + +#include <sys/time.h> +#include <sys/resource.h> + +#if 1 +#define THD_PROC_INFO(thd, msg) do { if(unlikely((thd)->profiling.enabled)) { (thd)->profiling.status((msg), __FUNCTION__, __FILE__, __LINE__); } else { (thd)->proc_info= (msg); } } while (0) +#else +#define THD_PROC_INFO(thd, msg) do { (thd)->proc_info= (msg); } while (0) +#endif + +#if 0 + + struct rusage { + struct timeval ru_utime; /* user time used */ + struct timeval ru_stime; /* system time used */ + long ru_maxrss; /* integral max resident set size */ + long ru_ixrss; /* integral shared text memory size */ + long ru_idrss; /* integral unshared data size */ + long ru_isrss; /* integral unshared stack size */ + long ru_minflt; /* page reclaims */ + long ru_majflt; /* page faults */ + long ru_nswap; /* swaps */ + long ru_inblock; /* block input operations */ + long ru_oublock; /* block output operations */ + long ru_msgsnd; /* messages sent */ + long ru_msgrcv; /* messages received */ + long ru_nsignals; /* signals received */ + long ru_nvcsw; /* voluntary context switches */ + long ru_nivcsw; /* involuntary context switches */ + }; + +#endif + +#define PROFILE_NONE 0 +#define PROFILE_CPU 1 +#define PROFILE_MEMORY 2 +#define PROFILE_BLOCK_IO 4 +#define PROFILE_CONTEXT 8 +#define PROFILE_PAGE_FAULTS 16 +#define PROFILE_IPC 32 +#define PROFILE_SWAPS 64 +#define PROFILE_SOURCE 16384 +#define PROFILE_ALL 32767 + +class PROFILE_ENTRY; +class PROFILE; +class PROFILING; + +/* + A single entry in a single profile. +*/ + +class PROFILE_ENTRY: public Sql_alloc +{ +public: + PROFILE *profile; + char *status; + ulonglong time; + struct rusage rusage; + + char *function; + char *file; + unsigned int line; + + PROFILE_ENTRY(); + PROFILE_ENTRY(PROFILE *profile_arg, const char *status_arg); + PROFILE_ENTRY(PROFILE *profile_arg, const char *status_arg, + const char *function_arg, + const char *file_arg, unsigned int line_arg); + ~PROFILE_ENTRY(); + + void set_status(const char *status_arg); + void collect(); +}; + +/* + The full profile for a single query. Includes multiple PROFILE_ENTRY + objects. +*/ + +class PROFILE: public Sql_alloc +{ +public: + PROFILING *profiling; + query_id_t query_id; + PROFILE_ENTRY profile_start; + PROFILE_ENTRY *profile_end; + List<PROFILE_ENTRY> entries; + + PROFILE(PROFILING *profiling_arg); + ~PROFILE(); + + /* 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 PROFILE objects. +*/ + +class PROFILING: public Sql_alloc +{ +public: + MEM_ROOT root; + THD *thd; + bool enabled; + bool keeping; + + PROFILE *current; + PROFILE *last; + List<PROFILE> history; + + PROFILING(); + ~PROFILING(); + + inline void set_thd(THD *thd_arg) { thd= thd_arg; }; + + /* + Should we try to collect profiling information at all? + + If we disable profiling, we cannot later decide to turn it back + on for the same query. + */ + inline void enable() { enabled= 1; }; + inline void disable() { enabled= 0; }; + + /* + 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= 1; }; + inline void discard() { keeping= 0; }; + + void status(const char *status_arg, + const char *function_arg, + const char *file_arg, unsigned int line_arg); + + /* Stash this profile in the profile history. */ + void store(); + + /* Reset the current profile and state of profiling for the next query. */ + void reset(); + + /* SHOW PROFILES */ + bool show_profiles(); + + /* SHOW PROFILE FOR QUERY query_id */ + bool show(uint options, uint query_id); + + /* SHOW PROFILE */ + bool show_last(uint options); +}; + +#endif /* SQL_PROFILE_H */ diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 8da8bbe25ca..5da31ed479a 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1197,7 +1197,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)) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 3d2f46a9982..cb8f39084df 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -599,6 +599,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 */ @@ -702,14 +703,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); + 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"; @@ -745,7 +746,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) { @@ -755,7 +756,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")); @@ -1102,8 +1103,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) && @@ -1161,7 +1163,7 @@ JOIN::optimize() if (need_tmp) { DBUG_PRINT("info",("Creating tmp table")); - thd->proc_info="Creating tmp table"; + THD_PROC_INFO(thd, "creating temporary table"); init_items_ref_array(); @@ -1190,7 +1192,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: @@ -1210,28 +1214,35 @@ 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, + DBUG_PRINT("info",("Sorting for 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; } } @@ -1354,6 +1365,7 @@ JOIN::exec() int tmp_error; DBUG_ENTER("JOIN::exec"); + THD_PROC_INFO(thd, "executing"); error= 0; if (procedure) { @@ -1493,7 +1505,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))) { @@ -1616,7 +1628,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; @@ -1630,7 +1642,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) @@ -1686,7 +1698,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, @@ -1747,7 +1759,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) @@ -1871,7 +1883,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), @@ -2019,7 +2031,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, @@ -2064,8 +2076,9 @@ mysql_select(THD *thd, Item ***rref_pointer_array, err: if (free_join) { - thd->proc_info="end"; + THD_PROC_INFO(thd, "cleaning up"); err|= select_lex->cleanup(); + THD_PROC_INFO(thd, "end"); DBUG_RETURN(err || thd->net.report_error); } DBUG_RETURN(join->error); @@ -9745,7 +9758,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) { @@ -9773,7 +9786,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; } @@ -9803,7 +9816,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)) @@ -9856,8 +9869,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: @@ -9869,7 +9882,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 ee310ea6fe4..f4e67fc24f4 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1350,7 +1350,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; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index c14529f6eb1..0e2fd67965f 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1728,7 +1728,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) @@ -1759,7 +1759,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: @@ -2878,7 +2878,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); @@ -2895,7 +2895,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; @@ -2958,7 +2958,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; @@ -3093,7 +3093,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 { @@ -3101,7 +3101,7 @@ view_err: VOID(pthread_mutex_lock(&LOCK_open)); if (new_name != table_name || new_db != db) { - thd->proc_info="rename"; + THD_PROC_INFO(thd, "rename"); /* Then do a 'simple' rename of the table */ error=0; if (!access(new_name_buff,F_OK)) @@ -3573,7 +3573,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) @@ -3645,7 +3645,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) @@ -3756,7 +3756,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 3b6aa5f1aa2..6d3e29516ba 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -167,7 +167,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); @@ -359,7 +359,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) @@ -424,7 +424,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(); @@ -512,7 +512,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)); /* @@ -960,7 +960,7 @@ int multi_update::prepare(List<Item> ¬_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); @@ -1512,11 +1512,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 53844eb0fd2..15c58bd0673 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -589,7 +589,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 a362d1ce4ea..9c60630fd50 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -181,6 +181,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 @@ -221,10 +222,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 @@ -298,6 +301,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 @@ -367,6 +371,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 @@ -435,6 +441,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 @@ -493,6 +500,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 @@ -510,6 +518,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 @@ -580,6 +590,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 @@ -610,6 +621,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 @@ -735,7 +748,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); union_opt select_derived_init option_type2 %type <ulong_num> - ulong_num raid_types merge_insert_types + ulong_num raid_types merge_insert_types opt_profile_query_arg %type <ulonglong_number> ulonglong_num @@ -6472,6 +6485,48 @@ 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_query_arg: + /* empty */ + { $$= 0; } + | QUERY_SYM NUM + { $$= atoi($2.str); } + ; + +opt_profile_args: + /* empty */ + | FOR_SYM opt_profile_query_arg + { Lex->profile_query_id = $2; } + ; + /* Show things */ show: SHOW @@ -6607,6 +6662,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; @@ -7698,6 +7757,7 @@ keyword_sp: | BERKELEY_DB_SYM {} | BINLOG_SYM {} | BIT_SYM {} + | BLOCK_SYM {} | BOOL_SYM {} | BOOLEAN_SYM {} | BTREE_SYM {} @@ -7714,6 +7774,8 @@ keyword_sp: | COMPRESSED_SYM {} | CONCURRENT {} | CONSISTENT_SYM {} + | CONTEXT_SYM {} + | CPU_SYM {} | CUBE_SYM {} | DATA_SYM {} | DATETIME {} @@ -7736,6 +7798,7 @@ keyword_sp: | EXPANSION_SYM {} | EXTENDED_SYM {} | FAST_SYM {} + | FAULTS_SYM {} | FOUND_SYM {} | DISABLE_SYM {} | ENABLE_SYM {} @@ -7760,6 +7823,8 @@ keyword_sp: | ISSUER_SYM {} | INNOBASE_SYM {} | INSERT_METHOD {} + | IO_SYM {} + | IPC_SYM {} | RELAY_THREAD {} | LAST_SYM {} | LEAVES {} @@ -7789,6 +7854,7 @@ keyword_sp: | MAX_UPDATES_PER_HOUR {} | MAX_USER_CONNECTIONS_SYM {} | MEDIUM_SYM {} + | MEMORY_SYM {} | MERGE_SYM {} | MICROSECOND_SYM {} | MIGRATE_SYM {} @@ -7815,6 +7881,7 @@ keyword_sp: | ONE_SHOT_SYM {} | ONE_SYM {} | PACK_KEYS_SYM {} + | PAGE_SYM {} | PARTIAL {} | PASSWORD {} | PHASE_SYM {} @@ -7824,6 +7891,8 @@ keyword_sp: | PRIVILEGES {} | PROCESS {} | PROCESSLIST_SYM {} + | PROFILE_SYM {} + | PROFILES_SYM {} | QUARTER_SYM {} | QUERY_SYM {} | QUICK {} @@ -7857,6 +7926,7 @@ keyword_sp: | SHUTDOWN {} | SNAPSHOT_SYM {} | SOUNDS_SYM {} + | SOURCE_SYM {} | SQL_CACHE_SYM {} | SQL_BUFFER_RESULT {} | SQL_NO_CACHE_SYM {} @@ -7868,6 +7938,8 @@ keyword_sp: | SUBJECT_SYM {} | SUPER_SYM {} | SUSPEND_SYM {} + | SWAPS_SYM {} + | SWITCHES_SYM {} | TABLES {} | TABLESPACE {} | TEMPORARY {} |