summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/rpl_sp.result44
-rw-r--r--mysql-test/r/rpl_trigger.result47
-rw-r--r--mysql-test/r/rpl_view.result37
-rw-r--r--mysql-test/r/show_check.result4
-rw-r--r--mysql-test/t/rpl_sp.test80
-rw-r--r--mysql-test/t/rpl_trigger.test92
-rw-r--r--mysql-test/t/rpl_view.test84
-rw-r--r--mysql-test/t/show_check.test11
-rw-r--r--sql/mysql_priv.h2
-rw-r--r--sql/sp.cc5
-rw-r--r--sql/sp_head.cc5
-rw-r--r--sql/sql_lex.cc24
-rw-r--r--sql/sql_lex.h2
-rw-r--r--sql/sql_show.cc81
-rw-r--r--sql/sql_trigger.cc5
-rw-r--r--sql/sql_view.cc6
16 files changed, 503 insertions, 26 deletions
diff --git a/mysql-test/r/rpl_sp.result b/mysql-test/r/rpl_sp.result
index 5dfda16c763..7b096b27733 100644
--- a/mysql-test/r/rpl_sp.result
+++ b/mysql-test/r/rpl_sp.result
@@ -420,4 +420,48 @@ SELECT * FROM t1;
col
test
DROP PROCEDURE p1;
+
+---> Test for BUG#20438
+
+---> Preparing environment...
+---> connection: master
+DROP PROCEDURE IF EXISTS p1;
+DROP FUNCTION IF EXISTS f1;
+
+---> Synchronizing slave with master...
+
+---> connection: master
+
+---> Creating procedure...
+/*!50003 CREATE PROCEDURE p1() SET @a = 1 */;
+/*!50003 CREATE FUNCTION f1() RETURNS INT RETURN 0 */;
+
+---> Checking on master...
+SHOW CREATE PROCEDURE p1;
+Procedure sql_mode Create Procedure
+p1 CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`()
+SET @a = 1
+SHOW CREATE FUNCTION f1;
+Function sql_mode Create Function
+f1 CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11)
+RETURN 0
+
+---> Synchronizing slave with master...
+---> connection: master
+
+---> Checking on slave...
+SHOW CREATE PROCEDURE p1;
+Procedure sql_mode Create Procedure
+p1 CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`()
+SET @a = 1
+SHOW CREATE FUNCTION f1;
+Function sql_mode Create Function
+f1 CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11)
+RETURN 0
+
+---> connection: master
+
+---> Cleaning up...
+DROP PROCEDURE p1;
+DROP FUNCTION f1;
drop table t1;
diff --git a/mysql-test/r/rpl_trigger.result b/mysql-test/r/rpl_trigger.result
index 3e4a3349e13..49f0f5c4c44 100644
--- a/mysql-test/r/rpl_trigger.result
+++ b/mysql-test/r/rpl_trigger.result
@@ -896,3 +896,50 @@ Tables_in_test (t_)
SHOW TRIGGERS;
Trigger Event Table Statement Timing Created sql_mode Definer
RESET MASTER;
+START SLAVE;
+
+---> Test for BUG#20438
+
+---> Preparing environment...
+---> connection: master
+DROP TABLE IF EXISTS t1;
+DROP TABLE IF EXISTS t2;
+
+---> Synchronizing slave with master...
+
+---> connection: master
+
+---> Creating objects...
+CREATE TABLE t1(c INT);
+CREATE TABLE t2(c INT);
+/*!50003 CREATE TRIGGER t1_bi BEFORE INSERT ON t1
+FOR EACH ROW
+INSERT INTO t2 VALUES(NEW.c * 10) */;
+
+---> Inserting value...
+INSERT INTO t1 VALUES(1);
+
+---> Checking on master...
+SELECT * FROM t1;
+c
+1
+SELECT * FROM t2;
+c
+10
+
+---> Synchronizing slave with master...
+---> connection: master
+
+---> Checking on slave...
+SELECT * FROM t1;
+c
+1
+SELECT * FROM t2;
+c
+10
+
+---> connection: master
+
+---> Cleaning up...
+DROP TABLE t1;
+DROP TABLE t2;
diff --git a/mysql-test/r/rpl_view.result b/mysql-test/r/rpl_view.result
index cf4c161b296..5a101defe38 100644
--- a/mysql-test/r/rpl_view.result
+++ b/mysql-test/r/rpl_view.result
@@ -54,3 +54,40 @@ slave-bin.000001 # Query 1 # use `test`; delete from v1 where a=2
slave-bin.000001 # Query 1 # use `test`; ALTER ALGORITHM=UNDEFINED DEFINER=root@localhost SQL SECURITY DEFINER VIEW v1 AS select a as b from t1
slave-bin.000001 # Query 1 # use `test`; drop view v1
slave-bin.000001 # Query 1 # use `test`; drop table t1
+
+---> Test for BUG#20438
+
+---> Preparing environment...
+---> connection: master
+DROP TABLE IF EXISTS t1;
+DROP VIEW IF EXISTS v1;
+
+---> Synchronizing slave with master...
+
+---> connection: master
+
+---> Creating objects...
+CREATE TABLE t1(c INT);
+/*!50003 CREATE VIEW v1 AS SELECT * FROM t1 */;
+
+---> Inserting value...
+INSERT INTO t1 VALUES(1);
+
+---> Checking on master...
+SELECT * FROM t1;
+c
+1
+
+---> Synchronizing slave with master...
+---> connection: master
+
+---> Checking on slave...
+SELECT * FROM t1;
+c
+1
+
+---> connection: master
+
+---> Cleaning up...
+DROP VIEW v1;
+DROP TABLE t1;
diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result
index 994501767ba..7bdfa78066c 100644
--- a/mysql-test/r/show_check.result
+++ b/mysql-test/r/show_check.result
@@ -625,3 +625,7 @@ View Create View
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select sql_cache 1 AS `1`
DROP PROCEDURE p1;
DROP VIEW v1;
+SHOW TABLES FROM no_such_database;
+ERROR 42000: Unknown database 'no_such_database'
+SHOW COLUMNS FROM no_such_table;
+ERROR 42S02: Table 'test.no_such_table' doesn't exist
diff --git a/mysql-test/t/rpl_sp.test b/mysql-test/t/rpl_sp.test
index 8be17be3822..7479794eded 100644
--- a/mysql-test/t/rpl_sp.test
+++ b/mysql-test/t/rpl_sp.test
@@ -435,6 +435,86 @@ connection master;
DROP PROCEDURE p1;
+
+#
+# BUG#20438: CREATE statements for views, stored routines and triggers can be
+# not replicable.
+#
+
+--echo
+--echo ---> Test for BUG#20438
+
+# Prepare environment.
+
+--echo
+--echo ---> Preparing environment...
+--echo ---> connection: master
+--connection master
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS p1;
+DROP FUNCTION IF EXISTS f1;
+--enable_warnings
+
+--echo
+--echo ---> Synchronizing slave with master...
+
+--save_master_pos
+--connection slave
+--sync_with_master
+
+--echo
+--echo ---> connection: master
+--connection master
+
+# Test.
+
+--echo
+--echo ---> Creating procedure...
+
+/*!50003 CREATE PROCEDURE p1() SET @a = 1 */;
+
+/*!50003 CREATE FUNCTION f1() RETURNS INT RETURN 0 */;
+
+--echo
+--echo ---> Checking on master...
+
+SHOW CREATE PROCEDURE p1;
+SHOW CREATE FUNCTION f1;
+
+--echo
+--echo ---> Synchronizing slave with master...
+
+--save_master_pos
+--connection slave
+--sync_with_master
+
+--echo ---> connection: master
+
+--echo
+--echo ---> Checking on slave...
+
+SHOW CREATE PROCEDURE p1;
+SHOW CREATE FUNCTION f1;
+
+# Cleanup.
+
+--echo
+--echo ---> connection: master
+--connection master
+
+--echo
+--echo ---> Cleaning up...
+
+DROP PROCEDURE p1;
+DROP FUNCTION f1;
+
+--save_master_pos
+--connection slave
+--sync_with_master
+--connection master
+
+
# cleanup
connection master;
drop table t1;
diff --git a/mysql-test/t/rpl_trigger.test b/mysql-test/t/rpl_trigger.test
index 35f0a0b0a4b..3c8cbb97b31 100644
--- a/mysql-test/t/rpl_trigger.test
+++ b/mysql-test/t/rpl_trigger.test
@@ -331,6 +331,98 @@ SHOW TRIGGERS;
RESET MASTER;
+# Restart slave.
+
+connection slave;
+START SLAVE;
+
+
+#
+# BUG#20438: CREATE statements for views, stored routines and triggers can be
+# not replicable.
+#
+
+--echo
+--echo ---> Test for BUG#20438
+
+# Prepare environment.
+
+--echo
+--echo ---> Preparing environment...
+--echo ---> connection: master
+--connection master
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+DROP TABLE IF EXISTS t2;
+--enable_warnings
+
+--echo
+--echo ---> Synchronizing slave with master...
+
+--save_master_pos
+--connection slave
+--sync_with_master
+
+--echo
+--echo ---> connection: master
+--connection master
+
+# Test.
+
+--echo
+--echo ---> Creating objects...
+
+CREATE TABLE t1(c INT);
+CREATE TABLE t2(c INT);
+
+/*!50003 CREATE TRIGGER t1_bi BEFORE INSERT ON t1
+ FOR EACH ROW
+ INSERT INTO t2 VALUES(NEW.c * 10) */;
+
+--echo
+--echo ---> Inserting value...
+
+INSERT INTO t1 VALUES(1);
+
+--echo
+--echo ---> Checking on master...
+
+SELECT * FROM t1;
+SELECT * FROM t2;
+
+--echo
+--echo ---> Synchronizing slave with master...
+
+--save_master_pos
+--connection slave
+--sync_with_master
+
+--echo ---> connection: master
+
+--echo
+--echo ---> Checking on slave...
+
+SELECT * FROM t1;
+SELECT * FROM t2;
+
+# Cleanup.
+
+--echo
+--echo ---> connection: master
+--connection master
+
+--echo
+--echo ---> Cleaning up...
+
+DROP TABLE t1;
+DROP TABLE t2;
+
+--save_master_pos
+--connection slave
+--sync_with_master
+--connection master
+
#
# End of tests
diff --git a/mysql-test/t/rpl_view.test b/mysql-test/t/rpl_view.test
index 0a0c6a6dddb..d0990b4fbee 100644
--- a/mysql-test/t/rpl_view.test
+++ b/mysql-test/t/rpl_view.test
@@ -45,3 +45,87 @@ drop table t1;
sync_slave_with_master;
--replace_column 2 # 5 #
show binlog events limit 1,100;
+
+
+
+#
+# BUG#20438: CREATE statements for views, stored routines and triggers can be
+# not replicable.
+#
+
+--echo
+--echo ---> Test for BUG#20438
+
+# Prepare environment.
+
+--echo
+--echo ---> Preparing environment...
+--echo ---> connection: master
+--connection master
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+DROP VIEW IF EXISTS v1;
+--enable_warnings
+
+--echo
+--echo ---> Synchronizing slave with master...
+
+--save_master_pos
+--connection slave
+--sync_with_master
+
+--echo
+--echo ---> connection: master
+--connection master
+
+# Test.
+
+--echo
+--echo ---> Creating objects...
+
+CREATE TABLE t1(c INT);
+
+/*!50003 CREATE VIEW v1 AS SELECT * FROM t1 */;
+
+--echo
+--echo ---> Inserting value...
+
+INSERT INTO t1 VALUES(1);
+
+--echo
+--echo ---> Checking on master...
+
+SELECT * FROM t1;
+
+--echo
+--echo ---> Synchronizing slave with master...
+
+--save_master_pos
+--connection slave
+--sync_with_master
+
+--echo ---> connection: master
+
+--echo
+--echo ---> Checking on slave...
+
+SELECT * FROM t1;
+
+# Cleanup.
+
+--echo
+--echo ---> connection: master
+--connection master
+
+--echo
+--echo ---> Cleaning up...
+
+DROP VIEW v1;
+DROP TABLE t1;
+
+--save_master_pos
+--connection slave
+--sync_with_master
+--connection master
+
diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test
index 6937cbe949d..65a81545c87 100644
--- a/mysql-test/t/show_check.test
+++ b/mysql-test/t/show_check.test
@@ -495,4 +495,15 @@ SHOW CREATE VIEW v1;
DROP PROCEDURE p1;
DROP VIEW v1;
+
+#
+# Check that SHOW TABLES and SHOW COLUMNS give a error if there is no
+# referenced database and table respectively.
+#
+--error ER_BAD_DB_ERROR
+SHOW TABLES FROM no_such_database;
+--error ER_NO_SUCH_TABLE
+SHOW COLUMNS FROM no_such_table;
+
+
# End of 5.0 tests.
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 51ce7224684..c0536fad738 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -865,8 +865,6 @@ bool mysqld_show_create_db(THD *thd, char *dbname, HA_CREATE_INFO *create);
void mysqld_list_processes(THD *thd,const char *user,bool verbose);
int mysqld_show_status(THD *thd);
int mysqld_show_variables(THD *thd,const char *wild);
-int mysql_find_files(THD *thd,List<char> *files, const char *db,
- const char *path, const char *wild, bool dir);
bool mysqld_show_storage_engines(THD *thd);
bool mysqld_show_privileges(THD *thd);
bool mysqld_show_column_types(THD *thd);
diff --git a/sql/sp.cc b/sql/sp.cc
index b5a4f8bad8f..e8d36e15fa2 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -633,7 +633,10 @@ db_create_routine(THD *thd, int type, sp_head *sp)
log_query.append(STRING_WITH_LEN("CREATE "));
append_definer(thd, &log_query, &thd->lex->definer->user,
&thd->lex->definer->host);
- log_query.append(thd->lex->stmt_definition_begin);
+ log_query.append(thd->lex->stmt_definition_begin,
+ (char *)sp->m_body_begin -
+ thd->lex->stmt_definition_begin +
+ sp->m_body.length);
/* Such a statement can always go directly to binlog, no trans cache */
Query_log_event qinfo(thd, log_query.c_ptr(), log_query.length(), 0,
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 7bcadde2760..eec6e0fc3cd 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -527,10 +527,7 @@ sp_head::init_strings(THD *thd, LEX *lex)
Trim "garbage" at the end. This is sometimes needed with the
"/ * ! VERSION... * /" wrapper in dump files.
*/
- while (m_body_begin < endp &&
- (endp[-1] <= ' ' || endp[-1] == '*' ||
- endp[-1] == '/' || endp[-1] == ';'))
- endp-= 1;
+ endp= skip_rear_comments(m_body_begin, endp);
m_body.length= endp - m_body_begin;
m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length);
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index efbf29cf207..bb66fde79fe 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1054,6 +1054,30 @@ int MYSQLlex(void *arg, void *yythd)
}
}
+
+/*
+ Skip comment in the end of statement.
+
+ SYNOPSIS
+ skip_rear_comments()
+ begin pointer to the beginning of statement
+ end pointer to the end of statement
+
+ DESCRIPTION
+ The function is intended to trim comments at the end of the statement.
+
+ RETURN
+ Pointer to the last non-comment symbol of the statement.
+*/
+
+uchar *skip_rear_comments(uchar *begin, uchar *end)
+{
+ while (begin < end && (end[-1] <= ' ' || end[-1] == '*' ||
+ end[-1] == '/' || end[-1] == ';'))
+ end-= 1;
+ return end;
+}
+
/*
st_select_lex structures initialisations
*/
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index e5b087fc72a..d7438a37d7e 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1115,4 +1115,4 @@ extern void lex_free(void);
extern void lex_start(THD *thd, uchar *buf,uint length);
extern void lex_end(LEX *lex);
extern int MYSQLlex(void *arg, void *yythd);
-
+extern uchar *skip_rear_comments(uchar *begin, uchar *end);
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 1a42ef81487..805b6c597e6 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -250,9 +250,35 @@ bool mysqld_show_column_types(THD *thd)
}
-int
-mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
- const char *wild, bool dir)
+/*
+ find_files() - find files in a given directory.
+
+ SYNOPSIS
+ find_files()
+ thd thread handler
+ files put found files in this list
+ db database name to set in TABLE_LIST structure
+ path path to database
+ wild filter for found files
+ dir read databases in path if TRUE, read .frm files in
+ database otherwise
+
+ RETURN
+ FIND_FILES_OK success
+ FIND_FILES_OOM out of memory error
+ FIND_FILES_DIR no such directory, or directory can't be read
+*/
+
+enum find_files_result {
+ FIND_FILES_OK,
+ FIND_FILES_OOM,
+ FIND_FILES_DIR
+};
+
+static
+find_files_result
+find_files(THD *thd, List<char> *files, const char *db,
+ const char *path, const char *wild, bool dir)
{
uint i;
char *ext;
@@ -262,7 +288,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
uint col_access=thd->col_access;
#endif
TABLE_LIST table_list;
- DBUG_ENTER("mysql_find_files");
+ DBUG_ENTER("find_files");
if (wild && !wild[0])
wild=0;
@@ -275,7 +301,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db);
else
my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), path, my_errno);
- DBUG_RETURN(-1);
+ DBUG_RETURN(FIND_FILES_DIR);
}
for (i=0 ; i < (uint) dirp->number_off_files ; i++)
@@ -337,7 +363,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
if (files->push_back(thd->strdup(file->name)))
{
my_dirend(dirp);
- DBUG_RETURN(-1);
+ DBUG_RETURN(FIND_FILES_OOM);
}
}
DBUG_PRINT("info",("found: %d files", files->elements));
@@ -345,7 +371,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
VOID(ha_find_files(thd,db,path,wild,dir,files));
- DBUG_RETURN(0);
+ DBUG_RETURN(FIND_FILES_OK);
}
@@ -1988,8 +2014,8 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table)
wild string otherwise it's db name;
RETURN
- 1 error
- 0 success
+ zero success
+ non-zero error
*/
int make_db_list(THD *thd, List<char> *files,
@@ -2015,8 +2041,8 @@ int make_db_list(THD *thd, List<char> *files,
if (files->push_back(thd->strdup(information_schema_name.str)))
return 1;
}
- return mysql_find_files(thd, files, NullS, mysql_data_home,
- idx_field_vals->db_value, 1);
+ return (find_files(thd, files, NullS, mysql_data_home,
+ idx_field_vals->db_value, 1) != FIND_FILES_OK);
}
/*
@@ -2043,7 +2069,8 @@ int make_db_list(THD *thd, List<char> *files,
if (files->push_back(thd->strdup(information_schema_name.str)))
return 1;
*with_i_schema= 1;
- return mysql_find_files(thd, files, NullS, mysql_data_home, NullS, 1);
+ return (find_files(thd, files, NullS,
+ mysql_data_home, NullS, 1) != FIND_FILES_OK);
}
@@ -2192,9 +2219,28 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
strxmov(path, mysql_data_home, "/", base_name, NullS);
end= path + (len= unpack_dirname(path,path));
len= FN_LEN - len;
- if (mysql_find_files(thd, &files, base_name,
- path, idx_field_vals.table_value, 0))
- goto err;
+ find_files_result res= find_files(thd, &files, base_name,
+ path, idx_field_vals.table_value, 0);
+ if (res != FIND_FILES_OK)
+ {
+ /*
+ Downgrade errors about problems with database directory to
+ warnings if this is not a 'SHOW' command. Another thread
+ may have dropped database, and we may still have a name
+ for that directory.
+ */
+ if (res == FIND_FILES_DIR && lex->orig_sql_command == SQLCOM_END)
+ {
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ thd->net.last_errno, thd->net.last_error);
+ thd->clear_error();
+ continue;
+ }
+ else
+ {
+ goto err;
+ }
+ }
if (lower_case_table_names)
orig_base_name= thd->strdup(base_name);
}
@@ -3949,7 +3995,12 @@ bool get_schema_tables_result(JOIN *join)
if (table_list->schema_table->fill_table(thd, table_list,
tab->select_cond))
+ {
result= 1;
+ join->error= 1;
+ table_list->is_schema_table_processed= TRUE;
+ break;
+ }
table_list->is_schema_table_processed= TRUE;
}
}
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index f46c7414fa4..e806dd4a3f3 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -295,7 +295,10 @@ end:
append_definer(thd, &log_query, &definer_user, &definer_host);
}
- log_query.append(thd->lex->stmt_definition_begin);
+ log_query.append(thd->lex->stmt_definition_begin,
+ (char *)thd->lex->sphead->m_body_begin -
+ thd->lex->stmt_definition_begin +
+ thd->lex->sphead->m_body.length);
}
/* Such a statement can always go directly to binlog, no trans cache. */
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 1561ade78af..80cb8970049 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -671,8 +671,10 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
view->query.str= (char*)str.ptr();
view->query.length= str.length()-1; // we do not need last \0
view->source.str= thd->query + thd->lex->create_view_select_start;
- view->source.length= (thd->query_length -
- thd->lex->create_view_select_start);
+ view->source.length= (char *)skip_rear_comments((uchar *)view->source.str,
+ (uchar *)thd->query +
+ thd->query_length) -
+ view->source.str;
view->file_version= 1;
view->calc_md5(md5);
view->md5.str= md5;