Re: [Maria-developers] Rev 4407: MDEV-6916: Upgrade from MySQL to MariaDB breaks already created views
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()
{"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.
+ 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. ===
+ 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.
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
+ }
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.
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.
+ 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 ?
+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.
#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.
+ 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.
+ { + 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() :) Second, REPAIR VIEW viewname - without FROM MYSQL - should work too, it should add mariadb_version field without changing the algorithm.
+ } + 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?
+ } lex->check_opt.init(); lex->alter_info.reset(); /* Will be overriden during execution. */
Regards, Sergei
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
Hi, Sanja! On Feb 09, Oleksandr Byelkin wrote:
On 09.02.15 15:19, Sergei Golubchik wrote:
On Feb 09, sanja@askmonty.org wrote:
At file:///home/bell/maria/bzr/work-maria-5.5-MDEV-6916-check_view/
=== 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?
Any option that is not mentioned in this switch. For example, --all-databases, --all-in-1, --auto-repair, --character-sets-dir, --compress, and so on, in fact almost all options are not set explicitly in the switch.
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?
Yes, please (but see below). Note that here we're talking about mysqlcheck. As for the option in mysql_upgrade - you've already agreed to remove 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.
Okay. Then I'd at least allow the syntax, for consistency. It won't do anything if you aren't going to write REPAIR VIEW to binlog anyway.
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.
yes, I meant two lists in my review. But on the other hand... When mysqlcheck is invoked from mysql_upgrade, you wouldn't want it to repair all tables. Okay, let's keep your implementation and only repair views when --fix-view-algorithm is set. In that case, the option should better be --upgrade-views, and defined as --upgrade-views = { no, yes, from_mysql } note that "no" and "yes" must be in that exactly order, first and second.
+ tables, options); table_name= tables; } else { char *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.
Please add also a test to show how REPAIR VIEW is replicated. Doesn't have to be a replication test, really. I'd say that "show binlog events" after the REPAIR VIEW should suffice. Use include/show_binlog_events.inc
+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/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.
Did you miss this comment or you will do the change?
+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.
Did you miss this comment or you will do the change?
+ { + 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?
Did you miss this comment or you will do the change?
+ { + 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?
Did you miss this comment or you will do the change?
retry_share:
mysql_mutex_lock(&LOCK_open);
if (!(share= get_table_share_with_discover(thd, table_list, key, - key_length, OPEN_VIEW, + key_length,
Regards, Sergei
Hi Sanja! I was trying to continue this piece of work (incorporating Sergei's review below) since it seems abandoned. Bit sad since it looks like you put a lot of effort into this. Unfortunately the binary mysql-test/std_data/mysql_upgrade/event.* files didn't make it to the mailing list (http://lists.askmonty.org/pipermail/commits/2015-February/007386.html). Any chance you could create a git branch of work so far (or even just email the mysql-test/std_data/mysql_upgrade/event.* files to me)? ----- Original Message -----
Hi, Sanja!
On Feb 09, Oleksandr Byelkin wrote:
On 09.02.15 15:19, Sergei Golubchik wrote:
On Feb 09, sanja@askmonty.org wrote:
At file:///home/bell/maria/bzr/work-maria-5.5-MDEV-6916-check_view/
-- -- Daniel Black, Engineer @ Open Query (http://openquery.com.au) Remote expertise & maintenance for MySQL/MariaDB server environments.
For review: Hi All, list of all changes: https://github.com/openquery/mariadb-server/commits/MDEV-6916-maria-5.5-chec... side by side comparision of Sanja/my work against 5.5 head: https://github.com/MariaDB/server/compare/5.5...openquery:MDEV-6916-maria-5.... One clarification on how to make mysqlcheck to upgrade views only needed. Original test files mysql-test/std_data/mysql_upgrade/* needed. Small addition of binlog tests needed. ----- Original Message -----
On Feb 09, Oleksandr Byelkin wrote:
On 09.02.15 15:19, Sergei Golubchik wrote:
On Feb 09, sanja@askmonty.org wrote:
At file:///home/bell/maria/bzr/work-maria-5.5-MDEV-6916-check_view/
=== 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?
Any option that is not mentioned in this switch. For example, --all-databases, --all-in-1, --auto-repair, --character-sets-dir, --compress, and so on, in fact almost all options are not set explicitly in the switch.
fixed https://github.com/openquery/mariadb-server/commit/87f5bae0b56253df965230f58...
+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.
Took opportunity to make adding/changing phases to be lest conflicts in merge https://github.com/openquery/mariadb-server/commit/25872e2802a4ae32d4f18132d...
  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?
I've changed this behaviour to handle tables and views. ref: https://github.com/MariaDB/server/compare/5.5...openquery:MDEV-6916-maria-5....
Then the option should absolutely be called --fix-view-algorithm So should I rename it?
Yes,
done: https://github.com/openquery/mariadb-server/commit/e5191dd11beaec44230f97ec1...
please (but see below). Note that here we're talking about mysqlcheck. Â As for the option in mysql_upgrade - you've already agreed to remove it.
done: https://github.com/openquery/mariadb-server/commit/ebd3c6ccbe43b691515cb05cf...
+    }      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.
Okay. Then I'd at least allow the syntax, for consistency. It won't do anything if you aren't going to write REPAIR VIEW to binlog anyway.
consistency restored: https://github.com/openquery/mariadb-server/commit/9b067a3e9fa253f90e8cb05b2... Is this appropriate too? Its consistent (even though I consider opt_write_binlog=true a bad default) https://github.com/openquery/mariadb-server/commit/96e277aed81d41c15621c0842... Â
   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.
Done: https://github.com/openquery/mariadb-server/commit/9b067a3e9fa253f90e8cb05b2...
-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.
yes, I meant two lists in my review.
Done: https://github.com/openquery/mariadb-server/commit/9b067a3e9fa253f90e8cb05b2...
But on the other hand... When mysqlcheck is invoked from mysql_upgrade, you wouldn't want it to repair all tables. Okay, let's keep your implementation and only repair views when --fix-view-algorithm is set.
Not done yet. --skip-tables or --{update|repair}-views-only perhaps?
In that case, the option should better be --upgrade-views, and defined as
 --upgrade-views = { no, yes, from_mysql }
note that "no" and "yes" must be in that exactly order, first and second.
and the 'from_mysql' means don't check tables? This doesn't sound intuitive. More correctly its a --repair-views.
+              tables, options);    table_name= tables;   }   else   {    char *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.
Please add also a test to show how REPAIR VIEW is replicated. Doesn't have to be a replication test, really. I'd say that "show binlog events" after the REPAIR VIEW should suffice. Use include/show_binlog_events.inc
I'll fix the test cases when I get the mysql-test/std_data/mysql_upgrade/* files.
+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/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.
Did you miss this comment or you will do the change?
Done: https://github.com/openquery/mariadb-server/commit/4409e04d891fba62a3f4ed291...
+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.
Did you miss this comment or you will do the change?
Done: https://github.com/openquery/mariadb-server/commit/4409e04d891fba62a3f4ed291...
+ Â Â { + Â Â Â 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?
Did you miss this comment or you will do the change?
Done: https://github.com/openquery/mariadb-server/commit/4409e04d891fba62a3f4ed291...
+   { +    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?
Did you miss this comment or you will do the change?
Done: https://github.com/openquery/mariadb-server/commit/4409e04d891fba62a3f4ed291... -- -- Daniel Black, Engineer @ Open Query (http://openquery.com.au) Remote expertise & maintenance for MySQL/MariaDB server environments.
Hi, Daniel! On Apr 12, Daniel Black wrote:
For review:
Hi All,
list of all changes: https://github.com/openquery/mariadb-server/commits/MDEV-6916-maria-5.5-chec...
side by side comparision of Sanja/my work against 5.5 head:
https://github.com/MariaDB/server/compare/5.5...openquery:MDEV-6916-maria-5....
Great, thanks a lot! Here, few comments are below:
+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.
Took opportunity to make adding/changing phases to be lest conflicts in merge
https://github.com/openquery/mariadb-server/commit/25872e2802a4ae32d4f18132d...
I don't know whether it's a good idea. It means that, for example "Running 'mysql_fix_privilege_tables'..." won't always be Phase 3/3, but depending on command-line options it might be a Phase 2 or 1 (well, may be mysql_fix_privilege_tables phase cannot - but there certainly are phases that are run conditionally). I personally don't know how bad it is, perhaps it's fine to renumber phases dynamically when some phases are skipped. So I have no opinion. I'm just saying that phases might be renumbered dynamically after your patch.
But on the other hand... When mysqlcheck is invoked from mysql_upgrade, you wouldn't want it to repair all tables. Okay, let's keep your implementation and only repair views when --fix-view-algorithm is set.
Not done yet.
--skip-tables or --{update|repair}-views-only perhaps?
In that case, the option should better be --upgrade-views, and defined as
 --upgrade-views = { no, yes, from_mysql }
note that "no" and "yes" must be in that exactly order, first and second.
and the 'from_mysql' means don't check tables? This doesn't sound intuitive. More correctly its a --repair-views.
I believe I meant these three variants: --upgrade-views = no don't change view frm's --upgrade-views = yes upgrade view frms adding mariadb_version field and if is_mysql() returned true also toggle the view algorithm --upgrade-views = from_mysql always toggle the view algorithm, even if is_mysql() returned false. The last case is useful if a user has migrated from MySQL some time ago, so it's a mariadb-to-mariadb upgrade but views stay wrong from older mysql-to-mariadb upgrade. Regards, Sergei
No "Phase 0" please, renumber all phases to start from 1.
Took opportunity to make adding/changing phases to be lest conflicts in merge
https://github.com/openquery/mariadb-server/commit/25872e2802a4ae32d4f18132d...
I don't know whether it's a good idea.
It means that, for example "Running 'mysql_fix_privilege_tables'..." won't always be Phase 3/3, but depending on command-line options it might be a Phase 2 or 1 (well, may be mysql_fix_privilege_tables phase cannot - but there certainly are phases that are run conditionally).
I personally don't know how bad it is, perhaps it's fine to renumber phases dynamically when some phases are skipped. So I have no opinion. I'm just saying that phases might be renumbered dynamically after your patch.
Well consistent phase numbering to activity is a good goal. So is knowing why phases that just don't occur. So is having a total phases that is actually reached. So produce output for every phase, even if it just says we're skipping it: https://github.com/MariaDB/server/commit/808608cb3f4e10ba81679c94b2a299a32cf...
I believe I meant these three variants:
--upgrade-views = no don't change view frm's --upgrade-views = yes upgrade view frms adding mariadb_version field and if is_mysql() returned true also toggle the view algorithm --upgrade-views = from_mysql always toggle the view algorithm, even if is_mysql() returned false.
Right is_mysql is in mysql_upgrade and --upgrade-view is mysqlcheck. Always exclusing if there is a mariadb-version in in the frm I assume.
The last case is useful if a user has migrated from MySQL some time ago, so it's a mariadb-to-mariadb upgrade but views stay wrong from older mysql-to-mariadb upgrade.
Added: https://github.com/MariaDB/server/commit/97e0aeaf72cde117b8d9551f68d407fa13a... I'm almost thinking mysqlcheck should default to --upgrade-views=yes? At least it will do a checksum but otherwise won't rewrite a view unless there is missing mariadb-version note mysql_upgrade: is_mysql detected run with --upgrade_views=from_mysql --upgrade-system-tables - upgrade_views not run otherwise --upgrade_views=yes to get old views upgraded (and checksum checked). https://github.com/MariaDB/server/commit/4987080ddb42abf1a9111bd6a79e7bed746...
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
Has been renamed to --upgrade-views and it doesn't skip all tables. This is done with --skip-fix-tables https://github.com/MariaDB/server/commit/fc277cd4ba6c506a6f6c71e04c462b24a3f...
--- 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 ?
done: https://github.com/MariaDB/server/commit/c584058f8f227a2004f9fdf6fd0897e11fd...
Please add also a test to show how REPAIR VIEW is replicated. Doesn't have to be a replication test, really. I'd say that "show binlog events" after the REPAIR VIEW should suffice. Use ----
done: https://github.com/MariaDB/server/commit/c584058f8f227a2004f9fdf6fd0897e11fd...
=== 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.
it was unnecessary - removed https://github.com/MariaDB/server/commit/7229b19c67d12ff579c64f1184e951015aa...
=== 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 (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.
corrected: https://github.com/MariaDB/server/commit/29721d7d5f85fe323d70f1856aa1e5294e1...
+ + 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.
Already covered earlier in the function
+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
fixed. like below. (https://github.com/MariaDB/server/commit/8a827d530a67d40332668d31683a45f5839...)
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() :)
I was too lazy. sorry.
Second, REPAIR VIEW viewname - without FROM MYSQL - should work too, it should add mariadb_version field without changing the algorithm.
ok. Done - https://github.com/MariaDB/server/commit/76c18f7e76dff6f5c5f8986e0480074a310...
=== 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?
corrected: https://github.com/MariaDB/server/commit/28b173134ee1634a2489d3cef6faf2f3ea1... -- -- Daniel Black, Engineer @ Open Query (http://openquery.com.au) Remote expertise & maintenance for MySQL/MariaDB server environments.
Hi, Daniel! Thanks! I've now reviewed the fix as a whole, all patches combined. There weren't many comments, but still there were some. And questions. If you'd like I can apply the latest changes (finishing touches, so to say) myself - it can speed things up. Or you can do that, whatever you prefer. Either way, I'd still like to see my questions answered first.
diff --git a/client/client_priv.h b/client/client_priv.h index e206804..a66c388 100644 --- a/client/client_priv.h +++ b/client/client_priv.h @@ -79,6 +79,7 @@ enum options_client OPT_SLAP_COMMIT, OPT_SLAP_DETACH, OPT_SLAP_NO_DROP, + OPT_UPGRADE_VIEWS,
please remove. see below
OPT_MYSQL_REPLACE_INTO, OPT_BASE64_OUTPUT_MODE, OPT_SERVER_ID, OPT_FIX_TABLE_NAMES, OPT_FIX_DB_NAMES, OPT_SSL_VERIFY_SERVER_CERT, OPT_AUTO_VERTICAL_OUTPUT, diff --git a/client/mysqlcheck.c b/client/mysqlcheck.c index a454943..43c7ff0 100644 --- a/client/mysqlcheck.c +++ b/client/mysqlcheck.c @@ -57,6 +58,20 @@ static uint opt_protocol=0;
enum operations { DO_CHECK=1, DO_REPAIR, DO_ANALYZE, DO_OPTIMIZE, DO_UPGRADE };
+typedef enum { + UPGRADE_VIEWS_NO=0, + UPGRADE_VIEWS_YES=1, + UPGRADE_VIEWS_FROM_MYSQL=2, + UPGRADE_VIEWS_COUNT
UPGRADE_VIEWS_COUNT is unused, please remove
+} enum_upgrade_views; +const char *upgrade_views_opts[]= +{"NO", "YES", "FROM_MYSQL", NullS}; +TYPELIB upgrade_views_typelib= + { array_elements(upgrade_views_opts) - 1, "", + upgrade_views_opts, NULL }; +static enum_upgrade_views opt_upgrade_views= UPGRADE_VIEWS_NO; +static char *opt_upgrade_views_str= NullS; + static struct my_option my_long_options[] = { {"all-databases", 'A', @@ -196,6 +211,15 @@ 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}, + {"upgrade-views", OPT_UPGRADE_VIEWS, + "Repairs/Upgrades views. 'No' (default) doesn't do anything to views. " + "'Yes' repairs the checksum and adds a MariaDB version to the frm table defination (if missing)." + "Using 'from_mysql' will, when upgrading from MySQL, and ensure the view algorithms are the" + "same as what they where previously.",
"definition" And the whole message could be reworded. for example "Repair views. One of: NO, YES (correct the checksum, if necessary, add the mariadb-version field), FROM_MYSQL (same as YES and toggle the algorithm MERGE<->TEMPTABLE)." In 10.1 mysqld --help automatically adds "One of: <list of values>" to all enum options, but only if the first option is not already present in the help text in the same letter case (it uses strstr for that). It's 5.5 but better to mention options in the same leter case and use the same "One of:" style to blend in better.
+ &opt_upgrade_views_str, &opt_upgrade_views_str, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
not GET_STR but GET_ENUM with 0 instead of OPT_UPGRADE_VIEWS
+ {"fix-tables", 'Y', + "Fix tables. Usually the default however mysql_upgrade will run with --skip-fix-tables.",
"Usually the default..." is redundant, my_getopt will say it automatically btw, it's mysqlcheck tool, and these two options are symmetrical, in a sense. so it'd be better to have matching names. may be --process-tables = no/yes --process-views = no/yes/from_mysql ? it's a bit strange to have --process-views=from_mysql. A more logical variant could be --process-views=upgrade_from_mysql, but it looks too long for me. on the other hand, upgrade_from_mysql value is not for manual use, it's supposed to be specified by mysql_upgrade, so it doesn't mater if it's long
+ &opt_fix_tables, &opt_fix_tables, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} };
@@ -332,7 +356,18 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case 'v': verbose++; break; - case 'V': print_version(); exit(0); + case 'V': + print_version(); exit(0); + break; + case OPT_UPGRADE_VIEWS: + if (argument == NULL) + opt_upgrade_views= UPGRADE_VIEWS_NO; + else + { + opt_upgrade_views= (enum_upgrade_views) + (find_type_or_exit(argument, &upgrade_views_typelib, opt->name)-1); + } + break;
remove, use GET_ENUM
case OPT_MYSQL_PROTOCOL: opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib, opt->name); @@ -363,6 +398,23 @@ static int get_options(int *argc, char ***argv) if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option))) exit(ho_error);
+ if (what_to_do==DO_REPAIR && !(opt_upgrade_views || opt_fix_tables)) + { + fprintf(stderr, "Error: %s doesn't support repair command without either upgrade-views or fix-tables specified.\n",
I'd reformulate. it's not "doesn't support", it's more like "command makes no sense", so I'd say "Nothing to repair when both --process-tables=NO and --process-views=NO"
+ my_progname); + exit(1); + } + if (opt_upgrade_views && what_to_do!=DO_REPAIR) + { + if (!what_to_do) + what_to_do= DO_REPAIR; + else + { + fprintf(stderr, "Error: %s doesn't support non-repair command with option upgrade-views.\n", + my_progname);
Better "%s of views is not supported", "analyzing"/"optimizing"/"checking" (depending on what_to_do) although I don't see why CHECK VIEW should not be supported
+ exit(1); + } + } if (!what_to_do) { size_t pnlen= strlen(my_progname); @@ -463,8 +515,41 @@ static int process_databases(char **db_names) } /* process_databases */
+/* returns: -1 for error, 1 for view, 0 for table */ +static int is_view(const char *table, uint length) +{ + char *query, *ptr; + MYSQL_RES *res; + MYSQL_FIELD *field; + int view; + DBUG_ENTER("is_view"); + + if (!(query =(char *) my_malloc((sizeof(char)*(length+110)), MYF(MY_WME)))) + DBUG_RETURN(-1); + ptr= strmov(query, "SHOW CREATE TABLE `"); + ptr= strxmov(ptr, table, NullS); + ptr= strxmov(ptr, "`", NullS);
Better use SHOW FULL TABLES WHERE TABLES_IN_MYSQL='xxxx'; That's the statement one should normally use to know whether xxxx is a view or a table. SHOW FULL TABLES only does just that, it doesn't even parse the frm file (and doesn't open the table in the engine, of course).
+ if (mysql_query(sock, query)) + { + fprintf(stderr, "Failed to %s\n", query); + fprintf(stderr, "Error: %s\n", mysql_error(sock)); + my_free(query); + DBUG_RETURN(-1); + } + res= mysql_store_result(sock); + field= mysql_fetch_field(res); + view= (strcmp(field->name,"View")==0) ? 1 : 0; + mysql_free_result(res); + my_free(query); + + DBUG_RETURN(view); +} + static int process_selected_tables(char *db, char **table_names, int tables) { + int view; + char *table; + uint table_len; DBUG_ENTER("process_selected_tables");
if (use_db(db)) @@ -494,12 +579,24 @@ static int process_selected_tables(char *db, char **table_names, int tables) *end++= ','; } *--end = 0; - handle_request_for_tables(table_names_comma_sep + 1, (uint) (tot_length - 1)); + table= table_names_comma_sep + 1; + table_len= tot_length - 1; + view= is_view(table, table_len); + if (view < 0) + DBUG_RETURN(1); + handle_request_for_tables(table, table_len, (view==1));
This looks wrong. table_names_comma_sep is a comma separated list of names, like "t1, t2, v1, v2, t3, v3, etc" but you check the first element in the list and assume that they all are of the same type. Let's just say that in --all-in-one mode we don't support mixing objects of different types. Then it becomes: if (opt_fix_tables && opt_upgrade_views) { fprintf(strerr, "not supported..."); return 1; } handle_request_for_tables(table_names_comma_sep + 1, (uint) (tot_length - 1), opt_upgrade_views); alternatively, of course, you can walk the list, create two strings - one for table and one for views - that is, do the same as in process_all_tables_in_db()
my_free(table_names_comma_sep); } else for (; tables > 0; tables--, table_names++) - handle_request_for_tables(*table_names, fixed_name_length(*table_names)); + { + table= *table_names; + table_len= fixed_name_length(*table_names); + view= is_view(table, table_len); + if (view < 0) + continue; + handle_request_for_tables(table, table_len, (view==1)); + } DBUG_RETURN(0); } /* process_selected_tables */
@@ -748,10 +887,13 @@ static int handle_request_for_tables(char *tables, uint length) 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) ? + "REPAIR" : "REPAIR NO_WRITE_TO_BINLOG"; if (opt_quick) end = strmov(end, " QUICK"); if (opt_extended) end = strmov(end, " EXTENDED"); if (opt_frm) end = strmov(end, " USE_FRM"); + if (opt_upgrade_views==UPGRADE_VIEWS_FROM_MYSQL && view) + end = strmov(end, " FROM MYSQL");
I'd protect other opt's with !view: if (!view) { if (opt_quick) .... ... } else { if (opt_upgrade_views == UPGRADE_VIEWS_FROM_MYSQL) end = strmov(end, " FROM MYSQL"); }
break; case DO_ANALYZE: op= (opt_write_binlog) ? "ANALYZE" : "ANALYZE NO_WRITE_TO_BINLOG"; @@ -785,7 +928,8 @@ static int handle_request_for_tables(char *tables, uint length) } if (mysql_real_query(sock, query, query_length)) { - sprintf(message, "when executing '%s TABLE ... %s'", op, options); + sprintf(message, "when executing '%s%s... %s'", op, tab_view, options); + /* sprintf(message, "when executing '%s'", query); */
forgotten debug comment?
DBerror(sock, message); my_free(query); DBUG_RETURN(1); diff --git a/client/mysql_upgrade.c b/client/mysql_upgrade.c index 5fb3b13..97e2ad92 100644 --- a/client/mysql_upgrade.c +++ b/client/mysql_upgrade.c @@ -752,12 +755,67 @@ static int run_mysqlcheck_upgrade(void) opt_write_binlog ? "--write-binlog" : "--skip-write-binlog", "2>&1", NULL); + if (retch || opt_systables_only) + verbose("Phase %d/%d: Skipping 'mysql_fix_privilege_tables'... not needed", phase++, phases_total);
This is a bit strange. I'd expect something like if (....) { run some step } else { verbose("step skipped"); } that's what you have below in run_mysqlcheck_views. but printing the skip message here is weird and confusing. better to break if() below (that calls run_sql_fix_privilege_tables) and print the message there.
+ return retch; }
+#define EVENTS_STRUCT_LEN 7000 + +static my_bool is_mysql() +{ + my_bool ret= TRUE; + DYNAMIC_STRING ds_events_struct; + + if (init_dynamic_string(&ds_events_struct, NULL, + EVENTS_STRUCT_LEN, EVENTS_STRUCT_LEN)) + die("Out of memory"); + + if (run_query("show create table mysql.event", + &ds_events_struct, FALSE) || + strstr(ds_events_struct.str, "IGNORE_BAD_TABLE_OPTIONS") != NULL) + ret= FALSE; + else + verbose("MySQL upgrade detected"); + + dynstr_free(&ds_events_struct); + return(ret); +} + +static int run_mysqlcheck_views(void) +{ + const char *upgrade_views="--upgrade-views=YES"; + if (is_mysql()) + { + upgrade_views="--upgrade-views=FROM_MYSQL"; + verbose("Phase %d/%d: Fixing views from mysql", phase++, phases_total); + } + else if (opt_systables_only) + { + verbose("Phase %d/%d: Fixing views - skipped - not required", phase++, phases_total); + return 0; + } + else + verbose("Phase %d/%d: Fixing views", phase++, phases_total); + + print_conn_args("mysqlcheck"); + return run_tool(mysqlcheck_path, + NULL, /* Send output from mysqlcheck directly to screen */ + "--no-defaults", + ds_args.str, + "--all-databases", + upgrade_views, + "--skip-fix-tables", + opt_verbose ? "--verbose": "", + opt_silent ? "--silent": "", + opt_write_binlog ? "--write-binlog" : "--skip-write-binlog", + "2>&1", + NULL); +}
static int run_mysqlcheck_fixnames(void) { - verbose("Phase 1/3: Fixing table and database names"); + verbose("Phase %d/%d: Fixing table and database names", phase++, phases_total); print_conn_args("mysqlcheck"); return run_tool(mysqlcheck_path, NULL, /* Send output from mysqlcheck directly to screen */ diff --git a/mysql-test/r/mysql_upgrade_view.result b/mysql-test/r/mysql_upgrade_view.result new file mode 100644 index 0000000..aa39319 --- /dev/null +++ b/mysql-test/r/mysql_upgrade_view.result @@ -0,0 +1,205 @@ +set sql_log_bin=0; +drop table if exists t1,v1,v2,v3,v4,v1badcheck; +drop view if exists t1,v1,v2,v3,v4,v1badcheck; +create table t1(a int); +create table kv(k varchar(30) NOT NULL PRIMARY KEY,v varchar(50)); +flush tables; +Phase 1/4: Fixing views +test.v1 OK +test.v1badcheck OK +test.v2 OK +test.v3 OK +Phase 2/4: Fixing table and database names +Phase 3/4: Checking and upgrading tables +Processing databases +information_schema +mtr +mtr.global_suppressions OK +mtr.test_suppressions OK +mysql +mysql.columns_priv OK +mysql.db OK +mysql.event OK +mysql.func OK +mysql.help_category OK +mysql.help_keyword OK +mysql.help_relation OK +mysql.help_topic OK +mysql.host OK +mysql.ndb_binlog_index OK +mysql.plugin OK +mysql.proc OK +mysql.procs_priv OK +mysql.proxies_priv OK +mysql.servers OK +mysql.tables_priv OK +mysql.time_zone OK +mysql.time_zone_leap_second OK +mysql.time_zone_name OK +mysql.time_zone_transition OK +mysql.time_zone_transition_type OK +mysql.user OK +performance_schema +test +test.kv OK +test.t1 OK +Phase 4/4: Running 'mysql_fix_privilege_tables'... +OK +show create view v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=MERGE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a` from `t1` utf8 utf8_general_ci +show create view v2; +View Create View character_set_client collation_connection +v2 CREATE ALGORITHM=TEMPTABLE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select `t1`.`a` AS `a` from `t1` utf8 utf8_general_ci +show create view v3; +View Create View character_set_client collation_connection +v3 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v3` AS select `t1`.`a` AS `a` from `t1` utf8 utf8_general_ci +set sql_log_bin=1; +REPAIR VIEW v1,v2; +Table Op Msg_type Msg_text +test.v1 repair status OK +test.v2 repair status OK +REPAIR VIEW v1badcheck; +Table Op Msg_type Msg_text +test.v1badcheck repair status OK +REPAIR NO_WRITE_TO_BINLOG VIEW v3; +Table Op Msg_type Msg_text +test.v3 repair status OK +set sql_log_bin=0; +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; REPAIR VIEW v1,v2 +master-bin.000001 # Query # # use `test`; REPAIR VIEW v1badcheck
Hm. As far as I remember, Sanja said that it makes no sense to write REPAIR VIEW to binlog, and that's why he didn't support NO_WRITE_TO_BINLOG. And I replied that let's support NO_WRITE_TO_BINLOG syntax for consistency, but not write REPAIR VIEW to binlog regardless, if that makes no sense. Do you think it *does* make sense to write REPAIR VIEW to binlog? I agree that it's consistent with REPAIR TABLE and will get us less bug reports about "REPAIR VIEW not in binlog". But can it be useful?
+LOAD DATA INFILE 'MYSQLD_DATADIR/test/v1.frm' REPLACE INTO TABLE kv FIELDS TERMINATED BY '='; +SELECT k,v from kv where k in ('md5','algorithm');
nice :)
+k v +algorithm 1 +md5 5e6eaf216e7b016fcedfd4e1113517af +SELECT k from kv where k ='mariadb-version'; +k +mariadb-version +truncate table kv; +LOAD DATA INFILE 'MYSQLD_DATADIR/test/v2.frm' REPLACE INTO TABLE kv FIELDS TERMINATED BY '='; +SELECT k,v from kv where k in ('md5','algorithm'); +k v +algorithm 2 +md5 5e6eaf216e7b016fcedfd4e1113517af +SELECT k from kv where k ='mariadb-version'; +k +mariadb-version +truncate table kv; +LOAD DATA INFILE 'MYSQLD_DATADIR/test/v3.frm' REPLACE INTO TABLE kv FIELDS TERMINATED BY '='; +SELECT k,v from kv where k in ('md5','algorithm'); +k v +algorithm 0 +md5 5e6eaf216e7b016fcedfd4e1113517af +SELECT k from kv where k ='mariadb-version'; +k +mariadb-version +truncate table kv; +LOAD DATA INFILE 'MYSQLD_DATADIR/test/v1badcheck.frm' REPLACE INTO TABLE kv FIELDS TERMINATED BY '='; +SELECT k,v from kv where k in ('md5','algorithm'); +k v +algorithm 1 +md5 5e6eaf216e7b016fcedfd4e1113517af +SELECT k from kv where k ='mariadb-version'; +k +mariadb-version +truncate table kv; +drop view if exists v1,v2,v3,v1badcheck; +flush tables; +create algorithm=temptable view v4 as select a from t1; +show create view v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=MERGE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a` from `t1` utf8 utf8_general_ci +show create view v2; +View Create View character_set_client collation_connection +v2 CREATE ALGORITHM=TEMPTABLE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select `t1`.`a` AS `a` from `t1` utf8 utf8_general_ci +show create view v3; +View Create View character_set_client collation_connection +v3 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v3` AS select `t1`.`a` AS `a` from `t1` utf8 utf8_general_ci +show create view v4; +View Create View character_set_client collation_connection +v4 CREATE ALGORITHM=TEMPTABLE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v4` AS select `t1`.`a` AS `a` from `t1` latin1 latin1_swedish_ci +MySQL upgrade detected +Phase 1/4: Fixing views from mysql +test.v1 OK +test.v2 OK +test.v3 OK +test.v4 OK +Phase 2/4: Fixing table and database names +Phase 3/4: Checking and upgrading tables +Processing databases +information_schema +mtr +mtr.global_suppressions OK +mtr.test_suppressions OK +mysql +mysql.columns_priv OK +mysql.db OK +mysql.ev_bk OK +mysql.event OK +mysql.func OK +mysql.help_category OK +mysql.help_keyword OK +mysql.help_relation OK +mysql.help_topic OK +mysql.host OK +mysql.ndb_binlog_index OK +mysql.plugin OK +mysql.proc OK +mysql.procs_priv OK +mysql.proxies_priv OK +mysql.servers OK +mysql.tables_priv OK +mysql.time_zone OK +mysql.time_zone_leap_second OK +mysql.time_zone_name OK +mysql.time_zone_transition OK +mysql.time_zone_transition_type OK +mysql.user OK +performance_schema +test +test.kv OK +test.t1 OK +Phase 4/4: Running 'mysql_fix_privilege_tables'... +OK +show create view v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=TEMPTABLE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a` from `t1` utf8 utf8_general_ci +show create view v2; +View Create View character_set_client collation_connection +v2 CREATE ALGORITHM=MERGE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select `t1`.`a` AS `a` from `t1` utf8 utf8_general_ci +show create view v3; +View Create View character_set_client collation_connection +v3 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v3` AS select `t1`.`a` AS `a` from `t1` utf8 utf8_general_ci +show create view v4; +View Create View character_set_client collation_connection +v4 CREATE ALGORITHM=TEMPTABLE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v4` AS select `t1`.`a` AS `a` from `t1` latin1 latin1_swedish_ci +LOAD DATA INFILE 'MYSQLD_DATADIR/test/v1.frm' REPLACE INTO TABLE kv FIELDS TERMINATED BY '='; +SELECT k,v from kv where k in ('md5','algorithm'); +k v +algorithm 2 +md5 5e6eaf216e7b016fcedfd4e1113517af +SELECT k from kv where k ='mariadb-version'; +k +mariadb-version +truncate table kv; +drop view if exists v1,v2,v3; +flush tables; +test.v1 OK +test.v2 OK +test.v3 OK +test.v4 OK +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; REPAIR VIEW v1,v2 +master-bin.000001 # Query # # use `test`; REPAIR VIEW v1badcheck +master-bin.000001 # Query # # use `test`; REPAIR VIEW `v1` FROM MYSQL +master-bin.000001 # Query # # use `test`; REPAIR VIEW `v2` FROM MYSQL +master-bin.000001 # Query # # use `test`; REPAIR VIEW `v3` FROM MYSQL +master-bin.000001 # Query # # use `test`; REPAIR VIEW `v4` FROM MYSQL +flush tables; +drop table if exists kv; +drop view v1,v2,v3,v4; +drop table t1; diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 92aa414..65292a5 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -380,7 +381,18 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, lex->query_tables_own_last= 0;
if (view_operator_func == NULL) + { table->required_type=FRMTYPE_TABLE; + DBUG_ASSERT(!lex->only_view); + } + else if (lex->only_view) + { + table->required_type= FRMTYPE_VIEW; + } + else if (!lex->only_view && lex->sql_command == SQLCOM_REPAIR) + { + table->required_type= FRMTYPE_TABLE; + }
That's a bit inconsistent, CHECK TABLE historically works for views, but REPAIR TABLE doesn't, one must use REPAIR VIEW, while CHECK VIEW is optional. But I don't like if REPAIR TABLE would work for views. May be issue a deprecation warning for CHECK TABLE? Or just leave it as is? may be nobody cares...
if (lex->sql_command == SQLCOM_CHECK || lex->sql_command == SQLCOM_REPAIR || diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 257001c..dfbc6c4 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -13786,7 +13807,18 @@ lock:
table_or_tables: TABLE_SYM + { Lex->only_view= FALSE; } + | TABLES + { Lex->only_view= FALSE; } + ; + +table_or_view: + TABLE_SYM + { Lex->only_view= FALSE; } | TABLES + { Lex->only_view= FALSE; } + | VIEW_SYM + { Lex->only_view= TRUE; }
reuse table_or_tables here
;
table_lock_list: diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 09784f7..24f01ae 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -791,6 +811,86 @@ 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 (!wrong_checksum && view->mariadb_version) + DBUG_RETURN(HA_ADMIN_OK); + + 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); + }
I wonder if that's necessary. A view is open at this point, you've just calculated its checksum and tested whether it has mariadb_version field (in view_repair()).
+ + if (swap_alg && view->algorithm != VIEW_ALGORITHM_UNDEFINED) + { + 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; + } + else + swap_alg= 0; + 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': versioned to %llu%s%s", + view->db, view->table_name, view->mariadb_version, + (wrong_checksum ? ", and checksum corrected" : ""), + (swap_alg ? + ((view->algorithm == VIEW_ALGORITHM_MERGE) ? + ", and algorithm swapped to 'MERGE'" + : ", and algorithm swapped to 'TEMPTABLE'")
I don't like how it looks "and algorithm swapped to 'MERGE'". I think "changed to" or "corrected to be" or "restored to be" looks better.
+ : "")); + + + DBUG_RETURN(HA_ADMIN_OK); +} + + /* Register VIEW (write .frm & process .frm's history backups)
@@ -1941,6 +2033,63 @@ int view_checksum(THD *thd, TABLE_LIST *view) 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_TABLE_NEEDS_UPGRADE, + ER(ER_TABLE_NEEDS_UPGRADE), + view->db, + view->table_name);
Is that necessary? I'd expect the caller (mysql_admin_table() to do that, when it gets HA_ADMIN_NEEDS_UPGRADE. There is nothing view specific in this warning message that you're printing.
+ DBUG_RETURN(HA_ADMIN_NEEDS_UPGRADE); + } + 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); + bool wrong_checksum= view_checksum(thd, view);
view_checksum() != HA_ADMIN_OK
+ int ret; + if (wrong_checksum || swap_alg || (!view->mariadb_version)) + { + ret= mariadb_fix_view(thd, view, wrong_checksum, swap_alg); + DBUG_RETURN(ret); + } + DBUG_RETURN(HA_ADMIN_OK); +} + /* rename view
Regards, Sergei
----- Original Message -----
Hi, Daniel!
Thanks!
I've now reviewed the fix as a whole, all patches combined. There weren't many comments, but still there were some. And questions.
If you'd like I can apply the latest changes (finishing touches, so to say) myself - it can speed things up.
Yes please.
Or you can do that, whatever you prefer. Either way, I'd still like to see my questions answered first.
k
static int process_selected_tables(char *db, char **table_names, int tables) { + int view; + char *table; + uint table_len; DBUG_ENTER("process_selected_tables");
if (use_db(db)) @@ -494,12 +579,24 @@ static int process_selected_tables(char *db, char **table_names, int tables) *end++= ','; } *--end = 0; - handle_request_for_tables(table_names_comma_sep + 1, (uint) (tot_length - 1)); + table= table_names_comma_sep + 1; + table_len= tot_length - 1; + view= is_view(table, table_len); + if (view < 0) + DBUG_RETURN(1); + handle_request_for_tables(table, table_len, (view==1));
This looks wrong. table_names_comma_sep is a comma separated list of names, like
"t1, t2, v1, v2, t3, v3, etc"
but you check the first element in the list and assume that they all are of the same type.
You're quite right.
case DO_ANALYZE: op= (opt_write_binlog) ? "ANALYZE" : "ANALYZE NO_WRITE_TO_BINLOG"; @@ -785,7 +928,8 @@ static int handle_request_for_tables(char *tables, uint length) } if (mysql_real_query(sock, query, query_length)) { - sprintf(message, "when executing '%s TABLE ... %s'", op, options); + sprintf(message, "when executing '%s%s... %s'", op, tab_view, options); + /* sprintf(message, "when executing '%s'", query); */
forgotten debug comment?
I was unsure if you wanted this additional output.
DBerror(sock, message); my_free(query); DBUG_RETURN(1); diff --git a/client/mysql_upgrade.c b/client/mysql_upgrade.c index 5fb3b13..97e2ad92 100644 --- a/client/mysql_upgrade.c +++ b/client/mysql_upgrade.c @@ -752,12 +755,67 @@ static int run_mysqlcheck_upgrade(void) opt_write_binlog ? "--write-binlog" : "--skip-write-binlog", "2>&1", NULL); + if (retch || opt_systables_only) + verbose("Phase %d/%d: Skipping 'mysql_fix_privilege_tables'... not needed", phase++, phases_total);
This is a bit strange. I'd expect something like
if (....) { run some step } else { verbose("step skipped"); }
that's what you have below in run_mysqlcheck_views. but printing the skip message here is weird and confusing.
better to break if() below (that calls run_sql_fix_privilege_tables) and print the message there.
Where it is called from is if ((!opt_systables_only && (run_mysqlcheck_views() || run_mysqlcheck_fixnames() || run_mysqlcheck_upgrade())) || run_sql_fix_privilege_tables()) So separating it was going to be invasive, however it would be easier to follow.
+set sql_log_bin=0; +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; REPAIR VIEW v1,v2 +master-bin.000001 # Query # # use `test`; REPAIR VIEW v1badcheck
Hm. As far as I remember, Sanja said that it makes no sense to write REPAIR VIEW to binlog, and that's why he didn't support NO_WRITE_TO_BINLOG. And I replied that let's support NO_WRITE_TO_BINLOG syntax for consistency, but not write REPAIR VIEW to binlog regardless, if that makes no sense.
Do you think it *does* make sense to write REPAIR VIEW to binlog? I agree that it's consistent with REPAIR TABLE and will get us less bug reports about "REPAIR VIEW not in binlog". But can it be useful?
repair view corrects the checksum if broken, adds the mariadb version if it isn't there. The only downside I can think of is in galera TOI mode it will reduce parallel operation for a very quick DDL.
+LOAD DATA INFILE 'MYSQLD_DATADIR/test/v1.frm' REPLACE INTO TABLE kv FIELDS TERMINATED BY '='; +SELECT k,v from kv where k in ('md5','algorithm');
nice :)
I got a laugh out of it too.
--- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -380,7 +381,18 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, lex->query_tables_own_last= 0;
if (view_operator_func == NULL) + { table->required_type=FRMTYPE_TABLE; + DBUG_ASSERT(!lex->only_view); + } + else if (lex->only_view) + { + table->required_type= FRMTYPE_VIEW; + } + else if (!lex->only_view && lex->sql_command == SQLCOM_REPAIR) + { + table->required_type= FRMTYPE_TABLE; + }
That's a bit inconsistent,
This was Sanja's bit (c8dbef22a)
CHECK TABLE historically works for views, but REPAIR TABLE doesn't, one must use REPAIR VIEW, while CHECK VIEW is optional. But I don't like if REPAIR TABLE would work for views. May be issue a deprecation warning for CHECK TABLE?
FWIW - I agree.
Or just leave it as is? may be nobody cares...
It adds supporting and documenting the syntax which is a bit of a maintenance burden and looks odd and adhoc for no real benefit.
diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 09784f7..24f01ae 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -791,6 +811,86 @@ 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 (!wrong_checksum && view->mariadb_version) + DBUG_RETURN(HA_ADMIN_OK); + + 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); + }
I wonder if that's necessary.
recreating the frm?
A view is open at this point, you've just calculated its checksum and tested whether it has mariadb_version field (in view_repair()).
But some of these aren't necessary true(?). I was assuming this was avoiding transient errors. Sanja?
+ + if (swap_alg && view->algorithm != VIEW_ALGORITHM_UNDEFINED) + { + 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; + } + else + swap_alg= 0; + 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': versioned to %llu%s%s", + view->db, view->table_name, view->mariadb_version, + (wrong_checksum ? ", and checksum corrected" : ""), + (swap_alg ? + ((view->algorithm == VIEW_ALGORITHM_MERGE) ? + ", and algorithm swapped to 'MERGE'" + : ", and algorithm swapped to 'TEMPTABLE'")
I don't like how it looks "and algorithm swapped to 'MERGE'". I think "changed to" or "corrected to be" or "restored to be" looks better.
fair call
+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_TABLE_NEEDS_UPGRADE, + ER(ER_TABLE_NEEDS_UPGRADE), + view->db, + view->table_name);
Is that necessary? I'd expect the caller (mysql_admin_table() to do that, when it gets HA_ADMIN_NEEDS_UPGRADE. There is nothing view specific in this warning message that you're printing.
Quite right. its probably something i didn't revert when changing the error messages back to the standard ones. -- -- Daniel Black, Engineer @ Open Query (http://openquery.com.au) Remote expertise & maintenance for MySQL/MariaDB server environments.
participants (4)
-
Daniel Black
-
Daniel Black
-
Oleksandr Byelkin
-
Sergei Golubchik