Commit 56b9586f authored by Gleb Shchepa's avatar Gleb Shchepa

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.


sql/parse_file.cc:
  Bug #40021: Renaming view fails, archived .frm for view is
              missing after downgrade
  
  1. static write_parameter(): the old_version parameter
     and the section for FILE_OPTIONS_REV have been re moved.
  2. write_parameter(): the max_versions parameter has been
     removed;
  3. sql_create_definition_file(): removal of dead code;
  4. rename_in_schema_file(): revision and num_view_backups
     parameters and dead code have been removed;
  5. File_parser::parse(): FILE_OPTIONS_REV section has been
     removed.
sql/parse_file.h:
  Bug #40021: Renaming view fails, archived .frm for view is
              missing after downgrade
  
  1. The FILE_OPTIONS_REV constant has been removed.
  2. sql_create_definition_file and rename_in_schema_file
     functions: obsolete versions, revision and
     num_view_backups parameters have been removed.
sql/sql_db.cc:
  Bug #40021: Renaming view fails, archived .frm for view is
              missing after downgrade
  
  Commentary update.
sql/sql_trigger.cc:
  Bug #40021: Renaming view fails, archived .frm for view is
              missing after downgrade
  
  sql_create_definition_file() calls have been updates to
  new parameter lists.
sql/sql_view.cc:
  Bug #40021: Renaming view fails, archived .frm for view is
              missing after downgrade
  
  1. The mysql_create_view function code is used for both
     CREATE VIEW and ALTER queries, but query cache is
     necessary for ALTER command only. Check for a non first
     view revision has been replaced with a direct check for
     ALTER query.
  2. The num_view_backups global constant has been removed.
  3. view_parameters: the "revision" .frm field support has
     been removed.
  4. sql_create_definition_file and rename_in_schema_file
     function calls have been updates to new parameter lists.
sql/table.h:
  Bug #40021: Renaming view fails, archived .frm for view is
              missing after downgrade
  
  TABLE_LIST: the revision field has been removed.
parent dbc062bf
...@@ -88,7 +88,6 @@ write_escaped_string(IO_CACHE *file, LEX_STRING *val_s) ...@@ -88,7 +88,6 @@ write_escaped_string(IO_CACHE *file, LEX_STRING *val_s)
file pointer to IO_CACHE structure for writing file pointer to IO_CACHE structure for writing
base pointer to data structure base pointer to data structure
parameter pointer to parameter descriptor parameter pointer to parameter descriptor
old_version for returning back old version number value
RETURN RETURN
FALSE - OK FALSE - OK
...@@ -96,8 +95,7 @@ write_escaped_string(IO_CACHE *file, LEX_STRING *val_s) ...@@ -96,8 +95,7 @@ write_escaped_string(IO_CACHE *file, LEX_STRING *val_s)
*/ */
static my_bool static my_bool
write_parameter(IO_CACHE *file, gptr base, File_option *parameter, write_parameter(IO_CACHE *file, gptr base, File_option *parameter)
ulonglong *old_version)
{ {
char num_buf[20]; // buffer for numeric operations char num_buf[20]; // buffer for numeric operations
// string for numeric operations // string for numeric operations
...@@ -125,15 +123,6 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter, ...@@ -125,15 +123,6 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter,
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
break; 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: case FILE_OPTIONS_TIMESTAMP:
{ {
/* string have to be allocated already */ /* string have to be allocated already */
...@@ -205,7 +194,6 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter, ...@@ -205,7 +194,6 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter,
base base address for parameter reading (structure like base base address for parameter reading (structure like
TABLE) TABLE)
parameters parameters description parameters parameters description
max_versions number of versions to save
RETURN RETURN
FALSE - OK FALSE - OK
...@@ -215,13 +203,11 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter, ...@@ -215,13 +203,11 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter,
my_bool my_bool
sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name, sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
const LEX_STRING *type, const LEX_STRING *type,
gptr base, File_option *parameters, gptr base, File_option *parameters)
uint max_versions)
{ {
File handler; File handler;
IO_CACHE file; IO_CACHE file;
char path[FN_REFLEN+1]; // +1 to put temporary file name for sure char path[FN_REFLEN+1]; // +1 to put temporary file name for sure
ulonglong old_version= ULONGLONG_MAX;
int path_end; int path_end;
File_option *param; File_option *param;
DBUG_ENTER("sql_create_definition_file"); DBUG_ENTER("sql_create_definition_file");
...@@ -255,7 +241,7 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name, ...@@ -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, if (my_b_append(&file, (const byte *)param->name.str,
param->name.length) || param->name.length) ||
my_b_append(&file, (const byte *)STRING_WITH_LEN("=")) || 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"))) my_b_append(&file, (const byte *)STRING_WITH_LEN("\n")))
goto err_w_cache; goto err_w_cache;
} }
...@@ -269,55 +255,6 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name, ...@@ -269,55 +255,6 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
} }
path[path_end]='\0'; 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 // rename temporary file
...@@ -346,8 +283,6 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name, ...@@ -346,8 +283,6 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
schema name of given schema schema name of given schema
old_name original file name old_name original file name
new_name new file name new_name new file name
revision revision number
num_view_backups number of backups
RETURN RETURN
0 - OK 0 - OK
...@@ -356,8 +291,7 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name, ...@@ -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, my_bool rename_in_schema_file(THD *thd,
const char *schema, const char *old_name, const char *schema, const char *old_name,
const char *new_name, ulonglong revision, const char *new_name)
uint num_view_backups)
{ {
char old_path[FN_REFLEN], new_path[FN_REFLEN], arc_path[FN_REFLEN]; 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, ...@@ -376,23 +310,6 @@ my_bool rename_in_schema_file(THD *thd,
strxnmov(arc_path, FN_REFLEN, mysql_data_home, "/", schema, "/arc", NullS); strxnmov(arc_path, FN_REFLEN, mysql_data_home, "/", schema, "/arc", NullS);
(void) unpack_filename(arc_path, arc_path); (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 { // remove obsolete 'arc' directory and files if any
MY_DIR *new_dirp; MY_DIR *new_dirp;
if ((new_dirp = my_dir(arc_path, MYF(MY_DONT_SORT)))) if ((new_dirp = my_dir(arc_path, MYF(MY_DONT_SORT))))
...@@ -401,7 +318,6 @@ my_bool rename_in_schema_file(THD *thd, ...@@ -401,7 +318,6 @@ my_bool rename_in_schema_file(THD *thd,
(void) mysql_rm_arc_files(thd, new_dirp, arc_path); (void) mysql_rm_arc_files(thd, new_dirp, arc_path);
} }
} }
#endif//FRM_ARCHIVE
return 0; return 0;
} }
...@@ -838,7 +754,6 @@ File_parser::parse(gptr base, MEM_ROOT *mem_root, ...@@ -838,7 +754,6 @@ File_parser::parse(gptr base, MEM_ROOT *mem_root,
break; break;
} }
case FILE_OPTIONS_ULONGLONG: case FILE_OPTIONS_ULONGLONG:
case FILE_OPTIONS_REV:
if (!(eol= strchr(ptr, '\n'))) if (!(eol= strchr(ptr, '\n')))
{ {
my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0), my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
......
...@@ -23,7 +23,6 @@ enum file_opt_type { ...@@ -23,7 +23,6 @@ enum file_opt_type {
FILE_OPTIONS_STRING, /* String (LEX_STRING) */ FILE_OPTIONS_STRING, /* String (LEX_STRING) */
FILE_OPTIONS_ESTRING, /* Escaped string (LEX_STRING) */ FILE_OPTIONS_ESTRING, /* Escaped string (LEX_STRING) */
FILE_OPTIONS_ULONGLONG, /* ulonglong parameter (ulonglong) */ FILE_OPTIONS_ULONGLONG, /* ulonglong parameter (ulonglong) */
FILE_OPTIONS_REV, /* Revision version number (ulonglong) */
FILE_OPTIONS_TIMESTAMP, /* timestamp (LEX_STRING have to be FILE_OPTIONS_TIMESTAMP, /* timestamp (LEX_STRING have to be
allocated with length 20 (19+1) */ allocated with length 20 (19+1) */
FILE_OPTIONS_STRLIST, /* list of escaped strings FILE_OPTIONS_STRLIST, /* list of escaped strings
...@@ -81,11 +80,10 @@ File_parser *sql_parse_prepare(const LEX_STRING *file_name, ...@@ -81,11 +80,10 @@ File_parser *sql_parse_prepare(const LEX_STRING *file_name,
my_bool my_bool
sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name, sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
const LEX_STRING *type, const LEX_STRING *type,
gptr base, File_option *parameters, uint versions); gptr base, File_option *parameters);
my_bool rename_in_schema_file(THD *thd, my_bool rename_in_schema_file(THD *thd,
const char *schema, const char *old_name, const char *schema, const char *old_name,
const char *new_name, ulonglong revision, const char *new_name);
uint num_view_backups);
class File_parser: public Sql_alloc class File_parser: public Sql_alloc
{ {
......
...@@ -909,7 +909,6 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, ...@@ -909,7 +909,6 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
/* .frm archive: /* .frm archive:
Those archives are obsolete, but following code should Those archives are obsolete, but following code should
exist to remove existent "arc" directories. exist to remove existent "arc" directories.
See #ifdef FRM_ARCHIVE directives for obsolete code.
*/ */
char newpath[FN_REFLEN]; char newpath[FN_REFLEN];
MY_DIR *new_dirp; MY_DIR *new_dirp;
...@@ -1069,7 +1068,6 @@ static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error) ...@@ -1069,7 +1068,6 @@ static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error)
NOTE NOTE
A support of "arc" directories is obsolete, however this A support of "arc" directories is obsolete, however this
function should exist to remove existent "arc" directories. 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) long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path)
{ {
......
...@@ -479,7 +479,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, ...@@ -479,7 +479,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
trigname.trigger_table.length= tables->table_name_length; trigname.trigger_table.length= tables->table_name_length;
if (sql_create_definition_file(&dir, &trigname_file, &trigname_file_type, if (sql_create_definition_file(&dir, &trigname_file, &trigname_file_type,
(gptr)&trigname, trigname_file_parameters, 0)) (gptr)&trigname, trigname_file_parameters))
return 1; return 1;
/* /*
...@@ -569,7 +569,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, ...@@ -569,7 +569,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
/* Create trigger definition file. */ /* Create trigger definition file. */
if (!sql_create_definition_file(&dir, &file, &triggers_file_type, if (!sql_create_definition_file(&dir, &file, &triggers_file_type,
(gptr)this, triggers_file_parameters, 0)) (gptr)this, triggers_file_parameters))
return 0; return 0;
err_with_cleanup: err_with_cleanup:
...@@ -656,7 +656,7 @@ static bool save_trigger_file(Table_triggers_list *triggers, const char *db, ...@@ -656,7 +656,7 @@ static bool save_trigger_file(Table_triggers_list *triggers, const char *db,
file.str= file_buff; file.str= file_buff;
return sql_create_definition_file(&dir, &file, &triggers_file_type, 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, ...@@ -1427,7 +1427,7 @@ Table_triggers_list::change_table_name_in_trignames(const char *db_name,
trigname.trigger_table= *new_table_name; trigname.trigger_table= *new_table_name;
if (sql_create_definition_file(&dir, &trigname_file, &trigname_file_type, if (sql_create_definition_file(&dir, &trigname_file, &trigname_file_type,
(gptr)&trigname, trigname_file_parameters, 0)) (gptr)&trigname, trigname_file_parameters))
return trigger; return trigger;
} }
......
...@@ -660,7 +660,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, ...@@ -660,7 +660,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
} }
VOID(pthread_mutex_unlock(&LOCK_open)); VOID(pthread_mutex_unlock(&LOCK_open));
if (view->revision != 1) if (mode != VIEW_CREATE_NEW)
query_cache_invalidate3(thd, view, 0); query_cache_invalidate3(thd, view, 0);
start_waiting_global_read_lock(thd); start_waiting_global_read_lock(thd);
if (res) if (res)
...@@ -678,12 +678,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, ...@@ -678,12 +678,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
} }
/* index of revision number in following table */ /* number of required parameters for making view */
static const int revision_number_position= 8; static const int required_view_parameters= 9;
/* 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;
/* /*
table of VIEW .frm field descriptors table of VIEW .frm field descriptors
...@@ -716,9 +712,6 @@ static File_option view_parameters[]= ...@@ -716,9 +712,6 @@ static File_option view_parameters[]=
{{(char*) STRING_WITH_LEN("with_check_option")}, {{(char*) STRING_WITH_LEN("with_check_option")},
my_offsetof(TABLE_LIST, with_check), my_offsetof(TABLE_LIST, with_check),
FILE_OPTIONS_ULONGLONG}, FILE_OPTIONS_ULONGLONG},
{{(char*) STRING_WITH_LEN("revision")},
my_offsetof(TABLE_LIST, revision),
FILE_OPTIONS_REV},
{{(char*) STRING_WITH_LEN("timestamp")}, {{(char*) STRING_WITH_LEN("timestamp")},
my_offsetof(TABLE_LIST, timestamp), my_offsetof(TABLE_LIST, timestamp),
FILE_OPTIONS_TIMESTAMP}, FILE_OPTIONS_TIMESTAMP},
...@@ -880,18 +873,9 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, ...@@ -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: read dependence list, too, to process cascade/restrict
TODO: special cascade/restrict procedure for alter? 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 else
{ {
...@@ -933,7 +917,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, ...@@ -933,7 +917,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
} }
if (sql_create_definition_file(&dir, &file, view_file_type, 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; error= thd->net.report_error? -1 : 1;
goto err; goto err;
...@@ -1868,8 +1852,7 @@ mysql_rename_view(THD *thd, ...@@ -1868,8 +1852,7 @@ mysql_rename_view(THD *thd,
goto err; goto err;
/* rename view and it's backups */ /* rename view and it's backups */
if (rename_in_schema_file(thd, view->db, view->table_name, new_name, if (rename_in_schema_file(thd, view->db, view->table_name, new_name))
view_def.revision - 1, num_view_backups))
goto err; goto err;
strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", view->db, "/", NullS); strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", view->db, "/", NullS);
...@@ -1883,12 +1866,10 @@ mysql_rename_view(THD *thd, ...@@ -1883,12 +1866,10 @@ mysql_rename_view(THD *thd,
- file_buff); - file_buff);
if (sql_create_definition_file(&pathstr, &file, view_file_type, if (sql_create_definition_file(&pathstr, &file, view_file_type,
(gptr)&view_def, view_parameters, (gptr)&view_def, view_parameters))
num_view_backups))
{ {
/* restore renamed view in case of error */ /* restore renamed view in case of error */
rename_in_schema_file(thd, view->db, new_name, view->table_name, rename_in_schema_file(thd, view->db, new_name, view->table_name);
view_def.revision - 1, num_view_backups);
goto err; goto err;
} }
} else } else
......
...@@ -657,7 +657,6 @@ struct TABLE_LIST ...@@ -657,7 +657,6 @@ struct TABLE_LIST
st_lex_user definer; /* definer of view */ st_lex_user definer; /* definer of view */
ulonglong file_version; /* version of file's field set */ ulonglong file_version; /* version of file's field set */
ulonglong updatable_view; /* VIEW can be updated */ ulonglong updatable_view; /* VIEW can be updated */
ulonglong revision; /* revision control number */
ulonglong algorithm; /* 0 any, 1 tmp tables , 2 merging */ ulonglong algorithm; /* 0 any, 1 tmp tables , 2 merging */
ulonglong view_suid; /* view is suid (TRUE dy default) */ ulonglong view_suid; /* view is suid (TRUE dy default) */
ulonglong with_check; /* WITH CHECK OPTION */ ulonglong with_check; /* WITH CHECK OPTION */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment