List:Commits« Previous MessageNext Message »
From:Ashish Agarwal Date:February 3 2012 2:38pm
Subject:bzr push into mysql-trunk branch (ashish.y.agarwal:3837 to 3838) Bug#11748748
View as plain text  
 3838 Ashish Agarwal	2012-02-03
      BUG#11748748 - 37280: CHECK AND REPAIR TABLE REPORT TABLE
                            CORRUPTED WHEN RUN CONCURRENTLY WITH
      
      ISSUE: Concurrent queries in archive causes table
             corruption. Running repair and check query
             concurrently with insert query causes table
             corruption. Improper locking.
      
      SOLUTION: Locks are properly aquired.
                Initial lock is aquired and number of
                rows are remembered. Inserting
                remembered rows to new file. Locks
                are again aquired to process the
                remaining rows. Concurrent optimization
                on a same table not allowed.

    modified:
      storage/archive/ha_archive.cc
      storage/archive/ha_archive.h
 3837 Nuno Carvalho	2012-02-03 [merge]
      Merge from mysql-5.5 to mysql-trunk.
      
      Conflicts:
       mysql-test/collections/default.experimental

    modified:
      mysql-test/collections/default.experimental
=== modified file 'storage/archive/ha_archive.cc'
--- a/storage/archive/ha_archive.cc	2012-02-03 14:13:57 +0000
+++ b/storage/archive/ha_archive.cc	2012-02-03 14:37:46 +0000
@@ -294,6 +294,15 @@ err:
   DBUG_RETURN(1);
 }
 
+static void save_auto_increment(TABLE *table, ulonglong *value)
+{
+  Field *field= table->found_next_number_field;
+  ulonglong auto_value=
+    (ulonglong) field->val_int(table->record[0] +
+                               field->offset(table->record[0]));
+  if (*value <= auto_value)
+    *value= auto_value + 1;
+}
 
 /**
   @brief Read version 1 meta file (5.0 compatibility routine).
@@ -476,6 +485,7 @@ ARCHIVE_SHARE *ha_archive::get_share(con
     share->table_name= tmp_name;
     share->crashed= FALSE;
     share->archive_write_open= FALSE;
+    share->in_optimize= false;
     fn_format(share->data_file_name, table_name, "",
               ARZ, MY_REPLACE_EXT | MY_UNPACK_FILENAME);
     strmov(share->table_name, table_name);
@@ -1506,34 +1516,45 @@ int ha_archive::optimize(THD* thd, HA_CH
 {
   int rc= 0;
   azio_stream writer;
+  ha_rows count;
+  my_bitmap_map *org_bitmap;
   char writer_filename[FN_REFLEN];
   DBUG_ENTER("ha_archive::optimize");
 
-  init_archive_reader();
-
-  // now we close both our writer and our reader for the rename
-  if (share->archive_write_open)
+  mysql_mutex_lock(&share->mutex);
+  if (share->in_optimize)
   {
-    if (share->archive_write.version == 1)
-      write_v1_metafile();
-    azclose(&(share->archive_write));
-    share->archive_write_open= FALSE;
+    mysql_mutex_unlock(&share->mutex);
+    DBUG_RETURN(HA_ADMIN_FAILED);
   }
+  share->in_optimize= true;
+  /* remember the number of rows */
+  count= share->rows_recorded;
+  if (share->archive_write_open)
+    azflush(&share->archive_write, Z_SYNC_FLUSH);
+  mysql_mutex_unlock(&share->mutex);
+
+  init_archive_reader();
 
   /* Lets create a file to contain the new data */
   fn_format(writer_filename, share->table_name, "", ARN, 
             MY_REPLACE_EXT | MY_UNPACK_FILENAME);
 
   if (!(azopen(&writer, writer_filename, O_CREAT|O_RDWR|O_BINARY)))
-    DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); 
+  {
+    share->in_optimize= false;
+    DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
+  }
 
   /*
     Transfer the embedded FRM so that the file can be discoverable.
     Write file offset is set to the end of the file.
   */
   if ((rc= frm_copy(&archive, &writer)))
+  {
+    share->in_optimize= false;
     goto error;
-
+  }
   /* 
     An extended rebuild is a lot more effort. We open up each row and re-record it. 
     Any dead rows are removed (aka rows that may have been partially recorded). 
@@ -1541,75 +1562,86 @@ int ha_archive::optimize(THD* thd, HA_CH
     As of Archive format 3, this is the only type that is performed, before this
     version it was just done on T_EXTEND
   */
-  if (1)
-  {
-    DBUG_PRINT("ha_archive", ("archive extended rebuild"));
 
-    /*
-      Now we will rewind the archive file so that we are positioned at the 
-      start of the file.
-    */
-    rc= read_data_header(&archive);
+  DBUG_PRINT("ha_archive", ("archive extended rebuild"));
 
-    /* 
-      On success of writing out the new header, we now fetch each row and
-      insert it into the new archive file. 
-    */
-    if (!rc)
-    {
-      share->rows_recorded= 0;
-      stats.auto_increment_value= 1;
-      share->archive_write.auto_increment= 0;
-      my_bitmap_map *org_bitmap= tmp_use_all_columns(table, table->read_set);
+  /*
+    Now we will rewind the archive file so that we are positioned at the 
+    start of the file.
+  */
+  if ((rc= read_data_header(&archive)))
+  {
+    share->in_optimize= false;
+    goto error;
+  }
 
-      while (!(rc= get_row(&archive, table->record[0])))
-      {
-        real_write_row(table->record[0], &writer);
-        /*
-          Long term it should be possible to optimize this so that
-          it is not called on each row.
-        */
-        if (table->found_next_number_field)
-        {
-          Field *field= table->found_next_number_field;
-          ulonglong auto_value=
-            (ulonglong) field->val_int(table->record[0] +
-                                       field->offset(table->record[0]));
-          if (share->archive_write.auto_increment < auto_value)
-            stats.auto_increment_value=
-              (share->archive_write.auto_increment= auto_value) + 1;
-        }
-      }
+  stats.auto_increment_value= 1;
+  org_bitmap= tmp_use_all_columns(table, table->read_set);
+  /* read rows upto the remembered rows */ 
+  for (ha_rows cur_count= count; cur_count; cur_count--)
+  {
+    if ((rc= get_row(&archive, table->record[0])))
+      break;
+    real_write_row(table->record[0], &writer);
+    if (table->found_next_number_field)
+      save_auto_increment(table, &stats.auto_increment_value);
+  }
 
-      tmp_restore_column_map(table->read_set, org_bitmap);
-      share->rows_recorded= (ha_rows)writer.rows;
+  mysql_mutex_lock(&share->mutex);
+
+  if (share->archive_write_open)
+  {
+    if (share->archive_write.version == 1)
+      write_v1_metafile();
+    azclose(&share->archive_write);
+    share->archive_write_open= FALSE;
+  }
+  if (!rc)
+  {
+    /* read the remaining rows */
+    for (count= share->rows_recorded - count; count; count--)
+    {
+      if ((rc= get_row(&archive, table->record[0])))
+        break;
+      real_write_row(table->record[0], &writer);
+      if (table->found_next_number_field)
+        save_auto_increment(table, &stats.auto_increment_value);
     }
+  }
 
-    DBUG_PRINT("info", ("recovered %llu archive rows", 
-                        (unsigned long long)share->rows_recorded));
+  tmp_restore_column_map(table->read_set, org_bitmap);
+  share->rows_recorded= (ha_rows) writer.rows;
+  share->archive_write.auto_increment= stats.auto_increment_value - 1;
+  DBUG_PRINT("info", ("recovered %llu archive rows", 
+                      (unsigned long long)share->rows_recorded));
 
-    DBUG_PRINT("ha_archive", ("recovered %llu archive rows", 
-                        (unsigned long long)share->rows_recorded));
+  DBUG_PRINT("ha_archive", ("recovered %llu archive rows", 
+                      (unsigned long long)share->rows_recorded));
 
-    /*
-      If REPAIR ... EXTENDED is requested, try to recover as much data
-      from data file as possible. In this case if we failed to read a
-      record, we assume EOF. This allows massive data loss, but we can
-      hardly do more with broken zlib stream. And this is the only way
-      to restore at least what is still recoverable.
-    */
-    if (rc && rc != HA_ERR_END_OF_FILE && !(check_opt->flags & T_EXTEND))
-      goto error;
-  } 
+  /*
+    If REPAIR ... EXTENDED is requested, try to recover as much data
+    from data file as possible. In this case if we failed to read a
+    record, we assume EOF. This allows massive data loss, but we can
+    hardly do more with broken zlib stream. And this is the only way
+    to restore at least what is still recoverable.
+  */
+  if (rc && rc != HA_ERR_END_OF_FILE && !(check_opt->flags & T_EXTEND))
+  {
+    share->in_optimize= false;
+    mysql_mutex_unlock(&share->mutex);
+    goto error;
+  }
 
   azclose(&writer);
   share->dirty= FALSE;
-  
-  azclose(&archive);
 
   // make the file we just wrote be our data file
   rc= my_rename(writer_filename, share->data_file_name, MYF(0));
+  share->in_optimize= false;
+  mysql_mutex_unlock(&share->mutex);
 
+  azclose(&archive);
+  archive_reader_open= FALSE;
 
   DBUG_RETURN(rc);
 error:

=== modified file 'storage/archive/ha_archive.h'
--- a/storage/archive/ha_archive.h	2011-10-07 06:03:14 +0000
+++ b/storage/archive/ha_archive.h	2012-02-03 14:37:46 +0000
@@ -35,6 +35,7 @@ typedef struct st_archive_share {
   mysql_mutex_t mutex;
   THR_LOCK lock;
   azio_stream archive_write;     /* Archive file we are working with */
+  bool in_optimize;
   bool archive_write_open;
   bool dirty;               /* Flag for if a flush should occur */
   bool crashed;             /* Meta file is crashed */

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-trunk branch (ashish.y.agarwal:3837 to 3838) Bug#11748748Ashish Agarwal6 Feb