On 09.02.15 15:19, Sergei Golubchik wrote:
Hi, Sanja!
On Feb 09, sanja@askmonty.org wrote:
At file:///home/bell/maria/bzr/work-maria-5.5-MDEV-6916-check_view/
------------------------------------------------------------ revno: 4407 revision-id: sanja@askmonty.org-20150209014828-kx3cr36hgrvjhtrg parent: svoj@mariadb.org-20150114135038-v50g2cul4vce63h8 committer: sanja@askmonty.org branch nick: work-maria-5.5-MDEV-6916-check_view timestamp: Mon 2015-02-09 02:48:28 +0100 message: MDEV-6916: Upgrade from MySQL to MariaDB breaks already created views
CHECK/REPAIR commands and mysql_upgradesupport for upgrade from MySQL server support. === modified file 'client/mysql_upgrade.c' --- a/client/mysql_upgrade.c 2014-03-27 21:26:58 +0000 +++ b/client/mysql_upgrade.c 2015-02-09 01:48:28 +0000 @@ -150,6 +151,14 @@ static struct my_option my_long_options[ &opt_not_used, &opt_not_used, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"mysql-upgrade", 'y', + "Skip automatic detection MySQL and assume that we upgrade it", + &opt_mysql_upgrade, &opt_mysql_upgrade, 0, GET_BOOL, NO_ARG, + 0, 0, 0, 0, 0, 0}, + {"skip-mysql-upgrade", 'Y', + "Skip view algorithm upgrade from MySQL", + &opt_skip_mysql_upgrade, &opt_skip_mysql_upgrade, 0, GET_BOOL, NO_ARG, + 0, 0, 0, 0, 0, 0}, I remember I've already commented on that in my previous review. Here you are, again:
=== Those two are very bad option names. First,
$ mysql_upgrade --mysql-upgrade
looks plain weird.
$ mysql_upgrade --skip-mysql-upgrade
is even worse. And what about
$ mysql_upgrade --mysql-upgrade --skip-mysql-upgrade
Second, "skip" is the prefix automatically recognized by my_getopt. Normally, --skip-xxx is the same as --xxx=0. But now you're introducing --skip-mysql-upgrade which is very different from --skip-mysql-upgrade. While simultaneously adding support for --skip-skip-mysql-upgrade.
A better option would be
--fix-view-algorithm = { AUTO | ALWAYS | NEVER } ===
In fact, I'd rather remove this option completely. You can simply add
if (!is_mysql()) return;
to run_mysqlcheck_views()
Ok let it be no arguments.
{"version-check", 'k', "Run this program only if its \'server version\' " "matches the version of the server to which it's connecting, (enabled by " "default); use --skip-version-check to avoid this check. Note: the \'server " @@ -344,6 +353,14 @@ get_one_option(int optid, const struct m case OPT_DEFAULT_AUTH: /* --default-auth */ add_one_option(&conn_args, opt, argument); break; + case 'y': + opt_mysql_upgrade= 1; + add_option= FALSE; + break; + case 'Y': + opt_skip_mysql_upgrade= 1; + add_option= FALSE;
This is a wrong way of implementing command-line options. Let my_getopt do its job and don't set values manually.
above code is full exactly the same examples that I did it this way. But arguing has no sens because it will be no arguments.
+ break; }
if (add_option) @@ -754,6 +771,23 @@ static int run_mysqlcheck_upgrade(void) NULL); }
+static int run_mysqlcheck_views(void) +{ + if (!opt_mysql_upgrade) + return 0; + verbose("Phase 0: Fixing views"); Again, was in my previous review: === No "Phase 0" please, renumber all phases to start from 1. ===
OK
+ print_conn_args("mysqlcheck"); + return run_tool(mysqlcheck_path, + NULL, /* Send output from mysqlcheck directly to screen */ + "--no-defaults", + ds_args.str, + "--all-databases", + "--mysql-upgrade", + opt_verbose ? "--verbose": "", + opt_silent ? "--silent": "", + "2>&1", + NULL); +}
static int run_mysqlcheck_fixnames(void) { === modified file 'client/mysqlcheck.c' --- a/client/mysqlcheck.c 2014-02-17 10:00:51 +0000 +++ b/client/mysqlcheck.c 2015-02-09 01:48:28 +0000 @@ -196,6 +197,9 @@ static struct my_option my_long_options[ NO_ARG, 0, 0, 0, 0, 0, 0}, {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"mysql-upgrade", 'y', + "Fix view algorithm view field if it is not new MariaDB view.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} };
@@ -332,7 +336,13 @@ get_one_option(int optid, const struct m case 'v': verbose++; break; - case 'V': print_version(); exit(0); + case 'V': + print_version(); exit(0); + break; + case 'y': + what_to_do= DO_REPAIR; + opt_mysql_upgrade= 1; + break; See above. could you show me example of wat you mean by my_getop() doing its job?
case OPT_MYSQL_PROTOCOL: opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib, opt->name); @@ -595,7 +605,15 @@ static int process_all_tables_in_db(char for (end = tables + 1; (row = mysql_fetch_row(res)) ;) { if ((num_columns == 2) && (strcmp(row[1], "VIEW") == 0)) - continue; + { + if (!opt_mysql_upgrade) + continue; + } + else + { + if (opt_mysql_upgrade) + continue;
Eh? If --mysql-upgrade is specified you skip all tables and only upgrade views? Then the option should absolutely be called --fix-view-algorithm So should I rename it?
+ }
end= fix_table_name(end, row[0]); *end++= ','; @@ -756,10 +782,12 @@ static int handle_request_for_tables(cha if (opt_upgrade) end = strmov(end, " FOR UPGRADE"); break; case DO_REPAIR: - op= (opt_write_binlog) ? "REPAIR" : "REPAIR NO_WRITE_TO_BINLOG"; + op= ((opt_write_binlog || opt_mysql_upgrade) ? + "REPAIR" : "REPAIR NO_WRITE_TO_BINLOG"); Really? For view upgrades you want it to be written to binlog? This is a very questionable idea.
It just has no that syntax because writing to binlog looks like nonsens.
if (opt_quick) end = strmov(end, " QUICK"); if (opt_extended) end = strmov(end, " EXTENDED"); if (opt_frm) end = strmov(end, " USE_FRM"); + if (opt_mysql_upgrade) end = strmov(end, " FROM MYSQL"); break; case DO_ANALYZE: op= (opt_write_binlog) ? "ANALYZE" : "ANALYZE NO_WRITE_TO_BINLOG"; @@ -776,14 +804,17 @@ static int handle_request_for_tables(cha if (opt_all_in_1) { /* No backticks here as we added them before */ - query_length= sprintf(query, "%s TABLE %s %s", op, tables, options); + query_length= sprintf(query, "%s %s %s %s", op, + (opt_mysql_upgrade ? "VIEW" : "TABLE"),
this is wrong, because mysqlcheck --mysql-upgrade (or, really, mysqcheck --fix-view-algorithm=xxx) should check/repair *both* tables and views.
-mysql-upgrade (i.e. REPAIR TABLE ... FROM MYSQL) with table is error now because it have nothing to do. What it should do for tables? I can add 2 lists and processing for CHECK/RAPAIR VIEW/TABLE but usage will be limited IMHO.
+ tables, options); table_name= tables; } else { char *ptr, *org;
- org= ptr= strmov(strmov(query, op), " TABLE "); + org= ptr= strmov(strmov(query, op), + (opt_mysql_upgrade ? " VIEW " : " TABLE ")); ptr= fix_table_name(ptr, tables); strmake(table_name_buff, org, min((int) sizeof(table_name_buff)-1, (int) (ptr - org)));
=== modified file 'mysql-test/r/view.result' --- a/mysql-test/r/view.result 2014-12-21 18:23:28 +0000 +++ b/mysql-test/r/view.result 2015-02-09 01:48:28 +0000 ... +check view v1,v2,v3,v4,v5 FOR UPGRADE; +Table Op Msg_type Msg_text +test.v1 check Note view 'test.v1' has no field mariadb server in its .frm file +test.v1 check status needs repair +test.v2 check Note view 'test.v2' has no field mariadb server in its .frm file +test.v2 check status needs repair +test.v3 check Note view 'test.v3' has no field mariadb server in its .frm file +test.v3 check status needs repair +test.v4 check note View text checksum failed +test.v5 check status OK +repair view v1,v2,v3,v4,v5 FROM MYSQL; where's the test for "repair view" without FROM MYSQL ?
I'll add.
+Table Op Msg_type Msg_text +test.v1 repair status view is repaired +test.v2 repair status view is repaired +test.v3 repair status view is repaired +test.v4 repair status view is repaired +test.v5 repair status OK
=== modified file 'sql/handler.h' --- a/sql/handler.h 2015-01-13 18:28:03 +0000 +++ b/sql/handler.h 2015-02-09 01:48:28 +0000 @@ -42,6 +42,7 @@
// the following is for checking tables
+#define HA_ADMIN_VIEW_REPAIR_IS_DONE 2 #define HA_ADMIN_ALREADY_DONE 1 #define HA_ADMIN_OK 0 #define HA_ADMIN_NOT_IMPLEMENTED -1 @@ -56,6 +57,7 @@ #define HA_ADMIN_NEEDS_UPGRADE -10 #define HA_ADMIN_NEEDS_ALTER -11 #define HA_ADMIN_NEEDS_CHECK -12 +#define HA_ADMIN_NEEDS_REPAIR -13
/* Bits in table_flags() to show what database can do */
=== modified file 'sql/share/errmsg-utf8.txt' --- a/sql/share/errmsg-utf8.txt 2014-05-27 06:45:01 +0000 +++ b/sql/share/errmsg-utf8.txt 2015-02-09 01:48:28 +0000 @@ -6565,3 +6565,9 @@ ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT ER_NO_SUCH_TABLE_IN_ENGINE 42S02 eng "Table '%-.192s.%-.192s' doesn't exist in engine" swe "Det finns ingen tabell som heter '%-.192s.%-.192s' i handlern" +ER_NO_MARIADB_SERVER_FIELD + eng "view '%-.192s.%-.192s' has no field mariadb server in its .frm file" First, it's "mariadb_version", not "mariadb_server". Second, I'm not sure such a specific condition needs a dedicated error code.
And last - we absolutely cannot add new error messages in 5.5, because 10.0 is already GA. I've already written that in my previous review.
+ER_VIEW_REPAIR_IS_DONE + eng "view is repaired" +ER_NEEDS_REPAIR + eng "needs repair"
=== modified file 'sql/sql_admin.cc' --- a/sql/sql_admin.cc 2014-05-03 16:12:17 +0000 +++ b/sql/sql_admin.cc 2015-02-09 01:48:28 +0000 @@ -867,6 +879,22 @@ send_result_message: fatal_error=1; break; } + case HA_ADMIN_VIEW_REPAIR_IS_DONE: Why did you need that, what's wrong with HA_ADMIN_OK? Btw, I've already written that in my previous review.
+ { + protocol->store(STRING_WITH_LEN("status"), system_charset_info); + protocol->store(ER(ER_VIEW_REPAIR_IS_DONE), + strlen(ER(ER_VIEW_REPAIR_IS_DONE)), + system_charset_info); + break; + } + case HA_ADMIN_NEEDS_REPAIR: Why did you need that, what's wrong with HA_ADMIN_NEEDS_UPGRADE?
+ { + protocol->store(STRING_WITH_LEN("status"), system_charset_info); + protocol->store(ER(ER_NEEDS_REPAIR), + strlen(ER(ER_NEEDS_REPAIR)), + system_charset_info); + break; + }
default: // Probably HA_ADMIN_INTERNAL_ERROR { === modified file 'sql/sql_base.cc' --- a/sql/sql_base.cc 2014-10-31 13:07:29 +0000 +++ b/sql/sql_base.cc 2015-02-09 01:48:28 +0000 @@ -3019,12 +3020,17 @@ bool open_table(THD *thd, TABLE_LIST *ta else if (table_list->open_strategy == TABLE_LIST::OPEN_STUB) DBUG_RETURN(FALSE);
+ second empty line?
retry_share:
mysql_mutex_lock(&LOCK_open);
if (!(share= get_table_share_with_discover(thd, table_list, key, - key_length, OPEN_VIEW, + key_length, + (OPEN_VIEW | + ((table_list->required_type == + FRMTYPE_VIEW) ? + OPEN_VIEW_ONLY : 0)), &error, hash_value))) {
=== modified file 'sql/sql_table.cc' --- a/sql/sql_table.cc 2015-01-14 11:10:13 +0000 +++ b/sql/sql_table.cc 2015-02-09 01:48:28 +0000 @@ -28,7 +28,7 @@ #include "sql_base.h" // open_table_uncached, lock_table_names #include "lock.h" // mysql_unlock_tables #include "strfunc.h" // find_type2, find_set -#include "sql_view.h" // view_checksum +#include "sql_view.h" // view_check You've modified only the comment, not the actual C++ code that uses view_checksum in this file.
So, either the comment is wrong (view_checksum is not used in this file) and should be removed, or the whole #include is unnecessary.
OK
#include "sql_truncate.h" // regenerate_locked_table #include "sql_partition.h" // mem_alloc_error, // generate_partition_syntax,
=== modified file 'sql/sql_view.cc' --- a/sql/sql_view.cc 2014-12-21 18:23:28 +0000 +++ b/sql/sql_view.cc 2015-02-09 01:48:28 +0000 @@ -729,6 +729,26 @@ err: }
+static void make_view_filename(LEX_STRING *dir, char *dir_buff, + size_t dir_buff_len, + LEX_STRING *path, char *path_buff, + size_t path_buff_len, + LEX_STRING *file, + TABLE_LIST *view) +{ + /* print file name */ + dir->length= build_table_filename(dir_buff, dir_buff_len - 1, + view->db, "", "", 0); + dir->str= dir_buff; + + path->length= build_table_filename(path_buff, path_buff_len - 1, + view->db, view->table_name, reg_ext, 0); + path->str= path_buff; + + file->str= path->str + dir->length; + file->length= path->length - dir->length; +} + /* number of required parameters for making view */ static const int required_view_parameters= 15;
@@ -791,6 +811,81 @@ static File_option view_parameters[]= static LEX_STRING view_file_type[]= {{(char*) STRING_WITH_LEN("VIEW") }};
+int mariadb_fix_view(THD *thd, TABLE_LIST *view, bool wrong_checksum, + bool swap_alg) +{ + char dir_buff[FN_REFLEN + 1], path_buff[FN_REFLEN + 1]; + LEX_STRING dir, file, path; + DBUG_ENTER("mariadb_fix_view"); + + if (view->algorithm == VIEW_ALGORITHM_UNDEFINED && + !wrong_checksum && view->mariadb_version) + DBUG_RETURN(HA_ADMIN_OK); Eh? Why do you care what view->algorithm is? It doesn't matter whether it's undefined, merge, or temptable - if mariadb_version is set, the algorithm is always correct.
it was checked by upper function.
+ make_view_filename(&dir, dir_buff, sizeof(dir_buff), + &path, path_buff, sizeof(path_buff), + &file, view); + /* init timestamp */ + if (!view->timestamp.str) + view->timestamp.str= view->timestamp_buffer; + + /* check old .frm */ + { + char path_buff[FN_REFLEN]; + LEX_STRING path; + File_parser *parser; + + path.str= path_buff; + fn_format(path_buff, file.str, dir.str, "", MY_UNPACK_FILENAME); + path.length= strlen(path_buff); + if (access(path.str, F_OK)) + DBUG_RETURN(HA_ADMIN_INVALID); + + if (!(parser= sql_parse_prepare(&path, thd->mem_root, 0))) + DBUG_RETURN(HA_ADMIN_INTERNAL_ERROR); + + if (!parser->ok() || !is_equal(&view_type, parser->type())) + DBUG_RETURN(HA_ADMIN_INVALID); + } + + if (swap_alg && view->algorithm != VIEW_ALGORITHM_UNDEFINED) only if mariadb_version is not set.
it was checked by upper function and swap_alg parameter was set (or not)
+ { + DBUG_ASSERT(view->algorithm == VIEW_ALGORITHM_MERGE || + view->algorithm == VIEW_ALGORITHM_TMPTABLE); + if (view->algorithm == VIEW_ALGORITHM_MERGE) + view->algorithm= VIEW_ALGORITHM_TMPTABLE; + else + view->algorithm= VIEW_ALGORITHM_MERGE; + } + if (wrong_checksum) + { + if (view->md5.length != 32) + { + if ((view->md5.str= (char *)thd->alloc(32 + 1)) == NULL) + DBUG_RETURN(HA_ADMIN_FAILED); + } + view->calc_md5(view->md5.str); + view->md5.length= 32; + } + view->mariadb_version= MYSQL_VERSION_ID; + + if (sql_create_definition_file(&dir, &file, view_file_type, + (uchar*)view, view_parameters)) + { + sql_print_error("View '%-.192s'.'%-.192s': algorithm swap error.", + view->db, view->table_name); + DBUG_RETURN(HA_ADMIN_INTERNAL_ERROR); + } + sql_print_information("View '%-.192s'.'%-.192s': algorithm swapped to '%s'", + view->db, view->table_name, + (view->algorithm == VIEW_ALGORITHM_MERGE)? + "MERGE":"TEMPTABLE"); + + + DBUG_RETURN(HA_ADMIN_VIEW_REPAIR_IS_DONE); +} + + /* Register VIEW (write .frm & process .frm's history backups)
@@ -927,17 +1022,9 @@ static int mysql_register_view(THD *thd, } loop_out: /* print file name */ - dir.length= build_table_filename(dir_buff, sizeof(dir_buff) - 1, - view->db, "", "", 0); - dir.str= dir_buff; - - path.length= build_table_filename(path_buff, sizeof(path_buff) - 1, - view->db, view->table_name, reg_ext, 0); - path.str= path_buff; - - file.str= path.str + dir.length; - file.length= path.length - dir.length; - + make_view_filename(&dir, dir_buff, sizeof(dir_buff), + &path, path_buff, sizeof(path_buff), + &file, view); /* init timestamp */ if (!view->timestamp.str) view->timestamp.str= view->timestamp_buffer; @@ -1941,6 +2028,63 @@ int view_checksum(THD *thd, TABLE_LIST * HA_ADMIN_OK); }
+/** + Check view + + @param thd thread handle + @param view view for check + @param check_opt check options + + @retval HA_ADMIN_OK OK + @retval HA_ADMIN_NOT_IMPLEMENTED it is not VIEW + @retval HA_ADMIN_WRONG_CHECKSUM check sum is wrong +*/ +int view_check(THD *thd, TABLE_LIST *view, HA_CHECK_OPT *check_opt) +{ + int res; + DBUG_ENTER("view_check"); + if ((res= view_checksum(thd, view)) != HA_ADMIN_OK) + DBUG_RETURN(res); + if (((check_opt->sql_flags & TT_FOR_UPGRADE) && + !view->mariadb_version)) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_NO_MARIADB_SERVER_FIELD, + ER(ER_NO_MARIADB_SERVER_FIELD), + view->db, + view->table_name); + DBUG_RETURN(HA_ADMIN_NEEDS_REPAIR); + } + DBUG_RETURN(HA_ADMIN_OK); +} + + +/** + Repair view + + @param thd thread handle + @param view view for check + @param check_opt check options + + @retval HA_ADMIN_OK OK + @retval HA_ADMIN_NOT_IMPLEMENTED it is not VIEW + @retval HA_ADMIN_WRONG_CHECKSUM check sum is wrong +*/ + +int view_repair(THD *thd, TABLE_LIST *view, HA_CHECK_OPT *check_opt) +{ + DBUG_ENTER("view_repair"); + bool swap_alg= + ((check_opt->sql_flags & TT_FROM_MYSQL) && + (!view->mariadb_version)); + bool wrong_checksum= view_checksum(thd, view); + if (wrong_checksum || swap_alg) + { + DBUG_RETURN(mariadb_fix_view(thd, view, wrong_checksum, swap_alg)); First, don't call functions from DBUG_RETURN, this produces weird debug traces and somebody will have to fix this later. Always
int res= mariadb_fix_view(thd, view, wrong_checksum, swap_alg); DBUG_RETURN(res);
Alternatively, feel free to fix dbug macros to support function calls in DBUG_RETURN() :)
OK (new code style)
Second, REPAIR VIEW viewname - without FROM MYSQL - should work too, it should add mariadb_version field without changing the algorithm.
yes it should (at least code was written so)
+ } + DBUG_RETURN(HA_ADMIN_OK); +} + /* rename view
=== modified file 'sql/sql_yacc.yy' --- a/sql/sql_yacc.yy 2014-11-08 18:54:42 +0000 +++ b/sql/sql_yacc.yy 2015-02-09 01:48:28 +0000 @@ -1145,6 +1145,7 @@ bool my_yyoverflow(short **a, YYSTYPE ** %token MULTIPOINT %token MULTIPOLYGON %token MUTEX_SYM +%token MYSQL_SYM %token MYSQL_ERRNO_SYM %token NAMES_SYM /* SQL-2003-N */ %token NAME_SYM /* SQL-2003-N */ @@ -7191,11 +7192,16 @@ opt_checksum_type: ;
repair: - REPAIR opt_no_write_to_binlog table_or_tables + REPAIR opt_no_write_to_binlog table_or_view { LEX *lex=Lex; lex->sql_command = SQLCOM_REPAIR; lex->no_write_to_binlog= $2; + if (lex->no_write_to_binlog && lex->only_view) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; Why? REPAIR NO_WRITE_TO_BINLOG VIEW makes perfect sense to me, why did you want to disallow it?
and write binlog also? I just tried to remove that syntax for view (maybe wrong but it was not discussed before)
+ } lex->check_opt.init(); lex->alter_info.reset(); /* Will be overriden during execution. */ Regards, Sergei