Commit b82094a0 authored by Gleb Shchepa's avatar Gleb Shchepa
Browse files

Bug #40021: Renaming view fails, archived .frm for view is

            missing after downgrade

Obsolete arc/ directory and view .frm file backup support
has been removed by the patch for bug 17823. However, that
bugfix caused a problem with "live downgrades" of the
server: if we rename some view 4 times under 5.1.29/5.0.72
and then try to rename it under 5.1.28/5.0.70 on the same
database, the server fails with a error:

  query 'RENAME TABLE ... TO ...' failed: 6: Error on
  delete of '....frm-0001' (Errcode: 2)

Also .frm file of that view may be lost (renamed to .frm~).

The server failed because it tried to rename latest 3
backup .frm files renaming the view: the server used an
integer value of the "revision" field of .frm file to
extract those file names. After the fix for bug 17823 those
files were not created/maintained any more, however the
"revision" field was incremented as usual. So, the server
failed renaming non existent files.

This fix solves the problem by removing the support for
"revision" .frm file field:
1. New server silently ignores existent "revision" fields
   in old .frm files and never write it down;
2. Old server assumes, that missing "revision" field in new
   .frm files means default value of 0.
3. Accordingly to the fix for bug 17823 the new server
   drops arc/ directory on alter/rename view, so after
   "live downgrade" old server begins maintenance of the
   arc/ directory from scratch without conflicts with .frm
   files.
parent ee3594b2
Loading
Loading
Loading
Loading
+4 −89
Original line number Diff line number Diff line
@@ -88,7 +88,6 @@ write_escaped_string(IO_CACHE *file, LEX_STRING *val_s)
    file	pointer to IO_CACHE structure for writing
    base	pointer to data structure
    parameter	pointer to parameter descriptor
    old_version	for returning back old version number value

  RETURN
    FALSE - OK
@@ -96,8 +95,7 @@ write_escaped_string(IO_CACHE *file, LEX_STRING *val_s)
*/

static my_bool
write_parameter(IO_CACHE *file, gptr base, File_option *parameter,
		ulonglong *old_version)
write_parameter(IO_CACHE *file, gptr base, File_option *parameter)
{
  char num_buf[20];			// buffer for numeric operations
  // string for numeric operations
@@ -125,15 +123,6 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter,
      DBUG_RETURN(TRUE);
    break;
  }
  case FILE_OPTIONS_REV:
  {
    ulonglong *val_i= (ulonglong *)(base + parameter->offset);
    *old_version= (*val_i)++;
    num.set(*val_i, &my_charset_bin);
    if (my_b_append(file, (const byte *)num.ptr(), num.length()))
      DBUG_RETURN(TRUE);
    break;
  }
  case FILE_OPTIONS_TIMESTAMP:
  {
    /* string have to be allocated already */
@@ -205,7 +194,6 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter,
    base		base address for parameter reading (structure like
			TABLE)
    parameters		parameters description
    max_versions	number of versions to save

  RETURN
    FALSE - OK
@@ -215,13 +203,11 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter,
my_bool
sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
			   const LEX_STRING *type,
			   gptr base, File_option *parameters,
			   uint max_versions)
			   gptr base, File_option *parameters)
{
  File handler;
  IO_CACHE file;
  char path[FN_REFLEN+1];	// +1 to put temporary file name for sure
  ulonglong old_version= ULONGLONG_MAX;
  int path_end;
  File_option *param;
  DBUG_ENTER("sql_create_definition_file");
@@ -255,7 +241,7 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
    if (my_b_append(&file, (const byte *)param->name.str,
                    param->name.length) ||
	my_b_append(&file, (const byte *)STRING_WITH_LEN("=")) ||
	write_parameter(&file, base, param, &old_version) ||
	write_parameter(&file, base, param) ||
	my_b_append(&file, (const byte *)STRING_WITH_LEN("\n")))
      goto err_w_cache;
  }
@@ -269,55 +255,6 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
  }

  path[path_end]='\0';
#ifdef FRM_ARCHIVE
  // archive copies management: disabled unused feature (see bug #17823).
  if (!access(path, F_OK))
  {
    if (old_version != ULONGLONG_MAX && max_versions != 0)
    {
      // save backup
      char path_arc[FN_REFLEN];
      // backup old version
      char path_to[FN_REFLEN];

      // check archive directory existence
      fn_format(path_arc, "arc", dir->str, "", MY_UNPACK_FILENAME);
      if (access(path_arc, F_OK))
      {
	if (my_mkdir(path_arc, 0777, MYF(MY_WME)))
	{
	  DBUG_RETURN(TRUE);
	}
      }

      my_snprintf(path_to, FN_REFLEN, "%s/%s-%04lu",
		  path_arc, file_name->str, (ulong) old_version);
      if (my_rename(path, path_to, MYF(MY_WME)))
      {
	DBUG_RETURN(TRUE);
      }

      // remove very old version
      if (old_version > max_versions)
      {
	my_snprintf(path_to, FN_REFLEN, "%s/%s-%04lu",
		    path_arc, file_name->str,
		    (ulong)(old_version - max_versions));
	if (!access(path_arc, F_OK) && my_delete(path_to, MYF(MY_WME)))
	{
	  DBUG_RETURN(TRUE);
	}
      }
    }
    else
    {
      if (my_delete(path, MYF(MY_WME)))	// no backups
      {
	DBUG_RETURN(TRUE);
      }
    }
  }
#endif//FRM_ARCHIVE

  {
    // rename temporary file
@@ -346,8 +283,6 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
    schema            name of given schema           
    old_name          original file name
    new_name          new file name
    revision          revision number
    num_view_backups  number of backups

  RETURN
    0 - OK 
@@ -356,8 +291,7 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
*/
my_bool rename_in_schema_file(THD *thd,
                              const char *schema, const char *old_name, 
                              const char *new_name, ulonglong revision, 
                              uint num_view_backups)
                              const char *new_name)
{
  char old_path[FN_REFLEN], new_path[FN_REFLEN], arc_path[FN_REFLEN];

@@ -376,23 +310,6 @@ my_bool rename_in_schema_file(THD *thd,
  strxnmov(arc_path, FN_REFLEN, mysql_data_home, "/", schema, "/arc", NullS);
  (void) unpack_filename(arc_path, arc_path);
  
#ifdef FRM_ARCHIVE
  if (revision > 0 && !access(arc_path, F_OK))
  {
    ulonglong limit= ((revision > num_view_backups) ?
                      revision - num_view_backups : 0);
    for (; revision > limit ; revision--)
    {
      my_snprintf(old_path, FN_REFLEN, "%s/%s%s-%04lu",
		  arc_path, old_name, reg_ext, (ulong)revision);
      (void) unpack_filename(old_path, old_path);
      my_snprintf(new_path, FN_REFLEN, "%s/%s%s-%04lu",
		  arc_path, new_name, reg_ext, (ulong)revision);
      (void) unpack_filename(new_path, new_path);
      my_rename(old_path, new_path, MYF(0));
    }
  }
#else//FRM_ARCHIVE
  { // remove obsolete 'arc' directory and files if any
    MY_DIR *new_dirp;
    if ((new_dirp = my_dir(arc_path, MYF(MY_DONT_SORT))))
@@ -401,7 +318,6 @@ my_bool rename_in_schema_file(THD *thd,
      (void) mysql_rm_arc_files(thd, new_dirp, arc_path);
    }
  }
#endif//FRM_ARCHIVE
  return 0;
}

@@ -838,7 +754,6 @@ File_parser::parse(gptr base, MEM_ROOT *mem_root,
	  break;
	}
	case FILE_OPTIONS_ULONGLONG:
	case FILE_OPTIONS_REV:
	  if (!(eol= strchr(ptr, '\n')))
	  {
	    my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
+2 −4
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@ enum file_opt_type {
  FILE_OPTIONS_STRING,		/* String (LEX_STRING) */
  FILE_OPTIONS_ESTRING,		/* Escaped string (LEX_STRING) */
  FILE_OPTIONS_ULONGLONG,	/* ulonglong parameter (ulonglong) */
  FILE_OPTIONS_REV,		/* Revision version number (ulonglong) */
  FILE_OPTIONS_TIMESTAMP,	/* timestamp (LEX_STRING have to be
				   allocated with length 20 (19+1) */
  FILE_OPTIONS_STRLIST,         /* list of escaped strings
@@ -81,11 +80,10 @@ File_parser *sql_parse_prepare(const LEX_STRING *file_name,
my_bool
sql_create_definition_file(const LEX_STRING *dir, const  LEX_STRING *file_name,
			   const LEX_STRING *type,
			   gptr base, File_option *parameters, uint versions);
			   gptr base, File_option *parameters);
my_bool rename_in_schema_file(THD *thd,
                              const char *schema, const char *old_name,
                              const char *new_name, ulonglong revision,
                              uint num_view_backups);
                              const char *new_name);

class File_parser: public Sql_alloc
{
+0 −2
Original line number Diff line number Diff line
@@ -909,7 +909,6 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
      /* .frm archive:
        Those archives are obsolete, but following code should
        exist to remove existent "arc" directories.
        See #ifdef FRM_ARCHIVE directives for obsolete code.
      */
      char newpath[FN_REFLEN];
      MY_DIR *new_dirp;
@@ -1069,7 +1068,6 @@ static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error)
  NOTE
    A support of "arc" directories is obsolete, however this
    function should exist to remove existent "arc" directories.
    See #ifdef FRM_ARCHIVE directives for obsolete code.
*/
long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path)
{
+4 −4
Original line number Diff line number Diff line
@@ -479,7 +479,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
  trigname.trigger_table.length= tables->table_name_length;

  if (sql_create_definition_file(&dir, &trigname_file, &trigname_file_type,
                                 (gptr)&trigname, trigname_file_parameters, 0))
                                 (gptr)&trigname, trigname_file_parameters))
    return 1;

  /*
@@ -569,7 +569,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
  /* Create trigger definition file. */

  if (!sql_create_definition_file(&dir, &file, &triggers_file_type,
                                  (gptr)this, triggers_file_parameters, 0))
                                  (gptr)this, triggers_file_parameters))
    return 0;

err_with_cleanup:
@@ -656,7 +656,7 @@ static bool save_trigger_file(Table_triggers_list *triggers, const char *db,
  file.str= file_buff;

  return sql_create_definition_file(&dir, &file, &triggers_file_type,
                                    (gptr)triggers, triggers_file_parameters, 0);
                                    (gptr)triggers, triggers_file_parameters);
}


@@ -1427,7 +1427,7 @@ Table_triggers_list::change_table_name_in_trignames(const char *db_name,
    trigname.trigger_table= *new_table_name;

    if (sql_create_definition_file(&dir, &trigname_file, &trigname_file_type,
        (gptr)&trigname, trigname_file_parameters, 0))
        (gptr)&trigname, trigname_file_parameters))
      return trigger;
  }

+7 −26
Original line number Diff line number Diff line
@@ -660,7 +660,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
  }

  VOID(pthread_mutex_unlock(&LOCK_open));
  if (view->revision != 1)
  if (mode != VIEW_CREATE_NEW)
    query_cache_invalidate3(thd, view, 0);
  start_waiting_global_read_lock(thd);
  if (res)
@@ -678,12 +678,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
}


/* index of revision number in following table */
static const int revision_number_position= 8;
/* index of last required parameter for making view */
static const int required_view_parameters= 10;
/* number of backups */
static const int num_view_backups= 3;
/* number of required parameters for making view */
static const int required_view_parameters= 9;

/*
  table of VIEW .frm field descriptors
@@ -716,9 +712,6 @@ static File_option view_parameters[]=
 {{(char*) STRING_WITH_LEN("with_check_option")},
  my_offsetof(TABLE_LIST, with_check),
  FILE_OPTIONS_ULONGLONG},
 {{(char*) STRING_WITH_LEN("revision")},
  my_offsetof(TABLE_LIST, revision),
  FILE_OPTIONS_REV},
 {{(char*) STRING_WITH_LEN("timestamp")},
  my_offsetof(TABLE_LIST, timestamp),
  FILE_OPTIONS_TIMESTAMP},
@@ -880,18 +873,9 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
      }

      /*
        read revision number

        TODO: read dependence list, too, to process cascade/restrict
        TODO: special cascade/restrict procedure for alter?
      */
      if (parser->parse((gptr)view, thd->mem_root,
                        view_parameters + revision_number_position, 1,
                        &file_parser_dummy_hook))
      {
        error= thd->net.report_error? -1 : 0;
        goto err;
      }
    }
    else
   {
@@ -933,7 +917,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
  }

  if (sql_create_definition_file(&dir, &file, view_file_type,
				 (gptr)view, view_parameters, num_view_backups))
				 (gptr)view, view_parameters))
  {
    error= thd->net.report_error? -1 : 1;
    goto err;
@@ -1868,8 +1852,7 @@ mysql_rename_view(THD *thd,
      goto err;

    /* rename view and it's backups */
    if (rename_in_schema_file(thd, view->db, view->table_name, new_name, 
                              view_def.revision - 1, num_view_backups))
    if (rename_in_schema_file(thd, view->db, view->table_name, new_name))
      goto err;

    strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", view->db, "/", NullS);
@@ -1883,12 +1866,10 @@ mysql_rename_view(THD *thd,
                  - file_buff);

    if (sql_create_definition_file(&pathstr, &file, view_file_type,
                                   (gptr)&view_def, view_parameters,
                                   num_view_backups)) 
                                   (gptr)&view_def, view_parameters))
    {
      /* restore renamed view in case of error */
      rename_in_schema_file(thd, view->db, new_name, view->table_name, 
                            view_def.revision - 1, num_view_backups);
      rename_in_schema_file(thd, view->db, new_name, view->table_name);
      goto err;
    }
  } else
Loading