List:Commits« Previous MessageNext Message »
From:Andrei Elkin Date:February 27 2008 8:22pm
Subject:Re: bk commit into 5.1 tree (mats:1.2652) BUG#31502
View as plain text  
Mats, hej.


We discussed some issues on #rep regarding to the 2nd patch.

We are facing -

  slave needs to handle pre-metadata master's extra columns
  with sometimes stopping gracefully (mostly solved by the 1st patch)

  as a side-effect of that solution that relies on table_map's column
  type info we got a problem of distinguishing between enum, set from
  the very string.

My point was that replacement in storing real_type() instead of
type().
With wl#3228 we actually have access to the real_type when it's
different from type.

To avoid the mentioned side-effect
it'd be enough to refine logics of the first patch new function
check_and_set_length(). When it executes

   switch (type) ...

   case MYSQL_TYPE_STRING:

to test additionally if there is a metadata info and if it is the
first byte must be the value of the real_type.
if the real_type is in MYSQL_TYPE_{ENUM,SET} then the length is
set basing on their rules not the MYSQL_TYPE_STRING rule
(can be implemented with introducing a new get_real_type(col), see
further).
With doing such refinement of logics of check_and_set_length() we
won't be taxed with an unnecessary replication stop\footnote{
because of the first arg of the if yielded true
+    if (table->field[col]->real_type() != type(col) ||
+        (m_field_metadata[col] == 0 &&
+           table->field[col]->real_type() != type(col)))
}
when old metadata-aware master updates a set or enum columns.

Answering one of your questions:

<mats> andrei, The real type is not stored for all fields, so I think
	      we should correct the coltype array to hold the right data.

We actually have all necessary info for the slave in the table_map.

As to an example 

<mats> andrei, There is also another scenario that could cause a
	      segfault...
<mats> master> CREATE TABLE t1 (a INT, b ENUM('RED','GREEN','BLUE'));
<mats> slave> ALTER TABLE t1 MODIFY b CHAR(10));
<mats> master> INSERT INTO t1 VALUES(1,'RED'), (2, 'GREEN');
<mats> *see slave crash*

we need indeed a similar to the 2nd patch refinement of logics in
table_def::compatible_with().
Something like this:


+    if (table->field[col]->type() != type(col) ||
+        (m_field_metadata[col] != 0 &&
+           table->field[col]->real_type() != get_real_type(col)))

where a new function get_real_type(col) returns
table->field[col]->real_type() if 
metadata does not carry it otherwise the master's real_type for the
col.

All in all, we should not have the side-effect plus your crash example
ceases.

I have some more comments as well but let us come to the conclusion on
this principal part.

regards,

Andrei



> Below is the list of changes that have just been committed into a local
> 5.1 repository of mats. When mats does a push these changes will
> be propagated to the main repository and, within 24 hours after the
> push, to the public repository.
> For information on how to access the public repository
> see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
>
> ChangeSet@stripped, 2008-02-22 15:25:56+01:00, mats@stripped +8 -0
>   BUG#31502 (5.1.20 -> 5.1.22 Slave crashes if it gets an event w/ data 
>              for non-exist column):
>   
>   Complimentary patch to fix the fact that type() instead of real_type()
>   was sent in the table map event. This cause a crash when using ENUM
>   extra columns since type() is MYSQL_TYPE_STRING and real_type() is
>   MYSQL_TYPE_ENUM. The packed format for MYSQL_TYPE_STRING is (len,
>   byte, ...) while the pack format for MYSQL_TYPE_ENUM is just the
>   integer value as a single byte (which will then be interpreted as the
>   length of a string by the slave).
>
>   sql/field.cc@stripped, 2008-02-22 15:25:43+01:00, mats@stripped +8
> -11
>     Removing debug printouts that are not needed any more.
>     In Field_string::unpack(), only 'min(field_length,length)' bytes
>     were read from the log, although 'length' bytes should be read,
>     since this is what is stored in the log. Changing the code so
>     that the function advances the point 'length' bytes, but just
>     copy 'min(field_length,lenght)' bytes from the buffer. This way
>     there is no risk of buffer overflow when length > field_length.
>
>   sql/field.h@stripped, 2008-02-22 15:25:44+01:00, mats@stripped +8
> -2
>     Removing gratuitous virtual for save_field_metadata(): the function
>     is never overridden. Adding debug printout to save_field_metadata().
>     
>
>   sql/log_event.cc@stripped, 2008-02-22 15:25:45+01:00, mats@stripped
> +1 -1
>     Storing real_type() instead of type() in the coltype array.
>
>   sql/log_event.h@stripped, 2008-02-22 15:25:45+01:00, mats@stripped
> +2 -1
>     Removing gratuitous virtual on save_field_metadata(): the
>     function is never overridden. Adding end of all rows to
>     unpack_row() to allow for checking if reading out of bounds.
>
>   sql/log_event_old.h@stripped, 2008-02-22 15:25:45+01:00,
> mats@stripped +1 -0
>     Adding end of all rows to unpack_row() to allow for checking
>     if reading out of bounds.
>
>   sql/rpl_record.cc@stripped, 2008-02-22 15:25:46+01:00, mats@stripped
> +20 -13
>     Adding end of all rows to unpack_row() to allow for checking
>     if reading out of bounds. Returning error if reading out of
>     bounds. Some changes to debug printout to get good information
>     for debugging.
>
>   sql/rpl_record.h@stripped, 2008-02-22 15:25:46+01:00, mats@stripped
> +1 -0
>     Adding parameter for end of all rows to allow check for
>     reading out of bounds data.
>
>   sql/rpl_utility.cc@stripped, 2008-02-22 15:25:46+01:00,
> mats@stripped +22 -6
>     Not printing an error for field types that require metadata
>     when we have a length value, i.e., when there were metadata
>     available for the field.
>
> diff -Nrup a/sql/field.cc b/sql/field.cc
> --- a/sql/field.cc	2007-11-21 21:09:21 +01:00
> +++ b/sql/field.cc	2008-02-22 15:25:43 +01:00
> @@ -4036,39 +4036,35 @@ uchar *
>  Field_real::pack(uchar *to, const uchar *from,
>                   uint max_length, bool low_byte_first)
>  {
> -  DBUG_ENTER("Field_real::pack");
>    DBUG_ASSERT(max_length >= pack_length());
> -  DBUG_PRINT("debug", ("pack_length(): %u", pack_length()));
>  #ifdef WORDS_BIGENDIAN
>    if (low_byte_first != table->s->db_low_byte_first)
>    {
>      const uchar *dptr= from + pack_length();
>      while (dptr-- > from)
>        *to++ = *dptr;
> -    DBUG_RETURN(to);
> +    return to;
>    }
>    else
>  #endif
> -    DBUG_RETURN(Field::pack(to, from, max_length, low_byte_first));
> +    return Field::pack(to, from, max_length, low_byte_first);
>  }
>  
>  const uchar *
>  Field_real::unpack(uchar *to, const uchar *from,
>                     uint param_data, bool low_byte_first)
>  {
> -  DBUG_ENTER("Field_real::unpack");
> -  DBUG_PRINT("debug", ("pack_length(): %u", pack_length()));
>  #ifdef WORDS_BIGENDIAN
>    if (low_byte_first != table->s->db_low_byte_first)
>    {
>      const uchar *dptr= from + pack_length();
>      while (dptr-- > from)
>        *to++ = *dptr;
> -    DBUG_RETURN(from + pack_length());
> +    return from + pack_length();
>    }
>    else
>  #endif
> -    DBUG_RETURN(Field::unpack(to, from, param_data, low_byte_first));
> +    return Field::unpack(to, from, param_data, low_byte_first);
>  }
>  
>  /****************************************************************************
> @@ -6675,7 +6671,7 @@ Field_string::unpack(uchar *to,
>                       bool low_byte_first __attribute__((unused)))
>  {
>    uint from_length=
> -    param_data ? min(param_data & 0x00ff, field_length) : field_length;
> +    param_data ? (param_data & 0x00ff) : field_length;
>    uint length;
>  
>    if (from_length > 255)
> @@ -6686,9 +6682,10 @@ Field_string::unpack(uchar *to,
>    else
>      length= (uint) *from++;
>  
> -  memcpy(to, from, length);
> +  memcpy(to, from, min(length, field_length));
>    // Pad the string with the pad character of the fields charset
> -  bfill(to + length, field_length - length, field_charset->pad_char);
> +  if (field_length > length)
> +    bfill(to + length, field_length - length, field_charset->pad_char);
>    return from+length;
>  }
>  
> diff -Nrup a/sql/field.h b/sql/field.h
> --- a/sql/field.h	2007-10-30 08:19:22 +01:00
> +++ b/sql/field.h	2008-02-22 15:25:44 +01:00
> @@ -167,8 +167,14 @@ public:
>      field_length + 1, field_length, and pack_length_no_ptr() respectfully.
>    */
>    virtual uint row_pack_length() { return 0; }
> -  virtual int save_field_metadata(uchar *first_byte)
> -  { return do_save_field_metadata(first_byte); }
> +  int save_field_metadata(uchar *first_byte)
> +  {
> +    int result= do_save_field_metadata(first_byte);
> +    DBUG_PRINT("info", ("field: %s; type: %3d; count: %2d, data: %02x %02x",
> +                        field_name, real_type(), result,
> +                        first_byte[0], first_byte[1]));
> +    return result;
> +  }
>  
>    /*
>      data_length() return the "real size" of the data in memory.
> diff -Nrup a/sql/log_event.cc b/sql/log_event.cc
> --- a/sql/log_event.cc	2007-12-13 21:27:23 +01:00
> +++ b/sql/log_event.cc	2008-02-22 15:25:45 +01:00
> @@ -6852,7 +6852,7 @@ Table_map_log_event::Table_map_log_event
>    {
>      m_coltype= reinterpret_cast<uchar*>(m_memory);
>      for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
> -      m_coltype[i]= m_table->field[i]->type();
> +      m_coltype[i]= m_table->field[i]->real_type();
>    }
>  
>    /*
> diff -Nrup a/sql/log_event.h b/sql/log_event.h
> --- a/sql/log_event.h	2007-12-13 21:27:23 +01:00
> +++ b/sql/log_event.h	2008-02-22 15:25:45 +01:00
> @@ -2874,7 +2874,7 @@ public:
>  
>    virtual int get_data_size() { return m_data_size; } 
>  #ifndef MYSQL_CLIENT
> -  virtual int save_field_metadata();
> +  int save_field_metadata();
>    virtual bool write_data_header(IO_CACHE *file);
>    virtual bool write_data_body(IO_CACHE *file);
>    virtual const char *get_db() { return m_dbnam; }
> @@ -3097,6 +3097,7 @@ protected:
>      DBUG_ASSERT(m_table);
>      ASSERT_OR_RETURN_ERROR(m_curr_row < m_rows_end, HA_ERR_CORRUPT_EVENT);
>      int const result= ::unpack_row(rli, m_table, m_width, m_curr_row, &m_cols,
> +                                   m_rows_end,
>                                     &m_curr_row_end, &m_master_reclength);
>      ASSERT_OR_RETURN_ERROR(m_curr_row_end <= m_rows_end, HA_ERR_CORRUPT_EVENT);
>      return result;
> diff -Nrup a/sql/log_event_old.h b/sql/log_event_old.h
> --- a/sql/log_event_old.h	2007-11-20 19:49:34 +01:00
> +++ b/sql/log_event_old.h	2008-02-22 15:25:45 +01:00
> @@ -212,6 +212,7 @@ protected:
>      DBUG_ASSERT(m_table);
>      ASSERT_OR_RETURN_ERROR(m_curr_row < m_rows_end, HA_ERR_CORRUPT_EVENT);
>      int const result= ::unpack_row(rli, m_table, m_width, m_curr_row, &m_cols,
> +                                   m_rows_end,
>                                     &m_curr_row_end, &m_master_reclength);
>      ASSERT_OR_RETURN_ERROR(m_curr_row_end <= m_rows_end, HA_ERR_CORRUPT_EVENT);
>      return result;
> diff -Nrup a/sql/rpl_record.cc b/sql/rpl_record.cc
> --- a/sql/rpl_record.cc	2007-12-13 21:27:24 +01:00
> +++ b/sql/rpl_record.cc	2008-02-22 15:25:46 +01:00
> @@ -77,8 +77,10 @@ pack_row(TABLE *table, MY_BITMAP const* 
>    unsigned int null_mask= 1U;
>    for ( ; (field= *p_field) ; p_field++)
>    {
> -    DBUG_PRINT("debug", ("null_mask=%d; null_ptr=%p; row_data=%p;
> null_byte_count=%d",
> -                         null_mask, null_ptr, row_data, null_byte_count));
> +    DBUG_PRINT("debug", ("field: %s; type: %d",
> +                         field->field_name, field->real_type()));
> +    DBUG_PRINT("debug", ("null_mask=%d; null_ptr=%p; null_byte_count=%d",
> +                         null_mask, null_ptr, null_byte_count));
>      if (bitmap_is_set(cols, p_field - table->field))
>      {
>        my_ptrdiff_t offset;
> @@ -104,11 +106,10 @@ pack_row(TABLE *table, MY_BITMAP const* 
>  #endif
>          pack_ptr= field->pack(pack_ptr, field->ptr + offset,
>                                field->max_data_length(), TRUE);
> -        DBUG_PRINT("debug", ("field: %s; pack_ptr: 0x%lx;"
> -                             " pack_ptr':0x%lx; bytes: %d",
> -                             field->field_name, (ulong) old_pack_ptr,
> -                             (ulong) pack_ptr,
> +        DBUG_PRINT("debug", ("pack_ptr: 0x%lx; pack_ptr':0x%lx; bytes: %d",
> +                             (ulong) old_pack_ptr, (ulong) pack_ptr,
>                               (int) (pack_ptr - old_pack_ptr)));
> +        DBUG_DUMP("row_data", old_pack_ptr, pack_ptr - old_pack_ptr);
>        }
>  
>        null_mask <<= 1;
> @@ -186,6 +187,7 @@ int
>  unpack_row(Relay_log_info const *rli,
>             TABLE *table, uint const colcnt,
>             uchar const *const row_data, MY_BITMAP const *cols,
> +           uchar const *const data_end,
>             uchar const **const row_end, ulong *const master_reclength)
>  {
>    DBUG_ENTER("unpack_row");
> @@ -247,11 +249,12 @@ unpack_row(Relay_log_info const *rli,
>          uchar const *const old_pack_ptr= pack_ptr;
>  #endif
>          pack_ptr= f->unpack(f->ptr, pack_ptr, metadata, TRUE);
> -	DBUG_PRINT("debug", ("field: %s; type: %d; metadata: 0x%x;"
> -                             " pack_ptr: 0x%lx; pack_ptr': 0x%lx; bytes: %d",
> -                             f->field_name, f->type(), metadata,
> +	DBUG_PRINT("debug", ("field: %s; type: %d; metadata: 0x%x",
> +                             f->field_name, f->type(), metadata));
> +	DBUG_PRINT("debug", ("pack_ptr: 0x%lx; pack_ptr': 0x%lx; bytes: %d",
>                               (ulong) old_pack_ptr, (ulong) pack_ptr,
>                               (int) (pack_ptr - old_pack_ptr)));
> +        DBUG_DUMP("row_data", old_pack_ptr, pack_ptr - old_pack_ptr);
>        }
>  
>        null_mask <<= 1;
> @@ -262,8 +265,8 @@ unpack_row(Relay_log_info const *rli,
>    /*
>      throw away master's extra fields
>    */
> -  DBUG_PRINT("info", ("i: %u; tabledef->size: %lu; cols->n_bits: %u",
> -                      i, tabledef->size(), cols->n_bits));
> +  DBUG_PRINT("info", ("tabledef->size: %lu; cols->n_bits: %u",
> +                      tabledef->size(), cols->n_bits));
>    uint max_cols= min(tabledef->size(), cols->n_bits);
>    for (; i < max_cols; i++)
>    {
> @@ -282,9 +285,13 @@ unpack_row(Relay_log_info const *rli,
>          if ((error= tabledef->calc_field_size(i, (uchar *) pack_ptr, &length,
> rli)))
>            DBUG_RETURN(error);
>          DBUG_PRINT("info",
> -                   ("type: %d; pack_ptr: 0x%lu; pack_ptr': 0x%lu",
> -                    tabledef->type(i),
> +                   ("type: %d; metadata: 0x%04x; bytes: %d",
> +                    tabledef->type(i), tabledef->field_metadata(i), length));
> +        DBUG_PRINT("info",
> +                   ("pack_ptr: 0x%lu; pack_ptr': 0x%lu",
>                      (ulong) pack_ptr, (ulong) pack_ptr + length));
> +        DBUG_DUMP("row_data", pack_ptr, min(length, 128));
> +        ASSERT_OR_RETURN_ERROR(pack_ptr + length <= data_end,
> HA_ERR_CORRUPT_EVENT);
>          pack_ptr += length;
>        }
>        null_mask <<= 1;
> diff -Nrup a/sql/rpl_record.h b/sql/rpl_record.h
> --- a/sql/rpl_record.h	2007-09-10 13:15:59 +02:00
> +++ b/sql/rpl_record.h	2008-02-22 15:25:46 +01:00
> @@ -27,6 +27,7 @@ size_t pack_row(TABLE* table, MY_BITMAP 
>  int unpack_row(Relay_log_info const *rli,
>                 TABLE *table, uint const colcnt,
>                 uchar const *const row_data, MY_BITMAP const *cols,
> +               uchar const *const data_end,
>                 uchar const **const row_end, ulong *const master_reclength);
>  
>  // Fill table's record[0] with default values.
> diff -Nrup a/sql/rpl_utility.cc b/sql/rpl_utility.cc
> --- a/sql/rpl_utility.cc	2007-12-13 21:27:24 +01:00
> +++ b/sql/rpl_utility.cc	2008-02-22 15:25:46 +01:00
> @@ -77,9 +77,14 @@ check_and_set_length(enum_field_types fi
>    case MYSQL_TYPE_VARCHAR:
>    case MYSQL_TYPE_BIT:
>    case MYSQL_TYPE_DECIMAL:                      // ???
> -    log->report(ERROR_LEVEL, ER_SLAVE_MISSING_METADATA,
> -                ER(ER_SLAVE_MISSING_METDATA), field_type);
> -    error= HA_ERR_SLAVE_MISSING_METADATA;
> +    if (length)
> +      *out_length = length;
> +    else
> +    {
> +      log->report(ERROR_LEVEL, ER_SLAVE_MISSING_METADATA,
> +                ER(ER_SLAVE_MISSING_METADATA), field_type);
> +      error= HA_ERR_SLAVE_MISSING_METADATA;
> +    }
>      break;
>    }
>    return error;
> @@ -192,7 +197,7 @@ table_def::calc_field_size(uint col, uch
>    case MYSQL_TYPE_BLOB:
>    case MYSQL_TYPE_GEOMETRY:
>    {
> -#if 1
> +#if 0
>      /*
>        BUG#29549: 
>        This is currently broken for NDB, which is using big-endian
> @@ -232,7 +237,7 @@ table_def::calc_field_size(uint col, uch
>      break;
>    }
>    default:
> -    length= -1;
> +    length= 0;
>    }
>  
>    int const error= check_and_set_length(type(col), length, log, len_arg);
> @@ -258,7 +263,18 @@ table_def::compatible_with(Relay_log_inf
>  
>    for (uint col= 0 ; col < cols_to_check ; ++col)
>    {
> -    if (table->field[col]->type() != type(col))
> +    /*
> +      Here we check if the type we received is different from the real
> +      type of the column. If it is, there is a problem and an error is
> +      reported.
> +
> +      To be able to replication from old servers not having metadata,
> +      we check the type (instead of the real type) since this is what
> +      was stored instead of the real type.
> +     */
> +    if (table->field[col]->real_type() != type(col) ||
> +        (m_field_metadata[col] == 0 &&
> +           table->field[col]->real_type() != type(col)))
>      {
>        DBUG_ASSERT(col < size() && col < tsh->fields);
>        DBUG_ASSERT(tsh->db.str && tsh->table_name.str);
>
> -- 
> MySQL Code Commits Mailing List
> For list archives: http://lists.mysql.com/commits
> To unsubscribe:    http://lists.mysql.com/commits?unsub=1
Thread
bk commit into 5.1 tree (mats:1.2652) BUG#31502Mats Kindahl22 Feb 2008
  • Re: bk commit into 5.1 tree (mats:1.2652) BUG#31502Andrei Elkin27 Feb 2008