From d22c33992af98cd0943d285ad39211adcdf6976f Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 31 May 2007 17:30:56 +0500 Subject: [PATCH] Fixed bug #28244. When the same VIEW was created at the master side twice, malformed (truncated after the word 'AS') query string was forwarded to client side, so error messages on the master and client was different, and replication was broken. The mysql_register_view function call failed too early: fields of `view' output argument of this function was not filled yet with correct data required for query replication. The mysql_register_view function also copied pointers to local buffers into a memory allocated by the caller. sql/sql_view.cc: Fixed bug #28244. Checking of existence of VIEW .frm file has been moved down to the place, when `view' argument is completely filled with proper data. view->query.str and view->query.md5 pointers has been set to the NULL before return from the mysql_register_view function. mysql-test/t/rpl_view.test: Updated test case for bug #28244. mysql-test/r/rpl_view.result: Updated test case for bug #28244. --- mysql-test/r/rpl_view.result | 6 ++ mysql-test/t/rpl_view.test | 12 +++ sql/sql_view.cc | 159 +++++++++++++++++++---------------- 3 files changed, 105 insertions(+), 72 deletions(-) diff --git a/mysql-test/r/rpl_view.result b/mysql-test/r/rpl_view.result index be03f3f080a..be7ed6e8c2a 100644 --- a/mysql-test/r/rpl_view.result +++ b/mysql-test/r/rpl_view.result @@ -106,4 +106,10 @@ a b 1 6 drop table t1; drop view v1; +CREATE TABLE t1(a INT); +CREATE VIEW v1 AS SELECT * FROM t1; +CREATE VIEW v1 AS SELECT * FROM t1; +ERROR 42S01: Table 'v1' already exists +DROP VIEW v1; +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/rpl_view.test b/mysql-test/t/rpl_view.test index 812e5d44d58..21748586130 100644 --- a/mysql-test/t/rpl_view.test +++ b/mysql-test/t/rpl_view.test @@ -149,4 +149,16 @@ drop view v1; sync_slave_with_master; +# +# BUG#28244 CREATE VIEW breaks replication when view exists +# +connection master; +CREATE TABLE t1(a INT); +CREATE VIEW v1 AS SELECT * FROM t1; +--error ER_TABLE_EXISTS_ERROR +CREATE VIEW v1 AS SELECT * FROM t1; +DROP VIEW v1; +DROP TABLE t1; +sync_slave_with_master; + --echo End of 5.0 tests diff --git a/sql/sql_view.cc b/sql/sql_view.cc index ba367040b36..b9a61418d5c 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -692,6 +692,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, bool can_be_merged; char dir_buff[FN_REFLEN], file_buff[FN_REFLEN]; LEX_STRING dir, file; + int error= 0; DBUG_ENTER("mysql_register_view"); /* print query */ @@ -702,80 +703,16 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, lex->unit.print(&str); thd->variables.sql_mode|= sql_mode; } - str.append('\0'); DBUG_PRINT("info", ("View: %s", str.ptr())); - /* print file name */ - (void) my_snprintf(dir_buff, FN_REFLEN, "%s/%s/", - mysql_data_home, view->db); - unpack_filename(dir_buff, dir_buff); - dir.str= dir_buff; - dir.length= strlen(dir_buff); - - file.str= file_buff; - file.length= (strxnmov(file_buff, FN_REFLEN, view->table_name, reg_ext, - NullS) - file_buff); - /* 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, 0, MY_UNPACK_FILENAME); - path.length= strlen(path_buff); - - if (!access(path.str, F_OK)) - { - if (mode == VIEW_CREATE_NEW) - { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), view->alias); - DBUG_RETURN(-1); - } - - if (!(parser= sql_parse_prepare(&path, thd->mem_root, 0))) - DBUG_RETURN(1); - - if (!parser->ok() || !is_equal(&view_type, parser->type())) - { - my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW"); - DBUG_RETURN(-1); - } - - /* - read revision number - - TODO: read dependence list, too, to process cascade/restrict - TODO: special cascade/restrict procedure for alter? - */ - if (parser->parse((gptr)view, thd->mem_root, - view_parameters + revision_number_position, 1, - &file_parser_dummy_hook)) - { - DBUG_RETURN(thd->net.report_error? -1 : 0); - } - } - else - { - if (mode == VIEW_ALTER) - { - my_error(ER_NO_SUCH_TABLE, MYF(0), view->db, view->alias); - DBUG_RETURN(-1); - } - } - } /* fill structure */ - view->query.str= (char*)str.ptr(); - view->query.length= str.length()-1; // we do not need last \0 + view->query.str= str.c_ptr(); + view->query.length= str.length(); view->source.str= thd->query + thd->lex->create_view_select_start; - view->source.length= (char *)skip_rear_comments((char *)view->source.str, - (char *)thd->query + - thd->query_length) - - view->source.str; + view->source.length= skip_rear_comments((char *)view->source.str, + (char *)thd->query + + thd->query_length) - + view->source.str; view->file_version= 1; view->calc_md5(md5); view->md5.str= md5; @@ -817,6 +754,76 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, } } loop_out: + /* print file name */ + (void) my_snprintf(dir_buff, FN_REFLEN, "%s/%s/", + mysql_data_home, view->db); + unpack_filename(dir_buff, dir_buff); + dir.str= dir_buff; + dir.length= strlen(dir_buff); + + file.str= file_buff; + file.length= (strxnmov(file_buff, FN_REFLEN, view->table_name, reg_ext, + NullS) - file_buff); + /* 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, 0, MY_UNPACK_FILENAME); + path.length= strlen(path_buff); + + if (!access(path.str, F_OK)) + { + if (mode == VIEW_CREATE_NEW) + { + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), view->alias); + error= -1; + goto err; + } + + if (!(parser= sql_parse_prepare(&path, thd->mem_root, 0))) + { + error= 1; + goto err; + } + + if (!parser->ok() || !is_equal(&view_type, parser->type())) + { + my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW"); + error= -1; + goto err; + } + + /* + read revision number + + TODO: read dependence list, too, to process cascade/restrict + TODO: special cascade/restrict procedure for alter? + */ + if (parser->parse((gptr)view, thd->mem_root, + view_parameters + revision_number_position, 1, + &file_parser_dummy_hook)) + { + error= thd->net.report_error? -1 : 0; + goto err; + } + } + else + { + if (mode == VIEW_ALTER) + { + my_error(ER_NO_SUCH_TABLE, MYF(0), view->db, view->alias); + error= -1; + goto err; + } + } + } /* Check that table of main select do not used in subqueries. @@ -841,15 +848,23 @@ loop_out: !view->updatable_view) { my_error(ER_VIEW_NONUPD_CHECK, MYF(0), view->db, view->table_name); - DBUG_RETURN(-1); + error= -1; + goto err; } if (sql_create_definition_file(&dir, &file, view_file_type, (gptr)view, view_parameters, num_view_backups)) { - DBUG_RETURN(thd->net.report_error? -1 : 1); + error= thd->net.report_error? -1 : 1; + goto err; } DBUG_RETURN(0); +err: + view->query.str= NULL; + view->query.length= 0; + view->md5.str= NULL; + view->md5.length= NULL; + DBUG_RETURN(error); }