mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
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.
This commit is contained in:
@ -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_)
|
|||||||
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
|
|||||||
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;
|
|||||||
|
|
||||||
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;
|
|||||||
|
|
||||||
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;
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
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);
|
|||||||
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:
|
|||||||
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,
|
|||||||
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;
|
||||||
|
Reference in New Issue
Block a user