=== modified file 'client/client_priv.h'
--- client/client_priv.h	2012-08-09 15:22:00 +0000
+++ client/client_priv.h	2013-03-25 09:17:49 +0000
@@ -64,6 +64,8 @@
   OPT_MYSQLDUMP_SLAVE_APPLY,
   OPT_MYSQLDUMP_SLAVE_DATA,
   OPT_MYSQLDUMP_INCLUDE_MASTER_HOST_PORT,
+  OPT_REVIEW,
+  OPT_REVIEW_DBNAME, OPT_REVIEW_TABLENAME,
   OPT_SLAP_CSV, OPT_SLAP_CREATE_STRING,
   OPT_SLAP_AUTO_GENERATE_SQL_LOAD_TYPE, OPT_SLAP_AUTO_GENERATE_WRITE_NUM,
   OPT_SLAP_AUTO_GENERATE_ADD_AUTO,

=== modified file 'client/mysqlbinlog.cc'
--- client/mysqlbinlog.cc	2013-01-23 15:18:09 +0000
+++ client/mysqlbinlog.cc	2013-03-27 06:39:40 +0000
@@ -42,7 +42,6 @@
 #include <welcome_copyright_notice.h> // ORACLE_WELCOME_COPYRIGHT_NOTICE
 
 
-#include "sql_string.h"   // needed for Rpl_filter
 #include "sql_list.h"     // needed for Rpl_filter
 #include "rpl_filter.h"
 
@@ -59,6 +58,10 @@
 /* Needed for Rpl_filter */
 CHARSET_INFO* system_charset_info= &my_charset_utf8_general_ci;
 
+/* Needed for Flashback */
+DYNAMIC_ARRAY binlog_events; // Storing the events output string
+String stop_event_string; // Storing the STOP_EVENT output string 
+
 char server_version[SERVER_VERSION_LENGTH];
 ulong server_id = 0;
 
@@ -79,7 +82,7 @@
 static void error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
 static void warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
 
-static bool one_database=0, to_last_remote_log= 0, disable_log_bin= 0;
+static bool one_database=0, one_table=0, to_last_remote_log= 0, disable_log_bin= 0;
 static bool opt_hexdump= 0, opt_version= 0;
 const char *base64_output_mode_names[]=
 {"NEVER", "AUTO", "ALWAYS", "UNSPEC", "DECODE-ROWS", NullS};
@@ -89,6 +92,7 @@
 static enum_base64_output_mode opt_base64_output_mode= BASE64_OUTPUT_UNSPEC;
 static char *opt_base64_output_mode_str= NullS;
 static char* database= 0;
+static char* table= 0;
 static my_bool force_opt= 0, short_form= 0, remote_opt= 0;
 static my_bool debug_info_flag, debug_check_flag;
 static my_bool force_if_open_opt= 1;
@@ -121,6 +125,10 @@
 static const char* dirname_for_local_load= 0;
 static bool opt_skip_annotate_row_events= 0;
 
+static my_bool opt_flashback;
+static my_bool opt_review;
+static char *review_dbname, *review_tablename;
+
 /**
   Pointer to the Format_description_log_event of the currently active binlog.
 
@@ -780,6 +788,23 @@
 }
 
 /**
+  Indicates whether the given table should be filtered out,
+  according to the --table=X option.
+
+  @param log_tblname Name of table.
+
+  @return nonzero if the table with the given name should be
+  filtered out, 0 otherwise.
+*/
+static bool shall_skip_table(const char *log_tblname)
+{
+  return one_table &&
+         (log_tblname != NULL) &&
+         strcmp(log_tblname, table);
+}
+
+
+/**
   Prints the given event in base64 format.
 
   The header is printed to the head cache and the body is printed to
@@ -849,6 +874,10 @@
   Exit_status retval= OK_CONTINUE;
   IO_CACHE *const head= &print_event_info->head_cache;
 
+  /* Bypass flashback settings to event */
+  ev->is_flashback= opt_flashback;
+  ev->need_review= opt_review;
+
   /*
     Format events are not concerned by --offset and such, we always need to
     read them to be able to process the wanted events.
@@ -885,8 +914,6 @@
       retval= OK_STOP;
       goto end;
     }
-    if (!short_form)
-      fprintf(result_file, "# at %s\n",llstr(pos,ll_buff));
 
     if (!opt_hexdump)
       print_event_info->hexdump_from= 0; /* Disabled */
@@ -1110,12 +1137,128 @@
     case TABLE_MAP_EVENT:
     {
       Table_map_log_event *map= ((Table_map_log_event *)ev);
-      if (shall_skip_database(map->get_db_name()))
+      if (shall_skip_database(map->get_db_name()) ||
+          shall_skip_table(map->get_table_name()))
       {
         print_event_info->m_table_map_ignored.set_table(map->get_table_id(), map);
         destroy_evt= FALSE;
         goto end;
       }
+      /* Create review table for Flashback */
+      if (opt_review)
+      {
+        // Check table has created or not
+        Table_map_log_event *exist_table;
+        exist_table= print_event_info->m_table_map.get_table(map->get_table_id());
+
+        if (!exist_table)
+        {
+
+          MYSQL *conn;  
+          MYSQL_RES *res;  
+          MYSQL_ROW row;
+          char tmp_sql[8096];
+          int  tmp_sql_offset;
+
+          conn = mysql_init(NULL);
+          if (!mysql_real_connect(conn, host, user, pass, 
+                map->get_db_name(), port, sock, 0)) 
+          {
+            fprintf(stderr, "%s\n", mysql_error(conn));  
+            exit(1);
+          }
+
+          if (mysql_query(conn, "SET group_concat_max_len=10000;")) 
+          {  
+            fprintf(stderr, "%s\n", mysql_error(conn));  
+            exit(1);  
+          }  
+
+          memset(tmp_sql, 0, sizeof(tmp_sql));
+          sprintf(tmp_sql, " "
+                  "SELECT Group_concat(cols) "
+                  "FROM   (SELECT 'op_type char(1)' cols "
+                  "  UNION ALL "
+                  "  SELECT Concat(column_name, '_old ', column_type, ' ', "
+                  "    IF(character_set_name IS NOT NULL, "
+                  "      Concat('character set ', character_set_name, ' '), ' '), "
+                  "    IF(collation_name IS NOT NULL, "
+                  "      Concat('collate ', collation_name, ' '), ' ')) cols "
+                  "  FROM   information_schema.columns "
+                  "  WHERE  table_schema = '%s' "
+                  "  AND table_name = '%s' "
+                  "  UNION ALL "
+                  "  SELECT Concat(column_name, '_new ', column_type, ' ', "
+                  "    IF(character_set_name IS NOT NULL, "
+                  "      Concat('character set ', character_set_name, ' '), ' '), "
+                  "    IF(collation_name IS NOT NULL, "
+                  "      Concat('collate ', collation_name, ' '), ' ')) cols "
+                  "  FROM   information_schema.columns "
+                  "  WHERE  table_schema = '%s' "
+                  "  AND table_name = '%s') tmp;", 
+            map->get_db_name(), map->get_table_name(),
+            map->get_db_name(), map->get_table_name());
+
+          if (mysql_query(conn, tmp_sql)) 
+          {  
+            fprintf(stderr, "%s\n", mysql_error(conn));  
+            exit(1);  
+          }  
+          res = mysql_use_result(conn);
+          if ((row = mysql_fetch_row(res)) != NULL)  // only one row
+          {
+            if (review_dbname)       
+            {
+              ev->set_review_dbname(review_dbname);
+            }
+            else
+            {
+              ev->set_review_dbname(map->get_db_name());
+            }
+            if (review_tablename) 
+            {
+              ev->set_review_tablename(review_tablename);
+            }
+            else
+            {
+              memset(tmp_sql, 0, sizeof(tmp_sql));
+              sprintf(tmp_sql, "__%s", map->get_table_name()); 
+              ev->set_review_tablename(tmp_sql);
+            }
+            memset(tmp_sql, 0, sizeof(tmp_sql));
+            tmp_sql_offset= sprintf(tmp_sql, "CREATE TABLE IF NOT EXISTS");
+            tmp_sql_offset+= sprintf(tmp_sql + tmp_sql_offset, " `%s`.`%s` (%s) Engine = InnoDB%s", 
+                ev->get_review_dbname(), ev->get_review_tablename(), row[0], print_event_info->delimiter);
+          }
+          fprintf(result_file, "%s\n", tmp_sql);
+          mysql_free_result(res);  
+          mysql_close(conn);
+        }
+        else
+        {
+          char tmp_str[128];
+
+          if (review_dbname) 
+          {                      
+            ev->set_review_dbname(review_dbname);
+          }
+          else
+          {
+            ev->set_review_dbname(map->get_db_name());
+          }
+          if (review_tablename) 
+          {
+            ev->set_review_tablename(review_tablename);
+          }
+          else
+          {
+            memset(tmp_str, 0, sizeof(tmp_str));
+            sprintf(tmp_str, "__%s", map->get_table_name()); 
+            ev->set_review_tablename(tmp_str);
+          }
+        }
+      }
+
       /*
         The Table map is to be printed, so it's just the time when we may
         print the kept Annotate event (if there is any).
@@ -1235,6 +1378,41 @@
   */
   if (ev)
   {
+    /* Holding event output if needed */
+    if (!ev->output_buf.is_empty())
+    {
+      String *tmp_str = new String[1];
+      if (!short_form)
+      {
+        tmp_str->append("# at ");
+        tmp_str->append(llstr(pos,ll_buff));
+        tmp_str->append("\n");
+      }
+      tmp_str->append(ev->output_buf);
+      if (opt_flashback)
+      {
+        if (ev_type == STOP_EVENT)
+        {
+          stop_event_string.copy(*tmp_str);
+        }
+        else
+        {
+          (void) push_dynamic(&binlog_events, (uchar *) tmp_str);
+        }
+      }
+      else
+      {
+        fprintf(result_file, "%s", tmp_str->ptr());
+        delete tmp_str;
+      }
+      ev->free_output_buffer();
+    }
+    else
+    {
+      if (!short_form)
+        fprintf(result_file, "# at %s\n",llstr(pos,ll_buff));
+    }
+
     if (destroy_evt) /* destroy it later if not set (ignored table map) */
       delete ev;
   }
@@ -1293,6 +1471,11 @@
     "already have. NOTE: you will need a SUPER privilege to use this option.",
    &disable_log_bin, &disable_log_bin, 0, GET_BOOL,
    NO_ARG, 0, 0, 0, 0, 0, 0},
+  {"flashback", 'B', "Flashback feature can rollback you committed data to a special time point,"
+   "before Flashback feature writing a row, original row can insert to review-dbname.review-tablename,"
+   "and mysqlbinlog will login mysql by user(-u) and password(-p) and host(-h).",
+   &opt_flashback, &opt_flashback, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
+   0, 0},
   {"force-if-open", 'F', "Force if binlog was not closed properly.",
    &force_if_open_opt, &force_if_open_opt, 0, GET_BOOL, NO_ARG,
    1, 0, 0, 0, 0, 0},
@@ -1330,6 +1513,17 @@
    0, 0},
   {"result-file", 'r', "Direct output to a given file.", 0, 0, 0, GET_STR,
    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"review", OPT_REVIEW, "Print review sql in output file.",
+    &opt_review, &opt_review, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
+    0, 0},
+  {"review-dbname", OPT_REVIEW_DBNAME,
+    "Writing flashback original row data into this db",
+    &review_dbname, &review_dbname,
+    0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"review-tablename", OPT_REVIEW_TABLENAME,
+    "Writing flashback original row data into this table",
+    &review_tablename, &review_tablename,
+    0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
   {"server-id", 0,
    "Extract only binlog entries created by the server having the given id.",
    &server_id, &server_id, 0, GET_ULONG,
@@ -1381,6 +1575,9 @@
    &stop_position, &stop_position, 0, GET_ULL,
    REQUIRED_ARG, (longlong)(~(my_off_t)0), BIN_LOG_HEADER_SIZE,
    (ulonglong)(~(my_off_t)0), 0, 0, 0},
+  {"table", 'T', "List entries for just this table (local log only).",
+   &table, &table, 0, GET_STR_ALLOC, REQUIRED_ARG,
+   0, 0, 0, 0, 0, 0},
   {"to-last-log", 't', "Requires -R. Will not stop at the end of the \
 requested binlog but rather continue printing until the end of the last \
 binlog of the MySQL server. If you send the output to the same MySQL server, \
@@ -1484,6 +1681,7 @@
 {
   my_free(pass);
   my_free(database);
+  my_free(table);
   my_free(host);
   my_free(user);
   my_free(const_cast<char*>(dirname_for_local_load));
@@ -1548,6 +1746,9 @@
     DBUG_PUSH(argument ? argument : default_dbug_option);
     break;
 #endif
+  case 'B':
+    opt_flashback= 1;
+    break;
   case 'd':
     one_database = 1;
     break;
@@ -1573,10 +1774,16 @@
   case 'R':
     remote_opt= 1;
     break;
+  case 'T':
+    one_table= 1;
+    break;
   case OPT_MYSQL_PROTOCOL:
     opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib,
                                     opt->name);
     break;
+  case OPT_REVIEW:
+    opt_review= 1;
+    break;
   case OPT_START_DATETIME:
     start_datetime= convert_str_to_timestamp(start_datetime_str);
     break;
@@ -1753,7 +1960,6 @@
        dump_local_log_entries(&print_event_info, logname));
 
   /* Set delimiter back to semicolon */
-  fprintf(result_file, "DELIMITER ;\n");
   strmov(print_event_info.delimiter, ";");
   return rc;
 }
@@ -2391,6 +2597,8 @@
   DBUG_ENTER("main");
   DBUG_PROCESS(argv[0]);
 
+  (void) my_init_dynamic_array(&binlog_events, sizeof(String), 1024, 1024, MYF(0)); 
+
   my_init_time(); // for time functions
 
   init_alloc_root(&s_mem_root, 16384, 0, MYF(0));
@@ -2478,6 +2686,35 @@
     start_position= BIN_LOG_HEADER_SIZE;
   }
 
+  /* If enable flashback, need to print the events from the end to the beginning */
+  if(opt_flashback)
+  {
+    if (binlog_events.elements > 0)
+    {
+      String *event_str= dynamic_element(&binlog_events, 0, String*); 
+      fprintf(result_file, "%s", event_str->ptr());
+      delete event_str;
+      delete_dynamic_element(&binlog_events, 0);
+    }
+    uint i= 0;
+    for (i=  binlog_events.elements; i > 0; --i)
+    {
+      String *event_str= dynamic_element(&binlog_events, i - 1, String*); 
+      fprintf(result_file, "%s", event_str->ptr());
+      delete event_str;
+      delete_dynamic_element(&binlog_events, i - 1);
+    }
+    fprintf(result_file, "COMMIT\n/*!*/;\n");
+  }
+  /* Set delimiter back to semicolon */
+  if (!stop_event_string.is_empty())
+  {
+    fprintf(result_file, "%s", stop_event_string.ptr());
+  }
+  fprintf(result_file, "DELIMITER ;\n");
+  delete_dynamic(&binlog_events);
+  /* End */
+
   /*
     Issue a ROLLBACK in case the last printed binlog was crashed and had half
     of transaction.

=== modified file 'include/my_sys.h'
--- include/my_sys.h	2013-02-07 22:21:23 +0000
+++ include/my_sys.h	2013-03-27 06:49:04 +0000
@@ -551,6 +551,7 @@
 
 /* tell write offset in the SEQ_APPEND cache */
 int      my_b_copy_to_file(IO_CACHE *cache, FILE *file);
+char*    my_b_copy_to_string(IO_CACHE *cache, size_t *bytes_in_cache); // Used for Flashback
 my_off_t my_b_append_tell(IO_CACHE* info);
 my_off_t my_b_safe_tell(IO_CACHE* info); /* picks the correct tell() */
 

=== modified file 'sql/log_event.cc'
--- sql/log_event.cc	2013-01-29 14:10:47 +0000
+++ sql/log_event.cc	2013-03-27 06:48:32 +0000
@@ -280,17 +280,26 @@
       constructor, but it would be possible to create a subclass
       holding the IO_CACHE itself.
    */
-  Write_on_release_cache(IO_CACHE *cache, FILE *file, flag_set flags = 0)
-    : m_cache(cache), m_file(file), m_flags(flags)
+  Write_on_release_cache(IO_CACHE *cache, FILE *file, flag_set flags = 0, Log_event *ev = NULL)
+    : m_cache(cache), m_file(file), m_flags(flags), m_ev(ev) 
   {
     reinit_io_cache(m_cache, WRITE_CACHE, 0L, FALSE, TRUE);
   }
 
   ~Write_on_release_cache()
   {
-    copy_event_cache_to_file_and_reinit(m_cache, m_file);
-    if (m_flags | FLUSH_F)
-      fflush(m_file);
+    if(m_ev == NULL)
+    {
+      copy_event_cache_to_file_and_reinit(m_cache, m_file);
+      if (m_flags | FLUSH_F)
+        fflush(m_file);
+    }
+    else // if m_ev<>NULL, then storing the output in output_buf
+    {
+      reinit_io_cache(m_cache, READ_CACHE, 0L, FALSE, FALSE);
+      m_ev->output_buf.append(m_cache, m_cache->end_of_file);
+      reinit_io_cache(m_cache, WRITE_CACHE, 0, FALSE, TRUE);
+    }
   }
 
   /*
@@ -320,6 +329,7 @@
   IO_CACHE *m_cache;
   FILE *m_file;
   flag_set m_flags;
+  Log_event *m_ev; // Used for Flashback
 };
 
 #ifndef DBUG_OFF
@@ -2061,7 +2071,7 @@
       d= (ulong) (i64 / 1000000);
       t= (ulong) (i64 % 1000000);
 
-      my_b_printf(file, "%04d-%02d-%02d %02d:%02d:%02d",
+      my_b_printf(file, "'%04d-%02d-%02d %02d:%02d:%02d'",
                   (int) (d / 10000), (int) (d % 10000) / 100, (int) (d % 100),
                   (int) (t / 10000), (int) (t % 10000) / 100, (int) t % 100);
       my_snprintf(typestr, typestr_length, "DATETIME");
@@ -2215,16 +2225,22 @@
 Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
                                       PRINT_EVENT_INFO *print_event_info,
                                       MY_BITMAP *cols_bitmap,
-                                      const uchar *value, const uchar *prefix)
+                                      const uchar *value, const uchar *prefix,
+                                      const my_bool only_parse)
 {
   const uchar *value0= value;
   const uchar *null_bits= value;
   uint null_bit_index= 0;
   char typestr[64]= "";
+  /* Storing the review SQL */
+  IO_CACHE *review_sql= &print_event_info->review_sql_cache;
+  uchar *begin_pos;
+  size_t bytes_in_cache;
   
   value+= (m_width + 7) / 8;
   
-  my_b_printf(file, "%s", prefix);
+  if (!only_parse)
+    my_b_printf(file, "%s", prefix);
   
   for (size_t i= 0; i < td->size(); i ++)
   {
@@ -2236,14 +2252,76 @@
     
     if (is_null)
     {
-      my_b_printf(file, "###   @%d=NULL", i + 1);
+      if (!only_parse)
+      {
+        my_b_printf(file, "###   @%d=NULL", i + 1);
+        if (need_review)
+        {
+          my_b_printf(review_sql, ", NULL");
+        }
+      }
     }
     else
     {
-      my_b_printf(file, "###   @%d=", i + 1);
-      size_t size= log_event_print_value(file, value,
-                                         td->type(i), td->field_metadata(i),
-                                         typestr, sizeof(typestr));
+      size_t size= 0;
+      if (!only_parse) 
+      {
+        my_b_printf(file, "###   @%d=", i + 1);
+        begin_pos= file->write_pos;
+        size= log_event_print_value(file, value,
+                                    td->type(i), td->field_metadata(i),
+                                    typestr, sizeof(typestr));
+        if (need_review)
+        {
+          String tmp_str, hex_str;
+          IO_CACHE tmp_cache;
+          char *buff;
+
+          // Using a tmp IO_CACHE to get the value output
+          open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP));
+          size= log_event_print_value(&tmp_cache, value,
+                                      td->type(i), td->field_metadata(i),
+                                      typestr, sizeof(typestr));
+          buff= copy_event_cache_to_string_and_reinit(&tmp_cache, &bytes_in_cache);
+          close_cached_file(&tmp_cache);
+
+          switch (td->type(i)) // Covert string to Hex
+          {
+            case MYSQL_TYPE_VARCHAR:
+            case MYSQL_TYPE_VAR_STRING:
+            case MYSQL_TYPE_STRING:
+            case MYSQL_TYPE_BLOB:
+              // Avoid write_pos changed to a new area
+              tmp_str.free();
+              tmp_str.append(buff + 1, bytes_in_cache - 2); // Removing quotation marks
+              if (hex_str.alloc(tmp_str.length()*2+1)) // if no memory
+              {
+                tmp_str.free();
+                tmp_str.append(buff, bytes_in_cache);
+                my_b_printf(review_sql, ", %s", tmp_str.ptr());
+                break;
+              }
+              octet2hex((char*) hex_str.ptr(), tmp_str.ptr(), tmp_str.length());
+              my_b_printf(review_sql, ", UNHEX('%s')", hex_str.ptr());
+              break;
+            default:
+              tmp_str.free();
+              tmp_str.append(buff, bytes_in_cache);
+              my_b_printf(review_sql, ", %s", tmp_str.ptr());
+              break;
+          }
+          my_free(buff);
+        }
+      }
+      else
+      {
+        IO_CACHE tmp_cache;
+        open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP));
+        size= log_event_print_value(&tmp_cache, value,
+                                    td->type(i), td->field_metadata(i),
+                                    typestr, sizeof(typestr));
+        close_cached_file(&tmp_cache);
+      }
       if (!size)
         return 0;
 
@@ -2252,26 +2330,79 @@
 
     if (print_event_info->verbose > 1)
     {
-      my_b_printf(file, " /* ");
-
-      if (typestr[0])
-        my_b_printf(file, "%s ", typestr);
-      else
-        my_b_printf(file, "type=%d ", td->type(i));
-      
-      my_b_printf(file, "meta=%d nullable=%d is_null=%d ",
-                  td->field_metadata(i),
-                  td->maybe_null(i), is_null);
-      my_b_printf(file, "*/");
+      if (!only_parse) 
+        my_b_printf(file, " /* ");
+
+      if (!only_parse) 
+      {
+        if (typestr[0])
+          my_b_printf(file, "%s ", typestr);
+        else
+          my_b_printf(file, "type=%d ", td->type(i));
+
+        my_b_printf(file, "meta=%d nullable=%d is_null=%d ",
+            td->field_metadata(i),
+            td->maybe_null(i), is_null);
+        my_b_printf(file, "*/");
+      }
     }
     
-    my_b_printf(file, "\n");
+    if (!only_parse)
+      my_b_printf(file, "\n");
     
     null_bit_index++;
   }
   return value - value0;
 }
 
+void Rows_log_event::exchange_update_rows(PRINT_EVENT_INFO *print_event_info,
+                                          uchar *rows_buff)
+{
+  Table_map_log_event *map;
+  table_def *td;
+  uchar *data_buff= rows_buff + m_rows_before_size;
+
+  if (!(map= print_event_info->m_table_map.get_table(m_table_id)) ||
+      !(td= map->create_table_def()))
+  {
+    return;
+  }
+
+  for (uchar *value= m_rows_buf; value < m_rows_end; )
+  {
+    uchar *start_pos= value;
+    size_t length1;
+    if (!(length1= print_verbose_one_row(NULL, td, print_event_info,
+                                         &m_cols, value,
+                                         (const uchar*) "", TRUE)))
+      return;
+    value+= length1;
+
+    size_t length2;
+    if (!(length2= print_verbose_one_row(NULL, td, print_event_info,
+                                        &m_cols, value,
+                                        (const uchar*) "", TRUE)))
+      return;
+    value+= length2;
+
+    /* Swap SET and WHERE part */
+    uchar *swap_buff1= (uchar *) my_malloc(length1, MYF(0));
+    uchar *swap_buff2= (uchar *) my_malloc(length2, MYF(0));
+
+    memcpy(swap_buff1, start_pos, length1); // SET part
+    memcpy(swap_buff2, start_pos + length1, length2); // WHERE part
+
+    memcpy(start_pos, swap_buff2, length2);
+    memcpy(start_pos + length2, swap_buff1, length1);
+
+    /* Free Swap Buffer */
+    my_free(swap_buff1);
+    my_free(swap_buff2);
+  }
+
+  /* Move to rows_buff */
+  memcpy(data_buff, m_rows_buf, m_rows_end - m_rows_buf);
+}
 
 /**
   Print a row event into IO cache in human readable form (in SQL format)
@@ -2285,26 +2416,32 @@
   Table_map_log_event *map;
   table_def *td;
   const char *sql_command, *sql_clause1, *sql_clause2;
+  const char *sql_command_short;
   Log_event_type type_code= get_type_code();
+  IO_CACHE *review_sql= &print_event_info->review_sql_cache;
   
   switch (type_code) {
   case WRITE_ROWS_EVENT:
     sql_command= "INSERT INTO";
     sql_clause1= "### SET\n";
     sql_clause2= NULL;
+    sql_command_short= "I";
     break;
   case DELETE_ROWS_EVENT:
     sql_command= "DELETE FROM";
     sql_clause1= "### WHERE\n";
     sql_clause2= NULL;
+    sql_command_short= "D";
     break;
   case UPDATE_ROWS_EVENT:
     sql_command= "UPDATE";
     sql_clause1= "### WHERE\n";
     sql_clause2= "### SET\n";
+    sql_command_short= "U";
     break;
   default:
     sql_command= sql_clause1= sql_clause2= NULL;
+    sql_command_short= "";
     DBUG_ASSERT(0); /* Not possible */
   }
   
@@ -2321,6 +2458,11 @@
     my_b_printf(file, "### %s %`s.%`s\n",
                       sql_command,
                       map->get_db_name(), map->get_table_name());
+    if (need_review) 
+    {
+      my_b_printf(review_sql, "\nINSERT INTO `%s`.`%s` VALUES ('%s'",
+          map->get_review_dbname(), map->get_review_tablename(), sql_command_short);
+    }
     /* Print the first image */
     if (!(length= print_verbose_one_row(file, td, print_event_info,
                                   &m_cols, value,
@@ -2337,6 +2479,20 @@
         goto end;
       value+= length;
     }
+    else
+    {
+      if (need_review)
+      {
+        for (size_t i= 0; i < td->size(); i ++)
+        {
+          my_b_printf(review_sql, ", NULL");
+        }
+      }
+    }
+    if (need_review)
+    {
+      my_b_printf(review_sql, ")%s\n", print_event_info->delimiter);
+    }
   }
 
 end:
@@ -2352,7 +2508,7 @@
                              PRINT_EVENT_INFO* print_event_info,
                              bool more)
 {
-  const uchar *ptr= (const uchar *)temp_buf;
+  uchar *ptr= (uchar *)temp_buf;
   uint32 size= uint4korr(ptr + EVENT_LEN_OFFSET);
   DBUG_ENTER("Log_event::print_base64");
 
@@ -2364,6 +2520,25 @@
     DBUG_VOID_RETURN;
   }
 
+  if(is_flashback)
+  {
+    switch (ptr[4]) {
+      case WRITE_ROWS_EVENT:
+        ptr[4]= DELETE_ROWS_EVENT;
+        break;
+      case DELETE_ROWS_EVENT:
+        ptr[4]= WRITE_ROWS_EVENT;
+        break;
+      case UPDATE_ROWS_EVENT:
+        Rows_log_event *ev= NULL;
+        ev= new Update_rows_log_event((const char*) ptr, size,
+                                       glob_description_event);
+        ev->exchange_update_rows(print_event_info, ptr);
+        delete ev;
+        break;
+    }
+  }
+
   if (base64_encode(ptr, (size_t) size, tmp_str))
   {
     DBUG_ASSERT(0);
@@ -2380,7 +2555,7 @@
       my_b_printf(file, "'%s\n", print_event_info->delimiter);
   }
   
-  if (print_event_info->verbose)
+  if (print_event_info->verbose || need_review)
   {
     Rows_log_event *ev= NULL;
     if (checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF &&
@@ -2392,6 +2567,11 @@
       Table_map_log_event *map; 
       map= new Table_map_log_event((const char*) ptr, size, 
                                    glob_description_event);
+      if (need_review) 
+      {
+        map->set_review_dbname(m_review_dbname.ptr());
+        map->set_review_tablename(m_review_tablename.ptr());
+      }
       print_event_info->m_table_map.set_table(map->get_table_id(), map);
     }
     else if (ptr[4] == WRITE_ROWS_EVENT)
@@ -2412,7 +2592,18 @@
     
     if (ev)
     {
-      ev->print_verbose(file, print_event_info);
+      ev->need_review= need_review;
+      if (print_event_info->verbose)
+      {
+        ev->print_verbose(file, print_event_info);
+      }
+      else
+      {
+        IO_CACHE tmp_cache;                                                                                                                                                                                                                 
+        open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP));
+        ev->print_verbose(&tmp_cache, print_event_info);
+        close_cached_file(&tmp_cache);
+      }
       delete ev;
     }
   }
@@ -3613,7 +3804,7 @@
 
 void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
 {
-  Write_on_release_cache cache(&print_event_info->head_cache, file);
+  Write_on_release_cache cache(&print_event_info->head_cache, file, 0, this);
 
   /**
     reduce the size of io cache so that the write function is called
@@ -3622,8 +3813,24 @@
   DBUG_EXECUTE_IF ("simulate_file_write_error",
                    {(&cache)->write_pos= (&cache)->write_end- 500;});
   print_query_header(&cache, print_event_info);
-  my_b_write(&cache, (uchar*) query, q_len);
-  my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
+  if (!is_flashback)
+  {
+    my_b_write(&cache, (uchar*) query, q_len);
+    my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
+  }
+  else // is_flashback == 1
+  {
+    if (strcmp("BEGIN", query) == 0)
+    {
+      my_b_write(&cache, (uchar*) "COMMIT", 6);
+      my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
+    }
+    else if (strcmp("COMMIT", query) == 0)
+    {
+      my_b_write(&cache, (uchar*) "BEGIN", 5);
+      my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
+    }
+  }
 }
 #endif /* MYSQL_CLIENT */
 
@@ -6326,7 +6533,7 @@
 void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
 {
   Write_on_release_cache cache(&print_event_info->head_cache, file,
-                               Write_on_release_cache::FLUSH_F);
+                               Write_on_release_cache::FLUSH_F, this);
 
   if (!print_event_info->short_form)
   {
@@ -6336,7 +6543,14 @@
     print_header(&cache, print_event_info, FALSE);
     my_b_printf(&cache, "\tXid = %s\n", buf);
   }
-  my_b_printf(&cache, "COMMIT%s\n", print_event_info->delimiter);
+  if (!is_flashback)
+  {
+    my_b_printf(&cache, "COMMIT%s\n", print_event_info->delimiter);
+  }
+  else
+  {
+     my_b_printf(&cache, "BEGIN%s\n", print_event_info->delimiter);
+  }
 }
 #endif /* MYSQL_CLIENT */
 
@@ -7060,8 +7274,8 @@
 #ifdef MYSQL_CLIENT
 void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
 {
-  Write_on_release_cache cache(&print_event_info->head_cache, file,
-                               Write_on_release_cache::FLUSH_F);
+  Write_on_release_cache cache(&print_event_info->head_cache, file, 
+                               Write_on_release_cache::FLUSH_F, this);
 
   if (print_event_info->short_form)
     return;
@@ -8321,6 +8535,7 @@
     m_rows_end= m_rows_buf + data_size;
     m_rows_cur= m_rows_end;
     memcpy(m_rows_buf, ptr_rows_data, data_size);
+    m_rows_before_size= ptr_rows_data - (const uchar *) buf; // Get the size that before SET part
   }
   else
     m_cols.bitmap= 0; // to not free it
@@ -8988,6 +9203,7 @@
 {
   IO_CACHE *const head= &print_event_info->head_cache;
   IO_CACHE *const body= &print_event_info->body_cache;
+  IO_CACHE *const sql= &print_event_info->review_sql_cache;
   if (!print_event_info->short_form)
   {
     bool const last_stmt_event= get_flags(STMT_END_F);
@@ -9000,8 +9216,17 @@
 
   if (get_flags(STMT_END_F))
   {
-    copy_event_cache_to_file_and_reinit(head, file);
-    copy_event_cache_to_file_and_reinit(body, file);
+    reinit_io_cache(head, READ_CACHE, 0L, FALSE, FALSE);
+    output_buf.append(head, head->end_of_file);
+    reinit_io_cache(head, WRITE_CACHE, 0, FALSE, TRUE);
+
+    reinit_io_cache(body, READ_CACHE, 0L, FALSE, FALSE);
+    output_buf.append(body, body->end_of_file);
+    reinit_io_cache(body, WRITE_CACHE, 0, FALSE, TRUE);
+
+    reinit_io_cache(sql, READ_CACHE, 0L, FALSE, FALSE);
+    output_buf.append(sql, sql->end_of_file);
+    reinit_io_cache(sql, WRITE_CACHE, 0, FALSE, TRUE);
   }
 }
 #endif
@@ -11252,6 +11477,7 @@
   myf const flags = MYF(MY_WME | MY_NABP);
   open_cached_file(&head_cache, NULL, NULL, 0, flags);
   open_cached_file(&body_cache, NULL, NULL, 0, flags);
+  open_cached_file(&review_sql_cache, NULL, NULL, 0, flags);
 }
 #endif
 

=== modified file 'sql/log_event.h'
--- sql/log_event.h	2013-01-29 14:10:47 +0000
+++ sql/log_event.h	2013-03-27 06:43:09 +0000
@@ -41,6 +41,7 @@
 #include "hash.h"
 #include "rpl_tblmap.h"
 #include "rpl_tblmap.cc"
+#include "sql_string.h"
 #endif
 
 #ifdef MYSQL_SERVER
@@ -50,7 +51,9 @@
 #endif
 
 /* Forward declarations */
+#ifndef MYSQL_CLIENT
 class String;
+#endif
 
 #define PREFIX_SQL_LOAD "SQL_LOAD-"
 #define LONG_FIND_ROW_THRESHOLD 60 /* seconds */
@@ -777,9 +780,10 @@
   ~st_print_event_info() {
     close_cached_file(&head_cache);
     close_cached_file(&body_cache);
+    close_cached_file(&review_sql_cache);
   }
   bool init_ok() /* tells if construction was successful */
-    { return my_b_inited(&head_cache) && my_b_inited(&body_cache); }
+    { return my_b_inited(&head_cache) && my_b_inited(&body_cache) && my_b_inited(&review_sql_cache); }
 
 
   /* Settings on how to print the events */
@@ -807,6 +811,9 @@
    */
   IO_CACHE head_cache;
   IO_CACHE body_cache;
+
+  /* Storing the SQL for reviewing */
+  IO_CACHE review_sql_cache;
 } PRINT_EVENT_INFO;
 #endif
 
@@ -1140,6 +1147,41 @@
   void print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info,
                     bool is_more);
 #endif
+  /* The following code used for Flashback */
+  my_bool is_flashback;
+  my_bool need_review;
+  String output_buf; // Storing the event output
+  String      m_review_dbname;
+  String      m_review_tablename;
+
+  void set_review_dbname(const char *name) 
+  {
+    if (name) 
+    {
+      m_review_dbname.free();
+      m_review_dbname.append(name);
+    }
+  }
+  void set_review_tablename(const char *name) 
+  {
+    if (name)
+    {
+      m_review_tablename.free();
+      m_review_tablename.append(name);
+    }
+  }
+
+  const char *get_review_dbname() const { return m_review_dbname.ptr(); }
+  const char *get_review_tablename() const { return m_review_tablename.ptr(); }
+  void free_output_buffer()
+  {
+    if (!output_buf.is_empty())
+    {
+      output_buf.free(); 
+    }
+  }
+  /* End */
+
   /* 
      The value is set by caller of FD constructor and
      Log_event::write_header() for the rest.
@@ -1225,7 +1267,13 @@
   }
   Log_event(const char* buf, const Format_description_log_event
             *description_event);
-  virtual ~Log_event() { free_temp_buf();}
+  virtual ~Log_event() 
+  { 
+    free_temp_buf(); 
+#ifdef MYSQL_CLIENT 
+    free_output_buffer(); 
+#endif
+  } 
   void register_temp_buf(char* buf, bool must_free) 
   { 
     temp_buf= buf; 
@@ -3828,12 +3876,14 @@
 #ifdef MYSQL_CLIENT
   /* not for direct call, each derived has its own ::print() */
   virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info)= 0;
+  void exchange_update_rows(PRINT_EVENT_INFO *print_event_info, uchar *rows_buff); // Exchanging the SET/WHERE part in UPDATE event
   void print_verbose(IO_CACHE *file,
                      PRINT_EVENT_INFO *print_event_info);
   size_t print_verbose_one_row(IO_CACHE *file, table_def *td,
                                PRINT_EVENT_INFO *print_event_info,
                                MY_BITMAP *cols_bitmap,
-                               const uchar *ptr, const uchar *prefix);
+                               const uchar *ptr, const uchar *prefix,
+                               const my_bool only_parse= 0); // if only_parse=1, then print result is unnecessary
 #endif
 
 #ifdef MYSQL_SERVER
@@ -3914,6 +3964,8 @@
   uchar    *m_rows_cur;		/* One-after the end of the data */
   uchar    *m_rows_end;		/* One-after the end of the allocated space */
 
+  size_t   m_rows_before_size;  /* The length before m_rows_buf BY P.Linux */
+
   flag_set m_flags;		/* Flags for row-level events */
 
   /* helper functions */
@@ -4305,6 +4357,23 @@
   LEX_STRING m_message;
 };
 
+static inline char *copy_event_cache_to_string_and_reinit(IO_CACHE *cache, size_t *bytes_in_cache)
+{
+  char *buff;
+  String tmp;
+
+  tmp.length(0);
+  reinit_io_cache(cache, READ_CACHE, 0L, FALSE, FALSE);
+  tmp.append(cache, cache->end_of_file);
+  reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE);
+
+  buff= (char *) my_malloc(tmp.length() + 1, MYF(0));
+  memcpy(buff, tmp.ptr(), tmp.length());
+  *bytes_in_cache= tmp.length();
+
+  return buff;
+}
+
 static inline bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache,
                                                        FILE *file)
 {

