diff --git a/client/mysqldump.c b/client/mysqldump.c index 7dccbf95e20..cf2936bd7d8 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -80,6 +80,7 @@ static char *add_load_option(char *ptr, const char *object, const char *statement); static ulong find_set(TYPELIB *lib, const char *x, uint length, char **err_pos, uint *err_len); +static char *alloc_query_str(ulong size); static char *field_escape(char *to,const char *from,uint length); static my_bool verbose=0,tFlag=0,dFlag=0,quick= 1, extended_insert= 1, @@ -95,6 +96,7 @@ static my_bool verbose=0,tFlag=0,dFlag=0,quick= 1, extended_insert= 1, opt_complete_insert= 0, opt_drop_database= 0, opt_replace_into= 0, opt_dump_triggers= 0, opt_routines=0, opt_tz_utc=1, + opt_events= 0, opt_alltspcs=0; static ulong opt_max_allowed_packet, opt_net_buffer_length; static MYSQL mysql_connection,*sock=0; @@ -231,6 +233,9 @@ static struct my_option my_long_options[] = {"disable-keys", 'K', "'/*!40000 ALTER TABLE tb_name DISABLE KEYS */; and '/*!40000 ALTER TABLE tb_name ENABLE KEYS */; will be put in the output.", (gptr*) &opt_disable_keys, (gptr*) &opt_disable_keys, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, + {"events", 'E', "Dump events.", + (gptr*) &opt_events, (gptr*) &opt_events, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, {"extended-insert", 'e', "Allows utilization of the new, much faster INSERT syntax.", (gptr*) &extended_insert, (gptr*) &extended_insert, 0, GET_BOOL, NO_ARG, @@ -1238,9 +1243,136 @@ static void print_xml_row(FILE *xml_file, const char *row_name, check_io(xml_file); } + +/* + create_delimiter + Generate a new (null-terminated) string that does not exist in query + and is therefore suitable for use as a query delimiter. Store this + delimiter in delimiter_buff . + + This is quite simple in that it doesn't even try to parse statements as an + interpreter would. It merely returns a string that is not in the query, which + is much more than adequate for constructing a delimiter. + + RETURN + ptr to the delimiter on Success + NULL on Failure +*/ +static char *create_delimiter(char *query, char *delimiter_buff, + int delimiter_max_size) +{ + int proposed_length; + char *presence; + + delimiter_buff[0]= ';'; /* start with one semicolon, and */ + + for (proposed_length= 2; proposed_length < delimiter_max_size; + delimiter_max_size++) { + + delimiter_buff[proposed_length-1]= ';'; /* add semicolons, until */ + delimiter_buff[proposed_length]= '\0'; + + presence = strstr(query, delimiter_buff); + if (presence == NULL) { /* the proposed delimiter is not in the query. */ + return delimiter_buff; + } + + } + return NULL; /* but if we run out of space, return nothing at all. */ +} + + +/* + dump_events_for_db + -- retrieves list of events for a given db, and prints out + the CREATE EVENT statement into the output (the dump). + + RETURN + 0 Success + 1 Error +*/ +static uint dump_events_for_db(char *db) +{ + char query_buff[QUERY_LENGTH]; + char db_name_buff[NAME_LEN*2+3], name_buff[NAME_LEN*2+3]; + char *event_name; + char delimiter[QUERY_LENGTH], *delimit_test; + FILE *sql_file= md_result_file; + MYSQL_RES *event_res, *event_list_res; + MYSQL_ROW row, event_list_row; + DBUG_ENTER("dump_events_for_db"); + DBUG_PRINT("enter", ("db: '%s'", db)); + + mysql_real_escape_string(sock, db_name_buff, db, strlen(db)); + + /* nice comments */ + if (opt_comments) + fprintf(sql_file, "\n--\n-- Dumping events for database '%s'\n--\n", db); + + /* + not using "mysql_query_with_error_report" because we may have not + enough privileges to lock mysql.events. + */ + if (lock_tables) + mysql_query(sock, "LOCK TABLES mysql.event READ"); + + if (mysql_query_with_error_report(sock, &event_list_res, "show events")) + { + safe_exit(EX_MYSQLERR); + DBUG_RETURN(0); + } + + strcpy(delimiter, ";"); + if (mysql_num_rows(event_list_res) > 0) + { + while ((event_list_row= mysql_fetch_row(event_list_res)) != NULL) + { + event_name= quote_name(event_list_row[1], name_buff, 0); + DBUG_PRINT("info", ("retrieving CREATE EVENT for %s", name_buff)); + my_snprintf(query_buff, sizeof(query_buff), "SHOW CREATE EVENT %s", + event_name); + + if (mysql_query_with_error_report(sock, &event_res, query_buff)) + DBUG_RETURN(1); + + while ((row= mysql_fetch_row(event_res)) != NULL) + { + /* + if the user has EXECUTE privilege he can see event names, but not the + event body! + */ + if (strlen(row[2]) != 0) + { + if (opt_drop) + fprintf(sql_file, "/*!50106 DROP EVENT IF EXISTS %s */%s\n", + event_name, delimiter); + + delimit_test= create_delimiter(row[2], delimiter, sizeof(delimiter)); + if (delimit_test == NULL) { + fprintf(stderr, "%s: Warning: Can't dump event '%s'\n", + event_name, my_progname); + DBUG_RETURN(1); + } + + fprintf(sql_file, "DELIMITER %s\n", delimiter); + fprintf(sql_file, "/*!50106 %s */ %s\n", row[2], delimiter); + } + } /* end of event printing */ + } /* end of list of events */ + fprintf(sql_file, "DELIMITER ;\n"); + mysql_free_result(event_res); + } + mysql_free_result(event_list_res); + + if (lock_tables) + VOID(mysql_query_with_error_report(sock, 0, "UNLOCK TABLES")); + DBUG_RETURN(0); +} + + /* dump_routines_for_db - -- retrievs list of routines for a given db, and prints out + -- retrieves list of routines for a given db, and prints out the CREATE PROCEDURE definition into the output (the dump). This function has logic to print the appropriate syntax depending on whether @@ -1253,7 +1385,7 @@ static void print_xml_row(FILE *xml_file, const char *row_name, static uint dump_routines_for_db(char *db) { - char query_buff[512]; + char query_buff[QUERY_LENGTH]; const char *routine_type[]= {"FUNCTION", "PROCEDURE"}; char db_name_buff[NAME_LEN*2+3], name_buff[NAME_LEN*2+3]; char *routine_name; @@ -1294,9 +1426,9 @@ static uint dump_routines_for_db(char *db) while ((routine_list_row= mysql_fetch_row(routine_list_res))) { + routine_name= quote_name(routine_list_row[1], name_buff, 0); DBUG_PRINT("info", ("retrieving CREATE %s for %s", routine_type[i], name_buff)); - routine_name= quote_name(routine_list_row[1], name_buff, 0); my_snprintf(query_buff, sizeof(query_buff), "SHOW CREATE %s %s", routine_type[i], routine_name); @@ -1313,19 +1445,64 @@ static uint dump_routines_for_db(char *db) routine_name, row[2], strlen(row[2]))); if (strlen(row[2])) { + char *query_str= NULL; + char *definer_begin; + if (opt_drop) fprintf(sql_file, "/*!50003 DROP %s IF EXISTS %s */;;\n", routine_type[i], routine_name); + + /* + Cover DEFINER-clause in version-specific comments. + + TODO: this is definitely a BAD IDEA to parse SHOW CREATE output. + We should user INFORMATION_SCHEMA instead. The only problem is + that now INFORMATION_SCHEMA does not provide information about + routine parameters. + */ + + definer_begin= strstr(row[2], " DEFINER"); + + if (definer_begin) + { + char *definer_end= strstr(definer_begin, " PROCEDURE"); + + if (!definer_end) + definer_end= strstr(definer_begin, " FUNCTION"); + + if (definer_end) + { + char *query_str_tail; + + /* + Allocate memory for new query string: original string + from SHOW statement and version-specific comments. + */ + query_str= alloc_query_str(strlen(row[2]) + 23); + + query_str_tail= strnmov(query_str, row[2], + definer_begin - row[2]); + query_str_tail= strmov(query_str_tail, "*/ /*!50019"); + query_str_tail= strnmov(query_str_tail, definer_begin, + definer_end - definer_begin); + query_str_tail= strxmov(query_str_tail, "*/ /*!50003", + definer_end, NullS); + } + } + /* we need to change sql_mode only for the CREATE PROCEDURE/FUNCTION otherwise we may need to re-quote routine_name */; fprintf(sql_file, "/*!50003 SET SESSION SQL_MODE=\"%s\"*/;;\n", row[1] /* sql_mode */); - fprintf(sql_file, "/*!50003 %s */;;\n", row[2]); + fprintf(sql_file, "/*!50003 %s */;;\n", + (query_str != NULL ? query_str : row[2])); fprintf(sql_file, "/*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/" ";;\n"); + + my_free(query_str, MYF(MY_ALLOW_ZERO_PTR)); } } /* end of routine printing */ } /* end of list of routines */ @@ -1364,7 +1541,7 @@ static uint get_table_structure(char *table, char *db, char *table_type, char *result_table, *opt_quoted_table; const char *insert_option; char name_buff[NAME_LEN+3],table_buff[NAME_LEN*2+3]; - char table_buff2[NAME_LEN*2+3], query_buff[512]; + char table_buff2[NAME_LEN*2+3], query_buff[QUERY_LENGTH]; FILE *sql_file = md_result_file; int len; MYSQL_RES *result; @@ -1840,7 +2017,7 @@ static void dump_triggers_for_table (char *table, char *db) { char *result_table; char name_buff[NAME_LEN*4+3], table_buff[NAME_LEN*2+3]; - char query_buff[512]; + char query_buff[QUERY_LENGTH]; uint old_opt_compatible_mode=opt_compatible_mode; FILE *sql_file = md_result_file; MYSQL_RES *result; @@ -2500,7 +2677,6 @@ static int dump_all_tablespaces() { MYSQL_ROW row; MYSQL_RES *tableres; - int result=0; char buf[FN_REFLEN]; int first; @@ -2798,6 +2974,12 @@ static int dump_all_tables_in_db(char *database) dump_triggers_for_table(table, database); } } + if (opt_events && !opt_xml && + mysql_get_server_version(sock) >= 50106) + { + DBUG_PRINT("info", ("Dumping events for database %s", database)); + dump_events_for_db(database); + } if (opt_routines && !opt_xml && mysql_get_server_version(sock) >= 50009) { @@ -3008,6 +3190,12 @@ static int dump_selected_tables(char *db, char **table_names, int tables) get_view_structure(table_name, db); } } + if (opt_events && !opt_xml && + mysql_get_server_version(sock) >= 50106) + { + DBUG_PRINT("info", ("Dumping events for database %s", db)); + dump_events_for_db(db); + } /* obtain dump of routines (procs/functions) */ if (opt_routines && !opt_xml && mysql_get_server_version(sock) >= 50009) diff --git a/configure.in b/configure.in index 8d1fcf70461..21bf9559245 100644 --- a/configure.in +++ b/configure.in @@ -214,7 +214,7 @@ AC_PROG_INSTALL test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' # Not critical since the generated file is distributed -AC_PROG_YACC +AC_CHECK_PROGS(YACC, ['bison -y -p MYSQL']) AC_CHECK_PROG(PDFMANUAL, pdftex, manual.pdf) AC_CHECK_PROG(DVIS, tex, manual.dvi) diff --git a/mysql-test/include/ndb_restore_slave_eoption.inc b/mysql-test/include/ndb_restore_slave_eoption.inc new file mode 100644 index 00000000000..f1f6cf96881 --- /dev/null +++ b/mysql-test/include/ndb_restore_slave_eoption.inc @@ -0,0 +1,11 @@ +###################################################### +# By JBM 2006-03-08 So that the code is not repeated # +# in test cases and can be reused. # +###################################################### + +--exec $NDB_TOOLS_DIR/ndb_restore --no-defaults --ndb-connectstring="localhost:$NDBCLUSTER_PORT_SLAVE" -p 8 -b $the_backup_id -n 1 -m -r --print --print_meta $NDB_BACKUP_DIR/BACKUP/BACKUP-$the_backup_id >> $NDB_TOOLS_OUTPUT + +--exec $NDB_TOOLS_DIR/ndb_restore --no-defaults --ndb-connectstring="localhost:$NDBCLUSTER_PORT_SLAVE" -p 8 -b $the_backup_id -n 2 -r -e --print --print_meta $NDB_BACKUP_DIR/BACKUP/BACKUP-$the_backup_id >> $NDB_TOOLS_OUTPUT + + + diff --git a/mysql-test/lib/mtr_cases.pl b/mysql-test/lib/mtr_cases.pl index 3978700c3e8..49cd57a2163 100644 --- a/mysql-test/lib/mtr_cases.pl +++ b/mysql-test/lib/mtr_cases.pl @@ -262,6 +262,12 @@ sub collect_one_test_case($$$$$$$) { $tinfo->{'skip'}= 1; return; } + if ( ! $::opt_with_ndbcluster ) + { + # Ndb is not supported, skip them + $tinfo->{'skip'}= 1; + return; + } } else { diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 5d1b8ac9771..22ff20b965c 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -2034,7 +2034,7 @@ sub run_testcase ($) { if ( $tinfo->{'master_restart'} or $master->[0]->{'running_master_is_special'} or # Stop if cluster is started but test cases does not need cluster - ( $tinfo->{'ndb_test'} != $using_ndbcluster_master ) ) + ( $opt_with_ndbcluster && $tinfo->{'ndb_test'} != $using_ndbcluster_master ) ) { stop_masters(); $master->[0]->{'running_master_is_special'}= 0; # Forget why we stopped @@ -2087,7 +2087,7 @@ sub run_testcase ($) { if ( $tinfo->{'component_id'} eq 'mysqld' and ! $opt_local_master ) { - if ( $master->[0]->{'ndbcluster'} ) + if ( $opt_with_ndbcluster and $master->[0]->{'ndbcluster'} ) { # Cluster is not started diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result index 049ba784dc9..13e2d56d83e 100644 --- a/mysql-test/r/gis.result +++ b/mysql-test/r/gis.result @@ -680,7 +680,7 @@ drop procedure if exists fn3; create function fn3 () returns point return GeomFromText("point(1 1)"); show create function fn3; Function sql_mode Create Function -fn3 CREATE FUNCTION `fn3`() RETURNS point +fn3 CREATE DEFINER=`root`@`localhost` FUNCTION `fn3`() RETURNS point return GeomFromText("point(1 1)") select astext(fn3()); astext(fn3()) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 457235e7003..d467ad23cea 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -322,7 +322,7 @@ Function sql_mode Create Function sub1 show create function sub2; Function sql_mode Create Function -sub2 CREATE FUNCTION `sub2`(i int) RETURNS int(11) +sub2 CREATE DEFINER=`mysqltest_1`@`localhost` FUNCTION `sub2`(i int) RETURNS int(11) return i+1 show function status like "sub2"; Db Name Type Definer Modified Created Security_type Comment @@ -330,7 +330,7 @@ test sub2 FUNCTION mysqltest_1@localhost # # DEFINER drop function sub2; show create procedure sel2; Procedure sql_mode Create Procedure -sel2 CREATE PROCEDURE `sel2`() +sel2 CREATE DEFINER=`root`@`localhost` PROCEDURE `sel2`() begin select * from t1; select * from t2; diff --git a/mysql-test/r/mysqldump.result b/mysql-test/r/mysqldump.result index e54dee1fcfd..0b954db54cb 100644 --- a/mysql-test/r/mysqldump.result +++ b/mysql-test/r/mysqldump.result @@ -2279,12 +2279,12 @@ UNLOCK TABLES; DELIMITER ;; /*!50003 DROP FUNCTION IF EXISTS `bug9056_func1` */;; /*!50003 SET SESSION SQL_MODE=""*/;; -/*!50003 CREATE FUNCTION `bug9056_func1`(a INT, b INT) RETURNS int(11) +/*!50003 CREATE*/ /*!50019 DEFINER=`root`@`localhost`*/ /*!50003 FUNCTION `bug9056_func1`(a INT, b INT) RETURNS int(11) RETURN a+b */;; /*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/;; /*!50003 DROP FUNCTION IF EXISTS `bug9056_func2` */;; /*!50003 SET SESSION SQL_MODE=""*/;; -/*!50003 CREATE FUNCTION `bug9056_func2`(f1 char binary) RETURNS char(1) +/*!50003 CREATE*/ /*!50019 DEFINER=`root`@`localhost`*/ /*!50003 FUNCTION `bug9056_func2`(f1 char binary) RETURNS char(1) begin set f1= concat( 'hello', f1 ); return f1; @@ -2292,17 +2292,17 @@ end */;; /*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/;; /*!50003 DROP PROCEDURE IF EXISTS `a'b` */;; /*!50003 SET SESSION SQL_MODE="REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ANSI"*/;; -/*!50003 CREATE PROCEDURE "a'b"() +/*!50003 CREATE*/ /*!50019 DEFINER="root"@"localhost"*/ /*!50003 PROCEDURE "a'b"() select 1 */;; /*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/;; /*!50003 DROP PROCEDURE IF EXISTS `bug9056_proc1` */;; /*!50003 SET SESSION SQL_MODE=""*/;; -/*!50003 CREATE PROCEDURE `bug9056_proc1`(IN a INT, IN b INT, OUT c INT) +/*!50003 CREATE*/ /*!50019 DEFINER=`root`@`localhost`*/ /*!50003 PROCEDURE `bug9056_proc1`(IN a INT, IN b INT, OUT c INT) BEGIN SELECT a+b INTO c; end */;; /*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/;; /*!50003 DROP PROCEDURE IF EXISTS `bug9056_proc2` */;; /*!50003 SET SESSION SQL_MODE=""*/;; -/*!50003 CREATE PROCEDURE `bug9056_proc2`(OUT a INT) +/*!50003 CREATE*/ /*!50019 DEFINER=`root`@`localhost`*/ /*!50003 PROCEDURE `bug9056_proc2`(OUT a INT) BEGIN select sum(id) from t1 into a; END */;; @@ -2724,11 +2724,11 @@ return 42 */| select 42 */| show create function f; Function sql_mode Create Function -f CREATE FUNCTION `f`() RETURNS bigint(20) +f CREATE DEFINER=`root`@`localhost` FUNCTION `f`() RETURNS bigint(20) return 42 show create procedure p; Procedure sql_mode Create Procedure -p CREATE PROCEDURE `p`() +p CREATE DEFINER=`root`@`localhost` PROCEDURE `p`() select 42 drop function f; drop procedure p; @@ -2907,3 +2907,40 @@ mysql-import: Error: 1146, Table 'test.words' doesn't exist, when using table: w drop table t1; drop table t2; drop table words2; +create database first; +use first; +set time_zone = 'UTC'; +create event ee1 on schedule at '2035-12-31 20:01:23' do set @a=5; +show events; +Db Name Definer Type Execute at Interval value Interval field Starts Ends Status +first ee1 root@localhost ONE TIME 2035-12-31 20:01:23 NULL NULL NULL NULL ENABLED +show create event ee1; +Event sql_mode Create Event +ee1 CREATE EVENT `ee1` ON SCHEDULE AT '2035-12-31 20:01:23' ON COMPLETION NOT PRESERVE ENABLE DO set @a=5 +drop database first; +create database second; +use second; +show events; +Db Name Definer Type Execute at Interval value Interval field Starts Ends Status +second ee1 root@localhost ONE TIME 2035-12-31 20:01:23 NULL NULL NULL NULL ENABLED +show create event ee1; +Event sql_mode Create Event +ee1 NO_AUTO_VALUE_ON_ZERO CREATE EVENT `ee1` ON SCHEDULE AT '2035-12-31 20:01:23' ON COMPLETION NOT PRESERVE ENABLE DO set @a=5 +create event ee2 on schedule at '2018-12-31 21:01:23' do set @a=5; +create event ee3 on schedule at '2030-12-31 22:01:23' do set @a=5; +show events; +Db Name Definer Type Execute at Interval value Interval field Starts Ends Status +second ee1 root@localhost ONE TIME 2035-12-31 20:01:23 NULL NULL NULL NULL ENABLED +second ee2 root@localhost ONE TIME 2018-12-31 21:01:23 NULL NULL NULL NULL ENABLED +second ee3 root@localhost ONE TIME 2030-12-31 22:01:23 NULL NULL NULL NULL ENABLED +drop database second; +create database third; +use third; +show events; +Db Name Definer Type Execute at Interval value Interval field Starts Ends Status +third ee1 root@localhost ONE TIME 2035-12-31 20:01:23 NULL NULL NULL NULL ENABLED +third ee2 root@localhost ONE TIME 2018-12-31 21:01:23 NULL NULL NULL NULL ENABLED +third ee3 root@localhost ONE TIME 2030-12-31 22:01:23 NULL NULL NULL NULL ENABLED +drop database third; +set time_zone = 'SYSTEM'; +use test; diff --git a/mysql-test/r/rpl_ndb_blob.result b/mysql-test/r/rpl_ndb_blob.result index 97b6de07085..c11de8c27e3 100644 --- a/mysql-test/r/rpl_ndb_blob.result +++ b/mysql-test/r/rpl_ndb_blob.result @@ -78,3 +78,58 @@ S 7 a6bae0cfe6b45ff8c3c12d2ce577a1cd3931190f 107 39ee712b4b9e47f2cf3ba7c9790b2bf S 8 e139adcb7b2974ee7ff227fd405709e5cb7c896c 108 ba8073b0e1a281d4111bd2d82c7722b01574c00b NULL S 9 1fc5168fe4be566b17b658d94e7813f0b5032cdb NULL NULL NULL drop table t1; +CREATE TABLE IF NOT EXISTS t1 ( +db VARBINARY(63) NOT NULL, +name VARBINARY(63) NOT NULL, +slock BINARY(32) NOT NULL, +query BLOB NOT NULL, +node_id INT UNSIGNED NOT NULL, +epoch BIGINT UNSIGNED NOT NULL, +id INT UNSIGNED NOT NULL, +version INT UNSIGNED NOT NULL, +type INT UNSIGNED NOT NULL, +PRIMARY KEY USING HASH (db,name)) +ENGINE=NDB; +insert into t1 values ('test','t1', +'abc',repeat(@s0,10), 11,12,13,14,15); +insert into t1 values ('test','t2', +'def',repeat(@s1,100), 21,22,23,24,25); +insert into t1 values ('test','t3', +'ghi',repeat(@s2,1000),31,32,33,34,35); +insert into t1 values ('testtttttttttt','t1', +'abc',repeat(@s0,10), 11,12,13,14,15); +insert into t1 values ('testttttttttttt','t1', +'def',repeat(@s1,100), 21,22,23,24,25); +insert into t1 values ('testtttttttttttt','t1', +'ghi',repeat(@s2,1000),31,32,33,34,35); +insert into t1 values ('t','t11111111111', +'abc',repeat(@s0,10), 11,12,13,14,15); +insert into t1 values ('t','t111111111111', +'def',repeat(@s1,100), 21,22,23,24,25); +insert into t1 values ('t','t1111111111111', +'ghi',repeat(@s2,1000),31,32,33,34,35); +select 'M', db, name, sha1(query), node_id, epoch, id, version, type +from t1 order by db, name; +M db name sha1(query) node_id epoch id version type +M t t11111111111 8fc937d303ee7e4795c0b964d4066cedd6d74cfd 11 12 13 14 15 +M t t111111111111 a5229e9f8977bc99afc3b3627c56f083e97e01bd 21 22 23 24 25 +M t t1111111111111 0820e6ad3eeb06ea60e5e04d0bfe36f286b91098 31 32 33 34 35 +M test t1 8fc937d303ee7e4795c0b964d4066cedd6d74cfd 11 12 13 14 15 +M test t2 a5229e9f8977bc99afc3b3627c56f083e97e01bd 21 22 23 24 25 +M test t3 0820e6ad3eeb06ea60e5e04d0bfe36f286b91098 31 32 33 34 35 +M testtttttttttt t1 8fc937d303ee7e4795c0b964d4066cedd6d74cfd 11 12 13 14 15 +M testttttttttttt t1 a5229e9f8977bc99afc3b3627c56f083e97e01bd 21 22 23 24 25 +M testtttttttttttt t1 0820e6ad3eeb06ea60e5e04d0bfe36f286b91098 31 32 33 34 35 +select 'S', db, name, sha1(query), node_id, epoch, id, version, type +from t1 order by db, name; +S db name sha1(query) node_id epoch id version type +S t t11111111111 8fc937d303ee7e4795c0b964d4066cedd6d74cfd 11 12 13 14 15 +S t t111111111111 a5229e9f8977bc99afc3b3627c56f083e97e01bd 21 22 23 24 25 +S t t1111111111111 0820e6ad3eeb06ea60e5e04d0bfe36f286b91098 31 32 33 34 35 +S test t1 8fc937d303ee7e4795c0b964d4066cedd6d74cfd 11 12 13 14 15 +S test t2 a5229e9f8977bc99afc3b3627c56f083e97e01bd 21 22 23 24 25 +S test t3 0820e6ad3eeb06ea60e5e04d0bfe36f286b91098 31 32 33 34 35 +S testtttttttttt t1 8fc937d303ee7e4795c0b964d4066cedd6d74cfd 11 12 13 14 15 +S testttttttttttt t1 a5229e9f8977bc99afc3b3627c56f083e97e01bd 21 22 23 24 25 +S testtttttttttttt t1 0820e6ad3eeb06ea60e5e04d0bfe36f286b91098 31 32 33 34 35 +drop table t1; diff --git a/mysql-test/r/rpl_ndb_dd_advance.result b/mysql-test/r/rpl_ndb_dd_advance.result new file mode 100644 index 00000000000..fd4b80d2066 --- /dev/null +++ b/mysql-test/r/rpl_ndb_dd_advance.result @@ -0,0 +1,421 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +DROP TABLE IF EXISTS t1, t2, t3; +***** Test 1 RPL of CDD and Alter ***** +***** Test 1 setup ***** +CREATE LOGFILE GROUP lg1 +ADD UNDOFILE 'undofile.dat' +INITIAL_SIZE 16M +UNDO_BUFFER_SIZE = 1M +ENGINE=NDB; +ALTER LOGFILE GROUP lg1 +ADD UNDOFILE 'undofile02.dat' +INITIAL_SIZE 4M +ENGINE=NDB; +CREATE TABLESPACE ts1 +ADD DATAFILE 'datafile.dat' +USE LOGFILE GROUP lg1 +INITIAL_SIZE 12M +ENGINE=NDB; +ALTER TABLESPACE ts1 +ADD DATAFILE 'datafile02.dat' +INITIAL_SIZE 4M +ENGINE=NDB; +CREATE TABLE t1 +(c1 INT NOT NULL PRIMARY KEY, +c2 INT NOT NULL, +c3 INT NOT NULL) +TABLESPACE ts1 STORAGE DISK +ENGINE=NDB; +***** insert some data ***** +***** Select from Master ***** +SELECT * FROM t1 ORDER BY c1 LIMIT 5; +c1 c2 c3 +1 2 4 +2 4 5 +3 6 6 +4 8 7 +5 10 8 +***** Select from Slave ***** +SELECT * FROM t1 ORDER BY c1 LIMIT 5; +c1 c2 c3 +1 2 4 +2 4 5 +3 6 6 +4 8 7 +5 10 8 +FILE_NAME FILE_TYPE TABLESPACE_NAME LOGFILE_GROUP_NAME +datafile.dat DATAFILE ts1 lg1 +datafile02.dat DATAFILE ts1 lg1 +undofile.dat UNDO LOG ts1 lg1 +undofile02.dat UNDO LOG ts1 lg1 +**** Do First Set of ALTERs in the master table **** +CREATE INDEX t1_i ON t1(c2, c3); +ALTER TABLE t1 ADD c4 TIMESTAMP; +ALTER TABLE t1 ADD c5 DOUBLE; +ALTER TABLE t1 ADD INDEX (c5); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int(11) NOT NULL, + `c2` int(11) NOT NULL, + `c3` int(11) NOT NULL, + `c4` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `c5` double DEFAULT NULL, + PRIMARY KEY (`c1`), + KEY `t1_i` (`c2`,`c3`), + KEY `c5` (`c5`) +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +**** Show first set of ALTERs on SLAVE **** +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int(11) NOT NULL, + `c2` int(11) NOT NULL, + `c3` int(11) NOT NULL, + `c4` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `c5` double DEFAULT NULL, + PRIMARY KEY (`c1`), + KEY `t1_i` (`c2`,`c3`), + KEY `c5` (`c5`) +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +**** Second set of alters test 1 **** +ALTER TABLE t1 RENAME t2; +ALTER TABLE t2 DROP INDEX c5; +CREATE TABLE t1(c1 INT)ENGINE=NDB; +INSERT INTO t1 VALUES(1); +DROP TABLE t1; +ALTER TABLE t2 RENAME t1; +**** Show second set of ALTERs on MASTER **** +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int(11) NOT NULL, + `c2` int(11) NOT NULL, + `c3` int(11) NOT NULL, + `c4` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `c5` double DEFAULT NULL, + PRIMARY KEY (`c1`), + KEY `t1_i` (`c2`,`c3`) +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +**** Show second set of ALTERs on SLAVE **** +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int(11) NOT NULL, + `c2` int(11) NOT NULL, + `c3` int(11) NOT NULL, + `c4` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `c5` double DEFAULT NULL, + PRIMARY KEY (`c1`), + KEY `t1_i` (`c2`,`c3`) +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +**** Third and last set of alters for test1 **** +ALTER TABLE t1 CHANGE c1 c1 DOUBLE; +ALTER TABLE t1 CHANGE c2 c2 DECIMAL(10,2); +ALTER TABLE t1 DROP COLUMN c3; +ALTER TABLE t1 CHANGE c4 c4 TEXT CHARACTER SET utf8; +ALTER TABLE t1 CHANGE c4 c4 BLOB; +ALTER TABLE t1 CHANGE c4 c3 BLOB; +set @b1 = 'b1'; +set @b1 = concat(@b1,@b1,@b1,@b1,@b1,@b1,@b1,@b1,@b1,@b1); +UPDATE t1 SET c3=@b1 where c1 = 1; +UPDATE t1 SET c3=@b1 where c1 = 2; +**** Show last set of ALTERs on MASTER **** +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` double NOT NULL DEFAULT '0', + `c2` decimal(10,2) DEFAULT NULL, + `c3` blob, + `c5` double DEFAULT NULL, + PRIMARY KEY (`c1`), + KEY `t1_i` (`c2`) +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +SELECT * FROM t1 ORDER BY c1 LIMIT 5; +c1 c2 c3 c5 +1 2.00 b1b1b1b1b1b1b1b1b1b1 NULL +2 4.00 b1b1b1b1b1b1b1b1b1b1 NULL +3 6.00 0000-00-00 00:00:00 NULL +4 8.00 0000-00-00 00:00:00 NULL +5 10.00 0000-00-00 00:00:00 NULL +**** Show last set of ALTERs on SLAVE **** +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` double NOT NULL DEFAULT '0', + `c2` decimal(10,2) DEFAULT NULL, + `c3` blob, + `c5` double DEFAULT NULL, + PRIMARY KEY (`c1`), + KEY `t1_i` (`c2`) +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +SELECT * FROM t1 where c1 = 1; +c1 c2 c3 c5 +1 2.00 b1b1b1b1b1b1b1b1b1b1 NULL +DROP TABLE t1; +STOP SLAVE; +RESET SLAVE; +RESET MASTER; +START SLAVE; +******** Create additional TABLESPACE test 2 ************** +CREATE TABLESPACE ts2 +ADD DATAFILE 'datafile03.dat' +USE LOGFILE GROUP lg1 +INITIAL_SIZE 12M +ENGINE=NDB; +ALTER TABLESPACE ts2 +ADD DATAFILE 'datafile04.dat' +INITIAL_SIZE 12M +ENGINE=NDB; +DROP DATABASE IF EXISTS tpcb; +Warnings: +Note 1008 Can't drop database 'tpcb'; database doesn't exist +CREATE DATABASE tpcb; +DROP DATABASE IF EXISTS test; +CREATE DATABASE test; +*********** Create TPCB Tables ***************** +CREATE TABLE tpcb.account +(id INT, bid INT, balance DECIMAL(10,2), +filler CHAR(255), PRIMARY KEY(id)) +TABLESPACE ts1 STORAGE DISK +ENGINE=NDB; +CREATE TABLE tpcb.branch +(bid INT, balance DECIMAL(10,2), filler VARCHAR(255), +PRIMARY KEY(bid)) +ENGINE=NDB; +CREATE TABLE tpcb.teller +(tid INT, balance DECIMAL(10,2), filler VARCHAR(255), +PRIMARY KEY(tid)) +TABLESPACE ts2 STORAGE DISK +ENGINE=NDB; +CREATE TABLE tpcb.history +(id MEDIUMINT NOT NULL AUTO_INCREMENT,aid INT, +tid INT, bid INT, amount DECIMAL(10,2), +tdate DATETIME, teller CHAR(20), uuidf LONGBLOB, +filler CHAR(80),PRIMARY KEY (id)) +TABLESPACE ts2 STORAGE DISK +ENGINE=NDB; +********* Create Procedures and Functions ************ +CREATE PROCEDURE tpcb.load() +BEGIN +DECLARE acct INT DEFAULT 1000; +DECLARE brch INT DEFAULT 100; +DECLARE tell INT DEFAULT 1000; +DECLARE tmp INT DEFAULT 100; +WHILE brch > 0 DO +SET tmp = 100; +WHILE tmp > 0 DO +INSERT INTO tpcb.account VALUES (acct, brch, 0.0, "FRESH ACCOUNT"); +SET acct = acct - 1; +SET tmp = tmp -1; +END WHILE; +INSERT INTO tpcb.branch VALUES (brch, 0.0, "FRESH BRANCH"); +SET brch = brch - 1; +END WHILE; +WHILE tell > 0 DO +INSERT INTO tpcb.teller VALUES (tell, 0.0, "FRESH TELLER"); +SET tell = tell - 1; +END WHILE; +END| +CREATE FUNCTION tpcb.account_id () RETURNS INT +BEGIN +DECLARE num INT; +DECLARE ran INT; +SELECT RAND() * 10 INTO ran; +IF (ran < 2) +THEN +SELECT RAND() * 10 INTO num; +ELSEIF (ran < 4) +THEN +SELECT RAND() * 100 INTO num; +ELSE +SELECT RAND() * 1000 INTO num; +END IF; +IF (num < 1) +THEN +RETURN 1; +END IF; +RETURN num; +END| +CREATE FUNCTION tpcb.teller_id () RETURNS INT +BEGIN +DECLARE num INT; +DECLARE ran INT; +SELECT RAND() * 10 INTO ran; +IF (ran < 2) +THEN +SELECT RAND() * 10 INTO num; +ELSEIF (ran < 5) +THEN +SELECT RAND() * 100 INTO num; +ELSE +SELECT RAND() * 1000 INTO num; +END IF; +IF (num < 1) +THEN +RETURN 1; +END IF; +RETURN num; +END| +CREATE PROCEDURE tpcb.trans() +BEGIN +DECLARE acct INT DEFAULT 0; +DECLARE brch INT DEFAULT 0; +DECLARE tell INT DEFAULT 0; +DECLARE bal DECIMAL(10,2) DEFAULT 0.0; +DECLARE amount DECIMAL(10,2) DEFAULT 1.00; +DECLARE test INT DEFAULT 0; +DECLARE bbal DECIMAL(10,2) DEFAULT 0.0; +DECLARE tbal DECIMAL(10,2) DEFAULT 0.0; +DECLARE local_uuid VARCHAR(255); +DECLARE local_user VARCHAR(255); +DECLARE local_time TIMESTAMP; +SELECT RAND() * 10 INTO test; +SELECT tpcb.account_id() INTO acct; +SELECT tpcb.teller_id() INTO tell; +SELECT account.balance INTO bal FROM tpcb.account WHERE id = acct; +SELECT account.bid INTO brch FROM tpcb.account WHERE id = acct; +SELECT teller.balance INTO tbal FROM tpcb.teller WHERE tid = tell; +SELECT branch.balance INTO bbal FROM tpcb.branch WHERE bid = brch; +IF (test < 5) +THEN +SET bal = bal + amount; +SET bbal = bbal + amount; +SET tbal = tbal + amount; +UPDATE tpcb.account SET balance = bal, filler = 'account updated' + WHERE id = acct; +UPDATE tpcb.branch SET balance = bbal, filler = 'branch updated' + WHERE bid = brch; +UPDATE tpcb.teller SET balance = tbal, filler = 'teller updated' + WHERE tid = tell; +ELSE +SET bal = bal - amount; +SET bbal = bbal - amount; +SET tbal = tbal - amount; +UPDATE tpcb.account SET balance = bal, filler = 'account updated' + WHERE id = acct; +UPDATE tpcb.branch SET balance = bbal, filler = 'branch updated' + WHERE bid = brch; +UPDATE tpcb.teller SET balance = tbal, filler = 'teller updated' + WHERE tid = tell; +END IF; +SET local_uuid=UUID(); +SET local_user=USER(); +SET local_time= NOW(); +INSERT INTO tpcb.history VALUES(NULL,acct,tell,brch,amount, local_time,local_user, +local_uuid,'completed trans'); +END| +****** TEST 2 test time ********************************* +USE tpcb; +*********** Load up the database ****************** +CALL tpcb.load(); +********** Check load master and slave ************** +SELECT COUNT(*) FROM account; +COUNT(*) +10000 +USE tpcb; +SELECT COUNT(*) FROM account; +COUNT(*) +10000 +******** Run in some transactions *************** +***** Time to try slave sync *********** +**** Must make sure slave is clean ***** +STOP SLAVE; +RESET SLAVE; +DROP PROCEDURE tpcb.load; +DROP PROCEDURE tpcb.trans; +DROP TABLE tpcb.account; +DROP TABLE tpcb.teller; +DROP TABLE tpcb.branch; +DROP TABLE tpcb.history; +DROP DATABASE tpcb; +ALTER TABLESPACE ts1 +DROP DATAFILE 'datafile.dat' +ENGINE=NDB; +ALTER TABLESPACE ts1 +DROP DATAFILE 'datafile02.dat' +ENGINE=NDB; +DROP TABLESPACE ts1 ENGINE=NDB; +ALTER TABLESPACE ts2 +DROP DATAFILE 'datafile03.dat' +ENGINE=NDB; +ALTER TABLESPACE ts2 +DROP DATAFILE 'datafile04.dat' +ENGINE=NDB; +DROP TABLESPACE ts2 ENGINE=NDB; +DROP LOGFILE GROUP lg1 ENGINE=NDB; +********** Take a backup of the Master ************* +SELECT COUNT(*) FROM history; +COUNT(*) +1000 +SELECT COUNT(*) FROM history; +COUNT(*) +2000 +CREATE TEMPORARY TABLE IF NOT EXISTS cluster.backup_info (id INT, backup_id INT) ENGINE = HEAP; +DELETE FROM cluster.backup_info; +LOAD DATA INFILE '../tmp.dat' INTO TABLE cluster.backup_info FIELDS TERMINATED BY ','; +SELECT @the_backup_id:=backup_id FROM cluster.backup_info; +@the_backup_id:=backup_id + +DROP TABLE IF EXISTS cluster.backup_info; +************ Restore the slave ************************ +CREATE DATABASE tpcb; +***** Check a few slave restore values *************** +USE tpcb; +SELECT COUNT(*) FROM account; +COUNT(*) +10000 +***** Add some more records to master ********* +***** Finsh the slave sync process ******* +* 1. * +@the_epoch:=MAX(epoch) + +* 2. * +@the_pos:=Position @the_file:=SUBSTRING_INDEX(FILE, '/', -1) + master-bin.000001 +* 3. * +* 4. * +* 5. * +START SLAVE; +**** We should be ready to continue on ************* +****** Let's make sure we match ******* +***** MASTER ******* +USE tpcb; +SELECT COUNT(*) FROM history; +COUNT(*) +4050 +****** SLAVE ******** +USE tpcb; +SELECT COUNT(*) FROM history; +COUNT(*) +4050 +*** DUMP MASTER & SLAVE FOR COMPARE ******** +*************** TEST 2 CLEANUP SECTION ******************** +DROP PROCEDURE tpcb.load; +DROP PROCEDURE tpcb.trans; +DROP TABLE tpcb.account; +DROP TABLE tpcb.teller; +DROP TABLE tpcb.branch; +DROP TABLE tpcb.history; +DROP DATABASE tpcb; +ALTER TABLESPACE ts1 +DROP DATAFILE 'datafile.dat' +ENGINE=NDB; +ALTER TABLESPACE ts1 +DROP DATAFILE 'datafile02.dat' +ENGINE=NDB; +DROP TABLESPACE ts1 ENGINE=NDB; +ALTER TABLESPACE ts2 +DROP DATAFILE 'datafile03.dat' +ENGINE=NDB; +ALTER TABLESPACE ts2 +DROP DATAFILE 'datafile04.dat' +ENGINE=NDB; +DROP TABLESPACE ts2 ENGINE=NDB; +DROP LOGFILE GROUP lg1 ENGINE=NDB; +****** Do dumps compare ************ diff --git a/mysql-test/r/rpl_sp.result b/mysql-test/r/rpl_sp.result index e0db67e3016..24c0a73c717 100644 --- a/mysql-test/r/rpl_sp.result +++ b/mysql-test/r/rpl_sp.result @@ -31,7 +31,7 @@ declare b int; set b = 8; insert into t1 values (b); insert into t1 values (unix_timestamp()); -end @ # # +end root@localhost # # set timestamp=1000000000; call foo(); select * from t1; @@ -118,7 +118,7 @@ select * from mysql.proc where name="foo4" and db='mysqltest1'; db name type specific_name language sql_data_access is_deterministic security_type param_list returns body definer created modified sql_mode comment mysqltest1 foo4 PROCEDURE foo4 SQL CONTAINS_SQL YES DEFINER begin insert into t2 values(20),(20); -end @ # # +end root@localhost # # drop procedure foo4; select * from mysql.proc where name="foo4" and db='mysqltest1'; db name type specific_name language sql_data_access is_deterministic security_type param_list returns body definer created modified sql_mode comment @@ -223,13 +223,13 @@ select * from mysql.proc where db='mysqltest1'; db name type specific_name language sql_data_access is_deterministic security_type param_list returns body definer created modified sql_mode comment mysqltest1 fn1 FUNCTION fn1 SQL NO_SQL NO DEFINER int(11) begin return unix_timestamp(); -end @ # # +end root@localhost # # mysqltest1 fn2 FUNCTION fn2 SQL NO_SQL NO DEFINER int(11) begin return unix_timestamp(); -end @ # # +end zedjzlcsjhd@localhost # # mysqltest1 fn3 FUNCTION fn3 SQL READS_SQL_DATA NO DEFINER int(11) begin return 0; -end @ # # +end root@localhost # # delete from t2; alter table t2 add unique (a); drop function fn1; @@ -274,7 +274,7 @@ Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # drop database if exists mysqltest1 master-bin.000001 # Query 1 # create database mysqltest1 master-bin.000001 # Query 1 # use `mysqltest1`; create table t1 (a varchar(100)) -master-bin.000001 # Query 1 # use `mysqltest1`; create procedure foo() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` procedure foo() begin declare b int; set b = 8; @@ -284,19 +284,19 @@ end master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values ( NAME_CONST('b',8)) master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values (unix_timestamp()) master-bin.000001 # Query 1 # use `mysqltest1`; delete from t1 -master-bin.000001 # Query 1 # use `mysqltest1`; create procedure foo2() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` procedure foo2() select * from mysqltest1.t1 master-bin.000001 # Query 1 # use `mysqltest1`; alter procedure foo2 contains sql master-bin.000001 # Query 1 # use `mysqltest1`; drop table t1 master-bin.000001 # Query 1 # use `mysqltest1`; create table t1 (a int) master-bin.000001 # Query 1 # use `mysqltest1`; create table t2 like t1 -master-bin.000001 # Query 1 # use `mysqltest1`; create procedure foo3() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` procedure foo3() deterministic insert into t1 values (15) master-bin.000001 # Query 1 # use `mysqltest1`; grant CREATE ROUTINE, EXECUTE on mysqltest1.* to "zedjzlcsjhd"@127.0.0.1 master-bin.000001 # Query 1 # use `mysqltest1`; grant SELECT on mysqltest1.t1 to "zedjzlcsjhd"@127.0.0.1 master-bin.000001 # Query 1 # use `mysqltest1`; grant SELECT, INSERT on mysqltest1.t2 to "zedjzlcsjhd"@127.0.0.1 -master-bin.000001 # Query 1 # use `mysqltest1`; create procedure foo4() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`zedjzlcsjhd`@`127.0.0.1` procedure foo4() deterministic begin insert into t2 values(3); @@ -311,7 +311,7 @@ master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values (5) master-bin.000001 # Query 1 # use `mysqltest1`; delete from t2 master-bin.000001 # Query 1 # use `mysqltest1`; alter table t2 add unique (a) master-bin.000001 # Query 1 # use `mysqltest1`; drop procedure foo4 -master-bin.000001 # Query 1 # use `mysqltest1`; create procedure foo4() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` procedure foo4() deterministic begin insert into t2 values(20),(20); @@ -321,7 +321,7 @@ master-bin.000001 # Query 1 # use `mysqltest1`; drop procedure foo4 master-bin.000001 # Query 1 # use `mysqltest1`; drop procedure foo master-bin.000001 # Query 1 # use `mysqltest1`; drop procedure foo2 master-bin.000001 # Query 1 # use `mysqltest1`; drop procedure foo3 -master-bin.000001 # Query 1 # use `mysqltest1`; create function fn1(x int) +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function fn1(x int) returns int deterministic begin @@ -332,7 +332,7 @@ master-bin.000001 # Query 1 # use `mysqltest1`; delete t1,t2 from t1,t2 master-bin.000001 # Query 1 # use `mysqltest1`; SELECT `fn1`(20) master-bin.000001 # Query 1 # use `mysqltest1`; insert into t2 values(fn1(21)) master-bin.000001 # Query 1 # use `mysqltest1`; drop function fn1 -master-bin.000001 # Query 1 # use `mysqltest1`; create function fn1() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function fn1() returns int no sql begin @@ -340,13 +340,13 @@ return unix_timestamp(); end master-bin.000001 # Query 1 # use `mysqltest1`; delete from t1 master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(fn1()) -master-bin.000001 # Query 1 # use `mysqltest1`; create function fn2() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`zedjzlcsjhd`@`127.0.0.1` function fn2() returns int no sql begin return unix_timestamp(); end -master-bin.000001 # Query 1 # use `mysqltest1`; create function fn3() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function fn3() returns int not deterministic reads sql data @@ -356,7 +356,7 @@ end master-bin.000001 # Query 1 # use `mysqltest1`; delete from t2 master-bin.000001 # Query 1 # use `mysqltest1`; alter table t2 add unique (a) master-bin.000001 # Query 1 # use `mysqltest1`; drop function fn1 -master-bin.000001 # Query 1 # use `mysqltest1`; create function fn1(x int) +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function fn1(x int) returns int begin insert into t2 values(x),(x); diff --git a/mysql-test/r/rpl_trigger.result b/mysql-test/r/rpl_trigger.result index a74c3102f54..53ec2c59cf3 100644 --- a/mysql-test/r/rpl_trigger.result +++ b/mysql-test/r/rpl_trigger.result @@ -105,7 +105,7 @@ t1_first root@localhost SELECT routine_name, definer FROM information_schema.routines; routine_name definer -bug12480 @ +bug12480 root@localhost SELECT trigger_name, definer FROM information_schema.triggers; trigger_name definer @@ -858,3 +858,44 @@ f3 drop trigger trg11; drop table t21,t31; drop table t11; +STOP SLAVE; +FLUSH LOGS; +RESET SLAVE; +START SLAVE; +SELECT MASTER_POS_WAIT('master-bin.000001', 513) >= 0; +MASTER_POS_WAIT('master-bin.000001', 513) >= 0 +1 +SHOW TABLES LIKE 't_'; +Tables_in_test (t_) +t1 +t2 +SHOW TRIGGERS; +Trigger Event Table Statement Timing Created sql_mode Definer +trg1 INSERT t1 INSERT INTO t2 VALUES(CURRENT_USER()) AFTER NULL +SELECT * FROM t1; +c +1 +SELECT * FROM t2; +s +@ +INSERT INTO t1 VALUES(2); +SELECT * FROM t1; +c +1 +2 +SELECT * FROM t2; +s +@ +root@localhost +DROP TRIGGER trg1; +Warnings: +Warning 1454 No definer attribute for trigger 'test'.'trg1'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger. +DROP TABLE t1; +DROP TABLE t2; +STOP SLAVE; +RESET SLAVE; +SHOW TABLES LIKE 't_'; +Tables_in_test (t_) +SHOW TRIGGERS; +Trigger Event Table Statement Timing Created sql_mode Definer +RESET MASTER; diff --git a/mysql-test/r/skip_grants.result b/mysql-test/r/skip_grants.result index 95464ee4ce5..6714fa9d217 100644 --- a/mysql-test/r/skip_grants.result +++ b/mysql-test/r/skip_grants.result @@ -2,10 +2,6 @@ drop table if exists t1,v1; drop view if exists t1,v1; drop procedure if exists f1; use test; -create table t1 (field1 INT); -CREATE VIEW v1 AS SELECT field1 FROM t1; -ERROR HY000: Definer is not fully qualified -drop table t1; create procedure f1() select 1; drop procedure f1; create table t1 (a int); @@ -15,3 +11,20 @@ drop table t1; drop function if exists f1; Warnings: Note 1305 FUNCTION f1 does not exist +DROP VIEW IF EXISTS v1; +DROP VIEW IF EXISTS v2; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(c INT); +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 +FOR EACH ROW +SET @a = 1; +CREATE VIEW v1 AS SELECT * FROM t1; +CREATE DEFINER=a@b TRIGGER ti_ai AFTER INSERT ON t1 +FOR EACH ROW +SET @b = 1; +CREATE DEFINER=a@b VIEW v2 AS SELECT * FROM t1; +DROP TRIGGER t1_bi; +DROP TRIGGER ti_ai; +DROP VIEW v1; +DROP VIEW v2; +DROP TABLE t1; diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index a1a197cb9d6..c8a0e40a971 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -323,3 +323,99 @@ Warning 1540 The syntax 'SHOW INNODB STATUS' is deprecated and will be removed i GRANT EXECUTE ON PROCEDURE p1 TO user_bug7787@localhost; DROP DATABASE db_bug7787; use test; + +---> connection: root +DROP DATABASE IF EXISTS mysqltest; +CREATE DATABASE mysqltest; +CREATE USER mysqltest_1@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_1@localhost; +CREATE USER mysqltest_2@localhost; +GRANT SUPER ON *.* TO mysqltest_2@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_2@localhost; + +---> connection: mysqltest_2_con +use mysqltest; +CREATE PROCEDURE wl2897_p1() SELECT 1; +CREATE FUNCTION wl2897_f1() RETURNS INT RETURN 1; + +---> connection: mysqltest_1_con +use mysqltest; +CREATE DEFINER=root@localhost PROCEDURE wl2897_p2() SELECT 2; +ERROR 42000: Access denied; you need the SUPER privilege for this operation +CREATE DEFINER=root@localhost FUNCTION wl2897_f2() RETURNS INT RETURN 2; +ERROR 42000: Access denied; you need the SUPER privilege for this operation + +---> connection: mysqltest_2_con +use mysqltest; +CREATE DEFINER='a @ b @ c'@localhost PROCEDURE wl2897_p3() SELECT 3; +Warnings: +Note 1449 There is no 'a @ b @ c'@'localhost' registered +CREATE DEFINER='a @ b @ c'@localhost FUNCTION wl2897_f3() RETURNS INT RETURN 3; +Warnings: +Note 1449 There is no 'a @ b @ c'@'localhost' registered + +---> connection: con1root +use mysqltest; +SHOW CREATE PROCEDURE wl2897_p1; +Procedure sql_mode Create Procedure +wl2897_p1 CREATE DEFINER=`mysqltest_2`@`localhost` PROCEDURE `wl2897_p1`() +SELECT 1 +SHOW CREATE PROCEDURE wl2897_p3; +Procedure sql_mode Create Procedure +wl2897_p3 CREATE DEFINER=`a @ b @ c`@`localhost` PROCEDURE `wl2897_p3`() +SELECT 3 +SHOW CREATE FUNCTION wl2897_f1; +Function sql_mode Create Function +wl2897_f1 CREATE DEFINER=`mysqltest_2`@`localhost` FUNCTION `wl2897_f1`() RETURNS int(11) +RETURN 1 +SHOW CREATE FUNCTION wl2897_f3; +Function sql_mode Create Function +wl2897_f3 CREATE DEFINER=`a @ b @ c`@`localhost` FUNCTION `wl2897_f3`() RETURNS int(11) +RETURN 3 +DROP USER mysqltest_1@localhost; +DROP USER mysqltest_2@localhost; +DROP DATABASE mysqltest; + +---> connection: root +DROP DATABASE IF EXISTS mysqltest; +CREATE DATABASE mysqltest; +CREATE USER mysqltest_1@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_1@localhost; +CREATE USER mysqltest_2@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_2@localhost; + +---> connection: mysqltest_1_con +use mysqltest; +CREATE PROCEDURE bug13198_p1() +SELECT 1; +CREATE FUNCTION bug13198_f1() RETURNS INT +RETURN 1; +CALL bug13198_p1(); +1 +1 +SELECT bug13198_f1(); +bug13198_f1() +1 + +---> connection: mysqltest_2_con +use mysqltest; +CALL bug13198_p1(); +1 +1 +SELECT bug13198_f1(); +bug13198_f1() +1 + +---> connection: root +DROP USER mysqltest_1@localhost; + +---> connection: mysqltest_2_con +use mysqltest; +CALL bug13198_p1(); +ERROR HY000: There is no 'mysqltest_1'@'localhost' registered +SELECT bug13198_f1(); +ERROR HY000: There is no 'mysqltest_1'@'localhost' registered + +---> connection: root +DROP USER mysqltest_2@localhost; +DROP DATABASE mysqltest; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 9d4730d9e73..73bf9eb691e 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -788,7 +788,7 @@ comment 'Characteristics procedure test' insert into t1 values ("chistics", 1)| show create procedure chistics| Procedure sql_mode Create Procedure -chistics CREATE PROCEDURE `chistics`() +chistics CREATE DEFINER=`root`@`localhost` PROCEDURE `chistics`() MODIFIES SQL DATA COMMENT 'Characteristics procedure test' insert into t1 values ("chistics", 1) @@ -800,7 +800,7 @@ delete from t1| alter procedure chistics sql security invoker| show create procedure chistics| Procedure sql_mode Create Procedure -chistics CREATE PROCEDURE `chistics`() +chistics CREATE DEFINER=`root`@`localhost` PROCEDURE `chistics`() MODIFIES SQL DATA SQL SECURITY INVOKER COMMENT 'Characteristics procedure test' @@ -815,7 +815,7 @@ comment 'Characteristics procedure test' return 42| show create function chistics| Function sql_mode Create Function -chistics CREATE FUNCTION `chistics`() RETURNS int(11) +chistics CREATE DEFINER=`root`@`localhost` FUNCTION `chistics`() RETURNS int(11) DETERMINISTIC SQL SECURITY INVOKER COMMENT 'Characteristics procedure test' @@ -828,7 +828,7 @@ no sql comment 'Characteristics function test'| show create function chistics| Function sql_mode Create Function -chistics CREATE FUNCTION `chistics`() RETURNS int(11) +chistics CREATE DEFINER=`root`@`localhost` FUNCTION `chistics`() RETURNS int(11) NO SQL DETERMINISTIC SQL SECURITY INVOKER @@ -1287,7 +1287,7 @@ end while; end| show create procedure opp| Procedure sql_mode Create Procedure -opp CREATE PROCEDURE `opp`(n bigint unsigned, out pp bool) +opp CREATE DEFINER=`root`@`localhost` PROCEDURE `opp`(n bigint unsigned, out pp bool) begin declare r double; declare b, s bigint unsigned default 0; @@ -1386,7 +1386,7 @@ alter procedure bar comment "3333333333"| alter procedure bar| show create procedure bar| Procedure sql_mode Create Procedure -bar CREATE PROCEDURE `bar`(x char(16), y int) +bar CREATE DEFINER=`root`@`localhost` PROCEDURE `bar`(x char(16), y int) COMMENT '3333333333' insert into test.t1 values (x, y) show procedure status like 'bar'| @@ -1966,13 +1966,13 @@ Db Name Type Definer Modified Created Security_type Comment test bug2267_4 FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER call bug2267_3()| Procedure sql_mode Create Procedure -bug2267_1 CREATE PROCEDURE `bug2267_1`() +bug2267_1 CREATE DEFINER=`root`@`localhost` PROCEDURE `bug2267_1`() begin show procedure status; end call bug2267_4()| Function sql_mode Create Function -bug2267_4 CREATE FUNCTION `bug2267_4`() RETURNS int(11) +bug2267_4 CREATE DEFINER=`root`@`localhost` FUNCTION `bug2267_4`() RETURNS int(11) return 100 drop procedure bug2267_1| drop procedure bug2267_2| @@ -2333,20 +2333,20 @@ return x || y$ set @@sql_mode = ''| show create procedure bug2564_1| Procedure sql_mode Create Procedure -bug2564_1 CREATE PROCEDURE `bug2564_1`() +bug2564_1 CREATE DEFINER=`root`@`localhost` PROCEDURE `bug2564_1`() COMMENT 'Joe''s procedure' insert into `t1` values ("foo", 1) show create procedure bug2564_2| Procedure sql_mode Create Procedure -bug2564_2 ANSI_QUOTES CREATE PROCEDURE "bug2564_2"() +bug2564_2 ANSI_QUOTES CREATE DEFINER="root"@"localhost" PROCEDURE "bug2564_2"() insert into "t1" values ('foo', 1) show create function bug2564_3| Function sql_mode Create Function -bug2564_3 CREATE FUNCTION `bug2564_3`(x int, y int) RETURNS int(11) +bug2564_3 CREATE DEFINER=`root`@`localhost` FUNCTION `bug2564_3`(x int, y int) RETURNS int(11) return x || y show create function bug2564_4| Function sql_mode Create Function -bug2564_4 REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ANSI CREATE FUNCTION "bug2564_4"(x int, y int) RETURNS int(11) +bug2564_4 REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ANSI CREATE DEFINER="root"@"localhost" FUNCTION "bug2564_4"(x int, y int) RETURNS int(11) return x || y drop procedure bug2564_1| drop procedure bug2564_2| @@ -4060,7 +4060,7 @@ return 42; end */;; show create function bug14723;; Function sql_mode Create Function -bug14723 CREATE FUNCTION `bug14723`() RETURNS bigint(20) +bug14723 CREATE DEFINER=`root`@`localhost` FUNCTION `bug14723`() RETURNS bigint(20) main_loop: begin return 42; end @@ -4073,7 +4073,7 @@ select 42; end */;; show create procedure bug14723;; Procedure sql_mode Create Procedure -bug14723 CREATE PROCEDURE `bug14723`() +bug14723 CREATE DEFINER=`root`@`localhost` PROCEDURE `bug14723`() main_loop: begin select 42; end diff --git a/mysql-test/r/sql_mode.result b/mysql-test/r/sql_mode.result index 2f17962fc15..31b67529f8d 100644 --- a/mysql-test/r/sql_mode.result +++ b/mysql-test/r/sql_mode.result @@ -427,23 +427,23 @@ SET @@SQL_MODE=''; create function `foo` () returns int return 5; show create function `foo`; Function sql_mode Create Function -foo CREATE FUNCTION `foo`() RETURNS int(11) +foo CREATE DEFINER=`root`@`localhost` FUNCTION `foo`() RETURNS int(11) return 5 SET @@SQL_MODE='ANSI_QUOTES'; show create function `foo`; Function sql_mode Create Function -foo CREATE FUNCTION `foo`() RETURNS int(11) +foo CREATE DEFINER=`root`@`localhost` FUNCTION `foo`() RETURNS int(11) return 5 drop function `foo`; create function `foo` () returns int return 5; show create function `foo`; Function sql_mode Create Function -foo ANSI_QUOTES CREATE FUNCTION "foo"() RETURNS int(11) +foo ANSI_QUOTES CREATE DEFINER="root"@"localhost" FUNCTION "foo"() RETURNS int(11) return 5 SET @@SQL_MODE=''; show create function `foo`; Function sql_mode Create Function -foo ANSI_QUOTES CREATE FUNCTION "foo"() RETURNS int(11) +foo ANSI_QUOTES CREATE DEFINER="root"@"localhost" FUNCTION "foo"() RETURNS int(11) return 5 drop function `foo`; SET @@SQL_MODE=''; diff --git a/mysql-test/r/trigger-grant.result b/mysql-test/r/trigger-grant.result index c3bbc82ef5b..10f1e08eded 100644 --- a/mysql-test/r/trigger-grant.result +++ b/mysql-test/r/trigger-grant.result @@ -135,6 +135,7 @@ SET @new_sum = 0; Warnings: Note 1449 There is no 'mysqltest_nonexs'@'localhost' registered INSERT INTO t1 VALUES(6); +ERROR HY000: There is no 'mysqltest_nonexs'@'localhost' registered SHOW TRIGGERS; Trigger Event Table Statement Timing Created sql_mode Definer trg1 INSERT t1 SET @new_sum = 0 BEFORE NULL mysqltest_inv@localhost diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index e3d683a58dc..c02e4e1f7c8 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -2539,3 +2539,17 @@ drop view v1; // View Create View v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select sql_no_cache `test`.`t1`.`id` AS `id` from `t1` +create table t1(f1 int, f2 int); +create view v1 as select ta.f1 as a, tb.f1 as b from t1 ta, t1 tb where ta.f1=tb +.f1 and ta.f2=tb.f2; +insert into t1 values(1,1),(2,2); +create view v2 as select * from v1 where a > 1 with check option; +select * from v2; +a b +2 2 +update v2 set b=3 where a=2; +select * from v2; +a b +3 3 +drop view v2, v1; +drop table t1; diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index 8da8805b14b..928e3635ec6 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -522,11 +522,16 @@ use test; drop user mysqltest_1@localhost; drop database mysqltest; create definer=some_user@`` sql security invoker view v1 as select 1; -ERROR HY000: Definer is not fully qualified -create definer=some_user@localhost sql security invoker view v1 as select 1; +Warnings: +Note 1449 There is no 'some_user'@'' registered +create definer=some_user@localhost sql security invoker view v2 as select 1; Warnings: Note 1449 There is no 'some_user'@'localhost' registered show create view v1; View Create View -v1 CREATE ALGORITHM=UNDEFINED DEFINER=`some_user`@`localhost` SQL SECURITY INVOKER VIEW `v1` AS select 1 AS `1` +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`some_user`@`` SQL SECURITY INVOKER VIEW `v1` AS select 1 AS `1` +show create view v2; +View Create View +v2 CREATE ALGORITHM=UNDEFINED DEFINER=`some_user`@`localhost` SQL SECURITY INVOKER VIEW `v2` AS select 1 AS `1` drop view v1; +drop view v2; diff --git a/mysql-test/std_data/bug16266.000001 b/mysql-test/std_data/bug16266.000001 new file mode 100644 index 00000000000..1b24d231511 Binary files /dev/null and b/mysql-test/std_data/bug16266.000001 differ diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 55fbf5eb518..c62ab917206 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -9,10 +9,10 @@ # Do not use any TAB characters for whitespace. # ############################################################################## -#ndb_alter_table_row : sometimes wrong error 1015!=1046 events_bugs : test case unstable (race conditions). andrey will fix events_stress : test case unstable. andrey will fix events : test case unstable. andrey will fix +#ndb_alter_table_row : sometimes wrong error 1015!=1046 ndb_autodiscover : Needs to be fixed w.r.t binlog ndb_autodiscover2 : Needs to be fixed w.r.t binlog ndb_binlog_basic : Results are not deterministic, Tomas will fix @@ -35,11 +35,11 @@ rpl_ndb_insert_ignore : Bugs: #17431: INSERT IGNORE INTO returns failed: 1296 rpl_ndb_myisam2ndb : Bugs#17400: delete & update of rows in table without pk fails rpl_ndb_log : result not deterministic rpl_ndb_relay_space : Bug#16993 +rpl_ndb_multi_update2 : BUG#17738 In progress rpl_ndb_multi_update3 : Bug#17400: delete & update of rows in table without pk fails rpl_ndb_sp007 : Bug #17290 rpl_row_inexist_tbl : Disabled since patch makes this test wait forever rpl_sp : Bug#16456 rpl_until : Unstable test case, bug#15886 sp-goto : GOTO is currently is disabled - will be fixed in the future -rpl_ndb_multi_update2 : BUG#17738 In progress rpl_rbr_to_sbr : BUG#18108 diff --git a/mysql-test/t/mysqldump.test b/mysql-test/t/mysqldump.test index 82428d607fd..5ba7838bb81 100644 --- a/mysql-test/t/mysqldump.test +++ b/mysql-test/t/mysqldump.test @@ -1166,3 +1166,43 @@ drop table t1; drop table t2; drop table words2; + +# +# BUG# 16853: mysqldump doesn't show events +# +create database first; +use first; +set time_zone = 'UTC'; + +## prove one works +create event ee1 on schedule at '2035-12-31 20:01:23' do set @a=5; +show events; +show create event ee1; +--exec $MYSQL_DUMP --events first > $MYSQLTEST_VARDIR/tmp/bug16853-1.sql +drop database first; + +create database second; +use second; +--exec $MYSQL second < $MYSQLTEST_VARDIR/tmp/bug16853-1.sql +show events; +show create event ee1; + +## prove three works +# start with one from the previous restore +create event ee2 on schedule at '2018-12-31 21:01:23' do set @a=5; +create event ee3 on schedule at '2030-12-31 22:01:23' do set @a=5; +show events; +--exec $MYSQL_DUMP --events second > $MYSQLTEST_VARDIR/tmp/bug16853-2.sql +drop database second; + +create database third; +use third; +--exec $MYSQL third < $MYSQLTEST_VARDIR/tmp/bug16853-2.sql +show events; +drop database third; + +# revert back to normal settings +set time_zone = 'SYSTEM'; +use test; + +##### diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test index 80d90d5d67a..42065cfae81 100644 --- a/mysql-test/t/mysqltest.test +++ b/mysql-test/t/mysqltest.test @@ -27,7 +27,7 @@ # ---------------------------------------------------------------------------- # $mysql_errno contains the return code of the last command -# send to the server. +# sent to the server. # ---------------------------------------------------------------------------- # get $mysql_errno before the first statement # $mysql_errno should be -1 @@ -50,7 +50,7 @@ select otto from (select 1 as otto) as t1; # ---------------------------------------------------------------------------- # Negative case(statement): -# The dervied table t1 does not contain a column named 'friedrich' . +# The derived table t1 does not contain a column named 'friedrich' . # --> ERROR 42S22: Unknown column 'friedrich' in 'field list and # --> 1054: Unknown column 'friedrich' in 'field list' # ---------------------------------------------------------------------------- @@ -68,7 +68,7 @@ select friedrich from (select 1 as otto) as t1; # The following unmasked unsuccessful statement must give # 1. mysqltest gives a 'failed' # 2. does not produce a r/.reject file !!! -# PLEASE uncomment it and check it's effect +# PLEASE uncomment it and check its effect #select friedrich from (select 1 as otto) as t1; @@ -114,7 +114,7 @@ select friedrich from (select 1 as otto) as t1; # test cases for $mysql_errno # # $mysql_errno is a builtin variable of mysqltest and contains the return code -# of the last command send to the server. +# of the last command sent to the server. # # The following test cases often initialize $mysql_errno to 1064 by # a command with wrong syntax. @@ -217,7 +217,7 @@ garbage ; execute stmt; eval select $mysql_errno as "after_successful_execute" ; -# failing execute (table dropped) +# failing execute (table has been dropped) drop table t1; --error 1064 garbage ; @@ -249,8 +249,8 @@ eval select $mysql_errno as "after_failing_deallocate" ; # ---------------------------------------------------------------------------- # test cases for "--disable_abort_on_error" # -# "--disable_abort_on_error" switches the abort of mysqltest -# after "unmasked" failing statements off. +# "--disable_abort_on_error" switches off the abort of mysqltest +# after "unmasked" failing statements. # # The default is "--enable_abort_on_error". # @@ -258,13 +258,13 @@ eval select $mysql_errno as "after_failing_deallocate" ; # --error and --error # in the line before the failing statement. # -# There are some additional test case for $mysql_errno +# There are some additional test cases for $mysql_errno # because "--disable_abort_on_error" enables a new situation. # Example: "unmasked" statement fails + analysis of $mysql_errno # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Switch the abort on error off and check the effect on $mysql_errno +# Switch off the abort on error and check the effect on $mysql_errno # ---------------------------------------------------------------------------- --error 1064 garbage ; @@ -345,7 +345,7 @@ select 3 from t1 ; # ---------------------------------------------------------------------------- # Test detect end of line "junk" -# Most likely causes by a missing delimiter +# Most likely caused by a missing delimiter # ---------------------------------------------------------------------------- # Too many parameters to function @@ -1040,7 +1040,7 @@ let $message= . Here comes a very very long message that --source include/show_msg80.inc # -# Test --enable_parsning / disable_parsning +# Test --enable_parsing / disable_parsing # --disable_query_log --disable_parsing @@ -1067,9 +1067,9 @@ select "this will be executed"; # # Test that mysqltest fails when there are no queries executed -# but a result file exist +# but a result file exists # NOTE! This will never happen as long as it's not allowed to have -# test files that does not produce any output +# test files that produce no output #--exec echo "something" > $MYSQLTEST_VARDIR/tmp/result_file.result #--exec echo "let \$i= 1;" > $MYSQLTEST_VARDIR/tmp/query.sql #--error 1 diff --git a/mysql-test/t/rpl_ndb_blob.test b/mysql-test/t/rpl_ndb_blob.test index 209cd51592a..94af986b222 100644 --- a/mysql-test/t/rpl_ndb_blob.test +++ b/mysql-test/t/rpl_ndb_blob.test @@ -83,12 +83,60 @@ from t1 order by a; drop table t1; --sync_slave_with_master -# -# view the binlog -# +# table with varsize key (future cluster/schema) + +# sql/ha_ndbcluster_binlog.cc +--connection master +CREATE TABLE IF NOT EXISTS t1 ( + db VARBINARY(63) NOT NULL, + name VARBINARY(63) NOT NULL, + slock BINARY(32) NOT NULL, + query BLOB NOT NULL, + node_id INT UNSIGNED NOT NULL, + epoch BIGINT UNSIGNED NOT NULL, + id INT UNSIGNED NOT NULL, + version INT UNSIGNED NOT NULL, + type INT UNSIGNED NOT NULL, + PRIMARY KEY USING HASH (db,name)) +ENGINE=NDB; + +insert into t1 values ('test','t1', + 'abc',repeat(@s0,10), 11,12,13,14,15); +insert into t1 values ('test','t2', + 'def',repeat(@s1,100), 21,22,23,24,25); +insert into t1 values ('test','t3', + 'ghi',repeat(@s2,1000),31,32,33,34,35); +insert into t1 values ('testtttttttttt','t1', + 'abc',repeat(@s0,10), 11,12,13,14,15); +insert into t1 values ('testttttttttttt','t1', + 'def',repeat(@s1,100), 21,22,23,24,25); +insert into t1 values ('testtttttttttttt','t1', + 'ghi',repeat(@s2,1000),31,32,33,34,35); +insert into t1 values ('t','t11111111111', + 'abc',repeat(@s0,10), 11,12,13,14,15); +insert into t1 values ('t','t111111111111', + 'def',repeat(@s1,100), 21,22,23,24,25); +insert into t1 values ('t','t1111111111111', + 'ghi',repeat(@s2,1000),31,32,33,34,35); + +select 'M', db, name, sha1(query), node_id, epoch, id, version, type +from t1 order by db, name; + +--sync_slave_with_master +--sleep 5 +--connection slave +select 'S', db, name, sha1(query), node_id, epoch, id, version, type +from t1 order by db, name; --connection master -let $VERSION=`select version()`; +drop table t1; +--sync_slave_with_master + +# +# view the binlog - not deterministic (mats) +# + +#--connection master +#let $VERSION=`select version()`; #--replace_result $VERSION VERSION -#--replace_regex /table_id: [0-9]+/table_id: #/ #show binlog events; diff --git a/mysql-test/t/rpl_ndb_dd_advance.test b/mysql-test/t/rpl_ndb_dd_advance.test new file mode 100644 index 00000000000..c3ef6a51228 --- /dev/null +++ b/mysql-test/t/rpl_ndb_dd_advance.test @@ -0,0 +1,600 @@ +############################################### +# Author: JBM # +# Date: 2006-03-07 # +# Purpose: To test advance DD and replication # +############################################### + +#### Include Section #### +--source include/have_ndb.inc +--source include/have_binlog_format_row.inc +#--source include/have_ndb_extra.inc +--source include/master-slave.inc + +#### Test start cleanup section ##### +--disable_warnings +DROP TABLE IF EXISTS t1, t2, t3; +--enable_warnings + +###################################################### +# Requirment: Cluster DD and replication must be able# +# to handle ALTER tables and indexes and must rpl # +# to the slave correctly # +###################################################### + +## Test #1 replication of CDD and Alter Tables ##### +--echo ***** Test 1 RPL of CDD and Alter ***** +--echo ***** Test 1 setup ***** + +CREATE LOGFILE GROUP lg1 +ADD UNDOFILE 'undofile.dat' +INITIAL_SIZE 16M +UNDO_BUFFER_SIZE = 1M +ENGINE=NDB; + +ALTER LOGFILE GROUP lg1 +ADD UNDOFILE 'undofile02.dat' +INITIAL_SIZE 4M +ENGINE=NDB; + +CREATE TABLESPACE ts1 +ADD DATAFILE 'datafile.dat' +USE LOGFILE GROUP lg1 +INITIAL_SIZE 12M +ENGINE=NDB; + +ALTER TABLESPACE ts1 +ADD DATAFILE 'datafile02.dat' +INITIAL_SIZE 4M +ENGINE=NDB; + +CREATE TABLE t1 + (c1 INT NOT NULL PRIMARY KEY, + c2 INT NOT NULL, + c3 INT NOT NULL) + TABLESPACE ts1 STORAGE DISK + ENGINE=NDB; + +--echo ***** insert some data ***** + +let $j= 900; +--disable_query_log +while ($j) +{ + eval INSERT INTO t1 VALUES($j,$j*2,$j+3); + dec $j; +} +--enable_query_log + +--echo ***** Select from Master ***** + +SELECT * FROM t1 ORDER BY c1 LIMIT 5; + +--echo ***** Select from Slave ***** +--sync_slave_with_master +connection slave; +SELECT * FROM t1 ORDER BY c1 LIMIT 5; + +################################### +# Just to some File Schema check # +################################### + +--disable_query_log +SELECT DISTINCT FILE_NAME, FILE_TYPE, TABLESPACE_NAME, LOGFILE_GROUP_NAME + FROM INFORMATION_SCHEMA.FILES + WHERE ENGINE="ndbcluster" ORDER BY FILE_NAME; +--enable_query_log + +--echo **** Do First Set of ALTERs in the master table **** +################################################### +# On this first set of alters I expect: +# 1. To be able to create and index on 2 columns +# 2. To be able to create a unique index +# 3. To be able to add two columns and have +# it all replicated correctly to the slave cluster. +################################################### +connection master; +CREATE INDEX t1_i ON t1(c2, c3); +#Bug 18039 +#CREATE UNIQUE INDEX t1_i2 ON t1(c2); +ALTER TABLE t1 ADD c4 TIMESTAMP; +ALTER TABLE t1 ADD c5 DOUBLE; +ALTER TABLE t1 ADD INDEX (c5); +SHOW CREATE TABLE t1; + +--echo **** Show first set of ALTERs on SLAVE **** +--sync_slave_with_master +connection slave; +SHOW CREATE TABLE t1; + +--echo **** Second set of alters test 1 **** +############################################ +# With this next set of alters we have had +# Some issues with renames of tables. So this +# test renames our main table, drop and index off +# of it, creates another table with then name +# of the orginal table, inserts a row, drops +# the table and renames the orginal table back. +# I want to make sure that 1) the cluster does +# okay with this and 2) that it is replicated +# correctly. +############################################# +connection master; +ALTER TABLE t1 RENAME t2; +ALTER TABLE t2 DROP INDEX c5; +CREATE TABLE t1(c1 INT)ENGINE=NDB; +INSERT INTO t1 VALUES(1); +DROP TABLE t1; +ALTER TABLE t2 RENAME t1; + +--echo **** Show second set of ALTERs on MASTER **** + +SHOW CREATE TABLE t1; + +--echo **** Show second set of ALTERs on SLAVE **** +--sync_slave_with_master +connection slave; +SHOW CREATE TABLE t1; + +--echo **** Third and last set of alters for test1 **** +######################################################### +# In this last set of alters, we are messing with the +# cluster ability to rebuild indexes, drop a column that make up +# an index with another column and change types several times in +# a row. I have choosen the BLOB as it seems to have had many +# issues in this release. I want to make sure that the cluster +# deals with these radical changes and that the replication to +# the slave cluster is dones correctly. +########################################################### +connection master; +ALTER TABLE t1 CHANGE c1 c1 DOUBLE; +ALTER TABLE t1 CHANGE c2 c2 DECIMAL(10,2); +ALTER TABLE t1 DROP COLUMN c3; +ALTER TABLE t1 CHANGE c4 c4 TEXT CHARACTER SET utf8; +ALTER TABLE t1 CHANGE c4 c4 BLOB; +ALTER TABLE t1 CHANGE c4 c3 BLOB; +set @b1 = 'b1'; +set @b1 = concat(@b1,@b1,@b1,@b1,@b1,@b1,@b1,@b1,@b1,@b1); +UPDATE t1 SET c3=@b1 where c1 = 1; +UPDATE t1 SET c3=@b1 where c1 = 2; + +--echo **** Show last set of ALTERs on MASTER **** + +SHOW CREATE TABLE t1; +SELECT * FROM t1 ORDER BY c1 LIMIT 5; + +--echo **** Show last set of ALTERs on SLAVE **** +--sync_slave_with_master +connection slave; +SHOW CREATE TABLE t1; +# Bug 18094 +#SELECT * FROM t1 ORDER BY c1 LIMIT 5; +SELECT * FROM t1 where c1 = 1; + +connection master; +DROP TABLE t1; +--sync_slave_with_master +connection slave; +STOP SLAVE; +RESET SLAVE; +connection master; +RESET MASTER; +connection slave; +START SLAVE; + +################### TEST 2 TPCB for disk data ########################### +# Requirement: To have Stored Procedures and Functions that are used to # +# populate and post transactions to the data base using CDD that span # +# 2 tables spaces and also use a memory only cluster tables. In addition# +# The slave is to be stopped, cleaned and restored and synced with the # +# Master cluster # +######################################################################### + +--echo ******** Create additional TABLESPACE test 2 ************** + +connection master; +CREATE TABLESPACE ts2 +ADD DATAFILE 'datafile03.dat' +USE LOGFILE GROUP lg1 +INITIAL_SIZE 12M +ENGINE=NDB; + +ALTER TABLESPACE ts2 +ADD DATAFILE 'datafile04.dat' +INITIAL_SIZE 12M +ENGINE=NDB; + +###### CLEAN UP SECTION ############## +DROP DATABASE IF EXISTS tpcb; +CREATE DATABASE tpcb; +DROP DATABASE IF EXISTS test; +CREATE DATABASE test; +######## Creat Table Section ######### +--echo *********** Create TPCB Tables ***************** +CREATE TABLE tpcb.account + (id INT, bid INT, balance DECIMAL(10,2), + filler CHAR(255), PRIMARY KEY(id)) + TABLESPACE ts1 STORAGE DISK + ENGINE=NDB; + +CREATE TABLE tpcb.branch + (bid INT, balance DECIMAL(10,2), filler VARCHAR(255), + PRIMARY KEY(bid)) + ENGINE=NDB; + +CREATE TABLE tpcb.teller + (tid INT, balance DECIMAL(10,2), filler VARCHAR(255), + PRIMARY KEY(tid)) + TABLESPACE ts2 STORAGE DISK + ENGINE=NDB; + +CREATE TABLE tpcb.history + (id MEDIUMINT NOT NULL AUTO_INCREMENT,aid INT, + tid INT, bid INT, amount DECIMAL(10,2), + tdate DATETIME, teller CHAR(20), uuidf LONGBLOB, + filler CHAR(80),PRIMARY KEY (id)) + TABLESPACE ts2 STORAGE DISK + ENGINE=NDB; + +--echo ********* Create Procedures and Functions ************ +delimiter |; +CREATE PROCEDURE tpcb.load() +BEGIN + DECLARE acct INT DEFAULT 1000; + DECLARE brch INT DEFAULT 100; + DECLARE tell INT DEFAULT 1000; + DECLARE tmp INT DEFAULT 100; + WHILE brch > 0 DO + SET tmp = 100; + WHILE tmp > 0 DO + INSERT INTO tpcb.account VALUES (acct, brch, 0.0, "FRESH ACCOUNT"); + SET acct = acct - 1; + SET tmp = tmp -1; + END WHILE; + INSERT INTO tpcb.branch VALUES (brch, 0.0, "FRESH BRANCH"); + SET brch = brch - 1; + END WHILE; + WHILE tell > 0 DO + INSERT INTO tpcb.teller VALUES (tell, 0.0, "FRESH TELLER"); + SET tell = tell - 1; + END WHILE; +END| + +CREATE FUNCTION tpcb.account_id () RETURNS INT +BEGIN + DECLARE num INT; + DECLARE ran INT; + SELECT RAND() * 10 INTO ran; + IF (ran < 2) + THEN + SELECT RAND() * 10 INTO num; + ELSEIF (ran < 4) + THEN + SELECT RAND() * 100 INTO num; + ELSE + SELECT RAND() * 1000 INTO num; + END IF; + IF (num < 1) + THEN + RETURN 1; + END IF; + RETURN num; +END| + +CREATE FUNCTION tpcb.teller_id () RETURNS INT +BEGIN + DECLARE num INT; + DECLARE ran INT; + SELECT RAND() * 10 INTO ran; + IF (ran < 2) + THEN + SELECT RAND() * 10 INTO num; + ELSEIF (ran < 5) + THEN + SELECT RAND() * 100 INTO num; + ELSE + SELECT RAND() * 1000 INTO num; + END IF; + IF (num < 1) + THEN + RETURN 1; + END IF; + RETURN num; +END| + +CREATE PROCEDURE tpcb.trans() +BEGIN + DECLARE acct INT DEFAULT 0; + DECLARE brch INT DEFAULT 0; + DECLARE tell INT DEFAULT 0; + DECLARE bal DECIMAL(10,2) DEFAULT 0.0; + DECLARE amount DECIMAL(10,2) DEFAULT 1.00; + DECLARE test INT DEFAULT 0; + DECLARE bbal DECIMAL(10,2) DEFAULT 0.0; + DECLARE tbal DECIMAL(10,2) DEFAULT 0.0; + DECLARE local_uuid VARCHAR(255); + DECLARE local_user VARCHAR(255); + DECLARE local_time TIMESTAMP; + + SELECT RAND() * 10 INTO test; + SELECT tpcb.account_id() INTO acct; + SELECT tpcb.teller_id() INTO tell; + + SELECT account.balance INTO bal FROM tpcb.account WHERE id = acct; + SELECT account.bid INTO brch FROM tpcb.account WHERE id = acct; + SELECT teller.balance INTO tbal FROM tpcb.teller WHERE tid = tell; + SELECT branch.balance INTO bbal FROM tpcb.branch WHERE bid = brch; + + IF (test < 5) + THEN + SET bal = bal + amount; + SET bbal = bbal + amount; + SET tbal = tbal + amount; + UPDATE tpcb.account SET balance = bal, filler = 'account updated' + WHERE id = acct; + UPDATE tpcb.branch SET balance = bbal, filler = 'branch updated' + WHERE bid = brch; + UPDATE tpcb.teller SET balance = tbal, filler = 'teller updated' + WHERE tid = tell; + ELSE + SET bal = bal - amount; + SET bbal = bbal - amount; + SET tbal = tbal - amount; + UPDATE tpcb.account SET balance = bal, filler = 'account updated' + WHERE id = acct; + UPDATE tpcb.branch SET balance = bbal, filler = 'branch updated' + WHERE bid = brch; + UPDATE tpcb.teller SET balance = tbal, filler = 'teller updated' + WHERE tid = tell; + END IF; + + SET local_uuid=UUID(); + SET local_user=USER(); + SET local_time= NOW(); + INSERT INTO tpcb.history VALUES(NULL,acct,tell,brch,amount, local_time,local_user, + local_uuid,'completed trans'); +END| +delimiter ;| + +--echo ****** TEST 2 test time ********************************* +USE tpcb; + +--echo *********** Load up the database ****************** +CALL tpcb.load(); + +--echo ********** Check load master and slave ************** +SELECT COUNT(*) FROM account; +--sync_slave_with_master +connection slave; +USE tpcb; +SELECT COUNT(*) FROM account; + +--echo ******** Run in some transactions *************** + +connection master; +let $j= 1000; +--disable_query_log +while ($j) +{ + CALL tpcb.trans(); + dec $j; +} +--enable_query_log + +--echo ***** Time to try slave sync *********** +--echo **** Must make sure slave is clean ***** +--connection slave +STOP SLAVE; +RESET SLAVE; +DROP PROCEDURE tpcb.load; +DROP PROCEDURE tpcb.trans; +DROP TABLE tpcb.account; +DROP TABLE tpcb.teller; +DROP TABLE tpcb.branch; +DROP TABLE tpcb.history; +DROP DATABASE tpcb; + +ALTER TABLESPACE ts1 +DROP DATAFILE 'datafile.dat' +ENGINE=NDB; + +ALTER TABLESPACE ts1 +DROP DATAFILE 'datafile02.dat' +ENGINE=NDB; + +DROP TABLESPACE ts1 ENGINE=NDB; + +ALTER TABLESPACE ts2 +DROP DATAFILE 'datafile03.dat' +ENGINE=NDB; + +ALTER TABLESPACE ts2 +DROP DATAFILE 'datafile04.dat' +ENGINE=NDB; + +DROP TABLESPACE ts2 ENGINE=NDB; + +DROP LOGFILE GROUP lg1 ENGINE=NDB; + +--echo ********** Take a backup of the Master ************* +connection master; + +SELECT COUNT(*) FROM history; + +let $j= 1000; +--disable_query_log +while ($j) +{ + CALL tpcb.trans(); + dec $j; +} +--enable_query_log + +SELECT COUNT(*) FROM history; + +#RESET MASTER; +--exec $NDB_MGM --no-defaults --ndb-connectstring="localhost:$NDBCLUSTER_PORT" -e "start backup" >> $NDB_TOOLS_OUTPUT + +--exec $NDB_TOOLS_DIR/ndb_select_all --ndb-connectstring="localhost:$NDBCLUSTER_PORT" -d sys --delimiter=',' SYSTAB_0 | grep 520093696 > $MYSQLTEST_VARDIR/tmp.dat + +CREATE TEMPORARY TABLE IF NOT EXISTS cluster.backup_info (id INT, backup_id INT) ENGINE = HEAP; + +DELETE FROM cluster.backup_info; + +LOAD DATA INFILE '../tmp.dat' INTO TABLE cluster.backup_info FIELDS TERMINATED BY ','; + +--replace_column 1 + +SELECT @the_backup_id:=backup_id FROM cluster.backup_info; + +let the_backup_id=`select @the_backup_id`; + +DROP TABLE IF EXISTS cluster.backup_info; +#RESET MASTER; + +--echo ************ Restore the slave ************************ +connection slave; +CREATE DATABASE tpcb; +--source include/ndb_restore_slave_eoption.inc + +--echo ***** Check a few slave restore values *************** +connection slave; +USE tpcb; +SELECT COUNT(*) FROM account; + +--echo ***** Add some more records to master ********* +connection master; +let $j= 1000; +--disable_query_log +while ($j) +{ + CALL tpcb.trans(); + dec $j; +} +--enable_query_log + +# +# now setup replication to continue from last epoch +# 1. get apply_status epoch from slave +# 2. get corresponding _next_ binlog postition from master +# 3. change master on slave +# 4. add some transaction for slave to process +# 5. start the replication + +--echo ***** Finsh the slave sync process ******* +# 1. +--echo * 1. * +connection slave; +--disable_query_log +--replace_column 1 +SELECT @the_epoch:=MAX(epoch) FROM cluster.apply_status; +--let $the_epoch= `select @the_epoch` + +# 2. +--echo * 2. * +connection master; +--replace_column 1 +eval SELECT @the_pos:=Position,@the_file:=SUBSTRING_INDEX(FILE, '/', -1) + FROM cluster.binlog_index WHERE epoch > $the_epoch ORDER BY epoch ASC LIMIT 1; +--let $the_pos= `SELECT @the_pos` +--let $the_file= `SELECT @the_file` + +# 3. +--echo * 3. * +connection slave; +eval CHANGE MASTER TO + master_log_file = '$the_file', + master_log_pos = $the_pos; +--enable_query_log + +# 4. +--echo * 4. * +connection master; +let $j= 1000; +--disable_query_log +while ($j) +{ + CALL tpcb.trans(); + dec $j; +} +--enable_query_log + +# 5. +--echo * 5. * +connection slave; +START SLAVE; + +--echo **** We should be ready to continue on ************* + +connection master; +let $j= 50; +--disable_query_log +while ($j) +{ + CALL tpcb.trans(); + dec $j; +} +--enable_query_log + +--echo ****** Let's make sure we match ******* +--echo ***** MASTER ******* +USE tpcb; +SELECT COUNT(*) FROM history; + +--echo ****** SLAVE ******** +--sync_slave_with_master +connection slave; +USE tpcb; +SELECT COUNT(*) FROM history; + +--echo *** DUMP MASTER & SLAVE FOR COMPARE ******** + +--exec $MYSQL_DUMP --compact --order-by-primary --skip-extended-insert tpcb account teller branch history > $MYSQLTEST_VARDIR/tmp/RPL_DD_ADV_M.sql + +--exec $MYSQL_DUMP_SLAVE --compact --order-by-primary --skip-extended-insert tpcb account teller branch history > $MYSQLTEST_VARDIR/tmp/RPL_DD_ADV_S.sql + +--echo *************** TEST 2 CLEANUP SECTION ******************** +connection master; +DROP PROCEDURE tpcb.load; +DROP PROCEDURE tpcb.trans; +DROP TABLE tpcb.account; +DROP TABLE tpcb.teller; +DROP TABLE tpcb.branch; +DROP TABLE tpcb.history; +DROP DATABASE tpcb; + +ALTER TABLESPACE ts1 +DROP DATAFILE 'datafile.dat' +ENGINE=NDB; + +ALTER TABLESPACE ts1 +DROP DATAFILE 'datafile02.dat' +ENGINE=NDB; + +DROP TABLESPACE ts1 ENGINE=NDB; + +ALTER TABLESPACE ts2 +DROP DATAFILE 'datafile03.dat' +ENGINE=NDB; + +ALTER TABLESPACE ts2 +DROP DATAFILE 'datafile04.dat' +ENGINE=NDB; + +DROP TABLESPACE ts2 ENGINE=NDB; + +DROP LOGFILE GROUP lg1 ENGINE=NDB; + +--sync_slave_with_master + +connection master; + +--echo ****** Do dumps compare ************ + +--exec diff $MYSQLTEST_VARDIR/tmp/RPL_DD_ADV_M.sql $MYSQLTEST_VARDIR/tmp/RPL_DD_ADV_S.sql + +## Note: Ths files should only get removed, if the above diff succeeds. + +--exec rm $MYSQLTEST_VARDIR/tmp/RPL_DD_ADV_M.sql +--exec rm $MYSQLTEST_VARDIR/tmp/RPL_DD_ADV_S.sql + +# End 5.1 test case diff --git a/mysql-test/t/rpl_trigger.test b/mysql-test/t/rpl_trigger.test index d041755f1cf..591941c19eb 100644 --- a/mysql-test/t/rpl_trigger.test +++ b/mysql-test/t/rpl_trigger.test @@ -170,6 +170,7 @@ use test; drop table t1,t2; drop database other; + # # Test specific triggers including SELECT into var with replication # BUG#13227: @@ -265,6 +266,79 @@ while ($rnd) } +# +# BUG#16266: Definer is not fully qualified error during replication. +# +# The idea of this test is to emulate replication of a trigger from the old +# master (master w/o "DEFINER in triggers" support) to the new slave and check +# that: +# 1. the trigger on the slave will be replicated w/o errors; +# 2. the trigger on the slave will be non-SUID (will have no DEFINER); +# 3. the trigger can be activated later on the slave w/o errors. +# +# In order to emulate this kind of replication, we make the slave playing the binlog, +# recorded by 5.0.16 master. This binlog contains the following statements: +# CREATE TABLE t1(c INT); +# CREATE TABLE t2(s CHAR(200)); +# CREATE TRIGGER trg1 AFTER INSERT ON t1 +# FOR EACH ROW +# INSERT INTO t2 VALUES(CURRENT_USER()); +# INSERT INTO t1 VALUES(1); +# + +# 1. Check that the trigger's replication is succeeded. + +# Stop the slave. + +connection slave; +STOP SLAVE; + +# Replace master's binlog. + +connection master; +FLUSH LOGS; +exec cp $MYSQL_TEST_DIR/std_data/bug16266.000001 $MYSQLTEST_VARDIR/log/master-bin.000001; + +# Make the slave to replay the new binlog. + +connection slave; +RESET SLAVE; +START SLAVE; + +SELECT MASTER_POS_WAIT('master-bin.000001', 513) >= 0; + +# Check that the replication succeeded. + +SHOW TABLES LIKE 't_'; +SHOW TRIGGERS; +SELECT * FROM t1; +SELECT * FROM t2; + +# 2. Check that the trigger is non-SUID on the slave; +# 3. Check that the trigger can be activated on the slave. + +INSERT INTO t1 VALUES(2); + +SELECT * FROM t1; +SELECT * FROM t2; + +# That's all, cleanup. + +DROP TRIGGER trg1; +DROP TABLE t1; +DROP TABLE t2; + +STOP SLAVE; +RESET SLAVE; + +# The master should be clean. + +connection master; +SHOW TABLES LIKE 't_'; +SHOW TRIGGERS; + +RESET MASTER; + # # End of tests diff --git a/mysql-test/t/skip_grants.test b/mysql-test/t/skip_grants.test index 27220c9b507..93e052d8c71 100644 --- a/mysql-test/t/skip_grants.test +++ b/mysql-test/t/skip_grants.test @@ -8,14 +8,6 @@ drop procedure if exists f1; --enable_warnings use test; -# -# test that we can create VIEW if privileges check switched off -# -create table t1 (field1 INT); --- error ER_MALFORMED_DEFINER -CREATE VIEW v1 AS SELECT field1 FROM t1; -drop table t1; - # # Test that we can create and drop procedure without warnings # see bug#9993 @@ -33,3 +25,48 @@ drop table t1; # BUG#17595: DROP FUNCTION IF EXISTS f1 crashes server drop function if exists f1; + +# +# BUG#16777: Can not create trigger nor view w/o definer if --skip-grant-tables +# specified +# +# Also, a test that we can create VIEW if privileges check switched off has +# been moved here. +# + +# Prepare. + +--disable_warnings + +DROP VIEW IF EXISTS v1; +DROP VIEW IF EXISTS v2; + +DROP TABLE IF EXISTS t1; + +--enable_warnings + +# Test case. + +CREATE TABLE t1(c INT); + +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 + FOR EACH ROW + SET @a = 1; + +CREATE VIEW v1 AS SELECT * FROM t1; + +CREATE DEFINER=a@b TRIGGER ti_ai AFTER INSERT ON t1 + FOR EACH ROW + SET @b = 1; + +CREATE DEFINER=a@b VIEW v2 AS SELECT * FROM t1; + +# Cleanup. + +DROP TRIGGER t1_bi; +DROP TRIGGER ti_ai; + +DROP VIEW v1; +DROP VIEW v2; + +DROP TABLE t1; diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index 19f94a32d9c..f369dc64b0e 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -547,4 +547,200 @@ GRANT EXECUTE ON PROCEDURE p1 TO user_bug7787@localhost; DROP DATABASE db_bug7787; use test; + +# +# WL#2897: Complete definer support in the stored routines. +# +# The following cases are tested: +# 1. check that if DEFINER-clause is not explicitly specified, stored routines +# are created with CURRENT_USER privileges; +# 2. check that if DEFINER-clause specifies non-current user, SUPER privilege +# is required to create a stored routine; +# 3. check that if DEFINER-clause specifies non-existent user, a warning is +# emitted. +# 4. check that SHOW CREATE PROCEDURE | FUNCTION works correctly; +# +# The following cases are tested in other test suites: +# - check that mysqldump dumps new attribute correctly; +# - check that slave replicates CREATE-statements with explicitly specified +# DEFINER correctly. +# + +# Setup the environment. + +--echo +--echo ---> connection: root +--connection con1root + +--disable_warnings +DROP DATABASE IF EXISTS mysqltest; +--enable_warnings + +CREATE DATABASE mysqltest; + +CREATE USER mysqltest_1@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_1@localhost; + +CREATE USER mysqltest_2@localhost; +GRANT SUPER ON *.* TO mysqltest_2@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_2@localhost; + +--connect (mysqltest_2_con,localhost,mysqltest_2,,mysqltest) +--connect (mysqltest_1_con,localhost,mysqltest_1,,mysqltest) + +# test case (1). + +--echo +--echo ---> connection: mysqltest_2_con +--connection mysqltest_2_con + +use mysqltest; + +CREATE PROCEDURE wl2897_p1() SELECT 1; + +CREATE FUNCTION wl2897_f1() RETURNS INT RETURN 1; + +# test case (2). + +--echo +--echo ---> connection: mysqltest_1_con +--connection mysqltest_1_con + +use mysqltest; + +--error ER_SPECIFIC_ACCESS_DENIED_ERROR +CREATE DEFINER=root@localhost PROCEDURE wl2897_p2() SELECT 2; + +--error ER_SPECIFIC_ACCESS_DENIED_ERROR +CREATE DEFINER=root@localhost FUNCTION wl2897_f2() RETURNS INT RETURN 2; + +# test case (3). + +--echo +--echo ---> connection: mysqltest_2_con +--connection mysqltest_2_con + +use mysqltest; + +CREATE DEFINER='a @ b @ c'@localhost PROCEDURE wl2897_p3() SELECT 3; + +CREATE DEFINER='a @ b @ c'@localhost FUNCTION wl2897_f3() RETURNS INT RETURN 3; + +# test case (4). + +--echo +--echo ---> connection: con1root +--connection con1root + +use mysqltest; + +SHOW CREATE PROCEDURE wl2897_p1; +SHOW CREATE PROCEDURE wl2897_p3; + +SHOW CREATE FUNCTION wl2897_f1; +SHOW CREATE FUNCTION wl2897_f3; + +# Cleanup. + +DROP USER mysqltest_1@localhost; +DROP USER mysqltest_2@localhost; + +DROP DATABASE mysqltest; + +--disconnect mysqltest_1_con +--disconnect mysqltest_2_con + + +# +# BUG#13198: SP executes if definer does not exist +# + +# Prepare environment. + +--echo +--echo ---> connection: root +--connection con1root + +--disable_warnings +DROP DATABASE IF EXISTS mysqltest; +--enable_warnings + +CREATE DATABASE mysqltest; + +CREATE USER mysqltest_1@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_1@localhost; + +CREATE USER mysqltest_2@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_2@localhost; + +--connect (mysqltest_1_con,localhost,mysqltest_1,,mysqltest) +--connect (mysqltest_2_con,localhost,mysqltest_2,,mysqltest) + +# Create a procedure/function under u1. + +--echo +--echo ---> connection: mysqltest_1_con +--connection mysqltest_1_con + +use mysqltest; + +CREATE PROCEDURE bug13198_p1() + SELECT 1; + +CREATE FUNCTION bug13198_f1() RETURNS INT + RETURN 1; + +CALL bug13198_p1(); + +SELECT bug13198_f1(); + +# Check that u2 can call the procedure/function. + +--echo +--echo ---> connection: mysqltest_2_con +--connection mysqltest_2_con + +use mysqltest; + +CALL bug13198_p1(); + +SELECT bug13198_f1(); + +# Drop user u1 (definer of the object); + +--echo +--echo ---> connection: root +--connection con1root + +--disconnect mysqltest_1_con + +DROP USER mysqltest_1@localhost; + +# Check that u2 can not call the procedure/function. + +--echo +--echo ---> connection: mysqltest_2_con +--connection mysqltest_2_con + +use mysqltest; + +--error ER_NO_SUCH_USER +CALL bug13198_p1(); + +--error ER_NO_SUCH_USER +SELECT bug13198_f1(); + +# Cleanup. + +--echo +--echo ---> connection: root +--connection con1root + +--disconnect mysqltest_2_con + +DROP USER mysqltest_2@localhost; + +DROP DATABASE mysqltest; + + # End of 5.0 bugs. diff --git a/mysql-test/t/trigger-grant.test b/mysql-test/t/trigger-grant.test index e79abf1b826..67aec1496dd 100644 --- a/mysql-test/t/trigger-grant.test +++ b/mysql-test/t/trigger-grant.test @@ -317,10 +317,7 @@ CREATE DEFINER='mysqltest_nonexs'@'localhost' # Check that trg2 will not be activated. -# --error ER_SPECIFIC_ACCESS_DENIED_ERROR -# -# TODO: Due to the BUG#13198(SP executes if definer does not exist) the -# following statement does not fail as it should. +--error ER_NO_SUCH_USER INSERT INTO t1 VALUES(6); # diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 294a254390f..eb7940158ff 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -2390,3 +2390,17 @@ show create view v1; drop view v1; // delimiter ;// + +# +# Bug#17726 Not checked empty list caused endless loop +# +create table t1(f1 int, f2 int); +create view v1 as select ta.f1 as a, tb.f1 as b from t1 ta, t1 tb where ta.f1=tb +.f1 and ta.f2=tb.f2; +insert into t1 values(1,1),(2,2); +create view v2 as select * from v1 where a > 1 with check option; +select * from v2; +update v2 set b=3 where a=2; +select * from v2; +drop view v2, v1; +drop table t1; diff --git a/mysql-test/t/view_grant.test b/mysql-test/t/view_grant.test index a3a8aa6a4c3..504c4892808 100644 --- a/mysql-test/t/view_grant.test +++ b/mysql-test/t/view_grant.test @@ -712,10 +712,11 @@ connection default; # # DEFINER information check # --- error ER_MALFORMED_DEFINER create definer=some_user@`` sql security invoker view v1 as select 1; -create definer=some_user@localhost sql security invoker view v1 as select 1; +create definer=some_user@localhost sql security invoker view v2 as select 1; show create view v1; +show create view v2; drop view v1; +drop view v2; diff --git a/sql/event_timed.cc b/sql/event_timed.cc index a8620197668..8348a75914e 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -14,13 +14,14 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define MYSQL_LEX 1 #include "event_priv.h" #include "event.h" #include "sp.h" -extern int yyparse(void *thd); +extern int MYSQLparse(void *thd); /* Init all member variables @@ -1063,8 +1064,6 @@ Event_timed::get_create_event(THD *thd, String *buf) DBUG_RETURN(EVEX_MICROSECOND_UNSUP); buf->append(STRING_WITH_LEN("CREATE EVENT ")); - append_identifier(thd, buf, dbname.str, dbname.length); - buf->append(STRING_WITH_LEN(".")); append_identifier(thd, buf, name.str, name.length); buf->append(STRING_WITH_LEN(" ON SCHEDULE ")); @@ -1338,7 +1337,7 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root) thd->lex= &lex; lex_start(thd, (uchar*)thd->query, thd->query_length); lex.et_compile_phase= TRUE; - if (yyparse((void *)thd) || thd->is_fatal_error) + if (MYSQLparse((void *)thd) || thd->is_fatal_error) { DBUG_PRINT("error", ("error during compile or thd->is_fatal_error=%d", thd->is_fatal_error)); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 6b8a5c2fdf4..6c19c96f77c 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2547,7 +2547,8 @@ Item_cond::fix_fields(THD *thd, Item **ref) { table_map tmp_table_map; while (item->type() == Item::COND_ITEM && - ((Item_cond*) item)->functype() == functype()) + ((Item_cond*) item)->functype() == functype() && + !((Item_cond*) item)->list.is_empty()) { // Identical function li.replace(((Item_cond*) item)->list); ((Item_cond*) item)->list.empty(); diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index dd2e49ee5f3..2eea891d51f 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -548,7 +548,8 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables); bool create_table_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *create_table); -bool get_default_definer(THD *thd, LEX_USER *definer); +void get_default_definer(THD *thd, LEX_USER *definer); +LEX_USER *create_default_definer(THD *thd); LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name); enum enum_mysql_completiontype { @@ -1223,7 +1224,7 @@ File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg); /* mysqld.cc */ -extern void yyerror(const char*); +extern void MYSQLerror(const char*); /* item_func.cc */ extern bool check_reserved_words(LEX_STRING *name); @@ -1634,7 +1635,7 @@ void free_list(I_List *list); void free_list(I_List *list); /* sql_yacc.cc */ -extern int yyparse(void *thd); +extern int MYSQLparse(void *thd); /* frm_crypt.cc */ #ifdef HAVE_CRYPTED_FRM diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 59af5a529d0..7fcbf5134f5 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1620,7 +1620,7 @@ static void network_init(void) #endif /*!EMBEDDED_LIBRARY*/ -void yyerror(const char *s) +void MYSQLerror(const char *s) { THD *thd=current_thd; char *yytext= (char*) thd->lex->tok_start; diff --git a/sql/sp.cc b/sql/sp.cc index 481d81edfe4..022417d15d1 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -20,6 +20,8 @@ #include "sp_cache.h" #include "sql_trigger.h" +#include + static bool create_string(THD *thd, String *buf, int sp_type, @@ -27,7 +29,9 @@ create_string(THD *thd, String *buf, const char *params, ulong paramslen, const char *returns, ulong returnslen, const char *body, ulong bodylen, - st_sp_chistics *chistics); + st_sp_chistics *chistics, + const LEX_STRING *definer_user, + const LEX_STRING *definer_host); static int db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, ulong sql_mode, const char *params, const char *returns, @@ -264,7 +268,7 @@ db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table) static int db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) { - extern int yyparse(void *thd); + extern int MYSQLparse(void *thd); TABLE *table; const char *params, *returns, *body; int ret; @@ -405,6 +409,15 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, ulong old_sql_mode= thd->variables.sql_mode; ha_rows old_select_limit= thd->variables.select_limit; sp_rcontext *old_spcont= thd->spcont; + + char definer_user_name_holder[USERNAME_LENGTH + 1]; + LEX_STRING_WITH_INIT definer_user_name(definer_user_name_holder, + USERNAME_LENGTH); + + char definer_host_name_holder[HOSTNAME_LENGTH + 1]; + LEX_STRING_WITH_INIT definer_host_name(definer_host_name_holder, + HOSTNAME_LENGTH); + int ret; thd->variables.sql_mode= sql_mode; @@ -413,14 +426,25 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, thd->lex= &newlex; newlex.current_select= NULL; + parse_user(definer, strlen(definer), + definer_user_name.str, &definer_user_name.length, + definer_host_name.str, &definer_host_name.length); + defstr.set_charset(system_charset_info); + + /* + We have to add DEFINER clause and provide proper routine characterstics in + routine definition statement that we build here to be able to use this + definition for SHOW CREATE PROCEDURE later. + */ + if (!create_string(thd, &defstr, type, name, params, strlen(params), returns, strlen(returns), body, strlen(body), - &chistics)) + &chistics, &definer_user_name, &definer_host_name)) { ret= SP_INTERNAL_ERROR; goto end; @@ -434,7 +458,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length()); thd->spcont= 0; - if (yyparse(thd) || thd->is_fatal_error || newlex.sphead == NULL) + if (MYSQLparse(thd) || thd->is_fatal_error || newlex.sphead == NULL) { sp_head *sp= newlex.sphead; @@ -448,7 +472,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, if (dbchanged && (ret= mysql_change_db(thd, olddb, 1))) goto end; *sphp= newlex.sphead; - (*sphp)->set_definer((char*) definer, (uint) strlen(definer)); + (*sphp)->set_definer(&definer_user_name, &definer_host_name); (*sphp)->set_info(created, modified, &chistics, sql_mode); (*sphp)->optimize(); } @@ -501,8 +525,10 @@ db_create_routine(THD *thd, int type, sp_head *sp) else { restore_record(table, s->default_values); // Get default values for fields - strxnmov(definer, sizeof(definer)-1, thd->security_ctx->priv_user, "@", - thd->security_ctx->priv_host, NullS); + + /* NOTE: all needed privilege checks have been already done. */ + strxnmov(definer, sizeof(definer)-1, thd->lex->definer->user.str, "@", + thd->lex->definer->host.str, NullS); if (table->s->fields != MYSQL_PROC_FIELD_COUNT) { @@ -593,9 +619,17 @@ db_create_routine(THD *thd, int type, sp_head *sp) else if (mysql_bin_log.is_open()) { thd->clear_error(); + + String log_query; + log_query.set_charset(system_charset_info); + 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); + /* Such a statement can always go directly to binlog, no trans cache */ thd->binlog_query(THD::MYSQL_QUERY_TYPE, - thd->query, thd->query_length, FALSE, FALSE); + log_query.c_ptr(), log_query.length(), FALSE, FALSE); } } @@ -1724,14 +1758,18 @@ create_string(THD *thd, String *buf, const char *params, ulong paramslen, const char *returns, ulong returnslen, const char *body, ulong bodylen, - st_sp_chistics *chistics) + st_sp_chistics *chistics, + const LEX_STRING *definer_user, + const LEX_STRING *definer_host) { /* Make some room to begin with */ if (buf->alloc(100 + name->m_qname.length + paramslen + returnslen + bodylen + - chistics->comment.length)) + chistics->comment.length + 10 /* length of " DEFINER= "*/ + + USER_HOST_BUFF_SIZE)) return FALSE; buf->append(STRING_WITH_LEN("CREATE ")); + append_definer(thd, buf, definer_user, definer_host); if (type == TYPE_ENUM_FUNCTION) buf->append(STRING_WITH_LEN("FUNCTION ")); else diff --git a/sql/sp_head.cc b/sql/sp_head.cc index d7490a5ac8e..d97dcc390ca 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1846,19 +1846,27 @@ sp_head::set_info(longlong created, longlong modified, void sp_head::set_definer(const char *definer, uint definerlen) { - uint user_name_len; - char user_name_str[USERNAME_LENGTH + 1]; - uint host_name_len; - char host_name_str[HOSTNAME_LENGTH + 1]; + char user_name_holder[USERNAME_LENGTH + 1]; + LEX_STRING_WITH_INIT user_name(user_name_holder, USERNAME_LENGTH); - parse_user(definer, definerlen, user_name_str, &user_name_len, - host_name_str, &host_name_len); + char host_name_holder[HOSTNAME_LENGTH + 1]; + LEX_STRING_WITH_INIT host_name(host_name_holder, HOSTNAME_LENGTH); - m_definer_user.str= strmake_root(mem_root, user_name_str, user_name_len); - m_definer_user.length= user_name_len; + parse_user(definer, definerlen, user_name.str, &user_name.length, + host_name.str, &host_name.length); - m_definer_host.str= strmake_root(mem_root, host_name_str, host_name_len); - m_definer_host.length= host_name_len; + set_definer(&user_name, &host_name); +} + + +void +sp_head::set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name) +{ + m_definer_user.str= strmake_root(mem_root, user_name->str, user_name->length); + m_definer_user.length= user_name->length; + + m_definer_host.str= strmake_root(mem_root, host_name->str, host_name->length); + m_definer_host.length= host_name->length; } @@ -3204,24 +3212,9 @@ sp_change_security_context(THD *thd, sp_head *sp, Security_context **backup) sp->m_definer_host.str, sp->m_db.str)) { -#ifdef NOT_YET_REPLICATION_SAFE - /* - Until we don't properly replicate information about stored routine - definer with stored routine creation statement all stored routines - on slave are created under ''@'' definer. Therefore we won't be able - to run any routine which was replicated from master on slave server - if we emit error here. This will cause big problems for users - who use slave for fail-over. So until we fully implement WL#2897 - "Complete definer support in the stored routines" we run suid - stored routines for which we were unable to find definer under - invoker security context. - */ my_error(ER_NO_SUCH_USER, MYF(0), sp->m_definer_user.str, sp->m_definer_host.str); return TRUE; -#else - return FALSE; -#endif } *backup= thd->security_ctx; thd->security_ctx= &sp->m_security_ctx; diff --git a/sql/sp_head.h b/sql/sp_head.h index 472e5343991..66e0a5856f4 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -303,6 +303,7 @@ public: st_sp_chistics *chistics, ulong sql_mode); void set_definer(const char *definer, uint definerlen); + void set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name); void reset_thd_mem_root(THD *thd); diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index 0e4198a5114..af9246c673a 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -27,6 +27,8 @@ #pragma implementation // gcc: Class implementation #endif +#define MYSQL_LEX 1 + #include "mysql_priv.h" #include "procedure.h" #include "sql_analyse.h" diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 20c71ae738b..b5c1d943339 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -17,6 +17,7 @@ /* A lexical scanner on a temporary buffer with a yacc interface */ +#define MYSQL_LEX 1 #include "mysql_priv.h" #include "item_create.h" #include @@ -228,7 +229,7 @@ static int find_keyword(LEX *lex, uint len, bool function) SYNOPSIS is_keyword() - name checked name + name checked name (must not be empty) len length of checked name RETURN VALUES @@ -238,6 +239,7 @@ static int find_keyword(LEX *lex, uint len, bool function) bool is_keyword(const char *name, uint len) { + DBUG_ASSERT(len != 0); return get_hash_symbol(name,len,0)!=0; } @@ -511,14 +513,14 @@ static inline uint int_token(const char *str,uint length) } /* - yylex remember the following states from the following yylex() + MYSQLlex remember the following states from the following MYSQLlex() - MY_LEX_EOQ Found end of query - MY_LEX_OPERATOR_OR_IDENT Last state was an ident, text or number (which can't be followed by a signed number) */ -int yylex(void *arg, void *yythd) +int MYSQLlex(void *arg, void *yythd) { reg1 uchar c; int tokval, result_state; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 08167f8b6d2..66f097a101c 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -41,8 +41,12 @@ class Event_timed; #define LEX_YYSTYPE void * #else #include "lex_symbol.h" +#if MYSQL_LEX #include "sql_yacc.h" #define LEX_YYSTYPE YYSTYPE * +#else +#define LEX_YYSTYPE void * +#endif #endif #endif @@ -753,7 +757,7 @@ typedef struct st_lex const uchar *buf; /* The beginning of string, used by SPs */ const uchar *ptr,*tok_start,*tok_end,*end_of_query; - /* The values of tok_start/tok_end as they were one call of yylex before */ + /* The values of tok_start/tok_end as they were one call of MYSQLlex before */ const uchar *tok_start_prev, *tok_end_prev; char *length,*dec,*change,*name; @@ -957,12 +961,16 @@ typedef struct st_lex SQL_LIST trg_table_fields; /* - trigger_definition_begin points to the beginning of the word "TRIGGER" in - CREATE TRIGGER statement. This is used to add possibly omitted DEFINER - clause to the trigger definition statement before dumping it to the - binlog. + stmt_definition_begin is intended to point to the next word after + DEFINER-clause in the following statements: + - CREATE TRIGGER (points to "TRIGGER"); + - CREATE PROCEDURE (points to "PROCEDURE"); + - CREATE FUNCTION (points to "FUNCTION" or "AGGREGATE"); + + This pointer is required to add possibly omitted DEFINER-clause to the + DDL-statement before dumping it to the binlog. */ - const char *trigger_definition_begin; + const char *stmt_definition_begin; /* If non-0 then indicates that query requires prelocking and points to @@ -1111,7 +1119,7 @@ extern void lex_init(void); extern void lex_free(void); extern void lex_start(THD *thd, const uchar *buf, uint length); extern void lex_end(LEX *lex); -extern int yylex(void *arg, void *yythd); +extern int MYSQLlex(void *arg, void *yythd); extern pthread_key(LEX*,THR_LEX); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 4ee47a574b1..112d2e3440a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -14,6 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define MYSQL_LEX 1 #include "mysql_priv.h" #include "sql_repl.h" #include "rpl_filter.h" @@ -4280,6 +4281,90 @@ end_with_restore_list: } #endif + /* + If the definer is not specified, this means that CREATE-statement missed + DEFINER-clause. DEFINER-clause can be missed in two cases: + + - The user submitted a statement w/o the clause. This is a normal + case, we should assign CURRENT_USER as definer. + + - Our slave received an updated from the master, that does not + replicate definer for stored rountines. We should also assign + CURRENT_USER as definer here, but also we should mark this routine + as NON-SUID. This is essential for the sake of backward + compatibility. + + The problem is the slave thread is running under "special" user (@), + that actually does not exist. In the older versions we do not fail + execution of a stored routine if its definer does not exist and + continue the execution under the authorization of the invoker + (BUG#13198). And now if we try to switch to slave-current-user (@), + we will fail. + + Actually, this leads to the inconsistent state of master and + slave (different definers, different SUID behaviour), but it seems, + this is the best we can do. + */ + + if (!lex->definer) + { + bool res= FALSE; + Query_arena original_arena; + Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena); + + if (!(lex->definer= create_default_definer(thd))) + res= TRUE; + + if (ps_arena) + thd->restore_active_arena(ps_arena, &original_arena); + + if (res) + { + /* Error has been already reported. */ + delete lex->sphead; + lex->sphead= 0; + goto error; + } + + if (thd->slave_thread) + lex->sphead->m_chistics->suid= SP_IS_NOT_SUID; + } + + /* + If the specified definer differs from the current user, we should check + that the current user has SUPER privilege (in order to create a stored + routine under another user one must have SUPER privilege). + */ + + else if (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) || + my_strcasecmp(system_charset_info, + lex->definer->host.str, + thd->security_ctx->priv_host)) + { + if (check_global_access(thd, SUPER_ACL)) + { + my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); + delete lex->sphead; + lex->sphead= 0; + goto error; + } + } + + /* Check that the specified definer exists. Emit a warning if not. */ + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (!is_acl_user(lex->definer->host.str, + lex->definer->user.str)) + { + push_warning_printf(thd, + MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_NO_SUCH_USER, + ER(ER_NO_SUCH_USER), + lex->definer->user.str, + lex->definer->host.str); + } +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ + /* We need to copy name and db in order to use them for check_routine_access which is called after lex->sphead has @@ -4294,7 +4379,7 @@ end_with_restore_list: /* We must cleanup the unit and the lex here because sp_grant_privileges calls (indirectly) db_find_routine, - which in turn may call yyparse with THD::lex. + which in turn may call MYSQLparse with THD::lex. TODO: fix db_find_routine to use a temporary lex. */ lex->unit.cleanup(); @@ -5733,7 +5818,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length) sp_cache_flush_obsolete(&thd->sp_proc_cache); sp_cache_flush_obsolete(&thd->sp_func_cache); - if (!yyparse((void *)thd) && ! thd->is_fatal_error) + if (!MYSQLparse((void *)thd) && ! thd->is_fatal_error) { #ifndef NO_EMBEDDED_ACCESS_CHECKS if (mqh_used && thd->user_connect && @@ -5825,7 +5910,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) DBUG_ENTER("mysql_test_parse_for_slave"); mysql_init_query(thd, (uchar*) inBuf, length); - if (!yyparse((void*) thd) && ! thd->is_fatal_error && + if (!MYSQLparse((void*) thd) && ! thd->is_fatal_error && all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first)) error= 1; /* Ignore question */ thd->end_statement(); @@ -7344,47 +7429,54 @@ Item *negate_expression(THD *thd, Item *expr) /* Set the specified definer to the default value, which is the current user in - the thread. Also check that the current user satisfies to the definers - requirements. + the thread. SYNOPSIS get_default_definer() thd [in] thread handler definer [out] definer - - RETURN - error status, that is: - - FALSE -- on success; - - TRUE -- on error (current user can not be a definer). */ -bool get_default_definer(THD *thd, LEX_USER *definer) +void get_default_definer(THD *thd, LEX_USER *definer) { - /* Check that current user has non-empty host name. */ - const Security_context *sctx= thd->security_ctx; - if (sctx->priv_host[0] == 0) - { - my_error(ER_MALFORMED_DEFINER, MYF(0)); - return TRUE; - } - - /* Fill in. */ - definer->user.str= (char *) sctx->priv_user; definer->user.length= strlen(definer->user.str); definer->host.str= (char *) sctx->priv_host; definer->host.length= strlen(definer->host.str); - - return FALSE; } /* - Create definer with the given user and host names. Also check that the user - and host names satisfy definers requirements. + Create default definer for the specified THD. + + SYNOPSIS + create_default_definer() + thd [in] thread handler + + RETURN + On success, return a valid pointer to the created and initialized + LEX_USER, which contains definer information. + On error, return 0. +*/ + +LEX_USER *create_default_definer(THD *thd) +{ + LEX_USER *definer; + + if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER)))) + return 0; + + get_default_definer(thd, definer); + + return definer; +} + + +/* + Create definer with the given user and host names. SYNOPSIS create_definer() @@ -7394,7 +7486,7 @@ bool get_default_definer(THD *thd, LEX_USER *definer) RETURN On success, return a valid pointer to the created and initialized - LEX_STRING, which contains definer information. + LEX_USER, which contains definer information. On error, return 0. */ @@ -7402,14 +7494,6 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name) { LEX_USER *definer; - /* Check that specified host name is valid. */ - - if (host_name->length == 0) - { - my_error(ER_MALFORMED_DEFINER, MYF(0)); - return 0; - } - /* Create and initialize. */ if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER)))) diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 257c1988cbd..014d3616d3d 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -33,6 +33,7 @@ /* Some general useful functions */ +#define MYSQL_LEX 1 #include "mysql_priv.h" #include #include @@ -3697,7 +3698,7 @@ bool mysql_unpack_partition(THD *thd, const uchar *part_buf, we then save in the partition info structure. */ thd->free_list= NULL; - lex.part_info= new partition_info();/* Indicates yyparse from this place */ + lex.part_info= new partition_info();/* Indicates MYSQLparse from this place */ if (!lex.part_info) { mem_alloc_error(sizeof(partition_info)); @@ -3706,7 +3707,7 @@ bool mysql_unpack_partition(THD *thd, const uchar *part_buf, lex.part_info->part_state= part_state; lex.part_info->part_state_len= part_state_len; DBUG_PRINT("info", ("Parse: %s", part_buf)); - if (yyparse((void*)thd) || thd->is_fatal_error) + if (MYSQLparse((void*)thd) || thd->is_fatal_error) { free_items(thd->free_list); goto end; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index a66b2bdb9b6..a0cfc5a7a9e 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2758,7 +2758,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) lex_start(thd, (uchar*) thd->query, thd->query_length); lex->stmt_prepare_mode= TRUE; - error= yyparse((void *)thd) || thd->is_fatal_error || + error= MYSQLparse((void *)thd) || thd->is_fatal_error || thd->net.report_error || init_param_array(this); lex->safe_to_cache_query= FALSE; /* diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 51c92977a27..43b7c494246 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -831,7 +831,8 @@ append_identifier(THD *thd, String *packet, const char *name, uint length) int get_quote_char_for_identifier(THD *thd, const char *name, uint length) { - if (!is_keyword(name,length) && + if (length && + !is_keyword(name,length) && !require_quotes(name, length) && !(thd->options & OPTION_QUOTE_SHOW_CREATE)) return EOF; diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 3e8b88c3a2e..f1ecae884d5 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -15,6 +15,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define MYSQL_LEX 1 #include "mysql_priv.h" #include "sp_head.h" #include "sql_trigger.h" @@ -270,8 +271,18 @@ end: log_query.set((char *) 0, 0, system_charset_info); /* reset log_query */ log_query.append(STRING_WITH_LEN("CREATE ")); - append_definer(thd, &log_query, &definer_user, &definer_host); - log_query.append(thd->lex->trigger_definition_begin); + + if (definer_user.str && definer_host.str) + { + /* + Append definer-clause if the trigger is SUID (a usual trigger in + new MySQL versions). + */ + + append_definer(thd, &log_query, &definer_user, &definer_host); + } + + log_query.append(thd->lex->stmt_definition_begin); } /* Such a statement can always go directly to binlog, no trans cache. */ @@ -295,17 +306,30 @@ end: LEX) tables - table list containing one open table for which the trigger is created. - definer_user - [out] after a call it points to 0-terminated string, - which contains user name part of the actual trigger - definer. The caller is responsible to provide memory for + definer_user - [out] after a call it points to 0-terminated string or + contains the NULL-string: + - 0-terminated is returned if the trigger is SUID. The + string contains user name part of the actual trigger + definer. + - NULL-string is returned if the trigger is non-SUID. + Anyway, the caller is responsible to provide memory for storing LEX_STRING object. - definer_host - [out] after a call it points to 0-terminated string, - which contains host name part of the actual trigger - definer. The caller is responsible to provide memory for + definer_host - [out] after a call it points to 0-terminated string or + contains the NULL-string: + - 0-terminated string is returned if the trigger is + SUID. The string contains host name part of the + actual trigger definer. + - NULL-string is returned if the trigger is non-SUID. + Anyway, the caller is responsible to provide memory for storing LEX_STRING object. NOTE - Assumes that trigger name is fully qualified. + - Assumes that trigger name is fully qualified. + - NULL-string means the following LEX_STRING instance: + { str = 0; length = 0 }. + - In other words, definer_user and definer_host should contain + simultaneously NULL-strings (non-SUID/old trigger) or valid strings + (SUID/new trigger). RETURN VALUE False - success @@ -342,12 +366,30 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, return 1; } - /* - Definer attribute of the Lex instance is always set in sql_yacc.yy when - trigger is created. - */ + if (!lex->definer) + { + /* + DEFINER-clause is missing. - DBUG_ASSERT(lex->definer); + If we are in slave thread, this means that we received CREATE TRIGGER + from the master, that does not support definer in triggers. So, we + should mark this trigger as non-SUID. Note that this does not happen + when we parse triggers' definitions during opening .TRG file. + LEX::definer is ignored in that case. + + Otherwise, we should use CURRENT_USER() as definer. + + NOTE: when CREATE TRIGGER statement is allowed to be executed in PS/SP, + it will be required to create the definer below in persistent MEM_ROOT + of PS/SP. + */ + + if (!thd->slave_thread) + { + if (!(lex->definer= create_default_definer(thd))) + return 1; + } + } /* If the specified definer differs from the current user, we should check @@ -355,10 +397,11 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, under another user one must have SUPER privilege). */ - if (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) || - my_strcasecmp(system_charset_info, - lex->definer->host.str, - thd->security_ctx->priv_host)) + if (lex->definer && + (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) || + my_strcasecmp(system_charset_info, + lex->definer->host.str, + thd->security_ctx->priv_host))) { if (check_global_access(thd, SUPER_ACL)) { @@ -450,8 +493,8 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, *trg_sql_mode= thd->variables.sql_mode; #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (!is_acl_user(lex->definer->host.str, - lex->definer->user.str)) + if (lex->definer && !is_acl_user(lex->definer->host.str, + lex->definer->user.str)) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, @@ -462,12 +505,30 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, } #endif /* NO_EMBEDDED_ACCESS_CHECKS */ - *definer_user= lex->definer->user; - *definer_host= lex->definer->host; + if (lex->definer) + { + /* SUID trigger. */ - trg_definer->str= trg_definer_holder; - trg_definer->length= strxmov(trg_definer->str, definer_user->str, "@", - definer_host->str, NullS) - trg_definer->str; + *definer_user= lex->definer->user; + *definer_host= lex->definer->host; + + trg_definer->str= trg_definer_holder; + trg_definer->length= strxmov(trg_definer->str, definer_user->str, "@", + definer_host->str, NullS) - trg_definer->str; + } + else + { + /* non-SUID trigger. */ + + definer_user->str= 0; + definer_user->length= 0; + + definer_host->str= 0; + definer_host->length= 0; + + trg_definer->str= (char*) ""; + trg_definer->length= 0; + } if (!sql_create_definition_file(NULL, &file, &triggers_file_type, (gptr)this, triggers_file_parameters, 0)) @@ -868,7 +929,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, lex_start(thd, (uchar*)trg_create_str->str, trg_create_str->length); thd->spcont= 0; - if (yyparse((void *)thd) || thd->is_fatal_error) + if (MYSQLparse((void *)thd) || thd->is_fatal_error) { /* Free lex associated resources. diff --git a/sql/sql_view.cc b/sql/sql_view.cc index c4cb9770e14..1b5e91daf3e 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -15,6 +15,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define MYSQL_LEX 1 #include "mysql_priv.h" #include "sql_select.h" #include "parse_file.h" @@ -208,6 +209,26 @@ bool mysql_create_view(THD *thd, if (mode != VIEW_CREATE_NEW) sp_cache_invalidate(); + if (!lex->definer) + { + /* + DEFINER-clause is missing; we have to create default definer in + persistent arena to be PS/SP friendly. + */ + + Query_arena original_arena; + Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena); + + if (!(lex->definer= create_default_definer(thd))) + res= TRUE; + + if (ps_arena) + thd->restore_active_arena(ps_arena, &original_arena); + + if (res) + goto err; + } + #ifndef NO_EMBEDDED_ACCESS_CHECKS /* check definer of view: @@ -817,8 +838,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table) push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_VIEW_FRM_NO_USER, ER(ER_VIEW_FRM_NO_USER), table->db, table->table_name); - if (get_default_definer(thd, &table->definer)) - goto err; + get_default_definer(thd, &table->definer); } /* @@ -872,7 +892,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table) MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES); CHARSET_INFO *save_cs= thd->variables.character_set_client; thd->variables.character_set_client= system_charset_info; - res= yyparse((void *)thd); + res= MYSQLparse((void *)thd); thd->variables.character_set_client= save_cs; thd->variables.sql_mode= save_mode; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index a64886d503d..a93d10d7173 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -826,7 +826,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type FUNC_ARG0 FUNC_ARG1 FUNC_ARG2 FUNC_ARG3 keyword keyword_sp -%type user grant_user get_definer +%type user grant_user %type opt_collate @@ -881,8 +881,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa load_data opt_field_or_var_spec fields_or_vars opt_load_data_set_spec definer view_replace_or_algorithm view_replace view_algorithm_opt - view_algorithm view_or_trigger_tail view_suid view_tail view_list_opt - view_list view_select view_check_option trigger_tail + view_algorithm view_or_trigger_or_sp view_or_trigger_or_sp_tail + view_suid view_tail view_list_opt view_list view_select + view_check_option trigger_tail sp_tail install uninstall partition_entry binlog_base64_event END_OF_INPUT @@ -1252,78 +1253,6 @@ create: lex->name=$4.str; lex->create_info.options=$3; } - | CREATE udf_func_type FUNCTION_SYM sp_name - { - LEX *lex=Lex; - lex->spname= $4; - lex->udf.type= $2; - } - create_function_tail - {} - | CREATE PROCEDURE sp_name - { - LEX *lex= Lex; - sp_head *sp; - - if (lex->sphead) - { - my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"); - YYABORT; - } - /* Order is important here: new - reset - init */ - sp= new sp_head(); - sp->reset_thd_mem_root(YYTHD); - sp->init(lex); - - sp->m_type= TYPE_ENUM_PROCEDURE; - lex->sphead= sp; - /* - * We have to turn of CLIENT_MULTI_QUERIES while parsing a - * stored procedure, otherwise yylex will chop it into pieces - * at each ';'. - */ - $$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; - YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); - } - '(' - { - LEX *lex= Lex; - - lex->sphead->m_param_begin= lex->tok_start+1; - } - sp_pdparam_list - ')' - { - LEX *lex= Lex; - - lex->sphead->m_param_end= lex->tok_start; - bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); - } - sp_c_chistics - { - LEX *lex= Lex; - - lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lex->tok_start; - } - sp_proc_stmt - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - - if (sp->check_backpatch(YYTHD)) - YYABORT; - sp->init_strings(YYTHD, lex, $3); - lex->sql_command= SQLCOM_CREATE_PROCEDURE; - - /* - Restore flag if it was cleared above - Be careful with counting. the block where we save the value - is $4. - */ - YYTHD->client_capabilities |= $4; - sp->restore_thd_mem_root(YYTHD); - } | CREATE EVENT_SYM opt_if_not_exists sp_name /* BE CAREFUL when you add a new rule to update the block where @@ -1390,7 +1319,7 @@ create: Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; Lex->create_view_suid= TRUE; } - view_or_trigger + view_or_trigger_or_sp {} | CREATE USER clear_privileges grant_list { @@ -10783,45 +10712,61 @@ subselect_end: lex->nest_level--; }; -definer: - get_definer - { - THD *thd= YYTHD; - - if (! (thd->lex->definer= create_definer(thd, &$1->user, &$1->host))) - YYABORT; - } +/************************************************************************** + + CREATE VIEW | TRIGGER | PROCEDURE statements. + +**************************************************************************/ + +view_or_trigger_or_sp: + definer view_or_trigger_or_sp_tail + {} + | view_replace_or_algorithm definer view_tail + {} ; -get_definer: - opt_current_definer - { - THD *thd= YYTHD; - - if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) - YYABORT; - - if (get_default_definer(thd, $$)) - YYABORT; - } - | DEFINER_SYM EQ ident_or_text '@' ident_or_text - { - if (!($$=(LEX_USER*) YYTHD->alloc(sizeof(st_lex_user)))) - YYABORT; - - $$->user= $3; - $$->host= $5; - } - ; - -opt_current_definer: - /* empty */ - | DEFINER_SYM EQ CURRENT_USER optional_braces +view_or_trigger_or_sp_tail: + view_tail + {} + | trigger_tail + {} + | sp_tail + {} ; /************************************************************************** - CREATE VIEW statement options. + DEFINER clause support. + +**************************************************************************/ + +definer: + /* empty */ + { + /* + We have to distinguish missing DEFINER-clause from case when + CURRENT_USER specified as definer explicitly in order to properly + handle CREATE TRIGGER statements which come to replication thread + from older master servers (i.e. to create non-suid trigger in this + case). + */ + YYTHD->lex->definer= 0; + } + | DEFINER_SYM EQ CURRENT_USER optional_braces + { + if (! (YYTHD->lex->definer= create_default_definer(YYTHD))) + YYABORT; + } + | DEFINER_SYM EQ ident_or_text '@' ident_or_text + { + if (!(YYTHD->lex->definer= create_definer(YYTHD, &$3, &$5))) + YYABORT; + } + ; + +/************************************************************************** + + CREATE VIEW statement parts. **************************************************************************/ @@ -10855,20 +10800,6 @@ view_algorithm_opt: {} ; -view_or_trigger: - definer view_or_trigger_tail - {} - | view_replace_or_algorithm definer view_tail - {} - ; - -view_or_trigger_tail: - view_tail - {} - | trigger_tail - {} - ; - view_suid: /* empty */ { Lex->create_view_suid= TRUE; } @@ -10967,7 +10898,7 @@ trigger_tail: sp->reset_thd_mem_root(YYTHD); sp->init(lex); - lex->trigger_definition_begin= $2; + lex->stmt_definition_begin= $2; lex->ident.str= $7; lex->ident.length= $10 - $7; @@ -11016,6 +10947,90 @@ trigger_tail: } ; +/************************************************************************** + + CREATE FUNCTION | PROCEDURE statements parts. + +**************************************************************************/ + +sp_tail: + udf_func_type remember_name FUNCTION_SYM sp_name + { + LEX *lex=Lex; + lex->udf.type= $1; + lex->stmt_definition_begin= $2; + lex->spname= $4; + } + create_function_tail + {} + | PROCEDURE remember_name sp_name + { + LEX *lex= Lex; + sp_head *sp; + + if (lex->sphead) + { + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"); + YYABORT; + } + + lex->stmt_definition_begin= $2; + + /* Order is important here: new - reset - init */ + sp= new sp_head(); + sp->reset_thd_mem_root(YYTHD); + sp->init(lex); + + sp->m_type= TYPE_ENUM_PROCEDURE; + lex->sphead= sp; + /* + * We have to turn of CLIENT_MULTI_QUERIES while parsing a + * stored procedure, otherwise yylex will chop it into pieces + * at each ';'. + */ + $$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); + } + '(' + { + LEX *lex= Lex; + + lex->sphead->m_param_begin= lex->tok_start+1; + } + sp_pdparam_list + ')' + { + LEX *lex= Lex; + + lex->sphead->m_param_end= lex->tok_start; + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + } + sp_c_chistics + { + LEX *lex= Lex; + + lex->sphead->m_chistics= &lex->sp_chistics; + lex->sphead->m_body_begin= lex->tok_start; + } + sp_proc_stmt + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + if (sp->check_backpatch(YYTHD)) + YYABORT; + sp->init_strings(YYTHD, lex, $3); + lex->sql_command= SQLCOM_CREATE_PROCEDURE; + /* + Restore flag if it was cleared above + Be careful with counting. the block where we save the value + is $4. + */ + YYTHD->client_capabilities |= $4; + sp->restore_thd_mem_root(YYTHD); + } + ; + /*************************************************************************/ xa: XA_SYM begin_or_start xid opt_join_or_resume diff --git a/storage/ndb/include/ndbapi/NdbBlob.hpp b/storage/ndb/include/ndbapi/NdbBlob.hpp index 9ae0ba6f023..13bbfa59e50 100644 --- a/storage/ndb/include/ndbapi/NdbBlob.hpp +++ b/storage/ndb/include/ndbapi/NdbBlob.hpp @@ -288,6 +288,7 @@ private: Buf(); ~Buf(); void alloc(unsigned n); + void zerorest(); void copyfrom(const Buf& src); }; Buf theKeyBuf; diff --git a/storage/ndb/src/ndbapi/NdbBlob.cpp b/storage/ndb/src/ndbapi/NdbBlob.cpp index 680f9e5c70e..5fbfd6f5111 100644 --- a/storage/ndb/src/ndbapi/NdbBlob.cpp +++ b/storage/ndb/src/ndbapi/NdbBlob.cpp @@ -307,6 +307,13 @@ NdbBlob::Buf::alloc(unsigned n) #endif } +void +NdbBlob::Buf::zerorest() +{ + assert(size <= maxsize); + memset(data + size, 0, maxsize - size); +} + void NdbBlob::Buf::copyfrom(const NdbBlob::Buf& src) { @@ -441,6 +448,7 @@ NdbBlob::packKeyValue(const NdbTableImpl* aTable, const Buf& srcBuf) assert(4 * pos == srcBuf.size); assert(4 * pack_pos <= thePackKeyBuf.maxsize); thePackKeyBuf.size = 4 * pack_pos; + thePackKeyBuf.zerorest(); DBUG_RETURN(0); } @@ -1316,6 +1324,7 @@ NdbBlob::atPrepare(NdbTransaction* aCon, NdbOperation* anOp, const NdbColumnImpl DBUG_RETURN(-1); } thePackKeyBuf.size = 4 * size; + thePackKeyBuf.zerorest(); if (unpackKeyValue(theTable, theKeyBuf) == -1) DBUG_RETURN(-1); } @@ -1328,6 +1337,7 @@ NdbBlob::atPrepare(NdbTransaction* aCon, NdbOperation* anOp, const NdbColumnImpl DBUG_RETURN(-1); } thePackKeyBuf.size = 4 * size; + thePackKeyBuf.zerorest(); if (unpackKeyValue(theAccessTable, theAccessKeyBuf) == -1) DBUG_RETURN(-1); } @@ -1634,6 +1644,7 @@ NdbBlob::postExecute(NdbTransaction::ExecType anExecType) // copy key from first blob theKeyBuf.copyfrom(tFirstBlob->theKeyBuf); thePackKeyBuf.copyfrom(tFirstBlob->thePackKeyBuf); + thePackKeyBuf.zerorest(); } } if (isReadOp()) { @@ -1795,6 +1806,7 @@ NdbBlob::atNextResult() DBUG_RETURN(-1); } thePackKeyBuf.size = 4 * size; + thePackKeyBuf.zerorest(); if (unpackKeyValue(theTable, theKeyBuf) == -1) DBUG_RETURN(-1); }