Commit b82094a0 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.
parent ee3594b2
......@@ -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),
......
......@@ -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
{
......
......@@ -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)
{
......
......@@ -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;
}
......
......@@ -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
......
......@@ -657,7 +657,6 @@ struct TABLE_LIST
st_lex_user definer; /* definer of view */
ulonglong file_version; /* version of file's field set */
ulonglong updatable_view; /* VIEW can be updated */
ulonglong revision; /* revision control number */
ulonglong algorithm; /* 0 any, 1 tmp tables , 2 merging */
ulonglong view_suid; /* view is suid (TRUE dy default) */
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