diff options
author | Michael Widenius <monty@askmonty.org> | 2011-07-01 15:08:30 +0300 |
---|---|---|
committer | Michael Widenius <monty@askmonty.org> | 2011-07-01 15:08:30 +0300 |
commit | 3c78bfe7f1a1530daea83f5dae091bd654ea6d37 (patch) | |
tree | b54abedc9d4f6ac33b7aa2128454fcd37d739d78 /sql | |
parent | 62e47b4402678abc85a689a820d201248dabbc36 (diff) | |
download | mariadb-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.h | 12 | ||||
-rw-r--r-- | sql/mysqld.cc | 7 | ||||
-rw-r--r-- | sql/protocol.cc | 50 | ||||
-rw-r--r-- | sql/protocol.h | 1 | ||||
-rw-r--r-- | sql/set_var.cc | 4 | ||||
-rw-r--r-- | sql/sql_acl.cc | 3 | ||||
-rw-r--r-- | sql/sql_class.cc | 119 | ||||
-rw-r--r-- | sql/sql_class.h | 22 | ||||
-rw-r--r-- | sql/sql_load.cc | 46 | ||||
-rw-r--r-- | sql/sql_parse.cc | 19 | ||||
-rw-r--r-- | sql/sql_plugin_services.h | 11 | ||||
-rw-r--r-- | sql/sql_show.cc | 50 | ||||
-rw-r--r-- | sql/sql_table.cc | 33 |
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) |