List:Commits« Previous MessageNext Message »
From:Nuno Carvalho Date:March 22 2012 5:35pm
Subject:bzr push into mysql-trunk branch (nuno.carvalho:3837 to 3838) Bug#13799489
View as plain text  
 3838 Nuno Carvalho	2012-03-22
      BUG#13799489: ROWS_QUERY_LOG_EVENTS CORRUPTED IF QUERY LONGER THAN 255 BYTES
      
       Problem:
        In Rows_query_log_events, the length of the query is stored using
        only one byte, length is stored on the first index of the stored char
        array and the complete query on the following indexes.
        When the query was read its length was set against the stored length
        on the first position, what made queries longer than 255 truncated.
      
       Solution:
        1. Change Rows_query_log_event::Rows_query_log_event(...) so that it
           ignores the length byte and uses the full length of the event.
        2. In log_event.cc, rename functions:
           - read_str -> read_str_at_most_255_bytes;
           - write_str -> write_str_at_most_255_bytes.
           Just to reduce the risk of future bugs.

    added:
      mysql-test/suite/binlog/r/binlog_row_query_log_events.result
      mysql-test/suite/binlog/t/binlog_row_query_log_events.test
    modified:
      sql/log_event.cc
 3837 Marko Mäkelä	2012-03-22
      Bug#13875241 INNODB-INDEX-ONLINE-PURGE.TEST TIMES OUT RANDOMLY
      
      innodb-index-online.test: Re-enable the core dump on DEBUG_SYNC timeout,
      so that we can analyze the failure better.

    modified:
      mysql-test/suite/innodb/r/innodb-index-online.result
      mysql-test/suite/innodb/t/innodb-index-online.test
=== added file 'mysql-test/suite/binlog/r/binlog_row_query_log_events.result'
--- a/mysql-test/suite/binlog/r/binlog_row_query_log_events.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/binlog/r/binlog_row_query_log_events.result	2012-03-22 17:33:57 +0000
@@ -0,0 +1,5 @@
+SET @@SESSION.BINLOG_ROWS_QUERY_LOG_EVENTS = 1;
+CREATE TABLE t1 (a VARCHAR(256));
+INSERT INTO t1 VALUES ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
+include/assert.inc [Logged query must match the one issued.]
+DROP TABLE t1;

=== added file 'mysql-test/suite/binlog/t/binlog_row_query_log_events.test'
--- a/mysql-test/suite/binlog/t/binlog_row_query_log_events.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/binlog/t/binlog_row_query_log_events.test	2012-03-22 17:33:57 +0000
@@ -0,0 +1,23 @@
+--source include/have_binlog_format_row.inc
+
+#######################################################################
+# BUG#13799489: ROWS_QUERY_LOG_EVENTS CORRUPTED IF QUERY LONGER THAN 255 BYTES
+#
+# Check that queries with more than 255 characters are not truncated
+# when stored on Rows_query_log_events.
+SET @@SESSION.BINLOG_ROWS_QUERY_LOG_EVENTS = 1;
+CREATE TABLE t1 (a VARCHAR(256));
+
+--let $as= `SELECT REPEAT('a', 256)`
+--let $query= INSERT INTO t1 VALUES ('$as')
+
+--let $assert_text= Logged query must match the one issued.
+--let $binlog_file= query_get_value("SHOW MASTER STATUS", File, 1)
+--let $binlog_position= query_get_value("SHOW MASTER STATUS", Position, 1)
+--eval $query
+--let $logged_query= query_get_value(SHOW BINLOG EVENTS IN "$binlog_file" FROM $binlog_position, Info, 2)
+--let $assert_cond= `SELECT "$logged_query" LIKE "%$query"`
+--source include/assert.inc
+
+# Clean up
+DROP TABLE t1;

=== modified file 'sql/log_event.cc'
--- a/sql/log_event.cc	2012-03-06 14:29:42 +0000
+++ b/sql/log_event.cc	2012-03-22 17:33:57 +0000
@@ -500,10 +500,15 @@ static void cleanup_load_tmpdir()
 
 
 /*
-  write_str()
+  Stores string to IO_CACHE file.
+
+  Writes str to file in the following format:
+   1. Stores length using only one byte (255 maximum value);
+   2. Stores complete str.
 */
 
-static bool write_str(IO_CACHE *file, const char *str, uint length)
+static bool write_str_at_most_255_bytes(IO_CACHE *file, const char *str,
+                                        uint length)
 {
   uchar tmp[1];
   tmp[0]= (uchar) length;
@@ -513,11 +518,20 @@ static bool write_str(IO_CACHE *file, co
 
 
 /*
-  read_str()
+  Reads string from buf.
+
+  Reads str from buf in the following format:
+   1. Read length stored on buf first index, as it only has 1 byte values
+      bigger than 255 where lost.
+   2. Set str pointer to buf second index.
+  Despite str contains the complete stored string, when it is read until
+  len its value will be truncated if original length was bigger than 255.
 */
 
-static inline int read_str(const char **buf, const char *buf_end,
-                           const char **str, uint8 *len)
+static inline int read_str_at_most_255_bytes(const char **buf,
+                                             const char *buf_end,
+                                             const char **str,
+                                             uint8 *len)
 {
   if (*buf + ((uint) (uchar) **buf) >= buf_end)
     return 1;
@@ -8510,11 +8524,11 @@ bool sql_ex_info::write_data(IO_CACHE* f
 {
   if (new_format())
   {
-    return (write_str(file, field_term, (uint) field_term_len) ||
-	    write_str(file, enclosed,   (uint) enclosed_len) ||
-	    write_str(file, line_term,  (uint) line_term_len) ||
-	    write_str(file, line_start, (uint) line_start_len) ||
-	    write_str(file, escaped,    (uint) escaped_len) ||
+    return (write_str_at_most_255_bytes(file, field_term, (uint) field_term_len) ||
+	    write_str_at_most_255_bytes(file, enclosed,   (uint) enclosed_len) ||
+	    write_str_at_most_255_bytes(file, line_term,  (uint) line_term_len) ||
+	    write_str_at_most_255_bytes(file, line_start, (uint) line_start_len) ||
+	    write_str_at_most_255_bytes(file, escaped,    (uint) escaped_len) ||
 	    my_b_safe_write(file,(uchar*) &opt_flags,1));
   }
   else
@@ -8554,11 +8568,11 @@ const char *sql_ex_info::init(const char
       the case when we have old format because we will be reusing net buffer
       to read the actual file before we write out the Create_file event.
     */
-    if (read_str(&buf, buf_end, &field_term, &field_term_len) ||
-        read_str(&buf, buf_end, &enclosed,   &enclosed_len) ||
-        read_str(&buf, buf_end, &line_term,  &line_term_len) ||
-        read_str(&buf, buf_end, &line_start, &line_start_len) ||
-        read_str(&buf, buf_end, &escaped,    &escaped_len))
+    if (read_str_at_most_255_bytes(&buf, buf_end, &field_term, &field_term_len) ||
+        read_str_at_most_255_bytes(&buf, buf_end, &enclosed,   &enclosed_len) ||
+        read_str_at_most_255_bytes(&buf, buf_end, &line_term,  &line_term_len) ||
+        read_str_at_most_255_bytes(&buf, buf_end, &line_start, &line_start_len) ||
+        read_str_at_most_255_bytes(&buf, buf_end, &escaped,    &escaped_len))
       return 0;
     opt_flags = *buf++;
   }
@@ -11580,7 +11594,7 @@ Incident_log_event::Incident_log_event(c
   char const *const str_end= buf + event_len;
   uint8 len= 0;                   // Assignment to keep compiler happy
   const char *str= NULL;          // Assignment to keep compiler happy
-  read_str(&ptr, str_end, &str, &len);
+  read_str_at_most_255_bytes(&ptr, str_end, &str, &len);
   if (!(m_message.str= (char*) my_malloc(len+1, MYF(MY_WME))))
   {
     /* Mark this event invalid */
@@ -11693,7 +11707,7 @@ Incident_log_event::write_data_body(IO_C
     crc= my_checksum(crc, (uchar*) m_message.str, m_message.length);
     // todo: report a bug on write_str accepts uint but treats it as uchar
   }
-  DBUG_RETURN(write_str(file, m_message.str, (uint) m_message.length));
+  DBUG_RETURN(write_str_at_most_255_bytes(file, m_message.str, (uint) m_message.length));
 }
 
 
@@ -11751,14 +11765,15 @@ Rows_query_log_event::Rows_query_log_eve
   DBUG_PRINT("info",("event_len: %u; common_header_len: %d; post_header_len: %d",
                      event_len, common_header_len, post_header_len));
 
-  char const *ptr= buf + common_header_len + post_header_len;
-  char const *const str_end= buf + event_len;
-  uint8 len= 0;                   // Assignment to keep compiler happy
-  const char *str= NULL;          // Assignment to keep compiler happy
-  read_str(&ptr, str_end, &str, &len);
+  /*
+   m_rows_query length is stored using only one byte, but that length is
+   ignored and the complete query is read.
+  */
+  int offset= common_header_len + post_header_len + 1;
+  int len= event_len - offset;
   if (!(m_rows_query= (char*) my_malloc(len+1, MYF(MY_WME))))
     return;
-  strmake(m_rows_query, str, len);
+  strmake(m_rows_query, buf + offset, len);
   DBUG_PRINT("info", ("m_rows_query: %s", m_rows_query));
   DBUG_VOID_RETURN;
 }
@@ -11804,7 +11819,12 @@ bool
 Rows_query_log_event::write_data_body(IO_CACHE *file)
 {
   DBUG_ENTER("Rows_query_log_event::write_data_body");
-  DBUG_RETURN(write_str(file, m_rows_query, (uint) strlen(m_rows_query)));
+  /*
+   m_rows_query length will be stored using only one byte, but on read
+   that length will be ignored and the complete query will be read.
+  */
+  DBUG_RETURN(write_str_at_most_255_bytes(file, m_rows_query,
+              (uint) strlen(m_rows_query)));
 }
 
 #if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-trunk branch (nuno.carvalho:3837 to 3838) Bug#13799489Nuno Carvalho22 Mar