From 75cd25a5794f43ae75cde851fb01ee4c3644c195 Mon Sep 17 00:00:00 2001
From: "sasha@mysql.sashanet.com" <>
Date: Thu, 26 Oct 2000 22:11:55 -0600
Subject: [PATCH] fixed --skip-slave-thread bug added PURGE MASTER LOGS TO and
 SHOW MASTER LOGS fixed the output of SHOW MASTER STATUS updated docs

---
 .bzrignore            |   4 +
 Docs/manual.texi      |  60 +++++++++++++++
 include/.my_sys.h.swp | Bin 0 -> 16384 bytes
 sql/lex.h             |   1 +
 sql/log.cc            | 141 ++++++++++++++++++++++++++++++++++-
 sql/mysqld.cc         |   4 +
 sql/sql_class.cc      |   1 +
 sql/sql_class.h       |  18 ++++-
 sql/sql_lex.h         |   3 +-
 sql/sql_parse.cc      |  19 +++++
 sql/sql_repl.cc       | 166 +++++++++++++++++++++++++++++++++++++++++-
 sql/sql_repl.h        |   5 +-
 sql/sql_yacc.yy       |  15 +++-
 13 files changed, 431 insertions(+), 6 deletions(-)
 create mode 100644 include/.my_sys.h.swp

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 0000000000000000000000000000000000000000..e9d01f0e65d1d4e4ce698ca12b4c8342d2ad1697
GIT binary patch
literal 16384
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

literal 0
HcmV?d00001

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 <my_dir.h>
 #include <stdarg.h>
@@ -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<THD> 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<THD> 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<Item> 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 <NONE>
 	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: