List:Commits« Previous MessageNext Message »
From:Georgi Kodinov Date:August 17 2011 2:34pm
Subject:bzr push into mysql-trunk branch (Georgi.Kodinov:3379 to 3380) Bug#11746029
View as plain text  
 3380 Georgi Kodinov	2011-08-17
      Bug #11746029: 22615: MYSQL SERVER INCORRECTLY CATOGORIZES THE LOST+FOUND DIRECTORY AS A DATABA
      
      Implemented an --ignore-db-dir option that takes a valid file name or an empty string.
      You can specify multiple options.
      When a non-empty string is specified it's added as a directory name to the list of directories to ignore 
      when executing SHOW commands and filling in INFORMATION_SCHEMA tables.
      When an empty string is specified as an argument the list is reset to empty.
      Note that the above list of ignored directories doesn't affect other SQL commands like e.g. USE or SELECT etc.
      Added a read-only global @@ignore_db_dirs system variable that will contain the current list in effect.
      
      In addition to the list above the server will always ignore the directories starting with a dot (.). This is safe 
      because since mysql-5.1 a name of a database directory can't start with a dot (.) thanks to the fact that we
      encode database and table names.
      
      Added a test case.
      Fixed the affected test cases.
      Fixed the memory handling when converting the directories array to a hash fails for some reason.

    added:
      mysql-test/suite/sys_vars/r/ignore_db_dirs_basic.result
      mysql-test/suite/sys_vars/t/ignore_db_dirs_basic-master.opt
      mysql-test/suite/sys_vars/t/ignore_db_dirs_basic.test
    modified:
      mysql-test/r/mysqld--help-notwin.result
      mysql-test/r/mysqld--help-win.result
      sql/mysqld.cc
      sql/mysqld.h
      sql/sql_show.cc
      sql/sql_show.h
      sql/sys_vars.cc
 3379 Jimmy Yang	2011-08-17 [merge]
      Merge from mysql-5.5 to mysql-trunk

    modified:
      mysql-test/suite/innodb/r/innodb_corrupt_bit.result
      storage/innobase/handler/ha_innodb.cc
=== modified file 'mysql-test/r/mysqld--help-notwin.result'
--- a/mysql-test/r/mysqld--help-notwin.result	2011-08-15 08:58:05 +0000
+++ b/mysql-test/r/mysqld--help-notwin.result	2011-08-17 14:33:45 +0000
@@ -192,6 +192,10 @@ The following options may be given as th
  -?, --help          Display this help and exit.
  --ignore-builtin-innodb 
  Disable initialization of builtin InnoDB plugin
+ --ignore-db-dir=name 
+ Specifies a directory to add to the ignore list when
+ collecting database names from the datadir. Put a blank
+ argument to reset the list accumulated so far.
  --init-connect=name Command(s) that are executed for each new connection
  --init-file=name    Read SQL commands from this file at startup
  --init-slave=name   Command(s) that are executed by a slave server each time

=== modified file 'mysql-test/r/mysqld--help-win.result'
--- a/mysql-test/r/mysqld--help-win.result	2011-08-15 08:58:05 +0000
+++ b/mysql-test/r/mysqld--help-win.result	2011-08-17 14:33:45 +0000
@@ -193,6 +193,10 @@ The following options may be given as th
  --ignore-builtin-innodb 
  Disable initialization of builtin InnoDB plugin
  --init-connect=name Command(s) that are executed for each new connection
+ --ignore-db-dir=name 
+ Specifies a directory to add to the ignore list when
+ collecting database names from the datadir. Put a blank
+ argument to reset the list accumulated so far.
  --init-file=name    Read SQL commands from this file at startup
  --init-slave=name   Command(s) that are executed by a slave server each time
  the SQL thread starts

=== added file 'mysql-test/suite/sys_vars/r/ignore_db_dirs_basic.result'
--- a/mysql-test/suite/sys_vars/r/ignore_db_dirs_basic.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/r/ignore_db_dirs_basic.result	2011-08-17 14:33:45 +0000
@@ -0,0 +1,18 @@
+select @@ignore_db_dirs;
+@@ignore_db_dirs
+e,lost+found,.mysqlgui,test
+# Check that SHOW DATABASES ignores all directories from
+# @@ignore_db_dirs and all directories with names starting
+# with '.'
+SHOW DATABASES;
+Database
+information_schema
+mtr
+mysql
+performance_schema
+SET @@global.ignore_db_dirs = 'aha';
+ERROR HY000: Variable 'ignore_db_dirs' is a read only variable
+SET @@local.ignore_db_dirs = 'aha';
+ERROR HY000: Variable 'ignore_db_dirs' is a read only variable
+SET @@ignore_db_dirs = 'aha';
+ERROR HY000: Variable 'ignore_db_dirs' is a read only variable

=== added file 'mysql-test/suite/sys_vars/t/ignore_db_dirs_basic-master.opt'
--- a/mysql-test/suite/sys_vars/t/ignore_db_dirs_basic-master.opt	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/t/ignore_db_dirs_basic-master.opt	2011-08-17 14:33:45 +0000
@@ -0,0 +1,11 @@
+--ignore-db-dir=a
+--ignore-db-dir=b
+--ignore-db-dir=c
+--ignore-db-dir=
+--ignore-db-dir=d
+--ignore-db-dir x
+--ignore-db-dir=
+--ignore-db-dir=e
+--ignore-db-dir=lost+found
+--ignore-db-dir=.mysqlgui
+--ignore-db-dir=test

=== added file 'mysql-test/suite/sys_vars/t/ignore_db_dirs_basic.test'
--- a/mysql-test/suite/sys_vars/t/ignore_db_dirs_basic.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/t/ignore_db_dirs_basic.test	2011-08-17 14:33:45 +0000
@@ -0,0 +1,20 @@
+select @@ignore_db_dirs;
+let $MYSQLD_DATADIR= `select @@datadir`;
+
+mkdir $MYSQLD_DATADIR/.mysqlgui;
+mkdir $MYSQLD_DATADIR/.otherdir;
+mkdir $MYSQLD_DATADIR/lost+found;
+--echo # Check that SHOW DATABASES ignores all directories from
+--echo # @@ignore_db_dirs and all directories with names starting
+--echo # with '.'
+SHOW DATABASES;
+rmdir $MYSQLD_DATADIR/.mysqlgui;
+rmdir $MYSQLD_DATADIR/.otherdir;
+rmdir $MYSQLD_DATADIR/lost+found;
+
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SET @@global.ignore_db_dirs = 'aha';
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SET @@local.ignore_db_dirs = 'aha';
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SET @@ignore_db_dirs = 'aha';

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2011-08-16 16:30:11 +0000
+++ b/sql/mysqld.cc	2011-08-17 14:33:45 +0000
@@ -1450,6 +1450,7 @@ void clean_up(bool print_message)
 #endif
   my_tz_free();
   my_dboptions_cache_free();
+  ignore_db_dirs_free();
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
   servers_free(1);
   acl_free(1);
@@ -3267,6 +3268,9 @@ int init_common_variables()
       mysql_init_variables())
     return 1;
 
+  if (ignore_db_dirs_init())
+    return 1;
+
 #ifdef HAVE_TZNAME
   {
     struct tm tm_tmp;
@@ -3703,6 +3707,12 @@ You should consider changing lower_case_
     return 1;
   }
 
+  if (ignore_db_dirs_process_additions())
+  {
+    sql_print_error("An error occurred while storing ignore_db_dirs to a hash.");
+    return 1;
+  }
+
   return 0;
 }
 
@@ -6206,6 +6216,11 @@ struct my_option my_long_options[]=
    &opt_super_large_pages, &opt_super_large_pages, 0,
    GET_BOOL, OPT_ARG, 0, 0, 1, 0, 1, 0},
 #endif
+  {"ignore-db-dir", OPT_IGNORE_DB_DIRECTORY,
+   "Specifies a directory to add to the ignore list when collecting "
+   "database names from the datadir. Put a blank argument to reset "
+   "the list accumulated so far.", 0, 0, 0, GET_STR, REQUIRED_ARG, 
+   0, 0, 0, 0, 0, 0},
   {"language", 'L',
    "Client error messages in given language. May be given as a full path. "
    "Deprecated. Use --lc-messages-dir instead.",
@@ -7670,6 +7685,22 @@ mysqld_get_one_option(int optid,
     if (argument == NULL) /* no argument */
       log_error_file_ptr= const_cast<char*>("");
     break;
+
+  case OPT_IGNORE_DB_DIRECTORY:
+    if (*argument == 0)
+      ignore_db_dirs_reset();
+    else
+    {
+      if (push_ignored_db_dir(argument))
+      {
+        sql_print_error("Can't start server: "
+                        "cannot process --ignore-db-dir=%.*s", 
+                        FN_REFLEN, argument);
+        return 1;
+      }
+    }
+    break;
+
   }
   return 0;
 }

=== modified file 'sql/mysqld.h'
--- a/sql/mysqld.h	2011-07-25 15:13:06 +0000
+++ b/sql/mysqld.h	2011-08-17 14:33:45 +0000
@@ -507,6 +507,7 @@ enum options_mysqld
   OPT_DEBUG_SYNC_TIMEOUT,
   OPT_DELAY_KEY_WRITE_ALL,
   OPT_ISAM_LOG,
+  OPT_IGNORE_DB_DIRECTORY,
   OPT_KEY_BUFFER_SIZE,
   OPT_KEY_CACHE_AGE_THRESHOLD,
   OPT_KEY_CACHE_BLOCK_SIZE,

=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc	2011-08-15 09:44:00 +0000
+++ b/sql/sql_show.cc	2011-08-17 14:33:45 +0000
@@ -384,6 +384,225 @@ bool mysqld_show_privileges(THD *thd)
 }
 
 
+/** Hash of LEX_STRINGs used to search for ignored db directories. */
+static HASH ignore_db_dirs_hash;
+
+/** 
+  An array of LEX_STRING pointers to collect the options at 
+  option parsing time.
+*/
+static DYNAMIC_ARRAY ignore_db_dirs_array;
+
+/**
+  A value for the read only system variable to show a list of
+  ignored directories.
+*/
+char *opt_ignore_db_dirs= NULL;
+
+
+/**
+  Sets up the data structures for collection of directories at option
+  processing time.
+  We need to collect the directories in an array first, because
+  we need the character sets initialized before setting up the hash.
+
+  @return state
+  @retval TRUE  failed
+  @retval FALSE success
+*/
+
+bool
+ignore_db_dirs_init()
+{
+  return my_init_dynamic_array(&ignore_db_dirs_array, sizeof(LEX_STRING *),
+                               0, 0);
+}
+
+
+/**
+  Retrieves the key (the string itself) from the LEX_STRING hash members.
+
+  Needed by hash_init().
+
+  @param     data         the data element from the hash
+  @param out len_ret      Placeholder to return the length of the key
+  @param                  unused
+  @return                 a pointer to the key
+*/
+
+static uchar *
+db_dirs_hash_get_key(const uchar *data, size_t *len_ret,
+                     my_bool __attribute__((unused)))
+{
+  LEX_STRING *e= (LEX_STRING *) data;
+
+  *len_ret= e->length;
+  return (uchar *) e->str;
+}
+
+
+/**
+  Wrap a directory name into a LEX_STRING and push it to the array.
+
+  Called at option processing time for each --ignore-db-dir option.
+
+  @param    path  the name of the directory to push
+  @return state
+  @retval TRUE  failed
+  @retval FALSE success
+*/
+
+bool
+push_ignored_db_dir(char *path)
+{
+  LEX_STRING *new_elt;
+  char *new_elt_buffer;
+  size_t path_len= strlen(path);
+
+  if (!path_len || path_len >= FN_REFLEN)
+    return true;
+
+  // No need to normalize, it's only a directory name, not a path.
+  if (!my_multi_malloc(0,
+                       &new_elt, sizeof(LEX_STRING),
+                       &new_elt_buffer, path_len + 1,
+                       NullS))
+    return true;
+  new_elt->str= new_elt_buffer;
+  memcpy(new_elt_buffer, path, path_len);
+  new_elt_buffer[path_len]= 0;
+  new_elt->length= path_len;
+  return insert_dynamic(&ignore_db_dirs_array, &new_elt);
+}
+
+
+/**
+  Clean up the directory ignore options accumulated so far.
+
+  Called at option processing time for each --ignore-db-dir option
+  with an empty argument.
+*/
+
+void
+ignore_db_dirs_reset()
+{
+  LEX_STRING **elt;
+  while (NULL!= (elt= (LEX_STRING **) pop_dynamic(&ignore_db_dirs_array)))
+    if (elt && *elt)
+      my_free(*elt);
+}
+
+
+/**
+  Free the directory ignore option variables.
+
+  Called at server shutdown.
+*/
+
+void
+ignore_db_dirs_free()
+{
+  if (opt_ignore_db_dirs)
+    my_free(opt_ignore_db_dirs);
+  ignore_db_dirs_reset();
+  delete_dynamic(&ignore_db_dirs_array);
+  my_hash_free(&ignore_db_dirs_hash);
+}
+
+
+/**
+  Initialize the ignore db directories hash and status variable from
+  the options collected in the array.
+
+  Called when option processing is over and the server's in-memory 
+  structures are fully initialized.
+
+  @return state
+  @retval TRUE  failed
+  @retval FALSE success
+*/
+
+bool
+ignore_db_dirs_process_additions()
+{
+  ulong i;
+  size_t len;
+  char *ptr;
+  LEX_STRING *dir;
+
+  DBUG_ASSERT(opt_ignore_db_dirs == NULL);
+
+  if (my_hash_init(&ignore_db_dirs_hash, 
+                   lower_case_table_names ?
+                     character_set_filesystem : &my_charset_bin,
+                   0, 0, 0, db_dirs_hash_get_key,
+                   my_free,
+                   HASH_UNIQUE))
+    return true;
+
+  /* len starts from 1 because of the terminating zero. */
+  len= 1;
+  for (i= 0; i < ignore_db_dirs_array.elements; i++)
+  {
+    get_dynamic(&ignore_db_dirs_array, (uchar *) &dir, i);
+    len+= dir->length + 1;                      // +1 for the comma
+  }
+
+  /* No delimiter for the last directory. */
+  if (len > 1)
+    len--;
+
+  ptr= opt_ignore_db_dirs= (char *) my_malloc(len, MYF(0));
+  if (!ptr)
+    return true;
+
+  /* Make sure we have an empty string to start with. */
+  *ptr= 0;
+
+  for (i= 0; i < ignore_db_dirs_array.elements; i++)
+  {
+    get_dynamic(&ignore_db_dirs_array, (uchar *) &dir, i);
+    if (my_hash_insert(&ignore_db_dirs_hash, (uchar *) dir))
+      return true;
+    ptr= strnmov(ptr, dir->str, dir->length);
+    if (i + 1 < ignore_db_dirs_array.elements)
+      ptr= strmov(ptr, ",");
+
+    /*
+      Set the transferred array element to NULL to avoid double free
+      in case of error.
+    */
+    dir= NULL;
+    set_dynamic(&ignore_db_dirs_array, (uchar *) &dir, i);
+  }
+
+  /* 
+    It's OK to empty the array here as the allocated elements are
+    referenced through the hash now.
+  */
+  reset_dynamic(&ignore_db_dirs_array);
+
+  return false;
+}
+
+
+/**
+  Check if a directory name is in the hash of ignored directories.
+
+  @return search result
+  @retval TRUE  found
+  @retval FALSE not found
+*/
+
+static inline bool
+is_in_ignore_db_dirs_list(const char *directory)
+{
+  return ignore_db_dirs_hash.records &&
+    NULL != my_hash_search(&ignore_db_dirs_hash, (const uchar *) directory, 
+                           strlen(directory));
+}
+
+
 /*
   find_files() - find files in a given directory.
 
@@ -409,11 +628,7 @@ find_files(THD *thd, List<LEX_STRING> *f
            const char *path, const char *wild, bool dir)
 {
   uint i;
-  char *ext;
   MY_DIR *dirp;
-  FILEINFO *file;
-  LEX_STRING *file_name= 0;
-  uint file_name_len;
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
   uint col_access=thd->col_access;
 #endif
@@ -445,15 +660,23 @@ find_files(THD *thd, List<LEX_STRING> *f
   for (i=0 ; i < (uint) dirp->number_off_files  ; i++)
   {
     char uname[NAME_LEN + 1];                   /* Unencoded name */
+    FILEINFO *file;
+    LEX_STRING *file_name= 0;
+    uint file_name_len;
+    char *ext;
+
     file=dirp->dir_entry+i;
     if (dir)
     {                                           /* Return databases */
-      if ((file->name[0] == '.' && 
-          ((file->name[1] == '.' && file->name[2] == '\0') ||
-            file->name[1] == '\0')))
-        continue;                               /* . or .. */
+      /*
+        Ignore all the directories having names that start with a  dot (.).
+        This covers '.' and '..' and other cases like e.g. '.mysqlgui'.
+        Note that since 5.1 database directory names can't start with a
+        dot (.) thanks to table name encoding.
+      */
+      if (file->name[0]  == '.')
+        continue;
 #ifdef USE_SYMDIR
-      char *ext;
       char buff[FN_REFLEN];
       if (my_use_symdir && !strcmp(ext=fn_ext(file->name), ".sym"))
       {
@@ -471,20 +694,9 @@ find_files(THD *thd, List<LEX_STRING> *f
       if (!MY_S_ISDIR(file->mystat->st_mode))
         continue;
 
-      file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
-      if (wild)
-      {
-	if (lower_case_table_names)
-	{
-          if (my_wildcmp(files_charset_info,
-                         uname, uname + file_name_len,
-                         wild, wild + wild_length,
-                         wild_prefix, wild_one,wild_many))
-            continue;
-	}
-	else if (wild_compare(uname, wild, 0))
-	  continue;
-      }
+      if (is_in_ignore_db_dirs_list(file->name))
+        continue;
+
     }
     else
     {
@@ -493,21 +705,24 @@ find_files(THD *thd, List<LEX_STRING> *f
           is_prefix(file->name, tmp_file_prefix))
         continue;
       *ext=0;
-      file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
-      if (wild)
+    }
+
+    file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
+
+    if (wild)
+    {
+      if (lower_case_table_names)
       {
-	if (lower_case_table_names)
-	{
-          if (my_wildcmp(files_charset_info,
-                         uname, uname + file_name_len,
-                         wild, wild + wild_length,
-                         wild_prefix, wild_one,wild_many))
-            continue;
-	}
-	else if (wild_compare(uname, wild, 0))
-	  continue;
+        if (my_wildcmp(files_charset_info,
+                       uname, uname + file_name_len,
+                       wild, wild + wild_length,
+                       wild_prefix, wild_one,wild_many))
+          continue;
       }
+      else if (wild_compare(uname, wild, 0))
+        continue;
     }
+
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
     /* Don't show tables where we don't have any privileges */
     if (db && !(col_access & TABLE_ACLS))

=== modified file 'sql/sql_show.h'
--- a/sql/sql_show.h	2011-06-30 15:50:45 +0000
+++ b/sql/sql_show.h	2011-08-17 14:33:45 +0000
@@ -131,4 +131,12 @@ enum enum_schema_tables get_schema_table
 /* These functions were under INNODB_COMPATIBILITY_HOOKS */
 int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
 
+/* Handle the ignored database directories list for SHOW/I_S. */
+bool ignore_db_dirs_init();
+void ignore_db_dirs_free();
+void ignore_db_dirs_reset();
+bool ignore_db_dirs_process_additions();
+bool push_ignored_db_dir(char *path);
+extern char *opt_ignore_db_dirs;
+
 #endif /* SQL_SHOW_H */

=== modified file 'sql/sys_vars.cc'
--- a/sql/sys_vars.cc	2011-07-29 09:55:15 +0000
+++ b/sql/sys_vars.cc	2011-08-17 14:33:45 +0000
@@ -51,6 +51,7 @@
                      // mysql_user_table_is_in_short_password_format
 #include "derror.h"  // read_texts
 #include "sql_base.h"                           // close_cached_tables
+#include "sql_show.h"                           // opt_ignore_db_dirs
 
 #ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
 #include "../storage/perfschema/pfs_server.h"
@@ -3486,3 +3487,9 @@ static Sys_var_tz Sys_time_zone(
        SESSION_VAR(time_zone), NO_CMD_LINE,
        DEFAULT(&default_tz), NO_MUTEX_GUARD, IN_BINLOG);
 
+static Sys_var_charptr Sys_ignore_db_dirs(
+       "ignore_db_dirs",
+       "The list of directories to ignore when collecting database lists",
+       READ_ONLY GLOBAL_VAR(opt_ignore_db_dirs), 
+       NO_CMD_LINE,
+       IN_FS_CHARSET, DEFAULT(0));

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-trunk branch (Georgi.Kodinov:3379 to 3380) Bug#11746029Georgi Kodinov22 Aug