Commit d36f5781 authored by unknown's avatar unknown

Fix for BUG#20438: CREATE statements for views, stored routines and triggers

can be not replicable.

Now CREATE statements for writing in the binlog are created as follows:
  - the beginning of the statement is re-created;
  - the rest of the statement is copied from the original query.

The problem appears when there is a version-specific comment (produced by
mysqldump), started in the re-created part of the statement and closed in the
copied part -- there is closing comment-parenthesis, but there is no opening
one.

The proper fix could be to re-create original statement, but we can not
implement it in 5.0. So, for 5.0 the fix is just to cut closing
comment-parenthesis. This technique is also used for SHOW CREATE PROCEDURE
statement (so we are able to reuse existing code).


mysql-test/r/rpl_sp.result:
  Updated result file.
mysql-test/r/rpl_trigger.result:
  Updated result file.
mysql-test/r/rpl_view.result:
  Updated result file.
mysql-test/t/rpl_sp.test:
  Added test case for BUG#20438.
mysql-test/t/rpl_trigger.test:
  Added test case for BUG#20438.
mysql-test/t/rpl_view.test:
  Added test case for BUG#20438.
sql/sp.cc:
  Trim comments at the end.
sql/sp_head.cc:
  Moved this code to the separate function to be re-used.
sql/sql_lex.cc:
  Added a new function.
sql/sql_lex.h:
  Added a new function.
sql/sql_trigger.cc:
  Trim comments at the end.
sql/sql_view.cc:
  Trim comments at the end.
parent 3c108584
...@@ -420,4 +420,48 @@ SELECT * FROM t1; ...@@ -420,4 +420,48 @@ SELECT * FROM t1;
col col
test test
DROP PROCEDURE p1; 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; drop table t1;
...@@ -896,3 +896,50 @@ Tables_in_test (t_) ...@@ -896,3 +896,50 @@ Tables_in_test (t_)
SHOW TRIGGERS; SHOW TRIGGERS;
Trigger Event Table Statement Timing Created sql_mode Definer Trigger Event Table Statement Timing Created sql_mode Definer
RESET MASTER; 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;
...@@ -54,3 +54,40 @@ slave-bin.000001 # Query 1 # use `test`; delete from v1 where a=2 ...@@ -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`; 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 view v1
slave-bin.000001 # Query 1 # use `test`; drop table t1 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;
...@@ -435,6 +435,86 @@ connection master; ...@@ -435,6 +435,86 @@ connection master;
DROP PROCEDURE p1; 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 # cleanup
connection master; connection master;
drop table t1; drop table t1;
......
...@@ -331,6 +331,98 @@ SHOW TRIGGERS; ...@@ -331,6 +331,98 @@ SHOW TRIGGERS;
RESET MASTER; 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 # End of tests
......
...@@ -45,3 +45,87 @@ drop table t1; ...@@ -45,3 +45,87 @@ drop table t1;
sync_slave_with_master; sync_slave_with_master;
--replace_column 2 # 5 # --replace_column 2 # 5 #
show binlog events limit 1,100; 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
...@@ -633,7 +633,10 @@ db_create_routine(THD *thd, int type, sp_head *sp) ...@@ -633,7 +633,10 @@ db_create_routine(THD *thd, int type, sp_head *sp)
log_query.append(STRING_WITH_LEN("CREATE ")); log_query.append(STRING_WITH_LEN("CREATE "));
append_definer(thd, &log_query, &thd->lex->definer->user, append_definer(thd, &log_query, &thd->lex->definer->user,
&thd->lex->definer->host); &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 */ /* 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, Query_log_event qinfo(thd, log_query.c_ptr(), log_query.length(), 0,
......
...@@ -527,10 +527,7 @@ sp_head::init_strings(THD *thd, LEX *lex) ...@@ -527,10 +527,7 @@ sp_head::init_strings(THD *thd, LEX *lex)
Trim "garbage" at the end. This is sometimes needed with the Trim "garbage" at the end. This is sometimes needed with the
"/ * ! VERSION... * /" wrapper in dump files. "/ * ! VERSION... * /" wrapper in dump files.
*/ */
while (m_body_begin < endp && endp= skip_rear_comments(m_body_begin, endp);
(endp[-1] <= ' ' || endp[-1] == '*' ||
endp[-1] == '/' || endp[-1] == ';'))
endp-= 1;
m_body.length= endp - m_body_begin; m_body.length= endp - m_body_begin;
m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length); m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length);
......
...@@ -1054,6 +1054,30 @@ int MYSQLlex(void *arg, void *yythd) ...@@ -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 st_select_lex structures initialisations
*/ */
......
...@@ -1115,4 +1115,4 @@ extern void lex_free(void); ...@@ -1115,4 +1115,4 @@ extern void lex_free(void);
extern void lex_start(THD *thd, uchar *buf,uint length); extern void lex_start(THD *thd, uchar *buf,uint length);
extern void lex_end(LEX *lex); extern void lex_end(LEX *lex);
extern int MYSQLlex(void *arg, void *yythd); extern int MYSQLlex(void *arg, void *yythd);
extern uchar *skip_rear_comments(uchar *begin, uchar *end);
...@@ -295,7 +295,10 @@ end: ...@@ -295,7 +295,10 @@ end:
append_definer(thd, &log_query, &definer_user, &definer_host); 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. */ /* Such a statement can always go directly to binlog, no trans cache. */
......
...@@ -671,8 +671,10 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, ...@@ -671,8 +671,10 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
view->query.str= (char*)str.ptr(); view->query.str= (char*)str.ptr();
view->query.length= str.length()-1; // we do not need last \0 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.str= thd->query + thd->lex->create_view_select_start;
view->source.length= (thd->query_length - view->source.length= (char *)skip_rear_comments((uchar *)view->source.str,
thd->lex->create_view_select_start); (uchar *)thd->query +
thd->query_length) -
view->source.str;
view->file_version= 1; view->file_version= 1;
view->calc_md5(md5); view->calc_md5(md5);
view->md5.str= md5; view->md5.str= md5;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment