List:Commits« Previous MessageNext Message »
From:Andrei Elkin Date:September 14 2007 5:31pm
Subject:Re: bk commit into 5.2 tree (rafal:1.2601) BUG#30938
View as plain text  
Rafal, hello.


I find that the patch addresses the reported problem.
This part is okay.

I believe that other parts dealing with some refactoring should be
okay.
However, I can not be certain as I really could not undestand some of
logics (questions below).
Besides that concern, I'd like you to notice that a list's elements in
mysql style are to be separated by comma+space not just by comma.
Please fix that.

> Below is the list of changes that have just been committed into a local
> 5.2 repository of rafal. When rafal 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, 2007-09-12 20:36:58+02:00, rafal@quant.(none) +8 -0
>   BUG#30938 (BACKUP DATABASE crashes server if a table with a unloaded 
>   SE exists).
>   
>   Backup of a database failed if it contained a table using invalid storage
>   engine (e.g. plugin not loaded or unknown engine). This was because code
>   collecting list of tables in a database didn't exclude tables without a 
>   valid storage engine and later kernel attempted to open such tables. This
>   patch fixes this by filtering out tables for which 'engine' field in 
>   I_S.TABLES is NULL.
>
>   mysql-test/r/backup_no_engine.result@stripped, 2007-09-12 20:36:52+02:00,
> rafal@quant.(none) +30 -0
>     Results for the test.
>
>   mysql-test/r/backup_no_engine.result@stripped, 2007-09-12 20:36:52+02:00,
> rafal@quant.(none) +0 -0
>
>   mysql-test/std_data/bug30938.frm@stripped, 2007-09-12 20:36:52+02:00, rafal@quant.(none)
> +201 -0
>     .frm file with table which uses no-existing engine. Used in
> backup_no_engine.test
>
>   mysql-test/std_data/bug30938.frm@stripped, 2007-09-12 20:36:52+02:00, rafal@quant.(none)
> +0 -0
>
>   mysql-test/t/backup_no_engine.test@stripped, 2007-09-12 20:36:52+02:00,
> rafal@quant.(none) +35 -0
>     Test that backup works if database contains a table with invalid storage 
>     engine.
>
>   mysql-test/t/backup_no_engine.test@stripped, 2007-09-12 20:36:52+02:00,
> rafal@quant.(none) +0 -0
>
>   sql/backup/api_types.h@stripped, 2007-09-12 20:36:51+02:00, rafal@quant.(none) +3 -0
>     Added another describe() method which checks buffer size using its type.
>
>   sql/backup/backup_aux.h@stripped, 2007-09-12 20:36:51+02:00, rafal@quant.(none) +6 -0

>     Added != operator for Strings.

That's nice.
I strongly recommend to move it in sql_string.h.

>
>   sql/backup/backup_kernel.h@stripped, 2007-09-12 20:36:52+02:00, rafal@quant.(none) +4
> -1
>     Changed signature of Backup_info::add_table().
>
>   sql/backup/sql_backup.cc@stripped, 2007-09-12 20:36:52+02:00, rafal@quant.(none) +121
> -74

>     - Adding to Backup_info::Table_ref methods for opening and closing the 
>       table.

why? Is it needed for the bug. If not, please leave comments
justifying this refactoring.

>     - Change code which reads database or table list from I_S tables. For
>       each record, first interesting fields are read and examined to 
>       determine if it should be skipped or not. Then Db_ref or Table_ref
>       instance is created using data read from the record.
>     - When scanning tables in a database, skip tables with undefined storage
>       engine. Produce warning in error log.
>     - Handle the situation when Backup_info::add_table() informs that the
>       table should be skipped.
>     - Implement open()/close() methods for Backup_info::Table_ref.
>
>   sql/share/errmsg.txt@stripped, 2007-09-12 20:36:52+02:00, rafal@quant.(none) +4 -0

>     Add new messages.

I wonder about one of them, below.

>
> diff -Nrup a/mysql-test/r/backup_no_engine.result
> b/mysql-test/r/backup_no_engine.result
> --- /dev/null	Wed Dec 31 16:00:00 196900
> +++ b/mysql-test/r/backup_no_engine.result	2007-09-12 20:36:52 +02:00
> @@ -0,0 +1,30 @@
> +DROP DATABASE IF EXISTS db;
> +CREATE DATABASE db;
> +CREATE TABLE db.t1 (a int, b char(32))
> +ENGINE=myisam;
> +BACKUP DATABASE db TO "db.backup";
> +Backup Summary
> + header     =       12 bytes
> + meta-data  =      120 bytes
> + data       =        0 bytes
> +              --------------
> + total             132 bytes
> +DROP DATABASE db;
> +CREATE DATABASE db;
> +RESTORE FROM "db.backup";
> +Restore Summary
> + header     =       12 bytes
> + meta-data  =      120 bytes
> + data       =        0 bytes
> +              --------------
> + total             132 bytes
> +SHOW TABLES IN db;
> +Tables_in_db
> +t1
> +SHOW CREATE TABLE db.t1;
> +Table	Create Table
> +t1	CREATE TABLE `t1` (
> +  `a` int(11) DEFAULT NULL,
> +  `b` char(32) DEFAULT NULL
> +) ENGINE=MyISAM DEFAULT CHARSET=latin1
> +DROP DATABASE db;
> Binary files a/mysql-test/std_data/bug30938.frm and
> b/mysql-test/std_data/bug30938.frm differ
> diff -Nrup a/mysql-test/t/backup_no_engine.test b/mysql-test/t/backup_no_engine.test
> --- /dev/null	Wed Dec 31 16:00:00 196900
> +++ b/mysql-test/t/backup_no_engine.test	2007-09-12 20:36:52 +02:00
> @@ -0,0 +1,35 @@
> +# Test how online backup handles tables using non-existent storage engines
> +# The server used to crash in this situation (BUG#30938)
> +
> +# .frm file for a table using non-existent storage engine
> +--let $table_def=$MYSQL_TEST_DIR/std_data/bug30938.frm
> +--let $backup_out_path=$MYSQLTEST_VARDIR/master-data
> +
> +--disable_warnings
> +DROP DATABASE IF EXISTS db;
> +--enable_warnings
> +
> +CREATE DATABASE db;
> +CREATE TABLE db.t1 (a int, b char(32))
> +ENGINE=myisam;
> +
> +# copy description of a table using non-existent storage engine
> +--copy_file $table_def $MYSQLTEST_VARDIR/master-data/db/t2.frm
> +
> +# backup test database, the table with no storage engine will be ignored
> +BACKUP DATABASE db TO "db.backup";
> +
> +# wipe-out backed-up database
> +DROP DATABASE db;
> +CREATE DATABASE db;
> +
> +RESTORE FROM "db.backup";
> +
> +# check that the table with no engine was excluded
> +SHOW TABLES IN db;
> +# check that table t was correctly restored
> +SHOW CREATE TABLE db.t1;	
> +
> +# clean up
> +DROP DATABASE db;
> +--remove_file $backup_out_path/db.backup
> diff -Nrup a/sql/backup/api_types.h b/sql/backup/api_types.h
> --- a/sql/backup/api_types.h	2007-06-28 16:34:48 +02:00
> +++ b/sql/backup/api_types.h	2007-09-12 20:36:51 +02:00
> @@ -136,6 +136,9 @@ class Table_ref
>      return buf;
>    }
>  
> +  const char* describe(describe_buf &buf) const
> +  { return describe(buf,sizeof(buf)); }
> +  
>   protected:
>  
>    /*
> diff -Nrup a/sql/backup/backup_aux.h b/sql/backup/backup_aux.h
> --- a/sql/backup/backup_aux.h	2007-07-02 19:42:57 +02:00
> +++ b/sql/backup/backup_aux.h	2007-09-12 20:36:51 +02:00
> @@ -9,6 +9,12 @@ bool operator==(const String &s1, const 
>    return stringcmp(&s1,&s2) == 0;
>  }
>  
> +inline
> +bool operator!=(const String &s1, const String &s2)
> +{
> +  return !(s1 == s2);
> +}
> +
>  namespace backup {
>  
>  /**
> diff -Nrup a/sql/backup/backup_kernel.h b/sql/backup/backup_kernel.h
> --- a/sql/backup/backup_kernel.h	2007-08-20 10:37:06 +02:00
> +++ b/sql/backup/backup_kernel.h	2007-09-12 20:36:52 +02:00
> @@ -153,7 +153,10 @@ class Backup_info: public Archive_info, 
>    int snapshot_image_no; ///< Position of the snapshot image in @c images list,
> -1 if not used.
>  
>    Db_item*    add_db(const backup::Db_ref&);
> -  Table_item* add_table(Db_item&, const Table_ref&);
> +  Table_item* add_table(const Table_ref&);
> +  
> +  /// Value returned by @c add_table if it decides that the table should be
> skipped.
> +  static const Table_item *const skip_table;
>  
>    int add_db_items(Db_item&);
>    int add_table_items(Table_item&);
> diff -Nrup a/sql/backup/sql_backup.cc b/sql/backup/sql_backup.cc
> --- a/sql/backup/sql_backup.cc	2007-09-04 09:40:54 +02:00
> +++ b/sql/backup/sql_backup.cc	2007-09-12 20:36:52 +02:00
> @@ -324,13 +324,22 @@ class Backup_info::Table_ref:
>  {
>    String  m_db_name;
>    String  m_name;
> -  const ::handlerton   *m_hton;
> +  ::TABLE *m_table;
>  
>   public:
>  
> -  Table_ref(TABLE*);
> -  const ::handlerton *hton() const
> -  { return m_hton; }
> +  Table_ref(const Db_item&, const String&);
> +  ~Table_ref()
> +  { close(); }
> +  
> +  ::TABLE* open(THD*);
> +  void close();
> +  
> +  ::handlerton* hton() const
> +  { return m_table && m_table->s ? m_table->s->db_type() : NULL; }
> +  
> +  bool is_open() const 
> +  { return m_table != NULL; }
>  };
>  
>  /**
> @@ -363,20 +372,22 @@ class Backup_info::Table_ref:
>  int Backup_info::find_image(const Backup_info::Table_ref &tbl)
>  {
>     DBUG_ENTER("Backup_info::find_image");
> -
> -   const ::handlerton *hton= tbl.hton();
> +   // we assume that table has been opened already
> +   DBUG_ASSERT(tbl.is_open());
> +   
>     Image_info *img;
> -
> +   const ::handlerton *hton= tbl.hton();
>     // If table has no handlerton something is really bad - we crash here
>     DBUG_ASSERT(hton);
>  
> -   DBUG_PRINT("backup",("Adding table %s.%s using storage %s to archive%s",
> -                        tbl.db().name().ptr(),
> -                        tbl.name().ptr(),
> +   Table_ref::describe_buf buf;
> +   
> +   DBUG_PRINT("backup",("Adding table %s using storage %s to archive%s",
> +                        tbl.describe(buf),
>                          ::ha_resolve_storage_engine_name(hton),
>                          hton->get_backup_engine ? " (has native backup)." :
> "."));
>  
> -   // Point 3: try existing images but not the default one.
> +   // Point 3: try existing images but not the default or snapshot one.
>  
>     for (uint no=0; no < img_count && no < MAX_IMAGES ; ++no)
>     {
> @@ -457,7 +468,6 @@ int Backup_info::find_image(const Backup
>     if (img->accept(tbl,hton))
>       DBUG_RETURN(ino); // table accepted
>  
> -   Table_ref::describe_buf buf;
>     report_error(ER_BACKUP_NO_BACKUP_DRIVER,tbl.describe(buf,sizeof(buf)));
>     DBUG_RETURN(-1);
>  }
> @@ -476,6 +486,14 @@ namespace backup {
>  //  Returns tmp table containing records from a given I_S table
>  TABLE* get_schema_table(THD *thd, ST_SCHEMA_TABLE *st);
>  
> +/* 
> +  Backup_info::skip_table pointer is just for indicating that a table
> +  added with Backup_info::add_table() was skipped. It should have value not 
> +  possible for regular pointers.
> + */
> +const Backup_info::Table_item *const 
> +Backup_info::skip_table= reinterpret_cast<Backup_info::Table_item*>(1);
> +
>  /**
>    Create @c Backup_info structure and prepare it for populating with meta-data
>    items.
> @@ -502,7 +520,6 @@ Backup_info::Backup_info(THD *thd):
>  Backup_info::~Backup_info()
>  {
>    close();
> -  //close_tables();
>    m_state= DONE;
>  
>    Item_iterator it(*this);
> @@ -550,8 +567,7 @@ int Backup_info::save(OStream &s)
>  }
>  
>  /**
> -  Specialization of @c Db_ref which takes db name from @c LEX_STRING structure
> -  or reads it from INFORMATION_SCHEMA.SCHEMATA table.
> +  Specialization of @c backup::Db_ref with convenient constructors.
>   */
>  
>  class Backup_info::Db_ref: public backup::Db_ref
> @@ -560,18 +576,15 @@ class Backup_info::Db_ref: public backup
>  
>   public:
>  
> +  Db_ref(const String &s): backup::Db_ref(m_name), m_name(s)
> +  {}
> +
>    Db_ref(const LEX_STRING &s):
>      backup::Db_ref(m_name),
>      m_name(s.str,s.length,::system_charset_info)
>    {}
> -
> -  Db_ref(TABLE *t): backup::Db_ref(m_name)
> -  {
> -    t->field[1]->val_str(&m_name);
> -  }
>  };
>  
> -
>  /**
>    Add to archive all databases in the list.
>   */
> @@ -620,9 +633,6 @@ int Backup_info::add_dbs(List< ::LEX_STR
>    return backup::ERROR;
>  }
>  
> -static const LEX_STRING is_string = "information_schema";
> -static const LEX_STRING mysql_string = "mysql";
> -
>  /**
>    Add to archive all instance's databases (except the internal ones).
>   */
> @@ -645,8 +655,6 @@ int Backup_info::add_all_dbs()
>  
>    // TODO: locking
>  
> -  const Db_ref is_db(is_string);
> -  const Db_ref mysql_db(mysql_string);
>    int res= 0;
>  
>    old_map= dbug_tmp_use_all_columns(db_table, db_table->read_set);
> @@ -660,14 +668,20 @@ int Backup_info::add_all_dbs()
>  
>    while (!ha->rnd_next(db_table->record[0]))
>    {
> -    Db_ref db(db_table);
> -
> -    if (db==is_db || db==mysql_db)
> +    String db_name;
> +    
> +    db_table->field[1]->val_str(&db_name);
> +    
> +    // skip internal databases
> +    if (db_name == String("information_schema",&::my_charset_bin) 
> +        || db_name == String("mysql",&my_charset_bin))
>      {
> -      DBUG_PRINT("backup",(" Skipping internal database %s",db.name().ptr()));
> +      DBUG_PRINT("backup",(" Skipping internal database %s",db_name.ptr()));
>        continue;
>      }
>  
> +    Db_ref db(db_name);
> +
>      DBUG_PRINT("backup", (" Found database %s", db.name().ptr()));
>  
>      Db_item *it= add_db(db);
> @@ -782,48 +796,62 @@ int Backup_info::add_db_items(Db_item &d
>  
>    while (!ha->rnd_next(i_s_tables->record[0]))
>    {
> -    /*
> -      Check if it is table or view.
> -      
> -      FIXME: make it better when views are supported.
> -     */ 
> +    String db_name;
> +    String name;
>      String type;
> -    
> +    String engine;
> +
> +    /* 
> +      Read info about next table/view
> +      
> +      Note: this should be synchronized with the definition of 
> +      INFORMATION_SCHEMA.TABLES table.
> +     */
> +    i_s_tables->field[1]->val_str(&db_name);
> +    i_s_tables->field[2]->val_str(&name);
>      i_s_tables->field[3]->val_str(&type);
> +    i_s_tables->field[4]->val_str(&engine);
> +    
> +    if (db_name != db.name())
> +      continue; // skip tables not from the given database
>      
> -    if (type == String("VIEW",&::my_charset_bin))
> +    // FIXME: right now, we handle only tables
> +    if (type != String("BASE TABLE",&::my_charset_bin))
>      {
> -      String name, db_name;
> -      
> -      i_s_tables->field[1]->val_str(&db_name);
> -      i_s_tables->field[2]->val_str(&name);
>        report_error(log_level::WARNING,ER_BACKUP_SKIP_VIEW,
>                     name.c_ptr(),db_name.c_ptr());
>        continue;
>      }
> -    
> -    const Backup_info::Table_ref t(i_s_tables);
>  
> -    if (!t.hton())
> +    Backup_info::Table_ref t(db,name);
> +    
> +    if (engine.is_empty())
>      {
>        Table_ref::describe_buf buf;
> -      report_error(ER_NO_STORAGE_ENGINE,t.describe(buf,sizeof(buf)));
> -      goto error;
> +      report_error(log_level::WARNING,ER_BACKUP_NO_ENGINE,t.describe(buf));
> +      continue;                   
>      }
>  
> -    if (t.db() == db)
> +    DBUG_PRINT("backup", ("Found table %s for database %s",
> +                           t.name().ptr(), t.db().name().ptr()));
> +

As I understand your code creates a new temporary table (why - i was
asking above):

> +    if (!t.open(m_thd))
>      {
> -      DBUG_PRINT("backup", ("Found table %s for database %s",
> -                             t.name().ptr(), t.db().name().ptr()));
> +      Table_ref::describe_buf buf;

in which case the only possible error is out of memory.
However, the new error message is instead. 

> +      report_error(ER_BACKUP_TABLE_OPEN,t.describe(buf));

Why?

> +      goto error;
> +    }
> +    // add_table method selects/creates sub-image appropriate for storing given
> table
> +    Table_item *ti= add_table(t);
> +    
> +    if (!ti)
> +      goto error;
>  
> -      // add_table method selects/creates sub-image appropriate for storing given
> table
> -      Table_item *ti= add_table(db,t);
> -      if (!ti)
> -        goto error;
> +    if (ti == skip_table)
> +      continue;
>  
> -      if (add_table_items(*ti))
> -        goto error;
> -    }
> +    if (add_table_items(*ti))
> +      goto error;
>    }
>  
>    goto finish;
> @@ -841,34 +869,53 @@ int Backup_info::add_db_items(Db_item &d
>    return res;
>  }
>  
> -/// Gets table identity from a row of I_S.TABLES table
> +/* Implementation of Backup_info::Table_ref */
>  
> -Backup_info::Table_ref::Table_ref(TABLE *t):
> -  backup::Table_ref(m_db_name,m_name)
> +Backup_info::Table_ref::Table_ref(const Db_item &db, const String &name):
> +  backup::Table_ref(m_db_name,m_name), m_table(NULL)
>  {
> -  String engine_name;
> -  plugin_ref plugin;
> -
> -  t->field[1]->val_str(&m_db_name);
> -  t->field[2]->val_str(&m_name);
> -  t->field[4]->val_str(&engine_name);
> -
> -  LEX_STRING name_lex;
> +  m_db_name.append(db.name());
> +  m_name.append(name);
> +}
>  
> -  name_lex.str= const_cast<char*>(engine_name.ptr());
> -  name_lex.length= engine_name.length();
> +::TABLE* Backup_info::Table_ref::open(THD *thd)
> +{
> +  if (is_open())
> +    return m_table;
> +    
> +  char path[FN_REFLEN];
> +  const char *db= m_db_name.ptr();
> +  const char *name= m_name.ptr();
> +  ::build_table_filename(path, sizeof(path), db, name, "", 0);
> +  m_table= ::open_temporary_table(thd,path,db,name,FALSE /* don't link to
> thd->temporary_tables */);
> +  return m_table;
> +}
>  
> -  plugin= ::ha_resolve_by_name(::current_thd,&name_lex);
> -  m_hton= plugin ? plugin_data(plugin, handlerton*) : 0;
> +void Backup_info::Table_ref::close()
> +{
> +  if (m_table)
> +  {
> +    // TODO: check if ::free_tmp_table() is not better
> +    ::intern_close_table(m_table);
> +    ::my_free(m_table, MYF(0));
> +  }
> +  m_table= NULL;
> +  return;
>  }
>  
>  /**
> -  Add @c Table_item to archive's list of meta-data items.
> +  Add table to archive's list of meta-data items.
> +  
> +  @pre Table should be opened.
>   */
>  
>  Backup_info::Table_item*
> -Backup_info::add_table(Db_item&, const Table_ref &t)
> +Backup_info::add_table(const Table_ref &t)
>  {
> +  DBUG_ASSERT(t.is_open());
> +    
> +  // TODO: skip table if it is a tmp one
> +    
>    int no= find_image(t); // Note: find_image reports errors
>  
>    /*
> @@ -897,7 +944,7 @@ Backup_info::add_table(Db_item&, const T
>    if (tno < 0 || TEST_ERROR)
>    {
>      Table_ref::describe_buf buf;
> -   
> report_error(ER_BACKUP_NOT_ACCEPTED,img->name(),t.describe(buf,sizeof(buf)));
> +    report_error(ER_BACKUP_NOT_ACCEPTED,img->name(),t.describe(buf));
>      return NULL;
>    }
>  
> diff -Nrup a/sql/share/errmsg.txt b/sql/share/errmsg.txt
> --- a/sql/share/errmsg.txt	2007-08-26 08:11:37 +02:00
> +++ b/sql/share/errmsg.txt	2007-09-12 20:36:52 +02:00
> @@ -6132,6 +6132,10 @@ ER_BACKUP_LIST_DB_TABLES
>          eng "Can't enumerate tables in database %-.64s"
>  ER_BACKUP_SKIP_VIEW
>          eng "Skipping view %-.64s in database %-.64s"
> +ER_BACKUP_NO_ENGINE
> +				eng "Skipping table %-.64s since it has no valid storage engine"
> +ER_BACKUP_TABLE_OPEN
> +				eng "Can't open table %-.64s"
>  ER_BACKUP_READ_HEADER
>          eng "Can't read backup archive preamble"
>  ER_BACKUP_WRITE_HEADER
>
> -- 
> MySQL Code Commits Mailing List
> For list archives: http://lists.mysql.com/commits
> To unsubscribe:    http://lists.mysql.com/commits?unsub=1
>


regards,

Andrei

Thread
bk commit into 5.2 tree (rafal:1.2601) BUG#30938rsomla12 Sep
  • RE: bk commit into 5.2 tree (rafal:1.2601) BUG#30938Chuck Bell13 Sep
  • Re: bk commit into 5.2 tree (rafal:1.2601) BUG#30938Andrei Elkin14 Sep
    • Re: bk commit into 5.2 tree (rafal:1.2601) BUG#30938Rafal Somla1 Oct
      • Re: bk commit into 5.2 tree (rafal:1.2601) BUG#30938Andrei Elkin2 Oct