List:Commits« Previous MessageNext Message »
From:Oystein Grovlen Date:September 15 2008 11:18am
Subject:bzr push into mysql-6.0-backup-merge branch (oystein.grovlen:2684 to 2686)
View as plain text  
 2686 Oystein Grovlen	2008-09-15 [merge]
      Merge from mysql-6.0-backup to mysql-6.0-backup-merge.
added:
  mysql-test/r/backup_blob.result
  mysql-test/t/backup_blob.test
modified:
  sql/backup/be_default.cc
  sql/backup/stream.cc
  sql/backup/stream_v1.c
  sql/backup/stream_v1.h
  sql/backup/stream_v1_transport.c
  sql/field.h
  sql/log.cc
  sql/log_event.h
  sql/log_event_old.h
  sql/rpl_record.cc
  sql/rpl_record.h
  sql/sql_class.cc

 2685 Oystein Grovlen	2008-09-15 [merge]
      Merge from mysql-6.0 to mysql-6.0-backup-merge
modified:
  .bzr-mysql/default.conf

 2684 Oystein Grovlen	2008-09-11 [merge]
      Merge from mysql-6.0 to mysql-6.0-backup-merge.
removed:
  include/mysql_h.ic
added:
  include/mysql.h.pp
  include/mysql/plugin.h.pp
  sql/mysql_priv.h.pp
modified:
  Makefile.am
  configure.in
  include/Makefile.am
  sql/ha_ndbcluster.cc
  sql/handler.h
  sql/sql_insert.cc
  sql/sql_show.cc
  sql/table.h

=== modified file '.bzr-mysql/default.conf'

=== modified file '.bzr-mysql/default.conf'
--- a/.bzr-mysql/default.conf	2008-09-11 11:42:13 +0000
+++ b/.bzr-mysql/default.conf	2008-09-15 08:35:01 +0000
@@ -3,4 +3,3 @@
 post_commit_to = commits@stripped
 post_push_to = commits@stripped
 tree_name = mysql-6.0-backup-merge
-

=== added file 'mysql-test/r/backup_blob.result'
--- a/mysql-test/r/backup_blob.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/backup_blob.result	2008-09-09 08:19:21 +0000
@@ -0,0 +1,52 @@
+DROP DATABASE IF EXISTS mysqltest;
+
+Test blobs, InnoDB
+
+CREATE DATABASE mysqltest;
+USE mysqltest;
+CREATE TABLE t1 (id int, txt longblob) ENGINE=innodb;
+INSERT INTO t1 VALUES(1, "short text");
+INSERT INTO t1 VALUES(2, "a little longer, but still short text");
+CREATE TABLE t2 (id int, txt longblob) ENGINE=innodb;
+INSERT INTO t2 VALUES(1, REPEAT('x',1000));
+INSERT INTO t2 VALUES(2, REPEAT('y',10000));
+INSERT INTO t2 VALUES(3, REPEAT('z',1048576));
+CREATE TABLE t3 (id int, txt longblob, txt2 longblob, txt3 longblob) ENGINE=innodb;
+INSERT INTO t3 VALUES(1, REPEAT('x',1048576), REPEAT('y',1048576), REPEAT('z',1048576));
+
+Check tables before backup
+SELECT * FROM t1 ORDER BY id;
+id	txt
+1	short text
+2	a little longer, but still short text
+CHECKSUM TABLE t2;
+Table	Checksum
+mysqltest.t2	1646886041
+CHECKSUM TABLE t3;
+Table	Checksum
+mysqltest.t3	2938103984
+
+Backup, drop and restore database
+BACKUP DATABASE mysqltest TO 'blob.bak';
+backup_id
+#
+DROP DATABASE mysqltest;
+RESTORE FROM 'blob.bak';
+backup_id
+#
+
+Check tables after restore
+SELECT * FROM t1 ORDER BY id;
+id	txt
+1	short text
+2	a little longer, but still short text
+CHECKSUM TABLE t2;
+Table	Checksum
+mysqltest.t2	1646886041
+CHECKSUM TABLE t3;
+Table	Checksum
+mysqltest.t3	2938103984
+
+Cleanup 
+
+DROP DATABASE mysqltest;

=== added file 'mysql-test/t/backup_blob.test'
--- a/mysql-test/t/backup_blob.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/backup_blob.test	2008-09-09 08:19:21 +0000
@@ -0,0 +1,64 @@
+###########################################################################
+# Purpose: Test backup/restore of blobs
+###########################################################################
+
+# This is currently only a regression test for bug#37212. When the
+# test is moved to the backup suite, at least one storage engine for
+# each driver should be tested.
+
+--source include/not_embedded.inc
+--source include/have_innodb.inc
+
+--disable_warnings
+DROP DATABASE IF EXISTS mysqltest;
+--enable_warnings
+
+--echo
+--echo Test blobs, InnoDB
+--echo
+
+CREATE DATABASE mysqltest;
+USE mysqltest;
+
+CREATE TABLE t1 (id int, txt longblob) ENGINE=innodb;
+INSERT INTO t1 VALUES(1, "short text");
+INSERT INTO t1 VALUES(2, "a little longer, but still short text");
+
+CREATE TABLE t2 (id int, txt longblob) ENGINE=innodb;
+INSERT INTO t2 VALUES(1, REPEAT('x',1000));
+INSERT INTO t2 VALUES(2, REPEAT('y',10000));
+INSERT INTO t2 VALUES(3, REPEAT('z',1048576));
+
+CREATE TABLE t3 (id int, txt longblob, txt2 longblob, txt3 longblob) ENGINE=innodb;
+INSERT INTO t3 VALUES(1, REPEAT('x',1048576), REPEAT('y',1048576), REPEAT('z',1048576));
+
+--echo
+--echo Check tables before backup
+SELECT * FROM t1 ORDER BY id; 
+CHECKSUM TABLE t2;
+CHECKSUM TABLE t3;
+
+--echo
+--echo Backup, drop and restore database
+
+--replace_column 1 #
+BACKUP DATABASE mysqltest TO 'blob.bak';
+
+DROP DATABASE mysqltest;
+--replace_column 1 #
+RESTORE FROM 'blob.bak';
+
+--echo
+--echo Check tables after restore
+
+SELECT * FROM t1 ORDER BY id; 
+CHECKSUM TABLE t2;
+CHECKSUM TABLE t3;
+
+--echo
+--echo Cleanup 
+--echo
+
+DROP DATABASE mysqltest;
+
+--remove_file $MYSQLTEST_VARDIR/master-data/blob.bak

=== modified file 'sql/backup/be_default.cc'
--- a/sql/backup/be_default.cc	2008-08-18 08:25:56 +0000
+++ b/sql/backup/be_default.cc	2008-09-09 08:19:21 +0000
@@ -275,7 +275,7 @@
     my_bool use_bitbuf= n_fields <= sizeof(bitbuf) * 8;
     error= bitmap_init(&cols, use_bitbuf ? bitbuf : NULL, (n_fields + 7) & ~7UL, FALSE);
     bitmap_set_all(&cols);
-    size= pack_row(cur_table, &cols, packed_row, rcd);
+    size= pack_row(cur_table, &cols, packed_row, rcd, FALSE);
     if (!use_bitbuf)
       bitmap_free(&cols);
   }
@@ -680,7 +680,7 @@
     error= bitmap_init(&cols, use_bitbuf ? bitbuf : NULL, (n_fields + 7) & ~7UL, FALSE);
     bitmap_set_all(&cols);
     ulong length;
-    error= unpack_row(NULL, cur_table, n_fields, packed_row, &cols, &cur_row_end, &length);
+    error= unpack_row(NULL, cur_table, n_fields, packed_row, &cols, &cur_row_end, &length, FALSE);
     if (!use_bitbuf)
       bitmap_free(&cols);
   }
@@ -906,6 +906,7 @@
       memcpy(blob_ptrs[blob_ptr_index], (byte *)buf.data + META_SIZE, size);
       ((Field_blob*) cur_table->field[*cur_blob])->set_ptr(size, 
         (uchar *)blob_ptrs[blob_ptr_index]);
+      cur_table->field[*cur_blob]->set_notnull();
       blob_ptr_index++;
       mode= CHECK_BLOBS;
       DBUG_RETURN(PROCESSING);
@@ -944,6 +945,7 @@
       ptr= (byte *)blob_buffer.get_base_ptr();
       ((Field_blob*) cur_table->field[*cur_blob])->set_ptr(max_blob_size, 
         (uchar *)ptr);
+      cur_table->field[*cur_blob]->set_notnull();
       blob_ptr_index++;
       mode= CHECK_BLOBS;
       DBUG_RETURN(PROCESSING);

=== modified file 'sql/backup/stream.cc'
--- a/sql/backup/stream.cc	2008-09-08 07:25:00 +0000
+++ b/sql/backup/stream.cc	2008-09-11 11:14:59 +0000
@@ -591,6 +591,10 @@
   if (m_fd < 0)
     return;
 
+  /*
+   Note: Even if bstream_close() fails we want to do the lower level clean-up.
+   This is why errors from bstream_close() are ignored.
+  */ 
   bstream_close(this);
 #ifdef HAVE_COMPRESS
   if (m_with_compression)
@@ -632,7 +636,8 @@
 */
 bool Output_stream::rewind()
 {
-  bstream_close(this);
+  if (bstream_close(this) != BSTREAM_OK)
+    return FALSE;
 
   bool ret= Stream::rewind();
 
@@ -777,6 +782,10 @@
   if (m_fd < 0)
     return;
 
+  /*
+   Note: Even if bstream_close() fails we want to do the lower level clean-up.
+   This is why errors from bstream_close() are ignored.
+  */ 
   bstream_close(this);
 #ifdef HAVE_COMPRESS
   if (m_with_compression)
@@ -798,7 +807,8 @@
 */
 bool Input_stream::rewind()
 {
-  bstream_close(this);
+  if (bstream_close(this) != BSTREAM_OK)
+    return FALSE;
 
   bool ret= Stream::rewind();
 

=== modified file 'sql/backup/stream_v1.c'
--- a/sql/backup/stream_v1.c	2008-08-13 12:41:33 +0000
+++ b/sql/backup/stream_v1.c	2008-09-09 08:19:21 +0000
@@ -1811,9 +1811,11 @@
       {
         memmove(buf->begin, chunk->data.begin, howmuch);
         envelope= buf;
-        chunk->data= *buf;
       }
 
+      // update chunk->data to point to the new buffer
+      chunk->data= *buf;
+
       /* update to_read blob to indicate free space left */
       to_read.begin= buf->begin + howmuch;
       to_read.end= buf->end;

=== modified file 'sql/backup/stream_v1.h'
--- a/sql/backup/stream_v1.h	2008-05-14 00:35:24 +0000
+++ b/sql/backup/stream_v1.h	2008-09-11 09:47:53 +0000
@@ -378,6 +378,16 @@
   the bytes being written to/read from a backup stream. This is done by
   storing pointers to functions performing basic I/O operations inside
   this structure.
+  
+  Low-level @c write(), @c read() and @c forward() functions should return 
+  BSTREAM_OK on success and BSTREAM_ERROR on error. Additionally, @c read() 
+  should return BSTREAM_EOS if there is no more data in the input stream.
+  
+  Functions @c write() and @c read() should behave like functions
+  @c bstream_write_part() and @c bstream_read_part(), respectively. The latter
+  are documented in stream_v1_transport.c. The only difference is that low-level
+  @c read() doesn't deal with chunk boundaries and never returns BSTREAM_EOC. 
+  However, it should detect end of stream and return BSTREAM_EOS in that case.
  */
 struct st_abstract_stream
 {

=== modified file 'sql/backup/stream_v1_transport.c'
--- a/sql/backup/stream_v1_transport.c	2008-07-19 03:03:39 +0000
+++ b/sql/backup/stream_v1_transport.c	2008-09-11 09:47:53 +0000
@@ -24,6 +24,11 @@
 #endif
 
 /**
+  @brief Set stream to error state and return BSTREAM_ERROR
+*/
+#define SERROR(S)  ((S)->state= ERROR, BSTREAM_ERROR)
+
+/**
   @brief Default size of a stream block.
 
   When opening stream for writing a different size can be specified.
@@ -386,7 +391,11 @@
   int ret= BSTREAM_OK;
 
   if (s->buf.pos > s->buf.begin)
+  {
     ret= as_write_all(&s->stream,*(bstream_blob*)&s->buf,s->mem);
+    if (ret != BSTREAM_OK)
+      return SERROR(s);
+  }
 
   /*
     Now buffer should be empty. If whole block has been written, reset buffer
@@ -398,7 +407,7 @@
   else
     s->buf.begin= s->buf.header= s->buf.pos; /* now invariant should hold */
 
-  return ret;
+  return BSTREAM_OK;
 }
 
 /**
@@ -408,18 +417,16 @@
   is left for fragment`s header.
 */
 static
-int append_to_buffer(backup_stream *s, blob *b)
+void append_to_buffer(backup_stream *s, blob *b)
 {
   if (b->begin >= b->end) /* no data to append */
-    return BSTREAM_OK;
+    return;
 
   if(s->buf.pos == s->buf.header) /* current fragment empty */
     s->buf.pos++;
 
   while ((s->buf.pos < s->buf.end) && (b->begin < b->end))
    *(s->buf.pos++)= *(b->begin++);
-
-  return BSTREAM_OK;
 }
 
 /**
@@ -480,7 +487,7 @@
 
       ret= write_buffer(s); /* this empties the buffer */
       if (ret != BSTREAM_OK)
-        return ret;
+        return SERROR(s);
 
       buf->pos= current_fragment.end;
 
@@ -657,6 +664,8 @@
     data.end= s->buf.pos + howmuch;
 
     ret= as_read(&s->stream,&data,s->mem);
+    if (ret == BSTREAM_ERROR)
+      return SERROR(s);
 
     s->buf.pos= data.begin;
 
@@ -715,8 +724,7 @@
     }
     else if (block_size != s->block_size)
     {
-      s->state= ERROR;
-      return BSTREAM_ERROR;
+      return SERROR(s);
     }
     else
       s->init_block_count--;
@@ -751,6 +759,7 @@
   @retval BSTREAM_OK   next fragment has been entered
   @retval BSTREAM_EOC  the entered fragment is the last fragment of a chunk
   @retval BSTREAM_EOS  there are no more fragments in the stream
+  @retval BSTREAM_ERROR error when reading from underlying stream
 */
 static
 int load_next_fragment(backup_stream *s)
@@ -766,7 +775,7 @@
 
   s->state= NORMAL; /* default, unless changed below */
 
-  ret= read_fragment_header(&s->buf.header);
+  ret= read_fragment_header(&s->buf.header);    // Never errors.
 
   /*
     If buf.header was not moved, it means that the fragment extends to
@@ -916,6 +925,11 @@
 
   if (s->mode == WRITING)
   {
+    /*
+      Note: even if bstream_end_chunk() or bstream_flush() fail we still try
+      to write the EOS marker and continue with other clean-up. In case of
+      failure s->state will be set to ERROR which is detected later.
+    */ 
     bstream_end_chunk(s);
     bstream_flush(s);
     /* write EOS marker */
@@ -999,7 +1013,7 @@
   {
     ret= bstream_flush(s);
     if (ret != BSTREAM_OK)
-      return ret;
+      return BSTREAM_ERROR;
   }
 
   ASSERT(s->buf.pos < s->buf.end);
@@ -1044,7 +1058,7 @@
 
     ret= write_buffer(s);
     if (ret != BSTREAM_OK)
-      return ret;
+      return BSTREAM_ERROR;
 
     /* write bytes from data blob to fill the block */
     saved_end= data->end;
@@ -1052,7 +1066,7 @@
 
     ret= as_write_all(&s->stream,*data,buf);
     if (ret != BSTREAM_OK)
-      return ret;
+      return SERROR(s);
 
     data->begin= data->end;
     data->end= saved_end;
@@ -1081,13 +1095,15 @@
     /* write contents of the output buffer */
     ret= write_buffer(s);
     if (ret != BSTREAM_OK)
-      return ret;
+      return BSTREAM_ERROR;
 
     /* write remainder of the fragment from data blob */
     saved_end= data->end;
     data->end= data->begin + (fragment.begin - s->buf.pos);
 
     ret= as_write_all(&s->stream,*data,*data);
+    if (ret != BSTREAM_OK)
+      return SERROR(s);
 
     data->begin= data->end;
     data->end= saved_end;
@@ -1112,7 +1128,7 @@
     If nothing else worked, we just append the data to the output buffer
     and return.
    */
-  append_to_buffer(s,data);
+  append_to_buffer(s,data);  // Never errors.
   return BSTREAM_OK;
 }
 
@@ -1185,6 +1201,8 @@
   else
   {
     ret= close_current_fragment(s);
+    if (ret != BSTREAM_OK)
+      return BSTREAM_ERROR;
 
     /*
       If the last fragment in the buffer is a small fragment, we can mark it as
@@ -1270,7 +1288,7 @@
   /* Otherwise close the current fragment to set its header */
   ret= close_current_fragment(s);
   if (ret != BSTREAM_OK)
-    return ret;
+    return BSTREAM_ERROR;
 
   /*
     If there is only one byte left to the end of block, we fill this byte with
@@ -1335,7 +1353,6 @@
     if (s->buf.begin == s->buf.pos)
     {
       ret= load_buffer(s);
-
       if (ret != BSTREAM_OK)
         return ret;
     }
@@ -1423,7 +1440,6 @@
   if ((s->buf.begin == s->buf.end) || (s->buf.begin == s->buf.header))
   {
     ret= load_buffer(s);
-
     if (ret != BSTREAM_OK)
       return ret;
   }
@@ -1482,10 +1498,11 @@
     saved= *data;
     data->end= data->begin + howmuch;
 
-    if (as_read(&s->stream, data, buf) == BSTREAM_EOS)
-    {
+    ret= as_read(&s->stream, data, buf);
+    if (ret == BSTREAM_ERROR)
+      return SERROR(s);
+    if (ret == BSTREAM_EOS)
       s->state= EOS;
-    }
 
     s->buf.begin += data->begin - saved.begin;
     s->buf.pos= s->buf.begin;
@@ -1570,7 +1587,6 @@
   if (s->buf.begin == s->buf.end)
   {
     ret= load_buffer(s);
-
     if (ret != BSTREAM_OK)
       return ret;
   }
@@ -1592,7 +1608,10 @@
     else
     {
       howmuch= s->buf.header - s->buf.pos;
-      as_forward(&s->stream, &howmuch);
+      ret= as_forward(&s->stream, &howmuch);   
+      if (ret != BSTREAM_OK)
+        return SERROR(s);
+
       s->buf.begin= s->buf.pos= s->buf.header;
     }
   }
@@ -1611,7 +1630,6 @@
   if (s->buf.pos == s->buf.begin)
   {
     ret= load_buffer(s);
-
     if (ret != BSTREAM_OK)
       return ret;
   }
@@ -1620,7 +1638,8 @@
   ASSERT(s->buf.pos > s->buf.header);
 
   ret= load_next_fragment(s);
-
+  if (ret == BSTREAM_ERROR)     // To avoid invariant check below.
+    return BSTREAM_ERROR;
   /* if we hit EOC marker here, we treat it as empty chunk */
   if (ret == BSTREAM_EOC)
     ret= BSTREAM_OK;
@@ -1638,5 +1657,9 @@
 */
 int bstream_skip(backup_stream *s, unsigned long int howmuch)
 {
-  return as_forward(&s->stream, &howmuch);
+  int ret= as_forward(&s->stream, &howmuch);
+  if (ret != BSTREAM_OK)
+    return SERROR(s);
+
+  return BSTREAM_OK;
 }

=== modified file 'sql/field.h'
--- a/sql/field.h	2008-08-28 11:17:29 +0000
+++ b/sql/field.h	2008-09-09 08:19:21 +0000
@@ -1759,6 +1759,18 @@
     {
       memcpy_fixed((uchar*) str,ptr+packlength+row_offset,sizeof(char*));
     }
+
+  /**
+     Copy value in data to the field ptr. 
+
+     NOTE
+     If the null_bit for this field is set, @c
+     set_notnull(my_ptrdiff_t) needs to be called for set_ptr to have
+     any effect when inserting a record.
+
+     @param length  Length of data
+     @param data    The value of the field
+  */
   inline void set_ptr(uchar *length, uchar *data)
     {
       memcpy(ptr,length,packlength);

=== modified file 'sql/log.cc'
--- a/sql/log.cc	2008-08-28 15:13:31 +0000
+++ b/sql/log.cc	2008-09-11 18:21:54 +0000
@@ -3515,6 +3515,8 @@
 ulonglong MYSQL_BACKUP_LOG::get_next_backup_id()
 {
   ulonglong id= 0;
+  uchar *read_id= 0;
+  uchar *write_id= 0;
   char buff[FN_REFLEN], *file_path;
   File file= 0;
 
@@ -3538,7 +3540,6 @@
     file= my_open(file_path, O_RDWR|O_BINARY|O_CREAT, MYF(MY_WME));
     if (!file)
       goto err_end; 
-
     if (my_fstat(file, &state, MYF(0)))
       goto err;
     /*
@@ -3562,10 +3563,10 @@
     // else .... we read the next value in the file!
     else
     {
-      ulonglong read_id= 0;
       my_seek(file, 0, 0, MYF(MY_WME));
-      my_read(file, (uchar *)&read_id, sizeof(ulonglong), MYF(MY_WME|MY_NABP));
-      id= uint8korr(&read_id);
+      read_id= (uchar *)my_malloc(sizeof(ulonglong), MYF(MY_ZEROFILL));
+      my_read(file, read_id, sizeof(ulonglong), MYF(MY_WME|MY_NABP));
+      id= uint8korr(read_id);
       id++;
     }
   }
@@ -3579,8 +3580,6 @@
   */
   if ((m_next_id != id) && id)
   {
-    ulonglong write_id= 0;
-
     if (!file)
     {
       file_path= make_backup_log_name(buff, BACKUP_SETTINGS_NAME.str, ".obx");
@@ -3589,8 +3588,9 @@
         goto err_end; 
     }
     my_seek(file, 0, 0, MYF(MY_WME));
-    int8store(&write_id, id);
-    my_write(file, (uchar *)&write_id, sizeof(ulonglong), MYF(MY_WME));
+    write_id= (uchar *)my_malloc(sizeof(ulonglong), MYF(MY_ZEROFILL));
+    int8store(write_id, id);
+    my_write(file, write_id, sizeof(ulonglong), MYF(MY_WME));
   }
 err:
   if (file > 0)
@@ -3600,6 +3600,10 @@
   m_next_id= id;
   pthread_mutex_unlock(&LOCK_backupid);
   DBUG_PRINT("backup_log",("The next id is %lu.\n", (ulong)id));
+  if(read_id)
+    my_free(read_id, MYF(0));
+  if(write_id)
+    my_free(write_id, MYF(0));
   return id;
 }
 

=== modified file 'sql/log_event.h'
--- a/sql/log_event.h	2008-08-28 09:59:54 +0000
+++ b/sql/log_event.h	2008-09-09 08:19:21 +0000
@@ -3545,7 +3545,7 @@
     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, cols,
-                                   &m_curr_row_end, &m_master_reclength);
+                                   &m_curr_row_end, &m_master_reclength, TRUE);
     if (m_curr_row_end > m_rows_end)
       my_error(ER_SLAVE_CORRUPT_EVENT, MYF(0));
     ASSERT_OR_RETURN_ERROR(m_curr_row_end <= m_rows_end, HA_ERR_CORRUPT_EVENT);

=== modified file 'sql/log_event_old.h'
--- a/sql/log_event_old.h	2008-05-29 15:44:11 +0000
+++ b/sql/log_event_old.h	2008-09-09 08:19:21 +0000
@@ -204,7 +204,7 @@
     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_curr_row_end, &m_master_reclength);
+                                   &m_curr_row_end, &m_master_reclength, TRUE);
     ASSERT_OR_RETURN_ERROR(m_curr_row_end <= m_rows_end, HA_ERR_CORRUPT_EVENT);
     return result;
   }

=== modified file 'sql/rpl_record.cc'
--- a/sql/rpl_record.cc	2008-03-27 18:40:00 +0000
+++ b/sql/rpl_record.cc	2008-09-09 08:19:21 +0000
@@ -51,12 +51,19 @@
                    record[0] or @c record[1], but no such check is
                    made since the code does not rely on that.
 
+   @param pack_blobs Whether or not blob fields are packed. If true,
+                     row_data has to be big enough to store all blobs
+                     in this record. If false, the blobs are not
+                     stored in row_data but should instead be fetched
+                     by calling @c get_ptr
+
    @return The number of bytes written at @c row_data.
  */
 #if !defined(MYSQL_CLIENT)
 size_t
 pack_row(TABLE *table, MY_BITMAP const* cols,
-         uchar *row_data, const uchar *record)
+         uchar *row_data, const uchar *record,
+         bool pack_blobs)
 {
   Field **p_field= table->field, *field;
   int const null_byte_count= (bitmap_bits_set(cols) + 7) / 8;
@@ -92,6 +99,11 @@
         offset= def_offset;
         null_bits |= null_mask;
       }
+      else if (!pack_blobs && field->unireg_check==Field::BLOB_FIELD)
+      {
+        // The field is not empty. Fetch by calling get_ptr()
+        null_bits &= ~null_mask;
+      }
       else
       {
         offset= rec_offset;
@@ -179,6 +191,10 @@
                   Pointer to variable that will be set to the length of the
                   record on the master side
 
+   @param unpack_blobs Whether or not blob fields are unpacked. If
+                       false, the blobs must be added by calling
+                       @c set_ptr() and @c set_notnull().
+
    @retval 0 No error
 
    @retval ER_NO_DEFAULT_FOR_FIELD
@@ -191,7 +207,8 @@
 unpack_row(Relay_log_info const *rli,
            TABLE *table, uint const colcnt,
            uchar const *const row_data, MY_BITMAP const *cols,
-           uchar const **const row_end, ulong *const master_reclength)
+           uchar const **const row_end, ulong *const master_reclength,
+           bool unpack_blobs)
 {
   DBUG_ENTER("unpack_row");
   DBUG_ASSERT(row_data);
@@ -255,6 +272,11 @@
 
         f->set_null();
       }
+      else if (!unpack_blobs && f->unireg_check==Field::BLOB_FIELD)
+      {
+        DBUG_PRINT("debug", ("Not unpacking BLOB; field_ptr: %p", field_ptr));
+        f->set_null();
+      }
       else
       {
         f->set_notnull();

=== modified file 'sql/rpl_record.h'
--- a/sql/rpl_record.h	2008-03-25 15:10:50 +0000
+++ b/sql/rpl_record.h	2008-09-09 08:19:21 +0000
@@ -20,14 +20,16 @@
 
 #if !defined(MYSQL_CLIENT)
 size_t pack_row(TABLE* table, MY_BITMAP const* cols,
-                uchar *row_data, const uchar *data);
+                uchar *row_data, const uchar *data,
+                bool pack_blobs);
 #endif
 
 #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
 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 row_end, ulong *const master_reclength);
+               uchar const **const row_end, ulong *const master_reclength,
+               bool unpack_blobs);
 
 // Fill table's record[0] with default values.
 int prepare_record(TABLE *const, const MY_BITMAP *cols, uint width, const bool);

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2008-09-04 13:46:04 +0000
+++ b/sql/sql_class.cc	2008-09-09 08:19:21 +0000
@@ -3428,7 +3428,7 @@
 
   uchar *row_data= memory.slot(0);
 
-  size_t const len= pack_row(table, table->write_set, row_data, record);
+  size_t const len= pack_row(table, table->write_set, row_data, record, TRUE);
 
   Rows_log_event* const ev=
     binlog_prepare_pending_rows_event(table, server_id, len, is_trans,
@@ -3457,9 +3457,9 @@
   uchar *after_row= row_data.slot(1);
 
   size_t const before_size= pack_row(table, table->read_set, before_row,
-                                        before_record);
+                                     before_record, TRUE);
   size_t const after_size= pack_row(table, table->write_set, after_row,
-                                       after_record);
+                                    after_record, TRUE);
 
   /*
     Don't print debug messages when running valgrind since they can
@@ -3501,7 +3501,7 @@
   uchar *row_data= memory.slot(0);
 
   DBUG_DUMP("table->read_set", (uchar*) table->read_set->bitmap, (table->s->fields + 7) / 8);
-  size_t const len= pack_row(table, table->read_set, row_data, record);
+  size_t const len= pack_row(table, table->read_set, row_data, record, TRUE);
 
   Rows_log_event* const ev=
     binlog_prepare_pending_rows_event(table, server_id, len, is_trans,

Thread
bzr push into mysql-6.0-backup-merge branch (oystein.grovlen:2684 to 2686) Oystein Grovlen15 Sep