summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorMichael Widenius <monty@askmonty.org>2011-07-01 15:08:30 +0300
committerMichael Widenius <monty@askmonty.org>2011-07-01 15:08:30 +0300
commit3c78bfe7f1a1530daea83f5dae091bd654ea6d37 (patch)
treeb54abedc9d4f6ac33b7aa2128454fcd37d739d78 /sql
parent62e47b4402678abc85a689a820d201248dabbc36 (diff)
downloadmariadb-git-3c78bfe7f1a1530daea83f5dae091bd654ea6d37.tar.gz
Added progress reporting for alter table, LOAD DATA INFILE and for aria tables: check table, repair table, analyze table.
- The client gets a progress report message that triggers a callback function if requested with mysql_options(MYSQL_PROGRESS_CALLBACK, function) - Added Progress field last to 'show processlist' - Stage, Max_stage and Progress field added to information_schema.progresslist - The 'mysql' client by defaults enables progress reports when the output is a tty. - Added progress_report_time time variable to configure how often progress reports is sent to client Added read only system variable 'in_transaction' which is 1 if we have executed a BEGIN statement. client/client_priv.h: Added OPT_REPORT_PROGRESS client/mysql.cc: Added option --progress-reports (on by default if not batch mode) Progress reports is written to stdout for long running commands include/Makefile.am: Added mysql/service_progress_report.h include/myisamchk.h: Added variables to be able to do progress reporting in Aria and later in MyISAM include/mysql.h: Added new mysql_options() parameter: MYSQL_PROGRESS_CALLBACK include/mysql.h.pp: Added new mysql_options() parameter: MYSQL_PROGRESS_CALLBACK include/mysql/plugin.h: Added functions for reporting progress. include/mysql/plugin_auth.h.pp: Added functions for reporting progress. include/mysql_com.h: Added CLIENT_PROGRESS mysql_real_connect() flag. include/sql_common.h: Added callback function for reporting progress mysql-test/r/old-mode.result: Ensure that SHOW PROGRESSLIST doesn't have the Progress column in old mode. mysql-test/suite/funcs_1/datadict/datadict_priv.inc: Added new column mysql-test/suite/funcs_1/datadict/processlist_priv.inc: Test all new PROCESSLIST columns mysql-test/suite/funcs_1/r/is_columns_is.result: Updated results mysql-test/suite/funcs_1/r/is_columns_is_embedded.result: Updated results mysql-test/suite/funcs_1/r/is_columns_mysql_embedded.result: Updated results mysql-test/suite/funcs_1/r/is_tables_is_embedded.result: Updated results mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result: Updated results mysql-test/suite/funcs_1/r/processlist_priv_ps.result: Updated results mysql-test/suite/funcs_1/r/processlist_val_no_prot.result: Updated results mysql-test/suite/funcs_1/r/processlist_val_ps.result: Updated results mysql-test/suite/pbxt/r/pbxt_locking.result: Updated results mysql-test/suite/pbxt/r/skip_name_resolve.result: Updated results mysql-test/t/old-mode.test: Ensure that SHOW PROGRESSLIST doesn't have the Progress column in old mode. plugin/handler_socket/handlersocket/Makefile.am: Added -lmysqlservices scripts/mytop.sh: Made 'State' field width dynamic. Added 'Progress' to process list display. sql-common/client.c: Added handling of progress messages. Removed check_license() function. sql/mysql_priv.h: Added opt_progress_report_time sql/mysqld.cc: Added progress_report_time time variable to configure how often progress reports is sent to client sql/protocol.cc: Added net_send_progress_packet() sql/protocol.h: New prototypes sql/set_var.cc: Added variables progress_report_time and in_transaction sql/sql_acl.cc: Safety fix: Made client_capabilities ulonglong sql/sql_class.cc: Added interface functions for progress reporting sql/sql_class.h: Added varibles in THD for progress reporting. Added CF_REPORT_PROGRESS sql/sql_load.cc: Added progress reporting for LOAD DATA INFILE sql/sql_parse.cc: Added CF_REPORT_PROGRESS for top level commands for which it's safe to send progress reports to client sql/sql_show.cc: Added Progress field last to 'show processlist' Stage, Max_stage and Progress field added to information_schema.progresslist sql/sql_table.cc: Added progress reporting for ALTER TABLE Added THD as argument to copy_data_between_tables() storage/maria/ha_maria.cc: Added progress reporting for check table, repair table, analyze table Fixed a bug in start_bulk_insert() that caused alter table to always run with all keys enabled. storage/maria/ma_check.c: Added progress reporting Remember old state before starting repair. This removes some warnings from optimize_table if create-with-sort fails. storage/maria/ma_check_standalone.h: Added dummy reporting function for standalone Aria programs. storage/maria/ma_sort.c: Added progress reporting storage/maria/maria_chk.c: Updated version storage/maria/maria_def.h: Added new prototypes tests/mysql_client_test.c: Added test case for progress reporting
Diffstat (limited to 'sql')
-rw-r--r--sql/mysql_priv.h12
-rw-r--r--sql/mysqld.cc7
-rw-r--r--sql/protocol.cc50
-rw-r--r--sql/protocol.h1
-rw-r--r--sql/set_var.cc4
-rw-r--r--sql/sql_acl.cc3
-rw-r--r--sql/sql_class.cc119
-rw-r--r--sql/sql_class.h22
-rw-r--r--sql/sql_load.cc46
-rw-r--r--sql/sql_parse.cc19
-rw-r--r--sql/sql_plugin_services.h11
-rw-r--r--sql/sql_show.cc50
-rw-r--r--sql/sql_table.cc33
13 files changed, 342 insertions, 35 deletions
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index f12874be1ea..94bbc25a247 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -808,17 +808,6 @@ inline THD *_current_thd(void)
#endif
#define current_thd _current_thd()
-
-/**
- The meat of thd_proc_info(THD*, char*), a macro that packs the last
- three calling-info parameters.
-*/
-extern "C"
-const char *set_thd_proc_info(THD *thd, const char *info,
- const char *calling_func,
- const char *calling_file,
- const unsigned int calling_line);
-
/**
Enumerate possible types of a table from re-execution
standpoint.
@@ -2184,6 +2173,7 @@ extern ulonglong thd_startup_options;
extern ulong thread_id;
extern ulong binlog_cache_use, binlog_cache_disk_use;
extern ulong aborted_threads,aborted_connects;
+extern ulong opt_progress_report_time;
extern ulong delayed_insert_timeout;
extern ulong delayed_insert_limit, delayed_queue_size;
extern ulong delayed_insert_threads, delayed_insert_writes;
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 6cbd73f4a08..b987210f7ef 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -6086,7 +6086,7 @@ enum options_mysqld
OPT_SORT_BUFFER, OPT_TABLE_OPEN_CACHE, OPT_TABLE_DEF_CACHE,
OPT_THREAD_CONCURRENCY, OPT_THREAD_CACHE_SIZE,
OPT_TMP_TABLE_SIZE, OPT_THREAD_STACK,
- OPT_WAIT_TIMEOUT,
+ OPT_WAIT_TIMEOUT, OPT_PROGRESS_REPORT_TIME,
OPT_ERROR_LOG_FILE,
OPT_DEFAULT_WEEK_FORMAT,
OPT_RANGE_ALLOC_BLOCK_SIZE, OPT_ALLOW_SUSPICIOUS_UDFS,
@@ -7584,6 +7584,11 @@ each time the SQL thread starts.",
&global_system_variables.preload_buff_size,
&max_system_variables.preload_buff_size, 0, GET_ULONG,
REQUIRED_ARG, 32*1024L, 1024, 1024*1024*1024L, 0, 1, 0},
+ {"progress_report_time", OPT_PROGRESS_REPORT_TIME,
+ "Seconds between sending progress reports to the client for slow commands. Set to 0 to disable progress reporting.",
+ &global_system_variables.progress_report_time,
+ &max_system_variables.progress_report_time,
+ 0, GET_ULONG, REQUIRED_ARG, 5, 0, ULONG_MAX, 0, 1, 0},
{"query_alloc_block_size", OPT_QUERY_ALLOC_BLOCK_SIZE,
"Allocation block size for query parsing and execution.",
&global_system_variables.query_alloc_block_size,
diff --git a/sql/protocol.cc b/sql/protocol.cc
index a0e14423b73..af4324b4166 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -515,6 +515,56 @@ void net_end_statement(THD *thd)
}
+/**
+ Send a progress report to the client
+
+ What we send is:
+ header (255,255,255,1)
+ stage, max_stage as on byte integers
+ percentage withing the stage as percentage*1000
+ (that is, ratio*100000) as a 3 byte integer
+ proc_info as a string
+*/
+
+const uchar progress_header[2]= {(uchar) 255, (uchar) 255 };
+
+void net_send_progress_packet(THD *thd)
+{
+ uchar buff[200], *pos;
+ const char *proc_info= thd->proc_info ? thd->proc_info : "";
+ uint length= strlen(proc_info);
+ ulonglong progress;
+ DBUG_ENTER("net_send_progress_packet");
+
+ if (unlikely(!thd->net.vio))
+ DBUG_VOID_RETURN; // Socket is closed
+
+ pos= buff;
+ /*
+ Store number of strings first. This allows us to later expand the
+ progress indicator if needed.
+ */
+ *pos++= (uchar) 1; // Number of strings
+ *pos++= (uchar) thd->progress.stage + 1;
+ /*
+ We have the max() here to avoid problems if max_stage is not set,
+ which may happen during automatic repair of table
+ */
+ *pos++= (uchar) max(thd->progress.max_stage, thd->progress.stage + 1);
+ progress= 0;
+ if (thd->progress.max_counter)
+ progress= 100000ULL * thd->progress.counter / thd->progress.max_counter;
+ int3store(pos, progress); // Between 0 & 100000
+ pos+= 3;
+ pos= net_store_data(pos, (const uchar*) proc_info,
+ min(length, sizeof(buff)-7));
+ net_write_command(&thd->net, (uchar) 255, progress_header,
+ sizeof(progress_header), (uchar*) buff,
+ (uint) (pos - buff));
+ DBUG_VOID_RETURN;
+}
+
+
/****************************************************************************
Functions used by the protocol functions (like net_send_ok) to store
strings and numbers in the header result packet.
diff --git a/sql/protocol.h b/sql/protocol.h
index 4ab5c169928..e07af5208db 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -177,6 +177,7 @@ public:
void send_warning(THD *thd, uint sql_errno, const char *err=0);
bool net_send_error(THD *thd, uint sql_errno=0, const char *err=0);
void net_end_statement(THD *thd);
+void net_send_progress_packet(THD *thd);
uchar *net_store_data(uchar *to,const uchar *from, size_t length);
uchar *net_store_data(uchar *to,int32 from);
uchar *net_store_data(uchar *to,longlong from);
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 68f3ae73a37..0a06b3254f6 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -528,6 +528,10 @@ static sys_var_thd_ulong sys_optimizer_search_depth(&vars, "optimizer_sea
static sys_var_thd_optimizer_switch sys_optimizer_switch(&vars, "optimizer_switch",
&SV::optimizer_switch);
+static sys_var_thd_ulong sys_progress_report_time(&vars,
+ "progress_report_time",
+ &SV::progress_report_time);
+
static sys_var_const sys_pid_file(&vars, "pid_file",
OPT_GLOBAL, SHOW_CHAR,
(uchar*) pidfile_name);
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index b133e1e4d68..742a20ab4f3 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -7478,7 +7478,6 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
THD *thd= mpvio->thd;
NET *net= &thd->net;
char *end;
-
DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
if (pkt_len < MIN_HANDSHAKE_SIZE)
@@ -7490,7 +7489,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
ulong client_capabilities= uint2korr(net->read_pos);
if (client_capabilities & CLIENT_PROTOCOL_41)
{
- client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
+ client_capabilities|= ((ulonglong) uint2korr(net->read_pos+2)) << 16;
thd->max_client_packet_length= uint4korr(net->read_pos+4);
DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
if (thd_init_client_charset(thd, (uint) net->read_pos[8]))
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 3bd1e591054..0772ea56d17 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -725,6 +725,8 @@ THD::THD()
user_time.val= start_time= start_time_sec_part= 0;
start_utime= prior_thr_create_utime= 0L;
utime_after_lock= 0L;
+ progress.report_to_client= 0;
+ progress.max_counter= 0;
current_linfo = 0;
slave_thread = 0;
bzero(&variables, sizeof(variables));
@@ -991,6 +993,11 @@ void THD::update_all_stats()
ulonglong end_cpu_time, end_utime;
double busy_time, cpu_time;
+ /* Reset status variables used by information_schema.processlist */
+ progress.max_counter= 0;
+ progress.max_stage= 0;
+ progress.report= 0;
+
/* This is set at start of query if opt_userstat_running was set */
if (!userstat_running)
return;
@@ -3364,11 +3371,123 @@ void THD::restore_backup_open_tables_state(Open_tables_state *backup)
@retval 0 the user thread is active
@retval 1 the user thread has been killed
*/
+
extern "C" int thd_killed(const MYSQL_THD thd)
{
return(thd->killed);
}
+
+/**
+ Send an out-of-band progress report to the client
+
+ The report is sent every 'thd->...progress_report_time' second,
+ however not more often than global.progress_report_time.
+ If global.progress_report_time is 0, then don't send progress reports, but
+ check every second if the value has changed
+*/
+
+static void thd_send_progress(THD *thd)
+{
+ /* Check if we should send the client a progress report */
+ ulonglong report_time= my_interval_timer();
+ if (report_time > thd->progress.next_report_time)
+ {
+ uint seconds_to_next= max(thd->variables.progress_report_time,
+ global_system_variables.progress_report_time);
+ if (seconds_to_next == 0) // Turned off
+ seconds_to_next= 1; // Check again after 1 second
+
+ thd->progress.next_report_time= (report_time +
+ seconds_to_next * 1000000000ULL);
+ if (global_system_variables.progress_report_time &&
+ thd->variables.progress_report_time)
+ net_send_progress_packet(thd);
+ }
+}
+
+
+/** Initialize progress report handling **/
+
+extern "C" void thd_progress_init(MYSQL_THD thd, uint max_stage)
+{
+ /*
+ Send progress reports to clients that supports it, if the command
+ is a high level command (like ALTER TABLE) and we are not in a
+ stored procedure
+ */
+ thd->progress.report= ((thd->client_capabilities & CLIENT_PROGRESS) &&
+ thd->progress.report_to_client &&
+ !thd->in_sub_stmt);
+ thd->progress.next_report_time= 0;
+ thd->progress.stage= 0;
+ thd->progress.counter= thd->progress.max_counter= 0;
+ thd->progress.max_stage= max_stage;
+}
+
+
+/* Inform processlist and the client that some progress has been made */
+
+extern "C" void thd_progress_report(MYSQL_THD thd,
+ ulonglong progress, ulonglong max_progress)
+{
+ if (thd->progress.max_counter != max_progress) // Simple optimization
+ {
+ pthread_mutex_lock(&thd->LOCK_thd_data);
+ thd->progress.counter= progress;
+ thd->progress.max_counter= max_progress;
+ pthread_mutex_unlock(&thd->LOCK_thd_data);
+ }
+ else
+ thd->progress.counter= progress;
+
+ if (thd->progress.report)
+ thd_send_progress(thd);
+}
+
+/**
+ Move to next stage in process list handling
+
+ This will reset the timer to ensure the progress is sent to the client
+ if client progress reports are activated.
+*/
+
+extern "C" void thd_progress_next_stage(MYSQL_THD thd)
+{
+ pthread_mutex_lock(&thd->LOCK_thd_data);
+ thd->progress.stage++;
+ thd->progress.counter= 0;
+ DBUG_ASSERT(thd->progress.stage < thd->progress.max_stage);
+ pthread_mutex_unlock(&thd->LOCK_thd_data);
+ if (thd->progress.report)
+ {
+ thd->progress.next_report_time= 0; // Send new stage info
+ thd_send_progress(thd);
+ }
+}
+
+/**
+ Disable reporting of progress in process list.
+
+ @note
+ This function is safe to call even if one has not called thd_progress_init.
+
+ This function should be called by all parts that does progress
+ reporting to ensure that progress list doesn't contain 100 % done
+ forever.
+*/
+
+
+extern "C" void thd_progress_end(MYSQL_THD thd)
+{
+ /*
+ It's enough to reset max_counter to set disable progress indicator
+ in processlist.
+ */
+ thd->progress.max_counter= 0;
+}
+
+
/**
Return the thread id of a user thread
@param thd user thread
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 6f2852bf431..f20d7ae1aab 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -443,6 +443,7 @@ struct system_variables
ulong ndb_index_stat_cache_entries;
ulong ndb_index_stat_update_freq;
ulong binlog_format; // binlog format for this thd (see enum_binlog_format)
+ ulong progress_report_time;
my_bool binlog_annotate_rows_events;
my_bool binlog_direct_non_trans_update;
/*
@@ -1551,6 +1552,25 @@ public:
ulonglong prior_thr_create_utime, thr_create_utime;
ulonglong start_utime, utime_after_lock;
+ // Process indicator
+ struct {
+ /*
+ true, if the currently running command can send progress report
+ packets to a client. Set by mysql_execute_command() for safe commands
+ See CF_REPORT_PROGRESS
+ */
+ bool report_to_client;
+ /*
+ true, if we will send progress report packets to a client
+ (client has requested them, see CLIENT_PROGRESS; report_to_client
+ is true; not in sub-statement)
+ */
+ bool report;
+ uint stage, max_stage;
+ ulonglong counter, max_counter;
+ ulonglong next_report_time;
+ } progress;
+
thr_lock_type update_lock_default;
Delayed_insert *di;
@@ -3556,6 +3576,7 @@ public:
#define CF_STATUS_COMMAND 4
#define CF_SHOW_TABLE_COMMAND 8
#define CF_WRITE_LOGS_COMMAND 16
+
/**
Must be set for SQL statements that may contain
Item expressions and/or use joins and tables.
@@ -3570,6 +3591,7 @@ public:
joins are currently prohibited in these statements.
*/
#define CF_REEXECUTION_FRAGILE 32
+#define CF_REPORT_PROGRESS 64
/* Functions in sql_class.cc */
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 089c148778e..cc0527591e1 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -63,6 +63,8 @@ public:
::end_io_cache(&cache);
need_end_io_cache = 0;
}
+ my_off_t file_length() { return cache.end_of_file; }
+ my_off_t position() { return my_b_tell(&cache); }
/*
Either this method, or we need to make cache public
@@ -416,9 +418,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
}
}
+ thd_proc_info(thd, "reading file");
if (!(error=test(read_info.error)))
{
-
table->next_number_field=table->found_next_number_field;
if (ignore ||
handle_duplicates == DUP_REPLACE)
@@ -436,6 +438,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
(MODE_STRICT_TRANS_TABLES |
MODE_STRICT_ALL_TABLES)));
+ thd_progress_init(thd, 2);
if (!field_term->length() && !enclosed->length())
error= read_fixed_length(thd, info, table_list, fields_vars,
set_fields, set_values, read_info,
@@ -444,6 +447,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
error= read_sep_field(thd, info, table_list, fields_vars,
set_fields, set_values, read_info,
*enclosed, skip_lines, ignore);
+
+ thd_proc_info(thd, "End bulk insert");
+ thd_progress_next_stage(thd);
if (!thd->prelocked_mode && table->file->ha_end_bulk_insert() && !error)
{
table->file->print_error(my_errno, MYF(0));
@@ -736,9 +742,16 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
List_iterator_fast<Item> it(fields_vars);
Item_field *sql_field;
TABLE *table= table_list->table;
- bool err;
+ bool err, progress_reports;
+ ulonglong counter, time_to_report_progress;
DBUG_ENTER("read_fixed_length");
+ counter= 0;
+ time_to_report_progress= MY_HOW_OFTEN_TO_WRITE/10;
+ progress_reports= 1;
+ if ((thd->progress.max_counter= read_info.file_length()) == ~(my_off_t) 0)
+ progress_reports= 0;
+
while (!read_info.read_fixed_length())
{
if (thd->killed)
@@ -746,6 +759,16 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
thd->send_kill_message();
DBUG_RETURN(1);
}
+ if (progress_reports)
+ {
+ thd->progress.counter= read_info.position();
+ if (++counter >= time_to_report_progress)
+ {
+ time_to_report_progress+= MY_HOW_OFTEN_TO_WRITE/10;
+ thd_progress_report(thd, thd->progress.counter,
+ thd->progress.max_counter);
+ }
+ }
if (skip_lines)
{
/*
@@ -864,11 +887,18 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
Item *item;
TABLE *table= table_list->table;
uint enclosed_length;
- bool err;
+ bool err, progress_reports;
+ ulonglong counter, time_to_report_progress;
DBUG_ENTER("read_sep_field");
enclosed_length=enclosed.length();
+ counter= 0;
+ time_to_report_progress= MY_HOW_OFTEN_TO_WRITE/10;
+ progress_reports= 1;
+ if ((thd->progress.max_counter= read_info.file_length()) == ~(my_off_t) 0)
+ progress_reports= 0;
+
for (;;it.rewind())
{
if (thd->killed)
@@ -877,6 +907,16 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
DBUG_RETURN(1);
}
+ if (progress_reports)
+ {
+ thd->progress.counter= read_info.position();
+ if (++counter >= time_to_report_progress)
+ {
+ time_to_report_progress+= MY_HOW_OFTEN_TO_WRITE/10;
+ thd_progress_report(thd, thd->progress.counter,
+ thd->progress.max_counter);
+ }
+ }
restore_record(table, s->default_values);
while ((item= it++))
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 315673c450b..f10430b022c 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -265,17 +265,17 @@ void init_update_queries(void)
bzero((uchar*) &sql_command_flags, sizeof(sql_command_flags));
sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE;
- sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND;
+ sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_REPORT_PROGRESS;
+ sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND | CF_REPORT_PROGRESS;
sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND;
sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE;
+ sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_REPORT_PROGRESS;
sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_BACKUP_TABLE]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_RESTORE_TABLE]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_DROP_INDEX]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_DROP_INDEX]= CF_CHANGES_DATA | CF_REPORT_PROGRESS;
sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_DROP_VIEW]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_CREATE_EVENT]= CF_CHANGES_DATA;
@@ -368,9 +368,11 @@ void init_update_queries(void)
The following admin table operations are allowed
on log tables.
*/
- sql_command_flags[SQLCOM_REPAIR]= CF_WRITE_LOGS_COMMAND;
- sql_command_flags[SQLCOM_OPTIMIZE]= CF_WRITE_LOGS_COMMAND;
- sql_command_flags[SQLCOM_ANALYZE]= CF_WRITE_LOGS_COMMAND;
+ sql_command_flags[SQLCOM_REPAIR]= CF_WRITE_LOGS_COMMAND | CF_REPORT_PROGRESS;
+ sql_command_flags[SQLCOM_OPTIMIZE]= CF_WRITE_LOGS_COMMAND | CF_REPORT_PROGRESS;
+ sql_command_flags[SQLCOM_ANALYZE]= CF_WRITE_LOGS_COMMAND | CF_REPORT_PROGRESS;
+ sql_command_flags[SQLCOM_CHECK]= CF_REPORT_PROGRESS;
+ sql_command_flags[SQLCOM_CHECKSUM]= CF_REPORT_PROGRESS;
}
@@ -2166,6 +2168,8 @@ mysql_execute_command(THD *thd)
} /* endif unlikely slave */
#endif
status_var_increment(thd->status_var.com_stat[lex->sql_command]);
+ thd->progress.report_to_client= test(sql_command_flags[lex->sql_command] &
+ CF_REPORT_PROGRESS);
DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE);
@@ -2995,6 +2999,7 @@ end_with_restore_list:
thd->enable_slow_log= opt_log_slow_admin_statements;
thd->query_plan_flags|= QPLAN_ADMIN;
res= mysql_analyze_table(thd, first_table, &lex->check_opt);
+
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
diff --git a/sql/sql_plugin_services.h b/sql/sql_plugin_services.h
index 8d4055dd764..497e2c8d6bc 100644
--- a/sql/sql_plugin_services.h
+++ b/sql/sql_plugin_services.h
@@ -38,9 +38,18 @@ static struct thd_alloc_service_st thd_alloc_handler= {
thd_make_lex_string
};
+static struct progress_report_service_st progress_report_handler= {
+ thd_progress_init,
+ thd_progress_report,
+ thd_progress_next_stage,
+ thd_progress_end,
+ set_thd_proc_info
+};
+
static struct st_service_ref list_of_services[] __attribute__((unused)) =
{
{ "my_snprintf_service", VERSION_my_snprintf, &my_snprintf_handler },
- { "thd_alloc_service", VERSION_thd_alloc, &thd_alloc_handler }
+ { "thd_alloc_service", VERSION_thd_alloc, &thd_alloc_handler },
+ { "progress_report_service", VERSION_progress_report, &progress_report_handler }
};
#endif
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index e2949c49608..e4981701025 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1870,6 +1870,7 @@ public:
uint command;
const char *user,*host,*db,*proc_info,*state_info;
char *query;
+ double progress;
};
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
@@ -1898,6 +1899,11 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
field->maybe_null=1;
field_list.push_back(field=new Item_empty_string("Info",max_query_length));
field->maybe_null=1;
+ if (!thd->variables.old_mode)
+ {
+ field_list.push_back(field= new Item_float("Progress", 0.0, 3, 7));
+ field->maybe_null= 0;
+ }
if (protocol->send_fields(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_VOID_RETURN;
@@ -1957,6 +1963,8 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
thd_info->start_time= tmp->start_time;
thd_info->query=0;
+ thd_info->progress= 0.0;
+
/* Lock THD mutex that protects its data when looking at it. */
pthread_mutex_lock(&tmp->LOCK_thd_data);
if (tmp->query())
@@ -1964,6 +1972,20 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
uint length= min(max_query_length, tmp->query_length());
thd_info->query= (char*) thd->strmake(tmp->query(),length);
}
+
+ /*
+ Progress report. We need to do this under a lock to ensure that all
+ is from the same stage.
+ */
+ if (tmp->progress.max_counter)
+ {
+ uint max_stage= max(tmp->progress.max_stage, 1);
+ thd_info->progress= (((tmp->progress.stage / (double) max_stage) +
+ ((tmp->progress.counter /
+ (double) tmp->progress.max_counter) /
+ (double) max_stage)) *
+ 100.0);
+ }
pthread_mutex_unlock(&tmp->LOCK_thd_data);
thread_infos.append(thd_info);
}
@@ -1973,6 +1995,9 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
thread_info *thd_info;
time_t now= my_time(0);
+ char buff[20]; // For progress
+ String store_buffer(buff, sizeof(buff), system_charset_info);
+
while ((thd_info=thread_infos.get()))
{
protocol->prepare_for_resend();
@@ -1990,6 +2015,8 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
protocol->store_null();
protocol->store(thd_info->state_info, system_charset_info);
protocol->store(thd_info->query, system_charset_info);
+ if (!thd->variables.old_mode)
+ protocol->store(thd_info->progress, 3, &store_buffer);
if (protocol->write())
break; /* purecov: inspected */
}
@@ -2020,6 +2047,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
Security_context *tmp_sctx= tmp->security_ctx;
struct st_my_thread_var *mysys_var;
const char *val;
+ ulonglong max_counter;
if ((!tmp->vio_ok() && !tmp->system_thread) ||
(user && (!tmp_sctx->user || strcmp(tmp_sctx->user, user))))
@@ -2087,6 +2115,9 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
if (mysys_var)
pthread_mutex_unlock(&mysys_var->mutex);
+ /* TIME_MS */
+ table->field[8]->store((double)(utime / (HRTIME_RESOLUTION / 1000.0)));
+
/* INFO */
/* Lock THD mutex that protects its data when looking at it. */
pthread_mutex_lock(&tmp->LOCK_thd_data);
@@ -2097,10 +2128,19 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
tmp->query_length()), cs);
table->field[7]->set_notnull();
}
- pthread_mutex_unlock(&tmp->LOCK_thd_data);
- /* TIME_MS */
- table->field[8]->store((double)(utime / (HRTIME_RESOLUTION / 1000.0)));
+ /*
+ Progress report. We need to do this under a lock to ensure that all
+ is from the same stage.
+ */
+ if ((max_counter= tmp->progress.max_counter))
+ {
+ table->field[9]->store((longlong) tmp->progress.stage + 1, 1);
+ table->field[10]->store((longlong) tmp->progress.max_stage, 1);
+ table->field[11]->store((double) tmp->progress.counter /
+ (double) max_counter*100.0);
+ }
+ pthread_mutex_unlock(&tmp->LOCK_thd_data);
if (schema_table_store_record(thd, table))
{
@@ -7300,6 +7340,10 @@ ST_FIELD_INFO processlist_fields_info[]=
SKIP_OPEN_TABLE},
{"TIME_MS", 100 * (MY_INT64_NUM_DECIMAL_DIGITS + 1) + 3, MYSQL_TYPE_DECIMAL,
0, 0, "Time_ms", SKIP_OPEN_TABLE},
+ {"STAGE", 2, MYSQL_TYPE_TINY, 0, 0, "Stage", SKIP_OPEN_TABLE},
+ {"MAX_STAGE", 2, MYSQL_TYPE_TINY, 0, 0, "Max_stage", SKIP_OPEN_TABLE},
+ {"PROGRESS", 703, MYSQL_TYPE_DECIMAL, 0, 0, "Progress",
+ SKIP_OPEN_TABLE},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index c6730bc0853..299e88107d7 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -36,7 +36,8 @@ const char *primary_key_name="PRIMARY";
static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end);
static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end);
-static int copy_data_between_tables(TABLE *,TABLE *, List<Create_field> &, bool,
+static int copy_data_between_tables(THD *thd, TABLE *,TABLE *,
+ List<Create_field> &, bool,
uint, ORDER *, ha_rows *,ha_rows *,
enum enum_enable_or_disable, bool);
@@ -7497,8 +7498,7 @@ view_err:
/* We don't want update TIMESTAMP fields during ALTER TABLE. */
new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
new_table->next_number_field=new_table->found_next_number_field;
- thd_proc_info(thd, "copy to tmp table");
- error= copy_data_between_tables(table, new_table,
+ error= copy_data_between_tables(thd, table, new_table,
alter_info->create_list, ignore,
order_num, order, &copied, &deleted,
alter_info->keys_onoff,
@@ -7905,7 +7905,7 @@ err_with_placeholders:
/* Copy all rows from one table to another */
static int
-copy_data_between_tables(TABLE *from,TABLE *to,
+copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
List<Create_field> &create,
bool ignore,
uint order_num, ORDER *order,
@@ -7917,7 +7917,6 @@ copy_data_between_tables(TABLE *from,TABLE *to,
int error= 1, errpos= 0;
Copy_field *copy= NULL, *copy_end;
ha_rows found_count= 0, delete_count= 0;
- THD *thd= current_thd;
uint length= 0;
SORT_FIELD *sortorder;
READ_RECORD info;
@@ -7927,11 +7926,14 @@ copy_data_between_tables(TABLE *from,TABLE *to,
ha_rows examined_rows;
bool auto_increment_field_copied= 0;
ulong save_sql_mode= thd->variables.sql_mode;
- ulonglong prev_insert_id;
+ ulonglong prev_insert_id, time_to_report_progress;
List_iterator<Create_field> it(create);
Create_field *def;
DBUG_ENTER("copy_data_between_tables");
+ /* Two or 3 stages; Sorting, copying data and update indexes */
+ thd_progress_init(thd, 2 + test(order));
+
/*
Turn off recovery logging since rollback of an alter table is to
delete the new table so there is no need to log the changes to it.
@@ -8005,6 +8007,7 @@ copy_data_between_tables(TABLE *from,TABLE *to,
tables.alias= tables.table_name= from->s->table_name.str;
tables.db= from->s->db.str;
+ thd_proc_info(thd, "Sorting");
if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
setup_order(thd, thd->lex->select_lex.ref_pointer_array,
&tables, fields, all_fields, order) ||
@@ -8015,8 +8018,10 @@ copy_data_between_tables(TABLE *from,TABLE *to,
HA_POS_ERROR)
goto err;
}
- };
+ thd_progress_next_stage(thd);
+ }
+ thd_proc_info(thd, "copy to tmp table");
/* Tell handler that we have values for all columns in the to table */
to->use_all_columns();
to->mark_virtual_columns_for_write(TRUE);
@@ -8027,6 +8032,10 @@ copy_data_between_tables(TABLE *from,TABLE *to,
to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
thd->row_count= 0;
restore_record(to, s->default_values); // Create empty record
+
+ thd->progress.max_counter= from->file->records();
+ time_to_report_progress= MY_HOW_OFTEN_TO_WRITE/10;
+
while (!(error=info.read_record(&info)))
{
if (thd->killed)
@@ -8037,6 +8046,13 @@ copy_data_between_tables(TABLE *from,TABLE *to,
}
update_virtual_fields(thd, from);
thd->row_count++;
+ if (++thd->progress.counter >= time_to_report_progress)
+ {
+ time_to_report_progress+= MY_HOW_OFTEN_TO_WRITE/10;
+ thd_progress_report(thd, thd->progress.counter,
+ thd->progress.max_counter);
+ }
+
/* Return error if source table isn't empty. */
if (error_if_not_empty)
{
@@ -8100,6 +8116,9 @@ err:
free_io_cache(from);
delete [] copy;
+ thd_proc_info(thd, "Enabling keys");
+ thd_progress_next_stage(thd);
+
if (error > 0)
to->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
if (errpos >= 3 && to->file->ha_end_bulk_insert() && error <= 0)