diff --git a/.bzrignore b/.bzrignore index 1c9e79fc71b..3beaab80a01 100644 --- a/.bzrignore +++ b/.bzrignore @@ -159,3 +159,7 @@ PENDING/2000-10-11.06 BitKeeper/etc/csets-out BitKeeper/etc/csets-in support-files/mysql-3.23.26-beta.spec +include/.my_sys.h.swp +PENDING/2000-10-25.01 +PENDING/2000-10-25.02 +support-files/mysql-3.23.27-beta.spec diff --git a/Docs/manual.texi b/Docs/manual.texi index 16b5458afc2..e81178b69cf 100644 --- a/Docs/manual.texi +++ b/Docs/manual.texi @@ -24857,6 +24857,11 @@ propogation. @code{LOAD LOCAL DATA INFILE} will be skipped. @item Update queries that use user variables are not replication-safe (yet) @item +Temporary tables will not work if the table with the same name + is used in more than one thread - we plan on fixing this soon. For +now the only thing you can do is turn off logging of the trouble +queries with @code{SET SQL_LOG_BIN=0} +@item Starting in 3.23.26, it is safe to connect servers in a circular master-slave relationship with @code{log-slave-updates} enabled. Note, however, that many queries will not work right in this kind of @@ -24918,6 +24923,9 @@ databases should not be logged to the binary log with @code{binlog-ignore-db} Starting in 3.23.26, you can use @code{replicate-rewrite-db} to tell the slave to apply updates from one database on the master to the one with a different name on the slave +@item +Starting in 3.23.28, you can use @code{PURGE MASTER LOGS TO 'log-name'} +to get rid of old logs while the slave is running @end itemize @node Replication Options, Replication SQL, Replication Features, Replication @@ -25125,6 +25133,34 @@ command line. (Slave) @item @code{SHOW SLAVE STATUS} @tab Provides status info on essential parameters of the slave thread. (Slave) +@item @code{SHOW MASTER LOGS} + @tab Only available starting in 3.23.28. Lists the binary logs on the master. You should use this command +prior to @code{PURGE MASTER LOGS TO} to find out how far you should go. + +@item @code{PURGE MASTER LOGS TO 'logname'} + @tab Available starting in 3.23.28. Deletes all the +replication logs that are listed in the log +index as being prior to the specified log, and removed them from the +log index, so that the given log now becomes first. Example: + +@example +PURGE MASTER LOGS TO 'mysql-bin.010' +@end example + +This command will do nothing and fail with an error +if you have an active slave that +is currently reading one of the logs you are trying to delete. However, +if you have a dormant slave, and happen to purge one of the logs it +wants to read, the slave will be unable to replicate once it comes up. +The command is safe to run while slaves are replicating - you do not +need to stop them. + +You must first check all the slaves with @code{SHOW SLAVE STATUS} to +see which log they are on, then do a listing of the logs on the +master with @code{SHOW MASTER LOGS}, find the earliest log among all +the slaves ( if all the slaves are up to date, this will be the +last log on the list), backup all the logs you are about to delete +(optional) and purge up to the target log. @end multitable @@ -25164,7 +25200,20 @@ In 3.23.26 we added @code{server-id} to each replication server, and now all the old zombie threads are killed on the master when a new replication thread connects from the same slave +@strong{Q}: How do I rotate replication logs? + +@strong{A}: In 3.23.28 you should use @code{PURGE MASTER LOGS TO} +command after determining which logs can be deleted, and optionally +backing them up first. In earlier versions the process is much more +painful, and cannot be safely done without stopping all the slaves in +the case that you plan to re-use log names . +You will need to stop the slave threads, edit the binary log index + file, delete all the old logs, restart the master, start slave threads, +and then remove the old log files. + + @strong{Q}: How do I upgrade on a hot replication setup? + @strong{A}: If you are upgrading pre-3.23.26 versions, you should just lock the master tables, let the slave catch up, then run @code{FLUSH MASTER} on the master, and @code{FLUSH SLAVE} on the slave to reset the @@ -38194,6 +38243,17 @@ though, so 3.23 is not released as a stable version yet. Fixed bug in a BDB key compare function when comparing part keys. @item Added variable @code{bdb_lock_max} to @code{mysqld}. +@item +@code{SLAVE START} did not work if you started with + @code{--skip-slave-start} and had not explicitly run @code{CHANGE +MASTER TO} +@item +Fixed the output of @code{SHOW MASTER STATUS} to be consistent with +@code{SHOW SLAVE STATUS} ( no directory in the log name) +@item +Added @code{PURGE MASTER LOGS TO} +@item +Added @code{SHOW MASTER LOGS} @end itemize @node News-3.23.27, News-3.23.26, News-3.23.28, News-3.23.x diff --git a/include/.my_sys.h.swp b/include/.my_sys.h.swp new file mode 100644 index 00000000000..e9d01f0e65d Binary files /dev/null and b/include/.my_sys.h.swp differ diff --git a/sql/lex.h b/sql/lex.h index a5c1b6eb123..a75fa07c3b7 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -233,6 +233,7 @@ static SYMBOL symbols[] = { { "PACK_KEYS", SYM(PACK_KEYS_SYM),0,0}, { "PARTIAL", SYM(PARTIAL),0,0}, { "PASSWORD", SYM(PASSWORD),0,0}, + { "PURGE", SYM(PURGE),0,0}, { "PRECISION", SYM(PRECISION),0,0}, { "PRIMARY", SYM(PRIMARY_SYM),0,0}, { "PROCEDURE", SYM(PROCEDURE),0,0}, diff --git a/sql/log.cc b/sql/log.cc index d245f49bf7d..42af3a8397b 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -19,6 +19,7 @@ #include "mysql_priv.h" #include "sql_acl.h" +#include "sql_repl.h" #include #include @@ -256,7 +257,8 @@ int MYSQL_LOG::find_first_log(LOG_INFO* linfo, const char* log_name) } // if the log entry matches, empty string matching anything - if(!log_name_len || (fname[log_name_len] == '\n' && !memcmp(fname, log_name, log_name_len))) + if(!log_name_len || (fname[log_name_len] == '\n' && + !memcmp(fname, log_name, log_name_len))) { if(log_name_len) fname[log_name_len] = 0; // to kill \n @@ -275,6 +277,137 @@ err: return error; } + +int MYSQL_LOG::purge_logs(THD* thd, const char* to_log) +{ + if(!index_file) return LOG_INFO_INVALID; + if(no_rotate) return LOG_INFO_PURGE_NO_ROTATE; + int error; + char fname[FN_REFLEN]; + char* fname_end, *p; + uint fname_len, i; + bool logs_to_purge_inited = 0, logs_to_keep_inited = 0, found_log = 0; + DYNAMIC_ARRAY logs_to_purge, logs_to_keep; + my_off_t purge_offset ; + pthread_mutex_lock(&LOCK_index); + + if(my_fseek(index_file, 0, MY_SEEK_SET, + MYF(MY_WME) ) == MY_FILEPOS_ERROR) + { + error = LOG_INFO_SEEK; + goto err; + } + + if(init_dynamic_array(&logs_to_purge, sizeof(char*), 1024, 1024)) + { + error = LOG_INFO_MEM; + goto err; + } + logs_to_purge_inited = 1; + + if(init_dynamic_array(&logs_to_keep, sizeof(char*), 1024, 1024)) + { + error = LOG_INFO_MEM; + goto err; + } + logs_to_keep_inited = 1; + + + for(;;) + { + if(!fgets(fname, FN_REFLEN, index_file)) + { + if(feof(index_file)) + break; + else + error = LOG_INFO_IO; + goto err; + } + + *(fname_end = (strend(fname) - 1)) = 0; // kill \n + fname_len = (uint)(fname_end - fname); + + if(!memcmp(fname, to_log, fname_len + 1 )) + { + found_log = 1; + purge_offset = my_ftell(index_file, MYF(MY_WME)) - fname_len - 1; + } + + if(!found_log && log_in_use(fname)) + // if one of the logs before the target is in use + { + error = LOG_INFO_IN_USE; + goto err; + } + + p = sql_memdup(fname, (uint)(fname_end - fname) + 1); + if((found_log) ? + insert_dynamic(&logs_to_keep, (gptr) &p) : + insert_dynamic(&logs_to_purge, (gptr) &p) + ) + { + error = LOG_INFO_MEM; + goto err; + } + } + + if(!found_log) + { + error = LOG_INFO_EOF; + goto err; + } + + for(i = 0; i < logs_to_purge.elements; i++) + { + char* l; + get_dynamic(&logs_to_purge, (gptr)&l, i); + if(my_delete(l, MYF(MY_WME))) + sql_print_error("Error deleting %s during purge", l); + } + + // if we get killed -9 here, the sysadmin would have to do a small + // vi job on the log index file after restart - otherwise, this should + // be safe + my_fclose(index_file, MYF(MY_WME)); + if(!(index_file = my_fopen(index_file_name, O_BINARY|O_WRONLY, + MYF(MY_WME)))) + { + sql_print_error("Ouch! Could not re-open the binlog index file \ +during log purge for write"); + error = LOG_INFO_FATAL; + goto err; + } + + for(i = 0; i < logs_to_keep.elements; i++) + { + char* l; + get_dynamic(&logs_to_keep, (gptr)&l, i); + fprintf(index_file, "%s\n", l); + } + my_fclose(index_file, MYF(MY_WME)); + + if(!(index_file = my_fopen(index_file_name, O_BINARY|O_RDWR|O_APPEND, + MYF(MY_WME)))) + { + sql_print_error("Ouch! Could not re-open the binlog index file \ +during log purge for append"); + error = LOG_INFO_FATAL; + goto err; + } + // now update offsets + adjust_linfo_offsets(purge_offset); + error = 0; +err: + pthread_mutex_unlock(&LOCK_index); + if(logs_to_purge_inited) + delete_dynamic(&logs_to_purge); + if(logs_to_keep_inited) + delete_dynamic(&logs_to_keep); + + return error; + +} + int MYSQL_LOG::find_next_log(LOG_INFO* linfo) { // mutex needed because we need to make sure the file pointer does not move @@ -285,6 +418,12 @@ int MYSQL_LOG::find_next_log(LOG_INFO* linfo) char* end ; pthread_mutex_lock(&LOCK_index); + if(linfo->fatal) + { + error = LOG_INFO_FATAL; + goto err; + } + if(my_fseek(index_file, linfo->index_file_offset, MY_SEEK_SET, MYF(MY_WME) ) == MY_FILEPOS_ERROR) { error = LOG_INFO_SEEK; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 6cb24de17c5..d366c3bd999 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -158,6 +158,8 @@ static bool opt_log,opt_update_log,opt_bin_log,opt_slow_log,opt_noacl, opt_disable_networking=0, opt_bootstrap=0,opt_skip_show_db=0, opt_ansi_mode=0,opt_myisam_log=0; bool opt_sql_bin_update = 0, opt_log_slave_updates = 0; +extern MASTER_INFO glob_mi; +extern int init_master_info(MASTER_INFO* mi); // if sql_bin_update is true, SQL_LOG_UPDATE and SQL_LOG_BIN are kept in sync, and are // treated as aliases for each other @@ -1618,6 +1620,8 @@ int main(int argc, char **argv) if(!opt_skip_slave_start && pthread_create(&hThread, &connection_attrib, handle_slave, 0)) sql_print_error("Warning: Can't create thread to handle slave"); + else if(opt_skip_slave_start) + init_master_info(&glob_mi); } else sql_print_error("Server id is not set, slave thread will not be started"); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b65c5d1978f..f86c0cf7461 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -94,6 +94,7 @@ THD::THD() options=thd_startup_options; update_lock_default= low_priority_updates ? TL_WRITE_LOW_PRIORITY : TL_WRITE; start_time=(time_t) 0; + current_linfo = 0; last_nx_table = last_nx_db = 0; inactive_timeout=net_wait_timeout; open_options=ha_open_options; diff --git a/sql/sql_class.h b/sql/sql_class.h index 4ba3ee87144..d1e83c4ed01 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -34,12 +34,20 @@ enum enum_log_type { LOG_CLOSED, LOG_NORMAL, LOG_NEW, LOG_BIN }; #define LOG_INFO_IO -2 #define LOG_INFO_INVALID -3 #define LOG_INFO_SEEK -4 +#define LOG_INFO_PURGE_NO_ROTATE -5 +#define LOG_INFO_MEM -6 +#define LOG_INFO_FATAL -7 +#define LOG_INFO_IN_USE -8 typedef struct st_log_info { char log_file_name[FN_REFLEN]; my_off_t index_file_offset; - my_off_t pos; + my_off_t pos; + bool fatal; // if the purge happens to give us a negative offset + pthread_mutex_t lock; + st_log_info():fatal(0) { pthread_mutex_init(&lock, NULL);} + ~st_log_info() { pthread_mutex_destroy(&lock);} } LOG_INFO; typedef struct st_master_info @@ -114,6 +122,7 @@ public: int generate_new_name(char *new_name,const char *old_name); void make_log_name(char* buf, const char* log_ident); bool is_active(const char* log_file_name); + int purge_logs(THD* thd, const char* to_log); void flush(void); void close(bool exiting = 0); // if we are exiting, we also want to close the // index file @@ -126,6 +135,9 @@ public: inline bool is_open() { return log_type != LOG_CLOSED; } char* get_index_fname() { return index_file_name;} char* get_log_fname() { return log_file_name; } + void lock_index() { pthread_mutex_lock(&LOCK_index);} + void unlock_index() { pthread_mutex_unlock(&LOCK_index);} + FILE* get_index_file() { return index_file;} }; /* character conversion tables */ @@ -295,6 +307,10 @@ public: bool volatile killed,bootstrap; bool system_thread,in_lock_tables,global_read_lock; bool query_error; + LOG_INFO* current_linfo; + // if we do a purge of binary logs, log index info of the threads + // that are currently reading it needs to be adjusted. To do that + // each thread that is using LOG_INFO needs to adjust the pointer to it THD(); ~THD(); bool store_globals(); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index aa1a326cad7..674da73ba5d 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -48,7 +48,7 @@ enum enum_sql_command { SQLCOM_BEGIN, SQLCOM_LOAD_MASTER_TABLE, SQLCOM_SHOW_CREATE, SQLCOM_SHOW_MASTER_STAT, SQLCOM_SHOW_SLAVE_STAT, SQLCOM_CHANGE_MASTER, SQLCOM_RENAME_TABLE, SQLCOM_BACKUP_TABLE, SQLCOM_RESTORE_TABLE, - SQLCOM_RESET + SQLCOM_RESET, SQLCOM_PURGE, SQLCOM_SHOW_BINLOGS }; enum lex_states { STATE_START, STATE_CHAR, STATE_IDENT, @@ -97,6 +97,7 @@ typedef struct st_lex { char *length,*dec,*change,*name; char *db,*db1,*table1,*db2,*table2; /* For outer join using .. */ char *backup_dir; /* For RESTORE/BACKUP */ + char* to_log; /* For PURGE MASTER LOGS TO */ String *wild; sql_exchange *exchange; ha_rows select_limit,offset_limit; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 359ed495a83..b22c876d4fe 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -927,6 +927,13 @@ mysql_execute_command(void) #endif break; } + case SQLCOM_PURGE: + { + if(check_access(thd, PROCESS_ACL, any_db)) + goto error; + res = purge_master_logs(thd, lex->to_log); + break; + } case SQLCOM_BACKUP_TABLE: { if (check_db_used(thd,tables) || @@ -1183,6 +1190,18 @@ mysql_execute_command(void) res= -1; break; } + case SQLCOM_SHOW_BINLOGS: +#ifdef DONT_ALLOW_SHOW_COMMANDS + send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ + DBUG_VOID_RETURN; +#else + { + if(check_access(thd, PROCESS_ACL, any_db)) + goto error; + res = show_binlogs(thd); + break; + } +#endif case SQLCOM_SHOW_CREATE: #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 53a5b00d084..5cd5d3a159e 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -93,6 +93,87 @@ static int send_file(THD *thd) DBUG_RETURN(error); } +void adjust_linfo_offsets(my_off_t purge_offset) +{ + THD *tmp; + + pthread_mutex_lock(&LOCK_thread_count); + I_List_iterator it(threads); + + while((tmp=it++)) + { + LOG_INFO* linfo; + if((linfo = tmp->current_linfo)) + { + pthread_mutex_lock(&linfo->lock); + // no big deal if we just started reading the log + // nothing to adjust + if(linfo->index_file_offset < purge_offset) + linfo->fatal = (linfo->index_file_offset != 0); + else + linfo->index_file_offset -= purge_offset; + pthread_mutex_unlock(&linfo->lock); + } + } + + pthread_mutex_unlock(&LOCK_thread_count); +} + +bool log_in_use(const char* log_name) +{ + int log_name_len = strlen(log_name) + 1; + THD *tmp; + bool result = 0; + + pthread_mutex_lock(&LOCK_thread_count); + I_List_iterator it(threads); + + while((tmp=it++)) + { + LOG_INFO* linfo; + if((linfo = tmp->current_linfo)) + { + pthread_mutex_lock(&linfo->lock); + result = !memcmp(log_name, linfo->log_file_name, log_name_len); + pthread_mutex_unlock(&linfo->lock); + if(result) break; + } + } + + pthread_mutex_unlock(&LOCK_thread_count); + return result; +} + +int purge_master_logs(THD* thd, const char* to_log) +{ + char search_file_name[FN_REFLEN]; + mysql_bin_log.make_log_name(search_file_name, to_log); + int res = mysql_bin_log.purge_logs(thd, search_file_name); + char* errmsg = 0; + switch(res) + { + case 0: break; + case LOG_INFO_EOF: errmsg = "Target log not found in binlog index"; break; + case LOG_INFO_IO: errmsg = "I/O error reading log index file"; break; + case LOG_INFO_INVALID: errmsg = "Server configuration does not permit \ +binlog purge"; break; + case LOG_INFO_SEEK: errmsg = "Failed on fseek()"; break; + case LOG_INFO_PURGE_NO_ROTATE: errmsg = "Cannot purge unrotatable log"; + break; + case LOG_INFO_MEM: errmsg = "Out of memory"; break; + case LOG_INFO_FATAL: errmsg = "Fatal error during purge"; break; + case LOG_INFO_IN_USE: errmsg = "A purgable log is in use, will not purge"; + break; + default: + errmsg = "Unknown error during purge"; break; + } + + if(errmsg) + send_error(&thd->net, 0, errmsg); + else + send_ok(&thd->net); +} + void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) { LOG_INFO linfo; @@ -117,6 +198,9 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) mysql_bin_log.make_log_name(search_file_name, log_ident); else search_file_name[0] = 0; + + linfo.index_file_offset = 0; + thd->current_linfo = &linfo; if(mysql_bin_log.find_first_log(&linfo, search_file_name)) { @@ -366,9 +450,20 @@ sweepstakes if you report the bug"; send_eof(&thd->net); thd->proc_info = "waiting to finalize termination"; + pthread_mutex_lock(&LOCK_thread_count); + thd->current_linfo = 0; + pthread_mutex_unlock(&LOCK_thread_count); DBUG_VOID_RETURN; err: thd->proc_info = "waiting to finalize termination"; + pthread_mutex_lock(&LOCK_thread_count); + // exclude iteration through thread list + // this is needed for purge_logs() - it will iterate through + // thread list and update thd->current_linfo->index_file_offset + // this mutex will make sure that it never tried to update our linfo + // after we return from this stack frame + thd->current_linfo = 0; + pthread_mutex_unlock(&LOCK_thread_count); if(log) (void) my_fclose(log, MYF(MY_WME)); send_error(&thd->net, 0, errmsg); @@ -594,7 +689,8 @@ int show_binlog_info(THD* thd) { LOG_INFO li; mysql_bin_log.get_current_log(&li); - net_store_data(packet, li.log_file_name); + int dir_len = dirname_length(li.log_file_name); + net_store_data(packet, li.log_file_name + dir_len); net_store_data(packet, (longlong)li.pos); net_store_data(packet, &binlog_do_db); net_store_data(packet, &binlog_ignore_db); @@ -613,3 +709,71 @@ int show_binlog_info(THD* thd) send_eof(&thd->net); DBUG_RETURN(0); } + +int show_binlogs(THD* thd) +{ + const char* errmsg = 0; + FILE* index_file; + char fname[FN_REFLEN]; + NET* net = &thd->net; + List field_list; + String* packet = &thd->packet; + + if(!mysql_bin_log.is_open()) + { + errmsg = "binlog is not open"; + goto err; + } + + field_list.push_back(new Item_empty_string("Log_name", 128)); + if(send_fields(thd, field_list, 1)) + { + sql_print_error("Failed in send_fields"); + return 1; + } + + mysql_bin_log.lock_index(); + index_file = mysql_bin_log.get_index_file(); + if(!index_file) + { + errmsg = "Uninitialized index file pointer"; + mysql_bin_log.unlock_index(); + goto err; + } + if(my_fseek(index_file, 0, MY_SEEK_SET, MYF(MY_WME))) + { + errmsg = "Failed on fseek()"; + mysql_bin_log.unlock_index(); + goto err; + } + + while(fgets(fname, sizeof(fname), index_file)) + { + char* fname_end; + *(fname_end = (strend(fname) - 1)) = 0; + int dir_len = dirname_length(fname); + packet->length(0); + net_store_data(packet, fname + dir_len, (fname_end - fname)-dir_len); + if(my_net_write(net, (char*) packet->ptr(), packet->length())) + { + sql_print_error("Failed in my_net_write"); + mysql_bin_log.unlock_index(); + return 1; + } + } + + mysql_bin_log.unlock_index(); + send_eof(net); + err: + if(errmsg) + { + send_error(net, 0, errmsg); + return 1; + } + + send_ok(net); + return 0; +} + + + diff --git a/sql/sql_repl.h b/sql/sql_repl.h index 9c8d43bda54..a0b60497773 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -14,7 +14,10 @@ int stop_slave(THD* thd = 0, bool net_report = 1); int change_master(THD* thd); void reset_slave(); void reset_master(); - +int purge_master_logs(THD* thd, const char* to_log); +bool log_in_use(const char* log_name); +void adjust_linfo_offsets(my_off_t purge_offset); +int show_binlogs(THD* thd); extern int init_master_info(MASTER_INFO* mi); void kill_zombie_dump_threads(uint32 slave_server_id); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4b404081676..c647ac8a192 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -107,6 +107,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token MASTER_SYM %token REPAIR %token RESET_SYM +%token PURGE %token SLAVE %token START_SYM %token STOP_SYM @@ -493,7 +494,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %type query verb_clause create change select drop insert replace insert2 insert_values update delete show describe load alter optimize flush - reset begin commit rollback slave master_def master_defs + reset purge begin commit rollback slave master_def master_defs repair restore backup analyze check rename field_list field_list_item field_spec kill select_item_list select_item values_list no_braces @@ -549,6 +550,7 @@ verb_clause: | lock | kill | optimize + | purge | rename | repair | replace @@ -2147,6 +2149,10 @@ show_param: if (!add_table_to_list($3,NULL)) YYABORT; } + | MASTER_SYM LOGS_SYM + { + Lex->sql_command = SQLCOM_SHOW_BINLOGS; + } | keys_or_index FROM table_ident opt_db { Lex->sql_command= SQLCOM_SHOW_KEYS; @@ -2246,6 +2252,13 @@ reset_option: SLAVE { Lex->type|= REFRESH_SLAVE; } | MASTER_SYM { Lex->type|= REFRESH_MASTER; } +purge: + PURGE { Lex->sql_command = SQLCOM_PURGE; Lex->type=0;} + MASTER_SYM LOGS_SYM TO_SYM TEXT_STRING + { + Lex->to_log = $6.str; + } + /* kill threads */ kill: