From 28eac83ec5e7aec62fdfacd6285ded186cb1df65 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 28 Jul 2006 10:21:47 +0200 Subject: [PATCH 01/68] Add query_len variable to st_query, avoids a lot of strlen and allows better allocation of dynamic strings Tune some inital allocations of dynamic strings --- client/mysqltest.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 3b55ebb2f8b..2b0fcb0faa4 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -362,7 +362,7 @@ Q_COMMENT_WITH_COMMAND struct st_query { char *query, *query_buf,*first_argument,*last_argument,*end; - int first_word_len; + int first_word_len, query_len; my_bool abort_on_error, require_file; match_err expected_errno[MAX_EXPECTED_ERRORS]; uint expected_errors; @@ -758,12 +758,13 @@ static int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) if (my_read(fd, (byte*)tmp, stat_info.st_size, MYF(MY_WME|MY_NABP))) die(NullS); tmp[stat_info.st_size] = 0; - init_dynamic_string(&res_ds, "", 0, 65536); + init_dynamic_string(&res_ds, "", stat_info.st_size+256, 256); if (eval_result) { do_eval(&res_ds, tmp, FALSE); - res_ptr = res_ds.str; - if ((res_len = res_ds.length) != ds->length) + res_ptr= res_ds.str; + res_len= res_ds.length; + if (res_len != ds->length) { res= RESULT_LENGTH_MISMATCH; goto err; @@ -783,9 +784,9 @@ err: str_to_file(fn_format(eval_file, fname, "", ".eval",2), res_ptr, res_len); + dynstr_free(&res_ds); my_free((gptr) tmp, MYF(0)); my_close(fd, MYF(MY_WME)); - dynstr_free(&res_ds); DBUG_RETURN(res); } @@ -1183,7 +1184,7 @@ static void do_exec(struct st_query *query) die("Missing argument in exec"); query->last_argument= query->end; - init_dynamic_string(&ds_cmd, 0, strlen(cmd)+256, 256); + init_dynamic_string(&ds_cmd, 0, query->query_len+256, 256); /* Eval the command, thus replacing all environment variables */ do_eval(&ds_cmd, cmd, TRUE); cmd= ds_cmd.str; @@ -1306,7 +1307,7 @@ int var_query_set(VAR* var, const char *query, const char** query_end) MYSQL_FIELD *fields= mysql_fetch_fields(res); #endif - init_dynamic_string(&result, "", 16384, 65536); + init_dynamic_string(&result, "", 2048, 2048); lengths= mysql_fetch_lengths(res); for (i=0; i < mysql_num_fields(res); i++) { @@ -1498,7 +1499,7 @@ void do_system(struct st_query *command) if (strlen(command->first_argument) == 0) die("Missing arguments to system, nothing to do!"); - init_dynamic_string(&ds_cmd, 0, strlen(command->first_argument) + 64, 256); + init_dynamic_string(&ds_cmd, 0, command->query_len + 64, 256); /* Eval the system command, thus replacing all environment variables */ do_eval(&ds_cmd, command->first_argument, TRUE); @@ -1552,7 +1553,7 @@ int do_echo(struct st_query *command) ds= &ds_res; - init_dynamic_string(&ds_echo, "", 256, 256); + init_dynamic_string(&ds_echo, "", command->query_len, 256); do_eval(&ds_echo, command->first_argument, FALSE); dynstr_append_mem(ds, ds_echo.str, ds_echo.length); dynstr_append_mem(ds, "\n", 1); @@ -3205,6 +3206,7 @@ int read_query(struct st_query** q_ptr) q->record_file[0]= 0; q->require_file= 0; q->first_word_len= 0; + q->query_len= 0; q->type= Q_UNKNOWN; q->query_buf= q->query= 0; @@ -3255,6 +3257,7 @@ end: p++; q->first_argument= p; q->end= strend(q->query); + q->query_len= (q->end - q->query); parser.read_lines++; DBUG_RETURN(0); } @@ -4723,7 +4726,7 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags) */ if (command->type == Q_EVAL) { - init_dynamic_string(&eval_query, "", 16384, 65536); + init_dynamic_string(&eval_query, "", command->query_len+256, 1024); do_eval(&eval_query, command->query, FALSE); query = eval_query.str; query_len = eval_query.length; @@ -4742,7 +4745,7 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags) */ if (command->record_file[0]) { - init_dynamic_string(&ds_result, "", 16384, 65536); + init_dynamic_string(&ds_result, "", 1024, 1024); ds= &ds_result; } else @@ -5209,7 +5212,7 @@ int main(int argc, char **argv) memset(&master_pos, 0, sizeof(master_pos)); - init_dynamic_string(&ds_res, "", 0, 65536); + init_dynamic_string(&ds_res, "", 65536, 65536); init_dynamic_string(&ds_progress, "", 0, 2048); parse_args(argc, argv); @@ -5404,7 +5407,8 @@ int main(int argc, char **argv) } /* fix up query pointer if this is first iteration for this line */ if (q->query == q->query_buf) - q->query += q->first_word_len; + q->query+= q->first_word_len; + /* run_query() can execute a query partially, depending on the flags. QUERY_SEND flag without QUERY_REAP tells it to just send the From 7e5ed1893cc63d72ec2db85c9b17ce4a0399fa3b Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 29 Jul 2006 12:33:59 +0200 Subject: [PATCH 02/68] Add missing semicolon in csv test mysql-test/t/csv.test: Add missing semicolon --- mysql-test/t/csv.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/t/csv.test b/mysql-test/t/csv.test index 9ba99167ab9..f17f90c71a5 100644 --- a/mysql-test/t/csv.test +++ b/mysql-test/t/csv.test @@ -1295,7 +1295,7 @@ SELECT fld3 FROM t2; # DROP TABLE t1; -ALTER TABLE t2 RENAME t1 +ALTER TABLE t2 RENAME t1; # # Drop and recreate From 77c97139cde560d6af60a762c15e47c1490b8084 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 29 Jul 2006 12:35:30 +0200 Subject: [PATCH 03/68] Add missing semicolon in query_cache test mysql-test/r/query_cache.result: Add missing semicolon mysql-test/t/query_cache.test: Add missing semicolon --- mysql-test/r/query_cache.result | 2 +- mysql-test/t/query_cache.test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result index 27a84f90126..53f62dc91b4 100644 --- a/mysql-test/r/query_cache.result +++ b/mysql-test/r/query_cache.result @@ -934,7 +934,7 @@ abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzab zyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcba flush query cache; drop table t1, t2; -set GLOBAL query_cache_size=1355776 +set GLOBAL query_cache_size=1355776; #; flush status; CREATE TABLE t1 ( diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test index 2c94fe63c04..67c4c4cb20b 100644 --- a/mysql-test/t/query_cache.test +++ b/mysql-test/t/query_cache.test @@ -699,7 +699,7 @@ select a from t1; flush query cache; drop table t1, t2; -set GLOBAL query_cache_size=1355776 +set GLOBAL query_cache_size=1355776; # From 01b65e9032ff3502470ebf8fb7082ab6317adc79 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 29 Jul 2006 13:01:07 +0200 Subject: [PATCH 04/68] Remove spurios junk in result file caused by previous missing semicolon --- mysql-test/r/csv.result | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mysql-test/r/csv.result b/mysql-test/r/csv.result index 04f0636d400..00086684774 100644 --- a/mysql-test/r/csv.result +++ b/mysql-test/r/csv.result @@ -4913,8 +4913,7 @@ bonfire Colombo nondecreasing DROP TABLE t1; -ALTER TABLE t2 RENAME t1 -#; +ALTER TABLE t2 RENAME t1; DROP TABLE t1; CREATE TABLE t1 ( Period smallint(4) unsigned zerofill DEFAULT '0000' NOT NULL, From 12a6874af18ca3a44ec77fc8161c206843425598 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 29 Jul 2006 13:02:28 +0200 Subject: [PATCH 05/68] Remove spurious printout in result file caused by previous missing semicolon --- mysql-test/r/query_cache.result | 1 - 1 file changed, 1 deletion(-) diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result index 53f62dc91b4..f9c332138d2 100644 --- a/mysql-test/r/query_cache.result +++ b/mysql-test/r/query_cache.result @@ -935,7 +935,6 @@ zyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazy flush query cache; drop table t1, t2; set GLOBAL query_cache_size=1355776; -#; flush status; CREATE TABLE t1 ( `date` datetime NOT NULL default '0000-00-00 00:00:00', From f21668a90f4478bd1f485ccaba74a386206f361b Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 29 Jul 2006 13:03:17 +0200 Subject: [PATCH 06/68] Add missing semicolon to wait_timeout test mysql-test/t/wait_timeout.test: Add missing semicolon --- mysql-test/t/wait_timeout.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/t/wait_timeout.test b/mysql-test/t/wait_timeout.test index dbd792e48d8..bdff72cdc76 100644 --- a/mysql-test/t/wait_timeout.test +++ b/mysql-test/t/wait_timeout.test @@ -55,7 +55,7 @@ select 2; select 3; # Disconnect so that we will not be confused by a future abort from this # connection. -disconnect default +disconnect default; # # Do the same test as above on a TCP connection From a7ec90633cf0a18fc0ca71a3b8c15cd64c75a3d0 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 29 Jul 2006 18:11:19 +0200 Subject: [PATCH 07/68] Add missing delimiter in subselect test mysql-test/r/subselect.result: Add missing semicolon delimiter and junk caused by that mysql-test/t/subselect.test: Add missing semicolon delimiter --- mysql-test/r/subselect.result | 3 +-- mysql-test/t/subselect.test | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 75a9b422691..ec180a28188 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -2717,8 +2717,7 @@ select (1,2,3) = (select * from t1); ERROR 21000: Operand should contain 3 column(s) select (select * from t1) = (1,2,3); ERROR 21000: Operand should contain 2 column(s) -drop table t1 -#; +drop table t1; CREATE TABLE `t1` ( `itemid` bigint(20) unsigned NOT NULL auto_increment, `sessionid` bigint(20) unsigned default NULL, diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 8916a5cec6d..ec314019492 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -1728,7 +1728,7 @@ select (select a from t1) = (1,2); select (1,2,3) = (select * from t1); -- error 1241 select (select * from t1) = (1,2,3); -drop table t1 +drop table t1; # # Item_int_with_ref check (BUG#10020) From fddfab90d4859a74338285224514741bd3dbed4d Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 29 Jul 2006 19:36:11 +0200 Subject: [PATCH 08/68] Change faulty delimiter ; to | --- mysql-test/t/sp.test | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 99f3bbbbd14..4d0a0d24003 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -2944,11 +2944,11 @@ begin end| --disable_parsing --replace_regex /table_id: [0-9]+/table_id: #/ -show binlog events; -show storage engines; -show master status; -show slave hosts; -show slave status; +show binlog events| +show storage engines| +show master status| +show slave hosts| +show slave status| --enable_parsing call bug4902()| From ff99e042e478218cd2d76462905c5f5099630abd Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 29 Jul 2006 19:38:46 +0200 Subject: [PATCH 09/68] Change faulty delimiter ; to | and remove junk in result file caused by that mysql-test/t/sp_notembedded.test: Change faulty delimiter ; to | --- mysql-test/r/sp_notembedded.result | 3 +-- mysql-test/t/sp_notembedded.test | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/sp_notembedded.result b/mysql-test/r/sp_notembedded.result index a15f5013ef6..b620c77383c 100644 --- a/mysql-test/r/sp_notembedded.result +++ b/mysql-test/r/sp_notembedded.result @@ -76,8 +76,7 @@ flush status| flush query cache| delete from t1| drop procedure bug3583| -drop table t1; -#| +drop table t1| drop procedure if exists bug6807| create procedure bug6807() begin diff --git a/mysql-test/t/sp_notembedded.test b/mysql-test/t/sp_notembedded.test index 28abf448089..6335ad55606 100644 --- a/mysql-test/t/sp_notembedded.test +++ b/mysql-test/t/sp_notembedded.test @@ -19,11 +19,11 @@ begin show grants for 'root'@'localhost'; end| --disable_parsing -show binlog events; -show storage engines; -show master status; -show slave hosts; -show slave status; +show binlog events| +show storage engines| +show master status| +show slave hosts| +show slave status| --enable_parsing call bug4902()| @@ -110,7 +110,7 @@ flush status| flush query cache| delete from t1| drop procedure bug3583| -drop table t1; +drop table t1| # # BUG#6807: Stored procedure crash if CREATE PROCEDURE ... KILL QUERY From 5d39c1c64f7309754f6e0b40c80f9269818317c3 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 30 Jul 2006 17:57:15 +0200 Subject: [PATCH 10/68] Improve and fix bugs in 'read_line' function of mysqltest client/mysqltest.c: Fix bugs in 'read_line' function, add better comments and some DBUG printouts Add do_delimiter function so the "delimiter" command is parsed better, it should not be allowed to have emtpy delimiter Add checks that disable/enable_pasring is not used when it's already disabled or enabled. Don't allow mysqltest to end with parsing disabled Add function 'convert_to_format_v1' as the bugs in read_line caused the queries to be read a little funny and we want to preserve the result file format(at this time) mysql-test/r/mysqltest.result: Update result file --- client/mysqltest.c | 205 ++++++++++++++++++++++++---------- mysql-test/r/mysqltest.result | 4 +- 2 files changed, 147 insertions(+), 62 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 2b0fcb0faa4..2c00c4b339a 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -1530,7 +1530,7 @@ void do_system(struct st_query *command) SYNOPSIS do_echo() - q called command + command called command DESCRIPTION echo text @@ -1549,14 +1549,12 @@ void do_system(struct st_query *command) int do_echo(struct st_query *command) { - DYNAMIC_STRING *ds, ds_echo; - - ds= &ds_res; + DYNAMIC_STRING ds_echo; init_dynamic_string(&ds_echo, "", command->query_len, 256); do_eval(&ds_echo, command->first_argument, FALSE); - dynstr_append_mem(ds, ds_echo.str, ds_echo.length); - dynstr_append_mem(ds, "\n", 1); + dynstr_append_mem(&ds_res, ds_echo.str, ds_echo.length); + dynstr_append_mem(&ds_res, "\n", 1); dynstr_free(&ds_echo); command->last_argument= command->end; return(0); @@ -2901,9 +2899,7 @@ void do_block(enum block_cmd cmd, struct st_query* q) while (*p && my_isspace(charset_info, *p)) p++; - if (*p == '{') - die("Missing newline between %s and '{'", cmd_name); - if (*p) + if (*p && *p != '{') die("Missing '{' after %s. Found \"%s\"", cmd_name, p); var_init(&v,0,0,0,0); @@ -2971,6 +2967,27 @@ my_bool end_of_query(int c) } +void do_delimiter(struct st_query* command) +{ + char* p= command->first_argument; + DBUG_ENTER("do_delimiter"); + DBUG_PRINT("enter", ("first_argument: %s", command->first_argument)); + + while (*p && my_isspace(charset_info, *p)) + p++; + + if (!(*p)) + die("Can't set empty delimiter"); + + strmake(delimiter, p, sizeof(delimiter) - 1); + delimiter_length= strlen(delimiter); + + DBUG_PRINT("exit", ("delimiter: %s", delimiter)); + command->last_argument= p + delimiter_length; + DBUG_VOID_RETURN; +} + + /* Read one "line" from the file @@ -2997,19 +3014,19 @@ my_bool end_of_query(int c) int read_line(char *buf, int size) { - int c; - char quote; + char c, last_quote; char *p= buf, *buf_end= buf + size - 1; - int no_save= 0; - enum {R_NORMAL, R_Q, R_Q_IN_Q, R_SLASH_IN_Q, - R_COMMENT, R_LINE_START} state= R_LINE_START; + int skip_char= 0; + enum {R_NORMAL, R_Q, R_SLASH_IN_Q, + R_COMMENT, R_LINE_START} state= R_LINE_START; DBUG_ENTER("read_line"); - LINT_INIT(quote); + LINT_INIT(last_quote); start_lineno= cur_file->lineno; + DBUG_PRINT("info", ("start_lineno: %d", start_lineno)); for (; p < buf_end ;) { - no_save= 0; + skip_char= 0; c= my_getc(cur_file->file); if (feof(cur_file->file)) { @@ -3029,8 +3046,9 @@ int read_line(char *buf, int size) if (cur_block != block_stack) die("Missing end of block"); + *p= 0; DBUG_PRINT("info", ("end of file")); - DBUG_RETURN(1); + DBUG_RETURN(1); } cur_file--; start_lineno= cur_file->lineno; @@ -3044,61 +3062,74 @@ int read_line(char *buf, int size) /* Convert cr/lf to lf */ if (p != buf && *(p-1) == '\r') - *(p-1)= 0; + p--; } switch(state) { case R_NORMAL: - /* Only accept '{' in the beginning of a line */ if (end_of_query(c)) { *p= 0; + DBUG_PRINT("exit", ("Found delimiter '%s'", delimiter)); + DBUG_RETURN(0); + } + else if ((c == '{' && + (!strncasecmp(buf, "while", min(5, p - buf)) || + !strncasecmp(buf, "if", min(2, p - buf))))) + { + /* Only if and while commands can be terminated by { */ + *p++= c; + *p= 0; + DBUG_PRINT("exit", ("Found '{' indicating begining of block")); DBUG_RETURN(0); } else if (c == '\'' || c == '"' || c == '`') { - quote= c; + last_quote= c; state= R_Q; } - else if (c == '\n') - { - state = R_LINE_START; - } break; + case R_COMMENT: if (c == '\n') { + /* Comments are terminated by newline */ *p= 0; + DBUG_PRINT("exit", ("Found newline in comment")); DBUG_RETURN(0); } break; + case R_LINE_START: - /* Only accept start of comment if this is the first line in query */ - if ((cur_file->lineno == start_lineno) && - (c == '#' || c == '-' || parsing_disabled)) + if (c == '#' || c == '-') { + /* A # or - in the first position of the line - this is a comment */ state = R_COMMENT; } else if (my_isspace(charset_info, c)) { + /* Skip all space at begining of line */ if (c == '\n') start_lineno= cur_file->lineno; /* Query hasn't started yet */ - no_save= 1; + skip_char= 1; + } + else if (end_of_query(c)) + { + *p= 0; + DBUG_PRINT("exit", ("Found delimiter '%s'", delimiter)); + DBUG_RETURN(0); } else if (c == '}') { - *buf++= '}'; - *buf= 0; - DBUG_RETURN(0); - } - else if (end_of_query(c) || c == '{') - { + /* A "}" need to be by itself in the begining of a line to terminate */ + *p++= c; *p= 0; + DBUG_PRINT("exit", ("Found '}' in begining of a line")); DBUG_RETURN(0); } else if (c == '\'' || c == '"' || c == '`') { - quote= c; + last_quote= c; state= R_Q; } else @@ -3106,29 +3137,19 @@ int read_line(char *buf, int size) break; case R_Q: - if (c == quote) - state= R_Q_IN_Q; + if (c == last_quote) + state= R_NORMAL; else if (c == '\\') state= R_SLASH_IN_Q; break; - case R_Q_IN_Q: - if (end_of_query(c)) - { - *p= 0; - DBUG_RETURN(0); - } - if (c != quote) - state= R_NORMAL; - else - state= R_Q; - break; + case R_SLASH_IN_Q: state= R_Q; break; } - if (!no_save) + if (!skip_char) { /* Could be a multibyte character */ /* This code is based on the code in "sql_load.cc" */ @@ -3146,7 +3167,7 @@ int read_line(char *buf, int size) for (i= 1; i < charlen; i++) { if (feof(cur_file->file)) - goto found_eof; /* FIXME: could we just break here?! */ + goto found_eof; c= my_getc(cur_file->file); *p++ = c; } @@ -3163,10 +3184,64 @@ int read_line(char *buf, int size) *p++= c; } } - *p= 0; /* Always end with \0 */ - DBUG_RETURN(feof(cur_file->file)); + die("The input buffer is too small for this query.x\n" \ + "check your query or increase MAX_QUERY and recompile"); + DBUG_RETURN(0); } + +/* + Convert the read query to format version 1 + + That is: After newline, all spaces need to be skipped + unless the previous char was a quote + + This is due to an old bug that has now been fixed, but the + version 1 output format is preserved by using this function + +*/ + +static void convert_to_format_v1(char* query) +{ + int last_c_was_quote= 0; + char *p= query, *write= query; + char *end= strend(query); + char last_c; + + while (p <= end) + { + if (*p == '\n' && !last_c_was_quote) + { + *write++ = *p++; /* Save the newline */ + + /* Skip any spaces on next line */ + while (*p && my_isspace(charset_info, *p)) + p++; + + last_c_was_quote= 0; + } + else if (*p == '\'' || *p == '"' || *p == '`') + { + last_c= *p; + *write++ = *p++; + + /* Copy anything until the next quote of same type */ + while (*p && *p != last_c) + *write++ = *p++; + + *write++ = *p++; + + last_c_was_quote= 1; + } + else + { + *write++ = *p++; + last_c_was_quote= 0; + } + } +} + + /* Create a query from a set of lines @@ -3216,7 +3291,9 @@ int read_query(struct st_query** q_ptr) check_eol_junk(read_query_buf); DBUG_RETURN(1); } - + + convert_to_format_v1(read_query_buf); + DBUG_PRINT("info", ("query: %s", read_query_buf)); if (*p == '#') { @@ -3871,9 +3948,11 @@ static void fix_win_paths(const char* val, int len) } #endif + + /* Append the string to ds, with optional replace */ static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, - const char *val, int len) + const char *val, int len) { #ifdef __WIN__ fix_win_paths(val, len); @@ -5032,7 +5111,7 @@ void get_query_type(struct st_query* q) } else if (q->type == Q_COMMENT_WITH_COMMAND && q->first_word_len && - q->query[q->first_word_len-1] == ';') + strcmp(q->query + q->first_word_len - 1, delimiter) == 0) { /* Detect comment with command using extra delimiter @@ -5142,7 +5221,7 @@ static void init_var_hash(MYSQL *mysql) test run completes */ -static void mark_progress(struct st_query* q, int line) +static void mark_progress(struct st_query* q __attribute__((unused)), int line) { char buf[32], *end; ulonglong timer= timer_now(); @@ -5332,9 +5411,7 @@ int main(int argc, char **argv) case Q_ECHO: do_echo(q); query_executed= 1; break; case Q_SYSTEM: do_system(q); break; case Q_DELIMITER: - strmake(delimiter, q->first_argument, sizeof(delimiter) - 1); - delimiter_length= strlen(delimiter); - q->last_argument= q->first_argument+delimiter_length; + do_delimiter(q); break; case Q_DISPLAY_VERTICAL_RESULTS: display_result_vertically= TRUE; @@ -5494,7 +5571,10 @@ int main(int argc, char **argv) break; } case Q_DISABLE_PARSING: - parsing_disabled++; + if (parsing_disabled == 0) + parsing_disabled++; + else + die("Parsing is already disabled"); break; case Q_ENABLE_PARSING: /* @@ -5503,6 +5583,8 @@ int main(int argc, char **argv) */ if (parsing_disabled > 0) parsing_disabled--; + else + die("Parsing is already enabled"); break; case Q_EXIT: @@ -5545,6 +5627,9 @@ int main(int argc, char **argv) start_lineno= 0; + if (parsing_disabled) + die("Test ended with parsing disabled"); + /* The whole test has been executed _sucessfully_. Time to compare result or save it to record file. diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result index ef4dd83564b..4c2695e5caf 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -335,10 +335,10 @@ mysqltest: At line 1: missing ')' in while mysqltest: At line 1: Missing '{' after while. Found "dec $i" mysqltest: At line 1: Stray '}' - end of block before beginning mysqltest: At line 1: Stray 'end' command - end of block before beginning -mysqltest: At line 1: query '' failed: 1065: Query was empty +mysqltest: At line 1: query '{' failed: 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '{' at line 1 mysqltest: At line 1: Missing '{' after while. Found "echo hej" mysqltest: At line 3: Missing end of block -mysqltest: At line 1: Missing newline between while and '{' +mysqltest: At line 3: Missing end of block mysqltest: At line 1: missing '(' in if mysqltest: At line 1: Stray 'end' command - end of block before beginning select "b" bs col1, "c" bs col2; From 61b52edd780d02679a4da7935c245074e007999d Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 30 Jul 2006 18:00:08 +0200 Subject: [PATCH 11/68] Remove todo about better manual client/mysqltest.c: Remove the TODO saying we need better manual since that has been fixed now. --- client/mysqltest.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 2c00c4b339a..3afbf19ed7c 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -15,8 +15,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* mysqltest test tool - * See the manual for more information - * TODO: document better how mysqltest works + * See the "MySQL Test framework manual" for more information * * Written by: * Sasha Pachev From cafd2566255e11910b9bb49c2978450b2ca0ef75 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 30 Jul 2006 18:01:42 +0200 Subject: [PATCH 12/68] Remove outdated TODO list --- client/mysqltest.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 3afbf19ed7c..4100267fa08 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -24,23 +24,6 @@ * Jani **/ -/********************************************************************** - TODO: - -- Do comparison line by line, instead of doing a full comparison of - the text file. This will save space as we don't need to keep many - results in memory. It will also make it possible to do simple - 'comparison' fixes like accepting the result even if a float differed - in the last decimals. - -- Don't buffer lines from the test that you don't expect to need - again. - -- Change 'read_line' to be faster by using the readline.cc code; - We can do better than calling feof() for each character! - -**********************************************************************/ - #define MTEST_VERSION "2.6" #include From 1c0baed86c08917d8cd6171b9fdb73ec3b6a1d62 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 30 Jul 2006 18:21:31 +0200 Subject: [PATCH 13/68] Cleanup error messages in mysqltest client/mysqltest.c: Cleanup error messages, no need to supply line number in calls to 'die' that is done automatically if needed. Set start_lineno to 0 as default. --- client/mysqltest.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 4100267fa08..c3db4cf9659 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -162,7 +162,7 @@ typedef struct static test_file file_stack[MAX_INCLUDE_DEPTH]; static test_file* cur_file; static test_file* file_stack_end; -uint start_lineno; /* Start line of query */ +static uint start_lineno= 0; /* Start line of query */ /* Stores regex substitutions */ @@ -997,8 +997,8 @@ int do_wait_for_slave_to_stop(struct st_query *q __attribute__((unused))) if (mysql_query(mysql,"show status like 'Slave_running'") || !(res=mysql_store_result(mysql))) - die("Query failed while probing slave for stop: %s", - mysql_error(mysql)); + die("Query failed while probing slave for stop: %d %s", + mysql_errno(mysql), mysql_error(mysql)); if (!(row=mysql_fetch_row(res)) || !row[1]) { mysql_free_result(res); @@ -1271,8 +1271,8 @@ int var_query_set(VAR* var, const char *query, const char** query_end) !(res = mysql_store_result(mysql))) { *end = 0; - die("Error running query '%s': %d: %s", query, - mysql_errno(mysql) ,mysql_error(mysql)); + die("Error running query '%s': %d %s", query, + mysql_errno(mysql), mysql_error(mysql)); } if ((row = mysql_fetch_row(res)) && row[0]) @@ -1563,7 +1563,7 @@ int do_sync_with_master2(long offset) wait_for_position: if (mysql_query(mysql, query_buf)) - die("failed in %s: %d: %s", query_buf, mysql_errno(mysql), + die("failed in '%s': %d: %s", query_buf, mysql_errno(mysql), mysql_error(mysql)); if (!(res= mysql_store_result(mysql))) @@ -1629,13 +1629,12 @@ int do_save_master_pos() { ulong have_ndbcluster; if (mysql_query(mysql, query= "show variables like 'have_ndbcluster'")) - die("At line %u: failed in %s: %d: %s", start_lineno, query, + die("'%s' failed: %d %s", query, mysql_errno(mysql), mysql_error(mysql)); if (!(res= mysql_store_result(mysql))) - die("line %u: mysql_store_result() retuned NULL for '%s'", start_lineno, - query); + die("mysql_store_result() returned NULL for '%s'", query); if (!(row= mysql_fetch_row(res))) - die("line %u: empty result in %s", start_lineno, query); + die("Query '%s' returned empty result", query); have_ndbcluster= strcmp("YES", row[1]) == 0; mysql_free_result(res); @@ -1655,11 +1654,10 @@ int do_save_master_pos() if (count) sleep(1); if (mysql_query(mysql, query= "show engine ndb status")) - die("At line %u: failed in '%s': %d: %s", start_lineno, query, + die("failed in '%s': %d %s", query, mysql_errno(mysql), mysql_error(mysql)); if (!(res= mysql_store_result(mysql))) - die("line %u: mysql_store_result() retuned NULL for '%s'", - start_lineno, query); + die("mysql_store_result() returned NULL for '%s'", query); while ((row= mysql_fetch_row(res))) { if (strcmp(row[1], binlog) == 0) @@ -1677,8 +1675,8 @@ int do_save_master_pos() epoch= strtoull(status, (char**) 0, 10); } else - die("line %u: result does not contain '%s' in '%s'", - start_lineno, latest_trans_epoch, query); + die("result does not contain '%s' in '%s'", + latest_trans_epoch, query); } /* latest_applied_binlog_epoch */ while (*status && strncmp(status, latest_handled_binlog_epoch, @@ -1690,14 +1688,14 @@ int do_save_master_pos() tmp_epoch= strtoull(status, (char**) 0, 10); } else - die("line %u: result does not contain '%s' in '%s'", - start_lineno, latest_handled_binlog_epoch, query); + die("result does not contain '%s' in '%s'", + latest_handled_binlog_epoch, query); break; } } if (!row) - die("line %u: result does not contain '%s' in '%s'", - start_lineno, binlog, query); + die("result does not contain '%s' in '%s'", + binlog, query); count++; if (tmp_epoch >= epoch) do_continue= 0; @@ -1711,7 +1709,7 @@ int do_save_master_pos() } #endif if (mysql_query(mysql, query= "show master status")) - die("failed in show master status: %d: %s", + die("failed in 'show master status': %d %s", mysql_errno(mysql), mysql_error(mysql)); if (!(res = mysql_store_result(mysql))) From 1385320be8e7f9400da8ca9cf4473cc7cd317e37 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 30 Jul 2006 19:16:51 +0200 Subject: [PATCH 14/68] Add new commands to mysqltest - to make it easier to write portable test scripts client/Makefile.am: Link mysqltest with mysys library client/mysqltest.c: Add new commands to mysqltest: - remove_file - copy_file - file_exists - write_file - perl mysql-test/r/mysqltest.result: Update result file mysql-test/t/mysqltest.test: Add tests for new commands --- client/Makefile.am | 3 +- client/mysqltest.c | 467 ++++++++++++++++++++++++++++++---- mysql-test/r/mysqltest.result | 11 + mysql-test/t/mysqltest.test | 75 ++++++ 4 files changed, 500 insertions(+), 56 deletions(-) diff --git a/client/Makefile.am b/client/Makefile.am index ff97243815a..94174614d30 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -46,7 +46,8 @@ mysqladmin_SOURCES = mysqladmin.cc mysql_LDADD = @readline_link@ @TERMCAP_LIB@ $(LDADD) $(CXXLDFLAGS) mysqltest_SOURCES= mysqltest.c $(top_srcdir)/mysys/my_getsystime.c \ $(yassl_dummy_link_fix) -mysqltest_LDADD = $(top_builddir)/regex/libregex.a $(LDADD) +mysqltest_LDADD = $(top_builddir)/regex/libregex.a $(LDADD) \ + $(top_builddir)/mysys/libmysys.a mysqlbinlog_SOURCES = mysqlbinlog.cc $(top_srcdir)/mysys/mf_tempdir.c \ $(top_srcdir)/mysys/my_new.cc \ $(top_srcdir)/mysys/my_bit.c \ diff --git a/client/mysqltest.c b/client/mysqltest.c index c3db4cf9659..56c06a10f5b 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -24,7 +24,7 @@ * Jani **/ -#define MTEST_VERSION "2.6" +#define MTEST_VERSION "2.7" #include #include @@ -333,7 +333,8 @@ Q_EXIT, Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT, Q_IF, Q_DISABLE_PARSING, Q_ENABLE_PARSING, -Q_REPLACE_REGEX, +Q_REPLACE_REGEX, Q_REMOVE_FILE, Q_FILE_EXIST, +Q_WRITE_FILE, Q_COPY_FILE, Q_PERL, Q_UNKNOWN, /* Unknown command. */ Q_COMMENT, /* Comments, ignored. */ @@ -423,6 +424,11 @@ const char *command_names[]= "disable_parsing", "enable_parsing", "replace_regex", + "remove_file", + "file_exists", + "write_file", + "copy_file", + "perl", 0 }; @@ -468,7 +474,7 @@ static void replace_strings_append(struct st_replace *rep, DYNAMIC_STRING* ds, const char *from, int len); void free_pointer_array(POINTER_ARRAY *pa); static void do_eval(DYNAMIC_STRING *query_eval, const char *query, - my_bool pass_through_escape_chars); + const char* query_end, my_bool pass_through_escape_chars); static void str_to_file(const char *fname, char *str, int size); #ifdef __WIN__ @@ -501,8 +507,8 @@ static void handle_error(const char *query, struct st_query *q, const char *err_sqlstate, DYNAMIC_STRING *ds); static void handle_no_error(struct st_query *q); -static void do_eval(DYNAMIC_STRING* query_eval, const char *query, - my_bool pass_through_escape_chars) +static void do_eval(DYNAMIC_STRING *query_eval, const char *query, + const char *query_end, my_bool pass_through_escape_chars) { const char *p; register char c, next_c; @@ -510,7 +516,7 @@ static void do_eval(DYNAMIC_STRING* query_eval, const char *query, VAR* v; DBUG_ENTER("do_eval"); - for (p= query; (c = *p); ++p) + for (p= query; (c = *p) && p < query_end; ++p) { switch(c) { case '$': @@ -533,9 +539,9 @@ static void do_eval(DYNAMIC_STRING* query_eval, const char *query, escaped = 0; dynstr_append_mem(query_eval, p, 1); } - else if (next_c == '\\' || next_c == '$') + else if (next_c == '\\' || next_c == '$' || next_c == '"') { - /* Set escaped only if next char is \ or $ */ + /* Set escaped only if next char is \, " or $ */ escaped = 1; if (pass_through_escape_chars) @@ -556,6 +562,108 @@ static void do_eval(DYNAMIC_STRING* query_eval, const char *query, } +enum arg_type +{ + ARG_STRING, + ARG_REST +}; + +struct command_arg { + const char* argname; /* Name of argument */ + enum arg_type type; /* Type of argument */ + my_bool required; /* Argument required */ + DYNAMIC_STRING *ds; /* Storage for string argument */ + const char *description; /* Description of the argument */ +}; + +static void check_command_args(struct st_query *command, const char *arguments, + const struct command_arg *args, int num_args) +{ + int i; + const char *ptr= arguments; + const char *start; + + DBUG_ENTER("check_command_args"); + DBUG_PRINT("enter", ("num_args: %d", num_args)); + for (i= 0; i < num_args; i++) + { + const struct command_arg *arg= &args[i]; + + switch (arg->type) + { + /* A string surrounded by spaces */ + case ARG_STRING: + start= ptr; + /* Find end of arg */ + while (*ptr && !my_isspace(charset_info, *ptr)) + ptr++; + init_dynamic_string(arg->ds, 0, 256, 256); + do_eval(arg->ds, start, ptr, FALSE); + command->last_argument= (char*)ptr; + if (*ptr) + ptr++; + break; + + /* Rest of line */ + case ARG_REST: + start= ptr; + init_dynamic_string(arg->ds, 0, command->query_len, 256); + do_eval(arg->ds, start, command->end, FALSE); + command->last_argument= command->end; + break; + + default: + DBUG_ASSERT("Unknown argument type"); + break; + } + + /* Check required arg */ + if (arg->ds->length == 0 && arg->required) + die("Missing required argument '%s' to command '%.*s'", arg->argname, + command->first_word_len, command->query); + + } + DBUG_VOID_RETURN; +} + + +static void handle_command_error(struct st_query *command, uint error) +{ + DBUG_ENTER("handle_command_error"); + DBUG_PRINT("enter", ("error: %d", error)); + if (error != 0) + { + uint i; + + if (command->abort_on_error) + die("command \"%.*s\" failed", command->first_word_len, command->query); + for (i= 0; i < command->expected_errors; i++) + { + DBUG_PRINT("info", ("expected error: %d", + command->expected_errno[i].code.errnum)); + if ((command->expected_errno[i].type == ERR_ERRNO) && + (command->expected_errno[i].code.errnum == error)) + { + DBUG_PRINT("info", ("command \"%.*s\" failed with expected error: %d", + command->first_word_len, command->query, error)); + DBUG_VOID_RETURN; + } + } + die("command \"%.*s\" failed with wrong error: %d", + command->first_word_len, command->query, error); + } + else if (command->expected_errno[0].type == ERR_ERRNO && + command->expected_errno[0].code.errnum != 0) + { + /* Error code we wanted was != 0, i.e. not an expected success */ + die("command \"%.*s\" succeeded - should have failed with errno %d...", + command->first_word_len, command->query, + command->expected_errno[0].code.errnum); + } + DBUG_VOID_RETURN; +} + + static void close_cons() { DBUG_ENTER("close_cons"); @@ -743,7 +851,7 @@ static int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) init_dynamic_string(&res_ds, "", stat_info.st_size+256, 256); if (eval_result) { - do_eval(&res_ds, tmp, FALSE); + do_eval(&res_ds, tmp, tmp + stat_info.st_size, FALSE); res_ptr= res_ds.str; res_len= res_ds.length; if (res_len != ds->length) @@ -1168,7 +1276,7 @@ static void do_exec(struct st_query *query) init_dynamic_string(&ds_cmd, 0, query->query_len+256, 256); /* Eval the command, thus replacing all environment variables */ - do_eval(&ds_cmd, cmd, TRUE); + do_eval(&ds_cmd, cmd, query->end, TRUE); cmd= ds_cmd.str; DBUG_PRINT("info", ("Executing '%s' as '%s'", @@ -1484,7 +1592,7 @@ void do_system(struct st_query *command) init_dynamic_string(&ds_cmd, 0, command->query_len + 64, 256); /* Eval the system command, thus replacing all environment variables */ - do_eval(&ds_cmd, command->first_argument, TRUE); + do_eval(&ds_cmd, command->first_argument, command->end, TRUE); DBUG_PRINT("info", ("running system command '%s' as '%s'", command->first_argument, ds_cmd.str)); @@ -1505,6 +1613,291 @@ void do_system(struct st_query *command) } +/* + SYNOPSIS + do_remove_file + command called command + + DESCRIPTION + remove_file + Remove the file +*/ + +static void do_remove_file(struct st_query *command) +{ + int error; + DYNAMIC_STRING ds_filename; + const struct command_arg rm_args[] = { + "filename", ARG_STRING, TRUE, &ds_filename, "File to delete" + }; + DBUG_ENTER("do_remove_file"); + + check_command_args(command, command->first_argument, + rm_args, sizeof(rm_args)/sizeof(struct command_arg)); + + DBUG_PRINT("info", ("removing file: %s", ds_filename.str)); + error= my_delete(ds_filename.str, MYF(0)) != 0; + handle_command_error(command, error); + dynstr_free(&ds_filename); + DBUG_VOID_RETURN; +} + + +/* + SYNOPSIS + do_copy_file + command command handle + + DESCRIPTION + copy_file + Copy to + + NOTE! Will fail if exists +*/ + +static void do_copy_file(struct st_query *command) +{ + int error; + DYNAMIC_STRING ds_from_file; + DYNAMIC_STRING ds_to_file; + const struct command_arg copy_file_args[] = { + "from_file", ARG_STRING, TRUE, &ds_from_file, "Filename to copy from", + "to_file", ARG_STRING, TRUE, &ds_to_file, "Filename to copy to" + }; + DBUG_ENTER("do_copy_file"); + + check_command_args(command, command->first_argument, + copy_file_args, sizeof(copy_file_args)/sizeof(struct command_arg)); + + DBUG_PRINT("info", ("Copy %s to %s", ds_from_file.str, ds_to_file.str)); + error= (my_copy(ds_from_file.str, ds_to_file.str, + MYF(MY_DONT_OVERWRITE_FILE)) != 0); + handle_command_error(command, error); + dynstr_free(&ds_from_file); + dynstr_free(&ds_to_file); + DBUG_VOID_RETURN; +} + + +/* + SYNOPSIS + do_file_exists + command called command + + DESCRIPTION + fiile_exist + Check if file exists +*/ + +static void do_file_exist(struct st_query *command) +{ + int error; + DYNAMIC_STRING ds_filename; + const struct command_arg file_exist_args[] = { + "filename", ARG_STRING, TRUE, &ds_filename, "File to check if it exist" + }; + DBUG_ENTER("do_file_exist"); + + check_command_args(command, command->first_argument, + file_exist_args, sizeof(file_exist_args)/sizeof(struct command_arg)); + + DBUG_PRINT("info", ("Checking for existence of file: %s", ds_filename.str)); + error= (access(ds_filename.str, F_OK) != 0); + handle_command_error(command, error); + dynstr_free(&ds_filename); + DBUG_VOID_RETURN; +} + + +/* + Read characters from line buffer or file. This is needed to allow + my_ungetc() to buffer MAX_DELIMITER characters for a file + + NOTE: + This works as long as one doesn't change files (with 'source file_name') + when there is things pushed into the buffer. This should however not + happen for any tests in the test suite. +*/ + +static int my_getc(FILE *file) +{ + if (line_buffer_pos == line_buffer) + return fgetc(file); + return *--line_buffer_pos; +} + + +static void my_ungetc(int c) +{ + *line_buffer_pos++= (char) c; +} + + +static my_bool match_delimiter(int c, const char* delim, uint length) +{ + uint i; + char tmp[MAX_DELIMITER]; + + if (c != *delim) + return 0; + + for (i= 1; i < length && + (c= my_getc(cur_file->file)) == *(delim + i); + i++) + tmp[i]= c; + + if (i == length) + return 1; /* Found delimiter */ + + /* didn't find delimiter, push back things that we read */ + my_ungetc(c); + while (i > 1) + my_ungetc(tmp[--i]); + return 0; +} + + +static void read_until_EOF(DYNAMIC_STRING* ds) +{ + int c; + DBUG_ENTER("read_until_EOF"); + + /* Read from file until delimiter EOF is found */ + while (1) + { + c= my_getc(cur_file->file); + + if (feof(cur_file->file)) + die("End of file encountered before 'EOF' delimiter was found"); + + if (match_delimiter(c, "EOF", 3)) + { + DBUG_PRINT("exit", ("Found EOF")); + break; + } + dynstr_append_mem(ds, (const char*)&c, 1); + } + DBUG_PRINT("exit", ("ds: %s", ds->str)); + DBUG_VOID_RETURN; +} + + +/* + SYNOPSIS + do_write_file + command called command + + DESCRIPTION + write_file ; + + <...> + < what to write line n> + EOF + + --write_file ; + + <...> + < what to write line n> + EOF + + Write everything between the "write_file" command and EOF to "file_name" + + NOTE! Overwrites existing file + +*/ + +static void do_write_file(struct st_query *command) +{ + DYNAMIC_STRING ds_content; + DYNAMIC_STRING ds_filename; + const struct command_arg write_file_args[] = { + "filename", ARG_STRING, TRUE, &ds_filename, "File to write to", + }; + DBUG_ENTER("do_write_file"); + + check_command_args(command, + command->first_argument, + write_file_args, + sizeof(write_file_args)/sizeof(struct command_arg)); + + init_dynamic_string(&ds_content, "", 1024, 1024); + read_until_EOF(&ds_content); + DBUG_PRINT("info", ("Writing to file: %s", ds_filename.str)); + str_to_file(ds_filename.str, ds_content.str, ds_content.length); + dynstr_free(&ds_content); + dynstr_free(&ds_filename); + DBUG_VOID_RETURN; +} + + +/* + SYNOPSIS + do_perl + command command handle + + DESCRIPTION + perl; + + <...> + + EOF + + Execute everything after "perl" until EOF as perl. + Useful for doing more advanced things + but still being able to execute it on all platforms. + + The function sets delimiter to EOF and remembers that this + is a perl command by setting "perl mode". The following lines + will then be parsed as any normal query, but when searching + for command in get_query_type, this function will be called + again since "perl mode" is on and the perl script can be + executed. +*/ + +static void do_perl(struct st_query *command) +{ + int error; + char buf[FN_REFLEN]; + FILE *res_file; + DYNAMIC_STRING ds_script; + DBUG_ENTER("do_perl"); + + init_dynamic_string(&ds_script, "", 1024, 1024); + read_until_EOF(&ds_script); + + DBUG_PRINT("info", ("Executing perl: %s", ds_script.str)); + + /* Format a name for a tmp .pl file that is unique for this process */ + my_snprintf(buf, sizeof(buf), "%s/tmp/tmp_%d.pl", + getenv("MYSQLTEST_VARDIR"), getpid()); + str_to_file(buf, ds_script.str, ds_script.length); + + /* Format the perl command */ + my_snprintf(buf, sizeof(buf), "perl %s/tmp/tmp_%d.pl", + getenv("MYSQLTEST_VARDIR"), getpid()); + + if (!(res_file= popen(buf, "r")) && command->abort_on_error) + die("popen(\"%s\", \"r\") failed", buf); + + while (fgets(buf, sizeof(buf), res_file)) + { + if (disable_result_log) + { + buf[strlen(buf)-1]=0; + DBUG_PRINT("exec_result",("%s", buf)); + } + else + { + replace_dynstr_append(&ds_res, buf); + } + } + error= pclose(res_file); + handle_command_error(command, WEXITSTATUS(error)); + dynstr_free(&ds_script); + DBUG_VOID_RETURN; +} + + /* Print the content between echo and to result file. Evaluate all variables in the string before printing, allow @@ -1534,7 +1927,7 @@ int do_echo(struct st_query *command) DYNAMIC_STRING ds_echo; init_dynamic_string(&ds_echo, "", command->query_len, 256); - do_eval(&ds_echo, command->first_argument, FALSE); + do_eval(&ds_echo, command->first_argument, command->end, FALSE); dynstr_append_mem(&ds_res, ds_echo.str, ds_echo.length); dynstr_append_mem(&ds_res, "\n", 1); dynstr_free(&ds_echo); @@ -2900,50 +3293,9 @@ void do_block(enum block_cmd cmd, struct st_query* q) } -/* - Read characters from line buffer or file. This is needed to allow - my_ungetc() to buffer MAX_DELIMITER characters for a file - - NOTE: - This works as long as one doesn't change files (with 'source file_name') - when there is things pushed into the buffer. This should however not - happen for any tests in the test suite. -*/ - -int my_getc(FILE *file) -{ - if (line_buffer_pos == line_buffer) - return fgetc(file); - return *--line_buffer_pos; -} - -void my_ungetc(int c) -{ - *line_buffer_pos++= (char) c; -} - - my_bool end_of_query(int c) { - uint i; - char tmp[MAX_DELIMITER]; - - if (c != *delimiter) - return 0; - - for (i= 1; i < delimiter_length && - (c= my_getc(cur_file->file)) == *(delimiter + i); - i++) - tmp[i]= c; - - if (i == delimiter_length) - return 1; /* Found delimiter */ - - /* didn't find delimiter, push back things that we read */ - my_ungetc(c); - while (i > 1) - my_ungetc(tmp[--i]); - return 0; + return match_delimiter(c, delimiter, delimiter_length); } @@ -4786,7 +5138,7 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags) if (command->type == Q_EVAL) { init_dynamic_string(&eval_query, "", command->query_len+256, 1024); - do_eval(&eval_query, command->query, FALSE); + do_eval(&eval_query, command->query, command->end, FALSE); query = eval_query.str; query_len = eval_query.length; } @@ -5390,6 +5742,11 @@ int main(int argc, char **argv) case Q_DEC: do_modify_var(q, DO_DEC); break; case Q_ECHO: do_echo(q); query_executed= 1; break; case Q_SYSTEM: do_system(q); break; + case Q_REMOVE_FILE: do_remove_file(q); break; + case Q_FILE_EXIST: do_file_exist(q); break; + case Q_WRITE_FILE: do_write_file(q); break; + case Q_COPY_FILE: do_copy_file(q); break; + case Q_PERL: do_perl(q); break; case Q_DELIMITER: do_delimiter(q); break; diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result index 4c2695e5caf..fdc43e8852b 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -467,3 +467,14 @@ a D 1 1 1 4 drop table t1; +mysqltest: At line 1: Missing required argument 'filename' to command 'remove_file' +mysqltest: At line 1: Missing required argument 'filename' to command 'write_file' +mysqltest: At line 1: End of file encountered before 'EOF' delimiter was found +mysqltest: At line 1: End of line junk detected: "write_file filename "; +" +mysqltest: At line 1: Missing required argument 'filename' to command 'file_exists' +mysqltest: At line 1: Missing required argument 'from_file' to command 'copy_file' +mysqltest: At line 1: Missing required argument 'to_file' to command 'copy_file' +hello +hello +hello diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test index 86cfd66ae2b..8aa85937c17 100644 --- a/mysql-test/t/mysqltest.test +++ b/mysql-test/t/mysqltest.test @@ -1159,3 +1159,78 @@ insert into t1 values (2,4); --replace_regex /A/C/ /B/D/i /3/2/ /2/1/ select * from t1; drop table t1; + +# ---------------------------------------------------------------------------- +# test for remove_file +# ---------------------------------------------------------------------------- + +--error 1 +--exec echo "remove_file ;" | $MYSQL_TEST 2>&1 + +--error 1 +remove_file non_existing_file; + +# ---------------------------------------------------------------------------- +# test for write_file +# ---------------------------------------------------------------------------- +--error 1 +--exec echo "write_file ;" | $MYSQL_TEST 2>&1 + +--error 1 +--exec echo "write_file filename ;" | $MYSQL_TEST 2>&1 + +--error 1 +--exec echo "write_file filename \";" | $MYSQL_TEST 2>&1 + +# ---------------------------------------------------------------------------- +# test for file_exist +# ---------------------------------------------------------------------------- +--error 1 +--exec echo "file_exists ;" | $MYSQL_TEST 2>&1 + +--error 0,1 +remove_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +--error 1 +file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +write_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +Content for test_file1 +EOF +file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +remove_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +--error 1 +file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp; + + +# ---------------------------------------------------------------------------- +# test for copy_file +# ---------------------------------------------------------------------------- +--write_file $MYSQLTEST_VARDIR/tmp/file1.tmp +file1 +EOF + +copy_file $MYSQLTEST_VARDIR/tmp/file1.tmp $MYSQLTEST_VARDIR/tmp/file2.tmp; +file_exists $MYSQLTEST_VARDIR/tmp/file2.tmp; +remove_file $MYSQLTEST_VARDIR/tmp/file1.tmp; +remove_file $MYSQLTEST_VARDIR/tmp/file2.tmp; + +--error 1 +--exec echo "copy_file ;" | $MYSQL_TEST 2>&1 + +--error 1 +--exec echo "copy_file from_file;" | $MYSQL_TEST 2>&1 + +# ---------------------------------------------------------------------------- +# test for perl +# ---------------------------------------------------------------------------- +--perl +print "hello\n"; +EOF + +perl; +print "hello\n"; +EOF + +perl; + # Print "hello" + print "hello\n"; +EOF From 59601768338b0ef997be3bb06278f34b1c577415 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 30 Jul 2006 19:46:04 +0200 Subject: [PATCH 15/68] remove unneeded bugfix when read_line function in mysqltest has been fixed client/mysqltest.c: Remove the hack "q_send_flag" which seems to be a bug fix for a buggy 'read_line' function --- client/mysqltest.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 56c06a10f5b..13d5874628e 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -5588,7 +5588,7 @@ static void mark_progress(struct st_query* q __attribute__((unused)), int line) int main(int argc, char **argv) { struct st_query *q; - my_bool require_file=0, q_send_flag=0, abort_flag= 0, + my_bool require_file=0, abort_flag= 0, query_executed= 0; char save_file[FN_REFLEN]; MY_STAT res_info; @@ -5796,11 +5796,7 @@ int main(int argc, char **argv) int flags = QUERY_REAP; if (q->type != Q_REAP) /* for a full query, enable the send stage */ flags |= QUERY_SEND; - if (q_send_flag) - { - flags= QUERY_SEND; - q_send_flag=0; - } + if (save_file[0]) { strmov(q->record_file,save_file); @@ -5813,12 +5809,6 @@ int main(int argc, char **argv) break; } case Q_SEND: - if (!q->query[q->first_word_len]) - { - /* This happens when we use 'send' on its own line */ - q_send_flag=1; - break; - } /* fix up query pointer if this is first iteration for this line */ if (q->query == q->query_buf) q->query+= q->first_word_len; From c84223043b9674b3c670c12be7ffd97a95cd8512 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 22 Sep 2006 20:58:16 +0200 Subject: [PATCH 16/68] Immediately skip tests that need innodb if mysqld does not support it mysql-test/lib/mtr_cases.pl: immediately skip tests that need innodb if mysqld does not support it mysql-test/mysql-test-run.pl: Check if innodb is supported --- mysql-test/lib/mtr_cases.pl | 7 +++++++ mysql-test/mysql-test-run.pl | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/mysql-test/lib/mtr_cases.pl b/mysql-test/lib/mtr_cases.pl index cfd07abb9e0..09f28682345 100644 --- a/mysql-test/lib/mtr_cases.pl +++ b/mysql-test/lib/mtr_cases.pl @@ -503,6 +503,13 @@ sub collect_one_test_case($$$$$$$) { $tinfo->{'skip'}= 1; $tinfo->{'comment'}= "Test need debug binaries"; } + + if ( $tinfo->{'innodb_test'} && ! $::glob_innodb_supported ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Test need innodb"; + } + } # We can't restart a running server that may be in use diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 9f1ce3b9b01..00f87af01f1 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -333,6 +333,7 @@ our @data_dir_lst; our $used_binlog_format; our $debug_compiled_binaries; our $glob_tot_real_time= 0; +our $glob_innodb_supported; ###################################################################### # @@ -351,6 +352,7 @@ sub cleanup_stale_files (); sub check_ssl_support (); sub check_running_as_root(); sub check_ndbcluster_support (); +sub check_innodb_support (); sub rm_ndbcluster_tables ($); sub ndbcluster_start_install ($); sub ndbcluster_start ($$); @@ -391,6 +393,7 @@ sub main () { executable_setup(); check_ndbcluster_support(); + check_innodb_support(); check_ssl_support(); check_debug_support(); @@ -1832,6 +1835,25 @@ sub check_debug_support () { $debug_compiled_binaries= 1; } +sub check_innodb_support () { + + # check innodb support by testing using a switch + # that is only available in that case + if ( mtr_run($exe_mysqld, + ["--no-defaults", + "--innodb-data-file-path", + "--help"], + "", "/dev/null", "/dev/null", "") != 0 ) + { + # mtr_report("Binaries does not support innodb"); + $glob_innodb_supported= 0; + + return; + } + mtr_report("Using innodb when necessary"); + $glob_innodb_supported= 1; +} + ############################################################################## # # Start the ndb cluster From 31cfa6f11b5b958930f7cb3e9966098ff925cf97 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 29 Sep 2006 11:32:40 +0200 Subject: [PATCH 17/68] Add possibility to specify which delimiter to read until in "write_file" and "perl" commands fix memory leaks reported by valgrind mysql-test/r/mysqltest.result: Update result file mysql-test/t/mysqltest.test: Add tests for specifying delimiter --- client/mysqltest.c | 74 +++++++++++++++++++++++++---------- mysql-test/r/mysqltest.result | 4 ++ mysql-test/t/mysqltest.test | 26 ++++++++++++ 3 files changed, 84 insertions(+), 20 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 13d5874628e..d0238fe521f 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -16,12 +16,14 @@ /* mysqltest test tool * See the "MySQL Test framework manual" for more information + * http://dev.mysql.com/doc/mysqltest/en/index.html * * Written by: * Sasha Pachev * Matt Wagner * Monty * Jani + * Magnus **/ #define MTEST_VERSION "2.7" @@ -1332,6 +1334,7 @@ static void do_exec(struct st_query *query) query->first_argument, query->expected_errno[0].code.errnum); } + dynstr_free(&ds_cmd); free_replace(); DBUG_VOID_RETURN; } @@ -1757,22 +1760,32 @@ static my_bool match_delimiter(int c, const char* delim, uint length) } -static void read_until_EOF(DYNAMIC_STRING* ds) +static void read_until_delimiter(DYNAMIC_STRING *ds, + DYNAMIC_STRING *ds_delimiter) { int c; - DBUG_ENTER("read_until_EOF"); + DBUG_ENTER("read_until_delimiter"); + DBUG_PRINT("enter", ("delimiter: %s, length: %d", + ds_delimiter->str, ds_delimiter->length)); - /* Read from file until delimiter EOF is found */ + if (ds_delimiter->length > MAX_DELIMITER) + die("Max delimiter length(%d) exceeded", MAX_DELIMITER); + + /* Read from file until delimiter is found */ while (1) { c= my_getc(cur_file->file); - if (feof(cur_file->file)) - die("End of file encountered before 'EOF' delimiter was found"); + if (c == '\n') + cur_file->lineno++; - if (match_delimiter(c, "EOF", 3)) + if (feof(cur_file->file)) + die("End of file encountered before '%s' delimiter was found", + ds_delimiter->str); + + if (match_delimiter(c, ds_delimiter->str, ds_delimiter->length)) { - DBUG_PRINT("exit", ("Found EOF")); + DBUG_PRINT("exit", ("Found delimiter '%s'", ds_delimiter->str)); break; } dynstr_append_mem(ds, (const char*)&c, 1); @@ -1788,7 +1801,7 @@ static void read_until_EOF(DYNAMIC_STRING* ds) command called command DESCRIPTION - write_file ; + write_file []; <...> < what to write line n> @@ -1800,18 +1813,23 @@ static void read_until_EOF(DYNAMIC_STRING* ds) < what to write line n> EOF - Write everything between the "write_file" command and EOF to "file_name" + Write everything between the "write_file" command and 'delimiter' + to "file_name" NOTE! Overwrites existing file + Default is EOF + */ static void do_write_file(struct st_query *command) { DYNAMIC_STRING ds_content; DYNAMIC_STRING ds_filename; + DYNAMIC_STRING ds_delimiter; const struct command_arg write_file_args[] = { "filename", ARG_STRING, TRUE, &ds_filename, "File to write to", + "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until" }; DBUG_ENTER("do_write_file"); @@ -1820,12 +1838,17 @@ static void do_write_file(struct st_query *command) write_file_args, sizeof(write_file_args)/sizeof(struct command_arg)); + /* If no delimiter was provided, use EOF */ + if (ds_delimiter.length == 0) + dynstr_set(&ds_delimiter, "EOF"); + init_dynamic_string(&ds_content, "", 1024, 1024); - read_until_EOF(&ds_content); + read_until_delimiter(&ds_content, &ds_delimiter); DBUG_PRINT("info", ("Writing to file: %s", ds_filename.str)); str_to_file(ds_filename.str, ds_content.str, ds_content.length); dynstr_free(&ds_content); dynstr_free(&ds_filename); + dynstr_free(&ds_delimiter); DBUG_VOID_RETURN; } @@ -1836,22 +1859,17 @@ static void do_write_file(struct st_query *command) command command handle DESCRIPTION - perl; + perl []; <...> EOF - Execute everything after "perl" until EOF as perl. + Execute everything after "perl" until as perl. Useful for doing more advanced things but still being able to execute it on all platforms. - The function sets delimiter to EOF and remembers that this - is a perl command by setting "perl mode". The following lines - will then be parsed as any normal query, but when searching - for command in get_query_type, this function will be called - again since "perl mode" is on and the perl script can be - executed. + Default is EOF */ static void do_perl(struct st_query *command) @@ -1860,10 +1878,23 @@ static void do_perl(struct st_query *command) char buf[FN_REFLEN]; FILE *res_file; DYNAMIC_STRING ds_script; + DYNAMIC_STRING ds_delimiter; + const struct command_arg perl_args[] = { + "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until" + }; DBUG_ENTER("do_perl"); + check_command_args(command, + command->first_argument, + perl_args, + sizeof(perl_args)/sizeof(struct command_arg)); + + /* If no delimiter was provided, use EOF */ + if (ds_delimiter.length == 0) + dynstr_set(&ds_delimiter, "EOF"); + init_dynamic_string(&ds_script, "", 1024, 1024); - read_until_EOF(&ds_script); + read_until_delimiter(&ds_script, &ds_delimiter); DBUG_PRINT("info", ("Executing perl: %s", ds_script.str)); @@ -1894,6 +1925,7 @@ static void do_perl(struct st_query *command) error= pclose(res_file); handle_command_error(command, WEXITSTATUS(error)); dynstr_free(&ds_script); + dynstr_free(&ds_delimiter); DBUG_VOID_RETURN; } @@ -2693,6 +2725,7 @@ static void free_replace_regex() { if (glob_replace_regex) { + delete_dynamic(&glob_replace_regex->regex_arr); my_free(glob_replace_regex->even_buf,MYF(MY_ALLOW_ZERO_PTR)); my_free(glob_replace_regex->odd_buf,MYF(MY_ALLOW_ZERO_PTR)); my_free((char*) glob_replace_regex,MYF(0)); @@ -4163,7 +4196,8 @@ static int reg_replace(char** buf_p, int* buf_len_p, char *pattern, res_p += left_in_str; str_p= str_end; } - } + } + my_free((gptr)subs, MYF(0)); my_regfree(&r); *res_p= 0; *buf_p= buf; diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result index fdc43e8852b..67634e36ab0 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -478,3 +478,7 @@ mysqltest: At line 1: Missing required argument 'to_file' to command 'copy_file' hello hello hello +mysqltest: At line 1: Max delimiter length(16) exceeded +hello +hello +End of tests diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test index 8aa85937c17..4593955ca54 100644 --- a/mysql-test/t/mysqltest.test +++ b/mysql-test/t/mysqltest.test @@ -1182,6 +1182,18 @@ remove_file non_existing_file; --error 1 --exec echo "write_file filename \";" | $MYSQL_TEST 2>&1 +write_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +Content for test_file1 +EOF +file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +remove_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; + +write_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp END_DELIMITER; +Content for test_file1 contains EOF +END_DELIMITER +file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +remove_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; + # ---------------------------------------------------------------------------- # test for file_exist # ---------------------------------------------------------------------------- @@ -1226,6 +1238,17 @@ remove_file $MYSQLTEST_VARDIR/tmp/file2.tmp; print "hello\n"; EOF +--perl EOF +print "hello\n"; +EOF + +--perl DELIMITER +print "hello\n"; +DELIMITER + +--error 1 +--exec echo "perl TOO_LONG_DELIMITER ;" | $MYSQL_TEST 2>&1 + perl; print "hello\n"; EOF @@ -1234,3 +1257,6 @@ perl; # Print "hello" print "hello\n"; EOF + + +--echo End of tests From 8672a81bc631194934c7f1bef6bba783971f6c01 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 29 Sep 2006 11:39:54 +0200 Subject: [PATCH 18/68] No need to spcify start_lineno in die message, that is automatic --- client/mysqltest.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 856f39b4a1c..b8a5acccd34 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -2031,7 +2031,7 @@ int do_save_master_pos() if (have_ndbcluster) { ulonglong start_epoch= 0, applied_epoch= 0, - latest_epoch=0, latest_trans_epoch=0, + latest_epoch=0, latest_trans_epoch=0, latest_handled_binlog_epoch= 0, latest_received_binlog_epoch= 0, latest_applied_binlog_epoch= 0; int count= 0; @@ -2072,8 +2072,8 @@ int do_save_master_pos() latest_epoch= strtoull(status, (char**) 0, 10); } else - die("line %u: result does not contain '%s' in '%s'", - start_lineno, latest_epoch_str, query); + die("result does not contain '%s' in '%s'", + latest_epoch_str, query); /* latest_trans_epoch */ while (*status && strncmp(status, latest_trans_epoch_str, sizeof(latest_trans_epoch_str)-1)) @@ -2084,10 +2084,10 @@ int do_save_master_pos() latest_trans_epoch= strtoull(status, (char**) 0, 10); } else - die("line %u: result does not contain '%s' in '%s'", - start_lineno, latest_trans_epoch_str, query); + die("result does not contain '%s' in '%s'", + latest_trans_epoch_str, query); /* latest_received_binlog_epoch */ - while (*status && + while (*status && strncmp(status, latest_received_binlog_epoch_str, sizeof(latest_received_binlog_epoch_str)-1)) status++; @@ -2097,10 +2097,10 @@ int do_save_master_pos() latest_received_binlog_epoch= strtoull(status, (char**) 0, 10); } else - die("line %u: result does not contain '%s' in '%s'", - start_lineno, latest_received_binlog_epoch_str, query); + die("result does not contain '%s' in '%s'", + latest_received_binlog_epoch_str, query); /* latest_handled_binlog */ - while (*status && + while (*status && strncmp(status, latest_handled_binlog_epoch_str, sizeof(latest_handled_binlog_epoch_str)-1)) status++; @@ -2110,10 +2110,10 @@ int do_save_master_pos() latest_handled_binlog_epoch= strtoull(status, (char**) 0, 10); } else - die("line %u: result does not contain '%s' in '%s'", - start_lineno, latest_handled_binlog_epoch_str, query); + die("result does not contain '%s' in '%s'", + latest_handled_binlog_epoch_str, query); /* latest_applied_binlog_epoch */ - while (*status && + while (*status && strncmp(status, latest_applied_binlog_epoch_str, sizeof(latest_applied_binlog_epoch_str)-1)) status++; @@ -2123,16 +2123,16 @@ int do_save_master_pos() latest_applied_binlog_epoch= strtoull(status, (char**) 0, 10); } else - die("line %u: result does not contain '%s' in '%s'", - start_lineno, latest_applied_binlog_epoch_str, query); + die("result does not contain '%s' in '%s'", + latest_applied_binlog_epoch_str, query); if (count == 0) start_epoch= latest_trans_epoch; break; } } if (!row) - die("line %u: result does not contain '%s' in '%s'", - start_lineno, binlog, query); + die("result does not contain '%s' in '%s'", + binlog, query); if (latest_applied_binlog_epoch > applied_epoch) count= 0; applied_epoch= latest_applied_binlog_epoch; From 9368c7bcd888dde02fc3975ea7bd007417cec9ba Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 3 Oct 2006 15:33:44 +0200 Subject: [PATCH 19/68] Update mysqltest to latest version - ie. backport from 5.1 - also update testcase error dected by new version mysql-test/include/show_msg.inc: BitKeeper file /home/msvensson/mysql/same_tools/my41-same_tools/mysql-test/include/show_msg.inc mysql-test/include/show_msg80.inc: BitKeeper file /home/msvensson/mysql/same_tools/my41-same_tools/mysql-test/include/show_msg80.inc BitKeeper/deleted/.del-rpl_chain_temp_table.test: Delete: mysql-test/t/rpl_chain_temp_table.test BitKeeper/deleted/.del-rpl_chain_temp_table.result: Delete: mysql-test/r/rpl_chain_temp_table.result BitKeeper/deleted/.del-rpl_failsafe.result: Delete: mysql-test/r/rpl_failsafe.result BitKeeper/deleted/.del-rpl_failsafe.test: Delete: mysql-test/t/rpl_failsafe.test BitKeeper/deleted/.del-rpl_heap.test: Delete: mysql-test/t/rpl_heap.test BitKeeper/deleted/.del-rpl_heap.result: Delete: mysql-test/r/rpl_heap.result BitKeeper/deleted/.del-rpl000018.result: Delete: mysql-test/r/rpl000018.result BitKeeper/deleted/.del-rpl000018.test: Delete: mysql-test/t/rpl000018.test client/Makefile.am: Link mysqltest with mysys/my_copy.c client/mysqltest.c: Update mysqltest to latest version mysql-test/include/have_multi_ndb.inc: Remove old syntax "@filename" in favor of "--require filename" mysql-test/include/master-slave.inc: Remove old syntax "@filename" in favor of "--require filename" mysql-test/include/ps_query.inc: Remove the comment about no output now when it does. mysql-test/r/check.result: Update output from --send mysql-test/r/connect.result: Update result file for connect test after backport form 5.1 mysql-test/r/flush.result: Update output from --send mysql-test/r/flush_block_commit.result: Update output from --send mysql-test/r/func_misc.result: Update output from --send mysql-test/r/grant2.result: Update output from --send mysql-test/r/handler.result: Update output from --send mysql-test/r/kill.result: Update output from --send mysql-test/r/lock_multi.result: Update output from --send mysql-test/r/mix_innodb_myisam_binlog.result: Update output from --send mysql-test/r/mysqltest.result: Update mysqltest.result after backport mysql-test/r/ps_2myisam.result: Update result as the output from query is now printed mysql-test/r/ps_3innodb.result: Update result as the output from query is now printed mysql-test/r/ps_4heap.result: Update result as the output from query is now printed mysql-test/r/ps_5merge.result: Update result as the output from query is now printed mysql-test/r/ps_6bdb.result: Update result as the output from query is now printed mysql-test/r/ps_7ndb.result: Update result as the output from query is now printed mysql-test/r/rename.result: Update output from --send mysql-test/r/rpl000001.result: Update output from --send mysql-test/r/rpl_error_ignored_table.result: Update output from --send mysql-test/r/rpl_master_pos_wait.result: Update output from --send mysql-test/r/subselect.result: Update result file after adding missing ; mysql-test/r/synchronization.result: Update output from --send mysql-test/r/type_blob.result: Update result file after adding missing ; mysql-test/t/connect.test: Backport test from 5.1 mysql-test/t/init_file.test: Update test so something is printed mysql-test/t/mysql_client_test.test: Update test so result is sent to file and something is printed mysql-test/t/mysqltest.test: Backport latest mysqltest.test file mysql-test/t/ps.test: Move the --replace_column statement to just before the statetement it should replace mysql-test/t/ps_1general.test: Move the --replace_column statement to just before the statetement it should replace mysql-test/t/ps_grant.test: Remove the $DB, no other test uses it mysql-test/t/rpl_flush_tables.test: Fetch $SERVER_VERSION from the db server mysql-test/t/rpl_trunc_temp.test: Remove the selection of connection master after it's been disconnected already mysql-test/t/subselect.test: Add missing ; mysql-test/t/type_blob.test: Add missing ; --- client/Makefile.am | 4 +- client/mysqltest.c | 7702 +++++++++++------- mysql-test/include/have_multi_ndb.inc | 12 +- mysql-test/include/master-slave.inc | 6 +- mysql-test/include/ps_query.inc | 1 - mysql-test/include/show_msg.inc | 25 + mysql-test/include/show_msg80.inc | 118 + mysql-test/r/check.result | 2 +- mysql-test/r/connect.result | 20 + mysql-test/r/flush.result | 4 +- mysql-test/r/flush_block_commit.result | 6 +- mysql-test/r/func_misc.result | 2 +- mysql-test/r/grant2.result | 8 +- mysql-test/r/handler.result | 2 +- mysql-test/r/kill.result | 2 +- mysql-test/r/lock_multi.result | 12 +- mysql-test/r/mix_innodb_myisam_binlog.result | 2 +- mysql-test/r/mysqltest.result | 194 +- mysql-test/r/ps_2myisam.result | 2 + mysql-test/r/ps_3innodb.result | 2 + mysql-test/r/ps_4heap.result | 2 + mysql-test/r/ps_5merge.result | 4 + mysql-test/r/ps_6bdb.result | 2 + mysql-test/r/ps_7ndb.result | 2 + mysql-test/r/rename.result | 2 +- mysql-test/r/rpl000001.result | 2 +- mysql-test/r/rpl000018.result | 14 - mysql-test/r/rpl_chain_temp_table.result | 30 - mysql-test/r/rpl_error_ignored_table.result | 2 +- mysql-test/r/rpl_failsafe.result | 34 - mysql-test/r/rpl_heap.result | 29 - mysql-test/r/rpl_master_pos_wait.result | 2 +- mysql-test/r/subselect.result | 3 +- mysql-test/r/synchronization.result | 40 +- mysql-test/r/type_blob.result | 3 +- mysql-test/t/connect.test | 83 +- mysql-test/t/init_file.test | 5 +- mysql-test/t/mysql_client_test.test | 6 +- mysql-test/t/mysqltest.test | 745 +- mysql-test/t/ps.test | 6 +- mysql-test/t/ps_1general.test | 2 +- mysql-test/t/ps_grant.test | 2 +- mysql-test/t/rpl000018.test | 29 - mysql-test/t/rpl_chain_temp_table.test | 101 - mysql-test/t/rpl_failsafe.test | 24 - mysql-test/t/rpl_flush_tables.test | 2 + mysql-test/t/rpl_heap.test | 51 - mysql-test/t/rpl_trunc_temp.test | 3 - mysql-test/t/subselect.test | 2 +- mysql-test/t/type_blob.test | 2 +- 50 files changed, 6023 insertions(+), 3337 deletions(-) create mode 100755 mysql-test/include/show_msg.inc create mode 100755 mysql-test/include/show_msg80.inc delete mode 100644 mysql-test/r/rpl000018.result delete mode 100644 mysql-test/r/rpl_chain_temp_table.result delete mode 100644 mysql-test/r/rpl_failsafe.result delete mode 100644 mysql-test/r/rpl_heap.result delete mode 100644 mysql-test/t/rpl000018.test delete mode 100644 mysql-test/t/rpl_chain_temp_table.test delete mode 100644 mysql-test/t/rpl_failsafe.test delete mode 100644 mysql-test/t/rpl_heap.test diff --git a/client/Makefile.am b/client/Makefile.am index c0569d5fa6f..fe150defe03 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -30,7 +30,9 @@ mysql_SOURCES = mysql.cc readline.cc sql_string.cc completion_hash.cc mysqladmin_SOURCES = mysqladmin.cc mysql_LDADD = @readline_link@ @TERMCAP_LIB@ $(LDADD) $(CXXLDFLAGS) mysqlbinlog_LDADD = $(LDADD) $(CXXLDFLAGS) -mysqltest_SOURCES= mysqltest.c $(top_srcdir)/mysys/my_getsystime.c +mysqltest_SOURCES= mysqltest.c \ + $(top_srcdir)/mysys/my_getsystime.c \ + $(top_srcdir)/mysys/my_copy.c mysqltest_LDADD = $(top_builddir)/regex/libregex.a $(LDADD) mysqlbinlog_SOURCES = mysqlbinlog.cc ../mysys/mf_tempdir.c mysqlmanagerc_SOURCES = mysqlmanagerc.c diff --git a/client/mysqltest.c b/client/mysqltest.c index ad0f9f857bb..623c33d0f4f 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -14,35 +14,22 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* mysqltest test tool - * See the manual for more information - * TODO: document better how mysqltest works - * - * Written by: - * Sasha Pachev - * Matt Wagner - * Monty - * Jani - **/ +/* + mysqltest -/********************************************************************** - TODO: + Tool used for executing a .test file -- Do comparison line by line, instead of doing a full comparison of - the text file. This will save space as we don't need to keep many - results in memory. It will also make it possible to do simple - 'comparison' fixes like accepting the result even if a float differed - in the last decimals. + See the "MySQL Test framework manual" for more information + http://dev.mysql.com/doc/mysqltest/en/index.html -- Don't buffer lines from the test that you don't expect to need - again. + Written by: + Sasha Pachev + Matt Wagner + Monty + Jani +*/ -- Change 'read_line' to be faster by using the readline.cc code; - We can do better than calling feof() for each character! - -**********************************************************************/ - -#define MTEST_VERSION "2.5" +#define MTEST_VERSION "3.0" #include #include @@ -51,18 +38,18 @@ #include #include #include +#include #include #include -#include /* Error codes */ #include #include #include -#include #include -#include "my_regex.h" /* Our own version of lib */ +#include "my_regex.h" /* Our own version of regex */ #ifdef HAVE_SYS_WAIT_H #include #endif + #ifndef WEXITSTATUS # ifdef __WIN__ # define WEXITSTATUS(stat_val) (stat_val) @@ -70,166 +57,144 @@ # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) # endif #endif -#define MAX_QUERY 131072 -#define MAX_VAR_NAME 256 -#define MAX_COLUMNS 256 -#define PAD_SIZE 128 -#define MAX_CONS 128 -#define MAX_INCLUDE_DEPTH 16 -#define LAZY_GUESS_BUF_SIZE 8192 -#define INIT_Q_LINES 1024 -#define MIN_VAR_ALLOC 32 -#define BLOCK_STACK_DEPTH 32 -#define MAX_EXPECTED_ERRORS 10 -#define QUERY_SEND 1 -#define QUERY_REAP 2 -#ifndef MYSQL_MANAGER_PORT -#define MYSQL_MANAGER_PORT 23546 -#endif -#define MAX_SERVER_ARGS 64 -/* - Sometimes in a test the client starts before - the server - to solve the problem, we try again - after some sleep if connection fails the first - time -*/ -#define CON_RETRY_SLEEP 2 -#define MAX_CON_TRIES 5 +#define MAX_VAR_NAME_LENGTH 256 +#define MAX_COLUMNS 256 +#define MAX_EMBEDDED_SERVER_ARGS 64 +#define MAX_DELIMITER_LENGTH 16 -#define SLAVE_POLL_INTERVAL 300000 /* 0.3 of a sec */ -#define DEFAULT_DELIMITER ";" -#define MAX_DELIMITER 16 +/* Flags controlling send and reap */ +#define QUERY_SEND_FLAG 1 +#define QUERY_REAP_FLAG 2 -#define RESULT_OK 0 -#define RESULT_CONTENT_MISMATCH 1 -#define RESULT_LENGTH_MISMATCH 2 + enum { + RESULT_OK= 0, + RESULT_CONTENT_MISMATCH= 1, + RESULT_LENGTH_MISMATCH= 2 + }; -enum {OPT_MANAGER_USER=256,OPT_MANAGER_HOST,OPT_MANAGER_PASSWD, - OPT_MANAGER_PORT,OPT_MANAGER_WAIT_TIMEOUT, OPT_SKIP_SAFEMALLOC, - OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA, OPT_SSL_CAPATH, - OPT_SSL_CIPHER,OPT_PS_PROTOCOL}; - -/* ************************************************************************ */ -/* - The list of error codes to --error are stored in an internal array of - structs. This struct can hold numeric SQL error codes or SQLSTATE codes - as strings. The element next to the last active element in the list is - set to type ERR_EMPTY. When an SQL statement returns an error, we use - this list to check if this is an expected error. -*/ - -enum match_err_type -{ - ERR_EMPTY= 0, - ERR_ERRNO, - ERR_SQLSTATE +enum { + OPT_SKIP_SAFEMALLOC=256, OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, + OPT_SSL_CA, OPT_SSL_CAPATH, OPT_SSL_CIPHER, OPT_PS_PROTOCOL, + OPT_SP_PROTOCOL, OPT_CURSOR_PROTOCOL, OPT_VIEW_PROTOCOL, + OPT_SSL_VERIFY_SERVER_CERT, OPT_MAX_CONNECT_RETRIES, + OPT_MARK_PROGRESS }; -typedef struct -{ - enum match_err_type type; - union - { - uint errnum; - char sqlstate[SQLSTATE_LENGTH+1]; /* \0 terminated string */ - } code; -} match_err; - -static match_err global_expected_errno[MAX_EXPECTED_ERRORS]; -static uint global_expected_errors; - -/* ************************************************************************ */ - -static int record = 0, opt_sleep=0; -static char *db = 0, *pass=0; -const char *user = 0, *host = 0, *unix_sock = 0, *opt_basedir="./"; -static int port = 0; -static my_bool opt_big_test= 0, opt_compress= 0, silent= 0, verbose = 0; -static my_bool tty_password= 0, ps_protocol= 0, ps_protocol_enabled= 0; -static uint start_lineno= 0, *lineno; -const char *manager_user="root",*manager_host=0; -char *manager_pass=0; -int manager_port=MYSQL_MANAGER_PORT; -int manager_wait_timeout=3; -MYSQL_MANAGER* manager=0; +static int record= 0, opt_sleep= -1; +static char *db= 0, *pass= 0; +const char *user= 0, *host= 0, *unix_sock= 0, *opt_basedir= "./"; +const char *opt_include= 0; +static int port= 0; +static int opt_max_connect_retries; +static my_bool opt_compress= 0, silent= 0, verbose= 0; +static my_bool tty_password= 0; +static my_bool opt_mark_progress= 0; +static my_bool ps_protocol= 0, ps_protocol_enabled= 0; +static my_bool sp_protocol= 0, sp_protocol_enabled= 0; +static my_bool view_protocol= 0, view_protocol_enabled= 0; +static my_bool cursor_protocol= 0, cursor_protocol_enabled= 0; +static my_bool parsing_disabled= 0; +static my_bool display_result_vertically= FALSE, display_metadata= FALSE; +static my_bool disable_query_log= 0, disable_result_log= 0; +static my_bool disable_warnings= 0, disable_ps_warnings= 0; +static my_bool disable_info= 1; +static my_bool abort_on_error= 1; static char **default_argv; -static const char *load_default_groups[]= { "mysqltest","client",0 }; -static char line_buffer[MAX_DELIMITER], *line_buffer_pos= line_buffer; +static const char *load_default_groups[]= { "mysqltest", "client", 0 }; +static char line_buffer[MAX_DELIMITER_LENGTH], *line_buffer_pos= line_buffer; -typedef struct +static uint start_lineno= 0; /* Start line of current command */ + +static char delimiter[MAX_DELIMITER_LENGTH]= ";"; +static uint delimiter_length= 1; + +static char TMPDIR[FN_REFLEN]; + +/* Block stack */ +enum block_cmd { + cmd_none, + cmd_if, + cmd_while +}; + +struct st_block +{ + int line; /* Start line of block */ + my_bool ok; /* Should block be executed */ + enum block_cmd cmd; /* Command owning the block */ +}; + +static struct st_block block_stack[32]; +static struct st_block *cur_block, *block_stack_end; + +/* Open file stack */ +struct st_test_file { FILE* file; const char *file_name; -} test_file; + uint lineno; /* Current line in file */ +}; -static test_file file_stack[MAX_INCLUDE_DEPTH]; -static test_file* cur_file; -static test_file* file_stack_end; +static struct st_test_file file_stack[16]; +static struct st_test_file* cur_file; +static struct st_test_file* file_stack_end; -static uint lineno_stack[MAX_INCLUDE_DEPTH]; -static char TMPDIR[FN_REFLEN]; -static char delimiter[MAX_DELIMITER]= DEFAULT_DELIMITER; -static uint delimiter_length= 1; static CHARSET_INFO *charset_info= &my_charset_latin1; /* Default charset */ static const char *charset_name= "latin1"; /* Default character set name */ -static int embedded_server_arg_count=0; -static char *embedded_server_args[MAX_SERVER_ARGS]; - -static my_bool display_result_vertically= FALSE, display_metadata= FALSE; - -/* See the timer_output() definition for details */ -static char *timer_file = NULL; -static ulonglong timer_start; -static int got_end_timer= FALSE; -static void timer_output(void); -static ulonglong timer_now(void); - -static my_regex_t ps_re; /* Holds precompiled re for valid PS statements */ -static void ps_init_re(void); -static int ps_match_re(char *); -static char *ps_eprint(int); -static void ps_free_reg(void); - -static const char *embedded_server_groups[] = { +static const char *embedded_server_groups[]= +{ "server", "embedded", "mysqltest_SERVER", NullS }; +static int embedded_server_arg_count=0; +static char *embedded_server_args[MAX_EMBEDDED_SERVER_ARGS]; + +/* + Timer related variables + See the timer_output() definition for details +*/ +static char *timer_file = NULL; +static ulonglong timer_start; +static void timer_output(void); +static ulonglong timer_now(void); + +static ulonglong progress_start= 0; + +/* Precompiled re's */ +static my_regex_t ps_re; /* the query can be run using PS protocol */ +static my_regex_t sp_re; /* the query can be run as a SP */ +static my_regex_t view_re; /* the query can be run as a view*/ + +static void init_re(void); +static int match_re(my_regex_t *, char *); +static void free_re(void); + DYNAMIC_ARRAY q_lines; #include "sslopt-vars.h" -typedef struct +struct +{ + int read_lines,current_line; +} parser; + +struct { char file[FN_REFLEN]; ulong pos; -} MASTER_POS ; +} master_pos; -struct connection -{ - MYSQL mysql; - char *name; -}; - -typedef struct -{ - int read_lines,current_line; -} PARSER; - -MYSQL_RES *last_result=0; - -PARSER parser; -MASTER_POS master_pos; /* if set, all results are concated and compared against this file */ -const char *result_file = 0; +const char *result_file= 0; -typedef struct +typedef struct st_var { char *name; int name_len; @@ -239,81 +204,71 @@ typedef struct int alloced_len; int int_dirty; /* do not update string if int is updated until first read */ int alloced; + char *env_s; } VAR; -#if defined(__NETWARE__) || defined(__WIN__) -/* - Netware doesn't proved environment variable substitution that is done - by the shell in unix environments. We do this in the following function: -*/ - -static char *subst_env_var(const char *cmd); -static FILE *my_popen(const char *cmd, const char *mode); -#define popen(A,B) my_popen((A),(B)) -#endif /* __NETWARE__ */ - -VAR var_reg[10]; /*Perl/shell-like variable registers */ +VAR var_reg[10]; + HASH var_hash; -my_bool disable_query_log=0, disable_result_log=0, disable_warnings=0; -my_bool disable_info= 1; /* By default off */ -my_bool abort_on_error= 1; -struct connection cons[MAX_CONS]; -struct connection* cur_con, *next_con, *cons_end; - - /* Add new commands before Q_UNKNOWN !*/ - -enum enum_commands { -Q_CONNECTION=1, Q_QUERY, -Q_CONNECT, Q_SLEEP, Q_REAL_SLEEP, -Q_INC, Q_DEC, -Q_SOURCE, Q_DISCONNECT, -Q_LET, Q_ECHO, -Q_WHILE, Q_END_BLOCK, -Q_SYSTEM, Q_RESULT, -Q_REQUIRE, Q_SAVE_MASTER_POS, -Q_SYNC_WITH_MASTER, -Q_SYNC_SLAVE_WITH_MASTER, -Q_ERROR, -Q_SEND, Q_REAP, -Q_DIRTY_CLOSE, Q_REPLACE, Q_REPLACE_COLUMN, -Q_PING, Q_EVAL, -Q_RPL_PROBE, Q_ENABLE_RPL_PARSE, -Q_DISABLE_RPL_PARSE, Q_EVAL_RESULT, -Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG, -Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG, -Q_SERVER_START, Q_SERVER_STOP,Q_REQUIRE_MANAGER, -Q_WAIT_FOR_SLAVE_TO_STOP, -Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS, -Q_ENABLE_INFO, Q_DISABLE_INFO, -Q_ENABLE_METADATA, Q_DISABLE_METADATA, -Q_EXEC, Q_DELIMITER, -Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR, -Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS, -Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, -Q_START_TIMER, Q_END_TIMER, -Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL, -Q_EXIT, -Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT, -Q_IF, - -Q_UNKNOWN, /* Unknown command. */ -Q_COMMENT, /* Comments, ignored. */ -Q_COMMENT_WITH_COMMAND -}; - -/* this should really be called command */ -struct st_query +struct st_connection { - char *query, *query_buf,*first_argument,*last_argument,*end; - int first_word_len; - my_bool abort_on_error, require_file; - match_err expected_errno[MAX_EXPECTED_ERRORS]; - uint expected_errors; - char record_file[FN_REFLEN]; - enum enum_commands type; + MYSQL mysql; + /* Used when creating views and sp, to avoid implicit commit */ + MYSQL* util_mysql; + char *name; + MYSQL_STMT* stmt; }; +struct st_connection connections[128]; +struct st_connection* cur_con, *next_con, *connections_end; + +/* + List of commands in mysqltest + Must match the "command_names" array + Add new commands before Q_UNKNOWN! +*/ +enum enum_commands { + Q_CONNECTION=1, Q_QUERY, + Q_CONNECT, Q_SLEEP, Q_REAL_SLEEP, + Q_INC, Q_DEC, + Q_SOURCE, Q_DISCONNECT, + Q_LET, Q_ECHO, + Q_WHILE, Q_END_BLOCK, + Q_SYSTEM, Q_RESULT, + Q_REQUIRE, Q_SAVE_MASTER_POS, + Q_SYNC_WITH_MASTER, + Q_SYNC_SLAVE_WITH_MASTER, + Q_ERROR, + Q_SEND, Q_REAP, + Q_DIRTY_CLOSE, Q_REPLACE, Q_REPLACE_COLUMN, + Q_PING, Q_EVAL, + Q_RPL_PROBE, Q_ENABLE_RPL_PARSE, + Q_DISABLE_RPL_PARSE, Q_EVAL_RESULT, + Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG, + Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG, + Q_WAIT_FOR_SLAVE_TO_STOP, + Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS, + Q_ENABLE_PS_WARNINGS, Q_DISABLE_PS_WARNINGS, + Q_ENABLE_INFO, Q_DISABLE_INFO, + Q_ENABLE_METADATA, Q_DISABLE_METADATA, + Q_EXEC, Q_DELIMITER, + Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR, + Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS, + Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, + Q_START_TIMER, Q_END_TIMER, + Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL, + Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT, + Q_IF, + Q_DISABLE_PARSING, Q_ENABLE_PARSING, + Q_REPLACE_REGEX, Q_REMOVE_FILE, Q_FILE_EXIST, + Q_WRITE_FILE, Q_COPY_FILE, Q_PERL, Q_DIE, + + Q_UNKNOWN, /* Unknown command. */ + Q_COMMENT, /* Comments, ignored. */ + Q_COMMENT_WITH_COMMAND +}; + const char *command_names[]= { @@ -348,16 +303,17 @@ const char *command_names[]= "enable_rpl_parse", "disable_rpl_parse", "eval_result", + /* Enable/disable that the _query_ is logged to result file */ "enable_query_log", "disable_query_log", + /* Enable/disable that the _result_ from a query is logged to result file */ "enable_result_log", "disable_result_log", - "server_start", - "server_stop", - "require_manager", "wait_for_slave_to_stop", "enable_warnings", "disable_warnings", + "enable_ps_warnings", + "disable_ps_warnings", "enable_info", "disable_info", "enable_metadata", @@ -375,80 +331,118 @@ const char *command_names[]= "character_set", "disable_ps_protocol", "enable_ps_protocol", - "exit", "disable_reconnect", "enable_reconnect", "if", + "disable_parsing", + "enable_parsing", + "replace_regex", + "remove_file", + "file_exists", + "write_file", + "copy_file", + "perl", + "die", 0 }; -/* Block stack */ -typedef struct + +/* + The list of error codes to --error are stored in an internal array of + structs. This struct can hold numeric SQL error codes, error names or + SQLSTATE codes as strings. The element next to the last active element + in the list is set to type ERR_EMPTY. When an SQL statement returns an + error, we use this list to check if this is an expected error. +*/ +enum match_err_type { - int line; /* Start line of block */ - my_bool ok; /* Should block be executed */ - enum enum_commands cmd; /* Command owning the block */ -} BLOCK; -static BLOCK block_stack[BLOCK_STACK_DEPTH]; -static BLOCK *cur_block, *block_stack_end; + ERR_EMPTY= 0, + ERR_ERRNO, + ERR_SQLSTATE +}; + +struct st_match_err +{ + enum match_err_type type; + union + { + uint errnum; + char sqlstate[SQLSTATE_LENGTH+1]; /* \0 terminated string */ + } code; +}; + +struct st_expected_errors +{ + struct st_match_err err[10]; + uint count; +}; +static struct st_expected_errors saved_expected_errors; + +struct st_command +{ + char *query, *query_buf,*first_argument,*last_argument,*end; + int first_word_len, query_len; + my_bool abort_on_error, require_file; + struct st_expected_errors expected_errors; + char record_file[FN_REFLEN]; + enum enum_commands type; +}; TYPELIB command_typelib= {array_elements(command_names),"", command_names, 0}; -DYNAMIC_STRING ds_res; -static void die(const char *fmt, ...); -static void init_var_hash(); -static VAR* var_from_env(const char *, const char *); -static byte* get_var_key(const byte* rec, uint* len, my_bool t); -static VAR* var_init(VAR* v, const char *name, int name_len, const char *val, - int val_len); +DYNAMIC_STRING ds_res, ds_progress; -static void var_free(void* v); +void die(const char *fmt, ...) + /* ATTRIBUTE_FORMAT(printf, 1, 2) */; +void abort_not_supported_test(const char *fmt, ...) + /* ATTRIBUTE_FORMAT(printf, 1, 2) */; +void verbose_msg(const char *fmt, ...) + /* ATTRIBUTE_FORMAT(printf, 1, 2) */; -int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname); -void reject_dump(const char *record_file, char *buf, int size); +VAR* var_from_env(const char *, const char *); +VAR* var_init(VAR* v, const char *name, int name_len, const char *val, + int val_len); +void var_free(void* v); +VAR* var_get(const char *var_name, const char** var_name_end, + my_bool raw, my_bool ignore_not_existing); +void eval_expr(VAR* v, const char *p, const char** p_end); +my_bool match_delimiter(int c, const char *delim, uint length); +void dump_result_to_reject_file(const char *record_file, char *buf, int size); +void dump_result_to_log_file(const char *record_file, char *buf, int size); -int close_connection(struct st_query*); -static void set_charset(struct st_query*); -VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw, - my_bool ignore_not_existing); -int eval_expr(VAR* v, const char *p, const char** p_end); -static int read_server_arguments(const char *name); - -/* Definitions for replace result */ - -typedef struct st_pointer_array { /* when using array-strings */ - TYPELIB typelib; /* Pointer to strings */ - byte *str; /* Strings is here */ - int7 *flag; /* Flag about each var. */ - uint array_allocs,max_count,length,max_length; -} POINTER_ARRAY; - -struct st_replace; -struct st_replace *init_replace(my_string *from, my_string *to, uint count, - my_string word_end_chars); -uint replace_strings(struct st_replace *rep, my_string *start, - uint *max_length, const char *from); -void free_replace(); -static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name); -void free_pointer_array(POINTER_ARRAY *pa); -static int initialize_replace_buffer(void); -static void free_replace_buffer(void); -static void do_eval(DYNAMIC_STRING *query_eval, const char *query); +void do_eval(DYNAMIC_STRING *query_eval, const char *query, + const char *query_end, my_bool pass_through_escape_chars); void str_to_file(const char *fname, char *str, int size); -int do_server_op(struct st_query *q,const char *op); -struct st_replace *glob_replace; -static char *out_buff; -static uint out_length; +#ifdef __WIN__ +void free_tmp_sh_file(); +void free_win_path_patterns(); +#endif + static int eval_result = 0; -/* For column replace */ -char *replace_column[MAX_COLUMNS]; -uint max_replace_column= 0; +/* For replace_column */ +static char *replace_column[MAX_COLUMNS]; +static uint max_replace_column= 0; +void do_get_replace_column(struct st_command*); +void free_replace_column(); + +/* For replace */ +void do_get_replace(struct st_command *command); +void free_replace(); + +/* For replace_regex */ +void do_get_replace_regex(struct st_command *command); +void free_replace_regex(); + + +void free_all_replace(){ + free_replace(); + free_replace_regex(); + free_replace_column(); +} -static void get_replace_column(struct st_query *q); -static void free_replace_column(); /* Disable functions that only exist in MySQL 4.0 */ #if MYSQL_VERSION_ID < 40000 @@ -457,42 +451,64 @@ void mysql_disable_rpl_parse(MYSQL* mysql __attribute__((unused))) {} int mysql_rpl_parse_enabled(MYSQL* mysql __attribute__((unused))) { return 1; } my_bool mysql_rpl_probe(MYSQL *mysql __attribute__((unused))) { return 1; } #endif -static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val, - int len); -static int handle_no_error(struct st_query *q); -static void do_eval(DYNAMIC_STRING* query_eval, const char *query) +void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val, + int len); +void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val); +void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val); + +void handle_error(struct st_command*, + unsigned int err_errno, const char *err_error, + const char *err_sqlstate, DYNAMIC_STRING *ds); +void handle_no_error(struct st_command*); + + + +void do_eval(DYNAMIC_STRING *query_eval, const char *query, + const char *query_end, my_bool pass_through_escape_chars) { const char *p; - register char c; + register char c, next_c; register int escaped = 0; - VAR* v; + VAR *v; DBUG_ENTER("do_eval"); - for (p= query; (c = *p); ++p) + for (p= query; (c= *p) && p < query_end; ++p) { switch(c) { case '$': if (escaped) { - escaped = 0; + escaped= 0; dynstr_append_mem(query_eval, p, 1); } else { - if (!(v = var_get(p, &p, 0, 0))) + if (!(v= var_get(p, &p, 0, 0))) die("Bad variable in eval"); dynstr_append_mem(query_eval, v->str_val, v->str_val_len); } break; case '\\': + next_c= *(p+1); if (escaped) { - escaped = 0; + escaped= 0; dynstr_append_mem(query_eval, p, 1); } + else if (next_c == '\\' || next_c == '$' || next_c == '"') + { + /* Set escaped only if next char is \, " or $ */ + escaped= 1; + + if (pass_through_escape_chars) + { + /* The escape char should be added to the output string. */ + dynstr_append_mem(query_eval, p, 1); + } + } else - escaped = 1; + dynstr_append_mem(query_eval, p, 1); break; default: dynstr_append_mem(query_eval, p, 1); @@ -503,24 +519,148 @@ static void do_eval(DYNAMIC_STRING* query_eval, const char *query) } -static void close_cons() +enum arg_type { - DBUG_ENTER("close_cons"); - if (last_result) - mysql_free_result(last_result); - for (--next_con; next_con >= cons; --next_con) + ARG_STRING, + ARG_REST +}; + +struct command_arg { + const char *argname; /* Name of argument */ + enum arg_type type; /* Type of argument */ + my_bool required; /* Argument required */ + DYNAMIC_STRING *ds; /* Storage for argument */ + const char *description; /* Description of the argument */ +}; + + +void check_command_args(struct st_command *command, + const char *arguments, + const struct command_arg *args, + int num_args, const char delimiter_arg) +{ + int i; + const char *ptr= arguments; + const char *start; + + DBUG_ENTER("check_command_args"); + DBUG_PRINT("enter", ("num_args: %d", num_args)); + for (i= 0; i < num_args; i++) { + const struct command_arg *arg= &args[i]; + + switch (arg->type) + { + /* A string */ + case ARG_STRING: + /* Skip leading spaces */ + while (*ptr && *ptr == ' ') + ptr++; + start= ptr; + /* Find end of arg, terminated by "delimiter_arg" */ + while (*ptr && *ptr != delimiter_arg) + ptr++; + if (ptr > start) + { + init_dynamic_string(arg->ds, 0, ptr-start, 32); + do_eval(arg->ds, start, ptr, FALSE); + } + else + { + /* Empty string */ + init_dynamic_string(arg->ds, "", 0, 0); + } + command->last_argument= (char*)ptr; + + /* Step past the delimiter */ + if (*ptr && *ptr == delimiter_arg) + ptr++; + DBUG_PRINT("info", ("val: %s", arg->ds->str)); + break; + + /* Rest of line */ + case ARG_REST: + start= ptr; + init_dynamic_string(arg->ds, 0, command->query_len, 256); + do_eval(arg->ds, start, command->end, FALSE); + command->last_argument= command->end; + DBUG_PRINT("info", ("val: %s", arg->ds->str)); + break; + + default: + DBUG_ASSERT("Unknown argument type"); + break; + } + + /* Check required arg */ + if (arg->ds->length == 0 && arg->required) + die("Missing required argument '%s' to command '%.*s'", arg->argname, + command->first_word_len, command->query); + + } + DBUG_VOID_RETURN; +} + + +void handle_command_error(struct st_command *command, uint error) +{ + DBUG_ENTER("handle_command_error"); + DBUG_PRINT("enter", ("error: %d", error)); + if (error != 0) + { + uint i; + + if (command->abort_on_error) + die("command \"%.*s\" failed with error %d", + command->first_word_len, command->query, error); + for (i= 0; i < command->expected_errors.count; i++) + { + DBUG_PRINT("info", ("expected error: %d", + command->expected_errors.err[i].code.errnum)); + if ((command->expected_errors.err[i].type == ERR_ERRNO) && + (command->expected_errors.err[i].code.errnum == error)) + { + DBUG_PRINT("info", ("command \"%.*s\" failed with expected error: %d", + command->first_word_len, command->query, error)); + DBUG_VOID_RETURN; + } + } + die("command \"%.*s\" failed with wrong error: %d", + command->first_word_len, command->query, error); + } + else if (command->expected_errors.err[0].type == ERR_ERRNO && + command->expected_errors.err[0].code.errnum != 0) + { + /* Error code we wanted was != 0, i.e. not an expected success */ + die("command \"%.*s\" succeeded - should have failed with errno %d...", + command->first_word_len, command->query, + command->expected_errors.err[0].code.errnum); + } + DBUG_VOID_RETURN; +} + + +void close_connections() +{ + DBUG_ENTER("close_connections"); + for (--next_con; next_con >= connections; --next_con) + { + if (next_con->stmt) + mysql_stmt_close(next_con->stmt); + next_con->stmt= 0; mysql_close(&next_con->mysql); + if (next_con->util_mysql) + mysql_close(next_con->util_mysql); my_free(next_con->name, MYF(MY_ALLOW_ZERO_PTR)); } DBUG_VOID_RETURN; } -static void close_files() +void close_files() { DBUG_ENTER("close_files"); - for (; cur_file != (file_stack-1) ; cur_file--) + for (; cur_file >= file_stack; cur_file--) { DBUG_PRINT("info", ("file_name: %s", cur_file->file_name)); if (cur_file->file && cur_file->file != stdin) @@ -532,25 +672,22 @@ static void close_files() } -static void free_used_memory() +void free_used_memory() { uint i; DBUG_ENTER("free_used_memory"); -#ifndef EMBEDDED_LIBRARY - if (manager) - mysql_manager_close(manager); -#endif - close_cons(); + + close_connections(); close_files(); hash_free(&var_hash); - for (i=0 ; i < q_lines.elements ; i++) + for (i= 0 ; i < q_lines.elements ; i++) { - struct st_query **q= dynamic_element(&q_lines, i, struct st_query**); + struct st_command **q= dynamic_element(&q_lines, i, struct st_command**); my_free((gptr) (*q)->query_buf,MYF(MY_ALLOW_ZERO_PTR)); my_free((gptr) (*q),MYF(0)); } - for (i=0; i < 10; i++) + for (i= 0; i < 10; i++) { if (var_reg[i].alloced_len) my_free(var_reg[i].str_val, MYF(MY_WME)); @@ -559,23 +696,35 @@ static void free_used_memory() my_free(embedded_server_args[--embedded_server_arg_count],MYF(0)); delete_dynamic(&q_lines); dynstr_free(&ds_res); - free_replace(); - free_replace_column(); + dynstr_free(&ds_progress); + free_all_replace(); my_free(pass,MYF(MY_ALLOW_ZERO_PTR)); free_defaults(default_argv); mysql_server_end(); - if (ps_protocol) - ps_free_reg(); + free_re(); +#ifdef __WIN__ + free_tmp_sh_file(); + free_win_path_patterns(); +#endif DBUG_VOID_RETURN; } -static void die(const char *fmt, ...) + +void die(const char *fmt, ...) { va_list args; DBUG_ENTER("die"); + DBUG_PRINT("enter", ("start_lineno: %d", start_lineno)); + + /* Print the error message */ va_start(args, fmt); if (fmt) { +#ifdef DBUG_ON + char buff[256]; + vsnprintf(buff, sizeof(buff), fmt, args); + DBUG_PRINT("error", ("%s", buff)); +#endif fprintf(stderr, "mysqltest: "); if (cur_file && cur_file != file_stack) fprintf(stderr, "In included file \"%s\": ", @@ -587,25 +736,69 @@ static void die(const char *fmt, ...) fflush(stderr); } va_end(args); + + /* Dump the result that has been accumulated so far to .log file */ + if (result_file && ds_res.length) + dump_result_to_log_file(result_file, ds_res.str, ds_res.length); + + /* Clean up and exit */ free_used_memory(); my_end(MY_CHECK_ERROR); + + if (!silent) + printf("not ok\n"); + exit(1); } -/* Note that we will get some memory leaks when calling this! */ -static void abort_not_supported_test() +void abort_not_supported_test(const char *fmt, ...) { + va_list args; + struct st_test_file* err_file= cur_file; DBUG_ENTER("abort_not_supported_test"); - fprintf(stderr, "This test is not supported by this installation\n"); - if (!silent) - printf("skipped\n"); + + /* Print include filestack */ + fprintf(stderr, "The test '%s' is not supported by this installation\n", + file_stack->file_name); + fprintf(stderr, "Detected in file %s at line %d\n", + err_file->file_name, err_file->lineno); + while (err_file != file_stack) + { + err_file--; + fprintf(stderr, "included from %s at line %d\n", + err_file->file_name, err_file->lineno); + } + + /* Print error message */ + va_start(args, fmt); + if (fmt) + { + fprintf(stderr, "reason: "); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + fflush(stderr); + } + va_end(args); + + /* Clean up and exit */ free_used_memory(); my_end(MY_CHECK_ERROR); + + if (!silent) + printf("skipped\n"); + exit(62); } -static void verbose_msg(const char *fmt, ...) + +void abort_not_in_this_version() +{ + die("Not available in mysqltest for MySQL %s", VERSION); +} + + +void verbose_msg(const char *fmt, ...) { va_list args; DBUG_ENTER("verbose_msg"); @@ -613,23 +806,20 @@ static void verbose_msg(const char *fmt, ...) DBUG_VOID_RETURN; va_start(args, fmt); - fprintf(stderr, "mysqltest: "); - if (start_lineno > 0) + if (start_lineno != 0) fprintf(stderr, "At line %u: ", start_lineno); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); + DBUG_VOID_RETURN; } -void init_parser() -{ - parser.current_line= parser.read_lines= 0; - memset(&var_reg, 0, sizeof(var_reg)); -} - +/* + Compare content of the string ds to content of file fname +*/ int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) { @@ -645,10 +835,10 @@ int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) if (!test_if_hard_path(fname)) { strxmov(eval_file, opt_basedir, fname, NullS); - fn_format(eval_file, eval_file,"","",4); + fn_format(eval_file, eval_file, "", "", MY_UNPACK_FILENAME); } else - fn_format(eval_file, fname,"","",4); + fn_format(eval_file, fname, "", "", MY_UNPACK_FILENAME); if (!my_stat(eval_file, &stat_info, MYF(MY_WME))) die(NullS); @@ -656,22 +846,24 @@ int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) { DBUG_PRINT("info",("Size differs: result size: %u file size: %u", ds->length, stat_info.st_size)); + DBUG_PRINT("info",("result: '%s'", ds->str)); DBUG_RETURN(RESULT_LENGTH_MISMATCH); } if (!(tmp = (char*) my_malloc(stat_info.st_size + 1, MYF(MY_WME)))) - die(NullS); + die("Out of memory"); if ((fd = my_open(eval_file, O_RDONLY, MYF(MY_WME))) < 0) - die(NullS); + die("Failed to open file %s", eval_file); if (my_read(fd, (byte*)tmp, stat_info.st_size, MYF(MY_WME|MY_NABP))) - die(NullS); + die("Failed to read from file %s, errno: %d", eval_file, errno); tmp[stat_info.st_size] = 0; - init_dynamic_string(&res_ds, "", 0, 65536); + init_dynamic_string(&res_ds, "", stat_info.st_size+256, 256); if (eval_result) { - do_eval(&res_ds, tmp); - res_ptr = res_ds.str; - if ((res_len = res_ds.length) != ds->length) + do_eval(&res_ds, tmp, tmp + stat_info.st_size, FALSE); + res_ptr= res_ds.str; + res_len= res_ds.length; + if (res_len != ds->length) { res= RESULT_LENGTH_MISMATCH; goto err; @@ -688,51 +880,138 @@ int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) err: if (res && eval_result) - str_to_file(fn_format(eval_file, fname, "", ".eval",2), res_ptr, - res_len); + str_to_file(fn_format(eval_file, fname, "", ".eval", + MY_REPLACE_EXT), + res_ptr, res_len); + dynstr_free(&res_ds); my_free((gptr) tmp, MYF(0)); my_close(fd, MYF(MY_WME)); - dynstr_free(&res_ds); DBUG_RETURN(res); } -static int check_result(DYNAMIC_STRING* ds, const char *fname, - my_bool require_option) + +/* + Check the content of ds against content of file fname + + SYNOPSIS + check_result + ds - content to be checked + fname - name of file to check against + require_option - if set and check fails, the test will be aborted + with the special exit code "not supported test" + + RETURN VALUES + error - the function will not return + +*/ + +void check_result(DYNAMIC_STRING* ds, const char *fname, + my_bool require_option) { - int error= RESULT_OK; int res= dyn_string_cmp(ds, fname); + DBUG_ENTER("check_result"); if (res && require_option) - abort_not_supported_test(); + { + char reason[FN_REFLEN]; + fn_format(reason, fname, "", "", MY_REPLACE_EXT | MY_REPLACE_DIR); + abort_not_supported_test("Test requires: '%s'", reason); + } switch (res) { case RESULT_OK: break; /* ok */ case RESULT_LENGTH_MISMATCH: - verbose_msg("Result length mismatch"); - error= RESULT_LENGTH_MISMATCH; + dump_result_to_reject_file(fname, ds->str, ds->length); + die("Result length mismatch"); break; case RESULT_CONTENT_MISMATCH: - verbose_msg("Result content mismatch"); - error= RESULT_CONTENT_MISMATCH; + dump_result_to_reject_file(fname, ds->str, ds->length); + die("Result content mismatch"); break; default: /* impossible */ die("Unknown error code from dyn_string_cmp()"); } - if (error) - reject_dump(fname, ds->str, ds->length); - return error; + + DBUG_VOID_RETURN; } -VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw, +static byte *get_var_key(const byte* var, uint* len, + my_bool __attribute__((unused)) t) +{ + register char* key; + key = ((VAR*)var)->name; + *len = ((VAR*)var)->name_len; + return (byte*)key; +} + + +VAR *var_init(VAR *v, const char *name, int name_len, const char *val, + int val_len) +{ + int val_alloc_len; + VAR *tmp_var; + if (!name_len && name) + name_len = strlen(name); + if (!val_len && val) + val_len = strlen(val) ; + val_alloc_len = val_len + 16; /* room to grow */ + if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var) + + name_len+1, MYF(MY_WME)))) + die("Out of memory"); + + tmp_var->name = (name) ? (char*) tmp_var + sizeof(*tmp_var) : 0; + tmp_var->alloced = (v == 0); + + if (!(tmp_var->str_val = my_malloc(val_alloc_len+1, MYF(MY_WME)))) + die("Out of memory"); + + memcpy(tmp_var->name, name, name_len); + if (val) + { + memcpy(tmp_var->str_val, val, val_len); + tmp_var->str_val[val_len]= 0; + } + tmp_var->name_len = name_len; + tmp_var->str_val_len = val_len; + tmp_var->alloced_len = val_alloc_len; + tmp_var->int_val = (val) ? atoi(val) : 0; + tmp_var->int_dirty = 0; + tmp_var->env_s = 0; + return tmp_var; +} + + +void var_free(void *v) +{ + my_free(((VAR*) v)->str_val, MYF(MY_WME)); + if (((VAR*)v)->alloced) + my_free((char*) v, MYF(MY_WME)); +} + + +VAR* var_from_env(const char *name, const char *def_val) +{ + const char *tmp; + VAR *v; + if (!(tmp = getenv(name))) + tmp = def_val; + + v = var_init(0, name, strlen(name), tmp, strlen(tmp)); + my_hash_insert(&var_hash, (byte*)v); + return v; +} + + +VAR* var_get(const char *var_name, const char **var_name_end, my_bool raw, my_bool ignore_not_existing) { int digit; - VAR* v; + VAR *v; DBUG_ENTER("var_get"); - DBUG_PRINT("enter",("var_name: %s",var_name)); + DBUG_PRINT("enter", ("var_name: %s",var_name)); if (*var_name != '$') goto err; @@ -751,16 +1030,16 @@ VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw, die("Empty variable"); } length= (uint) (var_name - save_var_name); - if (length >= MAX_VAR_NAME) + if (length >= MAX_VAR_NAME_LENGTH) die("Too long variable name: %s", save_var_name); if (!(v = (VAR*) hash_search(&var_hash, save_var_name, length))) { - char buff[MAX_VAR_NAME+1]; + char buff[MAX_VAR_NAME_LENGTH+1]; strmake(buff, save_var_name, length); v= var_from_env(buff, ""); } - var_name--; /* Point at last character */ + var_name--; /* Point at last character */ } else v = var_reg + digit; @@ -781,7 +1060,8 @@ err: DBUG_RETURN(0); } -static VAR *var_obtain(const char *name, int len) + +VAR *var_obtain(const char *name, int len) { VAR* v; if ((v = (VAR*)hash_search(&var_hash, name, len))) @@ -791,30 +1071,231 @@ static VAR *var_obtain(const char *name, int len) return v; } -int var_set(const char *var_name, const char *var_name_end, - const char *var_val, const char *var_val_end) + +/* + - if variable starts with a $ it is regarded as a local test varable + - if not it is treated as a environment variable, and the corresponding + environment variable will be updated +*/ + +void var_set(const char *var_name, const char *var_name_end, + const char *var_val, const char *var_val_end) { - int digit; - VAR* v; + int digit, env_var= 0; + VAR *v; DBUG_ENTER("var_set"); DBUG_PRINT("enter", ("var_name: '%.*s' = '%.*s' (length: %d)", (int) (var_name_end - var_name), var_name, (int) (var_val_end - var_val), var_val, (int) (var_val_end - var_val))); - if (*var_name++ != '$') - { - var_name--; - die("Variable name in %s does not start with '$'", var_name); - } - digit = *var_name - '0'; + if (*var_name != '$') + env_var= 1; + else + var_name++; + + digit= *var_name - '0'; if (!(digit < 10 && digit >= 0)) { - v = var_obtain(var_name, (uint) (var_name_end - var_name)); + v= var_obtain(var_name, (uint) (var_name_end - var_name)); } else - v = var_reg + digit; - DBUG_RETURN(eval_expr(v, var_val, (const char**)&var_val_end)); + v= var_reg + digit; + + eval_expr(v, var_val, (const char**) &var_val_end); + + if (env_var) + { + char buf[1024], *old_env_s= v->env_s; + if (v->int_dirty) + { + sprintf(v->str_val, "%d", v->int_val); + v->int_dirty= 0; + v->str_val_len= strlen(v->str_val); + } + strxmov(buf, v->name, "=", v->str_val, NullS); + if (!(v->env_s= my_strdup(buf, MYF(MY_WME)))) + die("Out of memory"); + putenv(v->env_s); + my_free((gptr)old_env_s, MYF(MY_ALLOW_ZERO_PTR)); + } + DBUG_VOID_RETURN; +} + +/* + Store an integer (typically the returncode of the last SQL) + statement in the mysqltest builtin variable $mysql_errno, by + simulating of a user statement "let $mysql_errno= " +*/ + +void var_set_errno(int sql_errno) +{ + /* TODO MASV make easier */ + const char *var_name= "$mysql_errno"; + char var_val[21]; + uint length= my_sprintf(var_val, (var_val, "%d", sql_errno)); + var_set(var_name, var_name + 12, var_val, var_val + length); + return; +} + + +/* + Set variable from the result of a query + + SYNOPSIS + var_query_set() + var variable to set from query + query start of query string to execute + query_end end of the query string to execute + + + DESCRIPTION + let @ = `` + + Execute the query and assign the first row of result to var as + a tab separated strings + + Also assign each column of the result set to + variable "$_" + Thus the tab separated output can be read from $ and + and each individual column can be read as $_ + +*/ + +void var_query_set(VAR *var, const char *query, const char** query_end) +{ + char* end = (char*)((query_end && *query_end) ? + *query_end : query + strlen(query)); + MYSQL_RES *res; + MYSQL_ROW row; + MYSQL* mysql = &cur_con->mysql; + DBUG_ENTER("var_query_set"); + LINT_INIT(res); + + while (end > query && *end != '`') + --end; + if (query == end) + die("Syntax error in query, missing '`'"); + ++query; + + if (mysql_real_query(mysql, query, (int)(end - query)) || + !(res = mysql_store_result(mysql))) + { + *end = 0; + die("Error running query '%s': %d %s", query, + mysql_errno(mysql), mysql_error(mysql)); + } + + if ((row = mysql_fetch_row(res)) && row[0]) + { + /* + Concatenate all row results with tab in between to allow us to work + with results from many columns (for example from SHOW VARIABLES) + */ + DYNAMIC_STRING result; + uint i; + ulong *lengths; + char *end; +#ifdef NOT_YET + MYSQL_FIELD *fields= mysql_fetch_fields(res); +#endif + + init_dynamic_string(&result, "", 2048, 2048); + lengths= mysql_fetch_lengths(res); + for (i=0; i < mysql_num_fields(res); i++) + { + if (row[0]) + { +#ifdef NOT_YET + /* Add to _ */ + uint j; + char var_col_name[MAX_VAR_NAME_LENGTH]; + uint length= snprintf(var_col_name, MAX_VAR_NAME_LENGTH, + "$%s_%s", var->name, fields[i].name); + /* Convert characters not allowed in variable names to '_' */ + for (j= 1; j < length; j++) + { + if (!my_isvar(charset_info,var_col_name[j])) + var_col_name[j]= '_'; + } + var_set(var_col_name, var_col_name + length, + row[i], row[i] + lengths[i]); +#endif + /* Add column to tab separated string */ + dynstr_append_mem(&result, row[i], lengths[i]); + } + dynstr_append_mem(&result, "\t", 1); + } + end= result.str + result.length-1; + eval_expr(var, result.str, (const char**) &end); + dynstr_free(&result); + } + else + eval_expr(var, "", 0); + + mysql_free_result(res); + DBUG_VOID_RETURN; +} + + +void var_copy(VAR *dest, VAR *src) +{ + dest->int_val= src->int_val; + dest->int_dirty= src->int_dirty; + + /* Alloc/realloc data for str_val in dest */ + if (dest->alloced_len < src->alloced_len && + !(dest->str_val= dest->str_val + ? my_realloc(dest->str_val, src->alloced_len, MYF(MY_WME)) + : my_malloc(src->alloced_len, MYF(MY_WME)))) + die("Out of memory"); + else + dest->alloced_len= src->alloced_len; + + /* Copy str_val data to dest */ + dest->str_val_len= src->str_val_len; + if (src->str_val_len) + memcpy(dest->str_val, src->str_val, src->str_val_len); +} + + +void eval_expr(VAR *v, const char *p, const char **p_end) +{ + static int MIN_VAR_ALLOC= 32; /* MASV why 32? */ + VAR *vp; + if (*p == '$') + { + if ((vp= var_get(p, p_end, 0, 0))) + { + var_copy(v, vp); + return; + } + } + else if (*p == '`') + { + var_query_set(v, p, p_end); + } + else + { + int new_val_len = (p_end && *p_end) ? + (int) (*p_end - p) : (int) strlen(p); + if (new_val_len + 1 >= v->alloced_len) + { + v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ? + MIN_VAR_ALLOC : new_val_len + 1; + if (!(v->str_val = + v->str_val ? my_realloc(v->str_val, v->alloced_len+1, + MYF(MY_WME)) : + my_malloc(v->alloced_len+1, MYF(MY_WME)))) + die("Out of memory"); + } + v->str_val_len = new_val_len; + memcpy(v->str_val, p, new_val_len); + v->str_val[new_val_len] = 0; + v->int_val=atoi(p); + v->int_dirty=0; + } + return; } @@ -828,7 +1309,7 @@ int open_file(const char *name) strxmov(buff, opt_basedir, name, NullS); name=buff; } - fn_format(buff,name,"","",4); + fn_format(buff, name, "", "", MY_UNPACK_FILENAME); if (cur_file == file_stack_end) die("Source directives are nesting too deep"); @@ -839,43 +1320,640 @@ int open_file(const char *name) die("Could not open file %s", buff); } cur_file->file_name= my_strdup(buff, MYF(MY_FAE)); - *++lineno=1; + cur_file->lineno=1; DBUG_RETURN(0); } /* - Check for unexpected "junk" after the end of query - This is normally caused by missing delimiters + Source and execute the given file + + SYNOPSIS + do_source() + query called command + + DESCRIPTION + source + + Open the file and execute it + */ -int check_eol_junk(const char *eol) +void do_source(struct st_command *command) { - const char *p= eol; - DBUG_ENTER("check_eol_junk"); - DBUG_PRINT("enter", ("eol: %s", eol)); - /* Remove all spacing chars except new line */ - while (*p && my_isspace(charset_info, *p) && (*p != '\n')) - p++; + DYNAMIC_STRING ds_filename; + const struct command_arg source_args[] = { + "filename", ARG_STRING, TRUE, &ds_filename, "File to source" + }; + DBUG_ENTER("do_source"); - /* Check for extra delimiter */ - if (*p && !strncmp(p, delimiter, delimiter_length)) - die("Extra delimiter \"%s\" found", delimiter); + check_command_args(command, command->first_argument, source_args, + sizeof(source_args)/sizeof(struct command_arg), + ' '); - /* Allow trailing # comment */ - if (*p && *p != '#') + /* + If this file has already been sourced, don't source it again. + It's already available in the q_lines cache. + */ + if (parser.current_line < (parser.read_lines - 1)) + ; /* Do nothing */ + else { - if (*p == '\n') - die("Missing delimiter"); - die("End of line junk detected: \"%s\"", p); + DBUG_PRINT("info", ("sourcing file: %s", ds_filename.str)); + open_file(ds_filename.str); } - DBUG_RETURN(0); + + dynstr_free(&ds_filename); + return; } -/* ugly long name, but we are following the convention */ -int do_wait_for_slave_to_stop(struct st_query *q __attribute__((unused))) +#ifdef __WIN__ +/* Variables used for temuprary sh files used for emulating Unix on Windows */ +char tmp_sh_name[64], tmp_sh_cmd[70]; + +void init_tmp_sh_file() { + /* Format a name for the tmp sh file that is unique for this process */ + my_snprintf(tmp_sh_name, sizeof(tmp_sh_name), "tmp_%d.sh", getpid()); + /* Format the command to execute in order to run the script */ + my_snprintf(tmp_sh_cmd, sizeof(tmp_sh_cmd), "sh %s", tmp_sh_name); +} + + +void free_tmp_sh_file() +{ + my_delete(tmp_sh_name, MYF(0)); +} +#endif + + +FILE* my_popen(DYNAMIC_STRING *ds_cmd, const char *mode) +{ +#ifdef __WIN__ + /* Dump the command into a sh script file and execute with popen */ + str_to_file(tmp_sh_name, ds_cmd->str, ds_cmd->length); + return popen(tmp_sh_cmd, mode); +#else + return popen(ds_cmd->str, mode); +#endif +} + + +/* + Execute given command. + + SYNOPSIS + do_exec() + query called command + + DESCRIPTION + exec + + Execute the text between exec and end of line in a subprocess. + The error code returned from the subprocess is checked against the + expected error array, previously set with the --error command. + It can thus be used to execute a command that shall fail. + + NOTE + Although mysqltest is executed from cygwin shell, the command will be + executed in "cmd.exe". Thus commands like "rm" etc can NOT be used, use + system for those commands. +*/ + +void do_exec(struct st_command *command) +{ + int error; + char buf[1024]; + FILE *res_file; + char *cmd= command->first_argument; + DYNAMIC_STRING ds_cmd; + DBUG_ENTER("do_exec"); + DBUG_PRINT("enter", ("cmd: '%s'", cmd)); + + while (*cmd && my_isspace(charset_info, *cmd)) + cmd++; + if (!*cmd) + die("Missing argument in exec"); + command->last_argument= command->end; + + init_dynamic_string(&ds_cmd, 0, command->query_len+256, 256); + /* Eval the command, thus replacing all environment variables */ + do_eval(&ds_cmd, cmd, command->end, TRUE); + cmd= ds_cmd.str; + + DBUG_PRINT("info", ("Executing '%s' as '%s'", + command->first_argument, cmd)); + + if (!(res_file= my_popen(&ds_cmd, "r")) && command->abort_on_error) + die("popen(\"%s\", \"r\") failed", command->first_argument); + + while (fgets(buf, sizeof(buf), res_file)) + { + if (disable_result_log) + { + buf[strlen(buf)-1]=0; + DBUG_PRINT("exec_result",("%s", buf)); + } + else + { + replace_dynstr_append(&ds_res, buf); + } + } + error= pclose(res_file); + if (error > 0) + { + uint status= WEXITSTATUS(error), i; + my_bool ok= 0; + + if (command->abort_on_error) + die("command \"%s\" failed", command->first_argument); + + DBUG_PRINT("info", + ("error: %d, status: %d", error, status)); + for (i= 0; i < command->expected_errors.count; i++) + { + DBUG_PRINT("info", ("expected error: %d", + command->expected_errors.err[i].code.errnum)); + if ((command->expected_errors.err[i].type == ERR_ERRNO) && + (command->expected_errors.err[i].code.errnum == status)) + { + ok= 1; + DBUG_PRINT("info", ("command \"%s\" failed with expected error: %d", + command->first_argument, status)); + } + } + if (!ok) + die("command \"%s\" failed with wrong error: %d", + command->first_argument, status); + } + else if (command->expected_errors.err[0].type == ERR_ERRNO && + command->expected_errors.err[0].code.errnum != 0) + { + /* Error code we wanted was != 0, i.e. not an expected success */ + die("command \"%s\" succeeded - should have failed with errno %d...", + command->first_argument, command->expected_errors.err[0].code.errnum); + } + + dynstr_free(&ds_cmd); + DBUG_VOID_RETURN; +} + +enum enum_operator +{ + DO_DEC, + DO_INC +}; + + +/* + Decrease or increase the value of a variable + + SYNOPSIS + do_modify_var() + query called command + operator operation to perform on the var + + DESCRIPTION + dec $var_name + inc $var_name + +*/ + +int do_modify_var(struct st_command *command, + enum enum_operator operator) +{ + const char *p= command->first_argument; + VAR* v; + if (!*p) + die("Missing argument to %.*s", command->first_word_len, command->query); + if (*p != '$') + die("The argument to %.*s must be a variable (start with $)", + command->first_word_len, command->query); + v= var_get(p, &p, 1, 0); + switch (operator) { + case DO_DEC: + v->int_val--; + break; + case DO_INC: + v->int_val++; + break; + default: + die("Invalid operator to do_modify_var"); + break; + } + v->int_dirty= 1; + command->last_argument= (char*)++p; + return 0; +} + + +/* + Wrapper for 'system' function + + NOTE + If mysqltest is executed from cygwin shell, the command will be + executed in the "windows command interpreter" cmd.exe and we prepend "sh" + to make it be executed by cygwins "bash". Thus commands like "rm", + "mkdir" as well as shellscripts can executed by "system" in Windows. + +*/ + +int my_system(DYNAMIC_STRING* ds_cmd) +{ +#ifdef __WIN__ + /* Dump the command into a sh script file and execute with system */ + str_to_file(tmp_sh_name, ds_cmd->str, ds_cmd->length); + return system(tmp_sh_cmd); +#else + return system(ds_cmd->str); +#endif +} + + +/* + SYNOPSIS + do_system + command called command + + DESCRIPTION + system + + Eval the query to expand any $variables in the command. + Execute the command with the "system" command. + +*/ + +void do_system(struct st_command *command) +{ + DYNAMIC_STRING ds_cmd; + DBUG_ENTER("do_system"); + + if (strlen(command->first_argument) == 0) + die("Missing arguments to system, nothing to do!"); + + init_dynamic_string(&ds_cmd, 0, command->query_len + 64, 256); + + /* Eval the system command, thus replacing all environment variables */ + do_eval(&ds_cmd, command->first_argument, command->end, TRUE); + + DBUG_PRINT("info", ("running system command '%s' as '%s'", + command->first_argument, ds_cmd.str)); + if (my_system(&ds_cmd)) + { + if (command->abort_on_error) + die("system command '%s' failed", command->first_argument); + + /* If ! abort_on_error, log message and continue */ + dynstr_append(&ds_res, "system command '"); + replace_dynstr_append(&ds_res, command->first_argument); + dynstr_append(&ds_res, "' failed\n"); + } + + command->last_argument= command->end; + dynstr_free(&ds_cmd); + DBUG_VOID_RETURN; +} + + +/* + SYNOPSIS + do_remove_file + command called command + + DESCRIPTION + remove_file + Remove the file +*/ + +void do_remove_file(struct st_command *command) +{ + int error; + DYNAMIC_STRING ds_filename; + const struct command_arg rm_args[] = { + "filename", ARG_STRING, TRUE, &ds_filename, "File to delete" + }; + DBUG_ENTER("do_remove_file"); + + check_command_args(command, command->first_argument, + rm_args, sizeof(rm_args)/sizeof(struct command_arg), + ' '); + + DBUG_PRINT("info", ("removing file: %s", ds_filename.str)); + error= my_delete(ds_filename.str, MYF(0)) != 0; + handle_command_error(command, error); + dynstr_free(&ds_filename); + DBUG_VOID_RETURN; +} + + +/* + SYNOPSIS + do_copy_file + command command handle + + DESCRIPTION + copy_file + Copy to + + NOTE! Will fail if exists +*/ + +void do_copy_file(struct st_command *command) +{ + int error; + DYNAMIC_STRING ds_from_file; + DYNAMIC_STRING ds_to_file; + const struct command_arg copy_file_args[] = { + "from_file", ARG_STRING, TRUE, &ds_from_file, "Filename to copy from", + "to_file", ARG_STRING, TRUE, &ds_to_file, "Filename to copy to" + }; + DBUG_ENTER("do_copy_file"); + + check_command_args(command, command->first_argument, + copy_file_args, + sizeof(copy_file_args)/sizeof(struct command_arg), + ' '); + + DBUG_PRINT("info", ("Copy %s to %s", ds_from_file.str, ds_to_file.str)); + error= (my_copy(ds_from_file.str, ds_to_file.str, + MYF(MY_DONT_OVERWRITE_FILE)) != 0); + handle_command_error(command, error); + dynstr_free(&ds_from_file); + dynstr_free(&ds_to_file); + DBUG_VOID_RETURN; +} + + +/* + SYNOPSIS + do_file_exists + command called command + + DESCRIPTION + fiile_exist + Check if file exists +*/ + +void do_file_exist(struct st_command *command) +{ + int error; + DYNAMIC_STRING ds_filename; + const struct command_arg file_exist_args[] = { + "filename", ARG_STRING, TRUE, &ds_filename, "File to check if it exist" + }; + DBUG_ENTER("do_file_exist"); + + check_command_args(command, command->first_argument, + file_exist_args, + sizeof(file_exist_args)/sizeof(struct command_arg), + ' '); + + DBUG_PRINT("info", ("Checking for existence of file: %s", ds_filename.str)); + error= (access(ds_filename.str, F_OK) != 0); + handle_command_error(command, error); + dynstr_free(&ds_filename); + DBUG_VOID_RETURN; +} + + +/* + Read characters from line buffer or file. This is needed to allow + my_ungetc() to buffer MAX_DELIMITER_LENGTH characters for a file + + NOTE: + This works as long as one doesn't change files (with 'source file_name') + when there is things pushed into the buffer. This should however not + happen for any tests in the test suite. +*/ + +int my_getc(FILE *file) +{ + if (line_buffer_pos == line_buffer) + return fgetc(file); + return *--line_buffer_pos; +} + + +void my_ungetc(int c) +{ + *line_buffer_pos++= (char) c; +} + + +void read_until_delimiter(DYNAMIC_STRING *ds, + DYNAMIC_STRING *ds_delimiter) +{ + int c; + DBUG_ENTER("read_until_delimiter"); + DBUG_PRINT("enter", ("delimiter: %s, length: %d", + ds_delimiter->str, ds_delimiter->length)); + + if (ds_delimiter->length > MAX_DELIMITER_LENGTH) + die("Max delimiter length(%d) exceeded", MAX_DELIMITER_LENGTH); + + /* Read from file until delimiter is found */ + while (1) + { + c= my_getc(cur_file->file); + + if (c == '\n') + cur_file->lineno++; + + if (feof(cur_file->file)) + die("End of file encountered before '%s' delimiter was found", + ds_delimiter->str); + + if (match_delimiter(c, ds_delimiter->str, ds_delimiter->length)) + { + DBUG_PRINT("exit", ("Found delimiter '%s'", ds_delimiter->str)); + break; + } + dynstr_append_mem(ds, (const char*)&c, 1); + } + DBUG_PRINT("exit", ("ds: %s", ds->str)); + DBUG_VOID_RETURN; +} + + +/* + SYNOPSIS + do_write_file + command called command + + DESCRIPTION + write_file []; + + <...> + < what to write line n> + EOF + + --write_file ; + + <...> + < what to write line n> + EOF + + Write everything between the "write_file" command and 'delimiter' + to "file_name" + + NOTE! Overwrites existing file + + Default is EOF + +*/ + +void do_write_file(struct st_command *command) +{ + DYNAMIC_STRING ds_content; + DYNAMIC_STRING ds_filename; + DYNAMIC_STRING ds_delimiter; + const struct command_arg write_file_args[] = { + "filename", ARG_STRING, TRUE, &ds_filename, "File to write to", + "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until" + }; + DBUG_ENTER("do_write_file"); + + check_command_args(command, + command->first_argument, + write_file_args, + sizeof(write_file_args)/sizeof(struct command_arg), + ' '); + + /* If no delimiter was provided, use EOF */ + if (ds_delimiter.length == 0) + dynstr_set(&ds_delimiter, "EOF"); + + init_dynamic_string(&ds_content, "", 1024, 1024); + read_until_delimiter(&ds_content, &ds_delimiter); + DBUG_PRINT("info", ("Writing to file: %s", ds_filename.str)); + str_to_file(ds_filename.str, ds_content.str, ds_content.length); + dynstr_free(&ds_content); + dynstr_free(&ds_filename); + dynstr_free(&ds_delimiter); + DBUG_VOID_RETURN; +} + + +/* + SYNOPSIS + do_perl + command command handle + + DESCRIPTION + perl []; + + <...> + + EOF + + Execute everything after "perl" until as perl. + Useful for doing more advanced things + but still being able to execute it on all platforms. + + Default is EOF +*/ + +void do_perl(struct st_command *command) +{ + int error; + char buf[FN_REFLEN]; + FILE *res_file; + DYNAMIC_STRING ds_script; + DYNAMIC_STRING ds_delimiter; + const struct command_arg perl_args[] = { + "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until" + }; + DBUG_ENTER("do_perl"); + + check_command_args(command, + command->first_argument, + perl_args, + sizeof(perl_args)/sizeof(struct command_arg), + ' '); + + /* If no delimiter was provided, use EOF */ + if (ds_delimiter.length == 0) + dynstr_set(&ds_delimiter, "EOF"); + + init_dynamic_string(&ds_script, "", 1024, 1024); + read_until_delimiter(&ds_script, &ds_delimiter); + + DBUG_PRINT("info", ("Executing perl: %s", ds_script.str)); + + /* Format a name for a tmp .pl file that is unique for this process */ + my_snprintf(buf, sizeof(buf), "%s/tmp/tmp_%d.pl", + getenv("MYSQLTEST_VARDIR"), getpid()); + str_to_file(buf, ds_script.str, ds_script.length); + + /* Format the perl command */ + my_snprintf(buf, sizeof(buf), "perl %s/tmp/tmp_%d.pl", + getenv("MYSQLTEST_VARDIR"), getpid()); + + if (!(res_file= popen(buf, "r")) && command->abort_on_error) + die("popen(\"%s\", \"r\") failed", buf); + + while (fgets(buf, sizeof(buf), res_file)) + { + if (disable_result_log) + { + buf[strlen(buf)-1]=0; + DBUG_PRINT("exec_result",("%s", buf)); + } + else + { + replace_dynstr_append(&ds_res, buf); + } + } + error= pclose(res_file); + handle_command_error(command, WEXITSTATUS(error)); + dynstr_free(&ds_script); + dynstr_free(&ds_delimiter); + DBUG_VOID_RETURN; +} + + +/* + Print the content between echo and to result file. + Evaluate all variables in the string before printing, allow + for variable names to be escaped using \ + + SYNOPSIS + do_echo() + command called command + + DESCRIPTION + echo text + Print the text after echo until end of command to result file + + echo $ + Print the content of the variable to result file + + echo Some text $ + Print "Some text" plus the content of the variable to + result file + + echo Some text \$ + Print "Some text" plus $ to result file +*/ + +int do_echo(struct st_command *command) +{ + DYNAMIC_STRING ds_echo; + + init_dynamic_string(&ds_echo, "", command->query_len, 256); + do_eval(&ds_echo, command->first_argument, command->end, FALSE); + dynstr_append_mem(&ds_res, ds_echo.str, ds_echo.length); + dynstr_append_mem(&ds_res, "\n", 1); + dynstr_free(&ds_echo); + command->last_argument= command->end; + return(0); +} + + +void do_wait_for_slave_to_stop(struct st_command *c __attribute__((unused))) +{ + static int SLAVE_POLL_INTERVAL= 300000; MYSQL* mysql = &cur_con->mysql; for (;;) { @@ -899,454 +1977,15 @@ int do_wait_for_slave_to_stop(struct st_query *q __attribute__((unused))) break; my_sleep(SLAVE_POLL_INTERVAL); } - return 0; -} - -int do_require_manager(struct st_query *query __attribute__((unused)) ) -{ - if (!manager) - abort_not_supported_test(); - return 0; -} - -#ifndef EMBEDDED_LIBRARY -int do_server_start(struct st_query *q) -{ - return do_server_op(q, "start"); -} - -int do_server_stop(struct st_query *q) -{ - return do_server_op(q, "stop"); -} - -int do_server_op(struct st_query *q, const char *op) -{ - char *p= q->first_argument; - char com_buf[256], *com_p; - if (!manager) - { - die("Manager is not initialized, manager commands are not possible"); - } - com_p= strmov(com_buf,op); - com_p= strmov(com_p,"_exec "); - if (!*p) - die("Missing server name in server_%s", op); - while (*p && !my_isspace(charset_info, *p)) - *com_p++= *p++; - *com_p++= ' '; - com_p= int10_to_str(manager_wait_timeout, com_p, 10); - *com_p++= '\n'; - *com_p= 0; - if (mysql_manager_command(manager, com_buf, (int)(com_p-com_buf))) - die("Error in command: %s(%d)", manager->last_error, manager->last_errno); - while (!manager->eof) - { - if (mysql_manager_fetch_line(manager, com_buf, sizeof(com_buf))) - die("Error fetching result line: %s(%d)", manager->last_error, - manager->last_errno); - } - - q->last_argument= p; - return 0; -} -#endif - - -/* - Source and execute the given file - - SYNOPSIS - do_source() - query called command - - DESCRIPTION - source - - Open the file and execute it - -*/ - -int do_source(struct st_query *query) -{ - char *p= query->first_argument, *name; - if (!*p) - die("Missing file name in source"); - name= p; - while (*p && !my_isspace(charset_info,*p)) - p++; - if (*p) - *p++= 0; - query->last_argument= p; - /* - If this file has already been sourced, don't source it again. - It's already available in the q_lines cache. - */ - if (parser.current_line < (parser.read_lines - 1)) - return 0; - return open_file(name); + return; } -/* - Execute given command. - - SYNOPSIS - do_exec() - query called command - - DESCRIPTION - exec - - Execute the text between exec and end of line in a subprocess. - The error code returned from the subprocess is checked against the - expected error array, previously set with the --error command. - It can thus be used to execute a command that shall fail. - -*/ - -static void do_exec(struct st_query *query) +void do_sync_with_master2(long offset) { - int error; - DYNAMIC_STRING *ds= NULL; - DYNAMIC_STRING ds_tmp; - char buf[1024]; - FILE *res_file; - char *cmd= query->first_argument; - DBUG_ENTER("do_exec"); - - while (*cmd && my_isspace(charset_info, *cmd)) - cmd++; - if (!*cmd) - die("Missing argument in exec"); - query->last_argument= query->end; - - DBUG_PRINT("info", ("Executing '%s'", cmd)); - - if (!(res_file= popen(cmd, "r")) && query->abort_on_error) - die("popen(\"%s\", \"r\") failed", cmd); - - if (disable_result_log) - { - while (fgets(buf, sizeof(buf), res_file)) - { - buf[strlen(buf)-1]=0; - DBUG_PRINT("exec_result",("%s", buf)); - } - } - else - { - if (query->record_file[0]) - { - init_dynamic_string(&ds_tmp, "", 16384, 65536); - ds= &ds_tmp; - } - else - ds= &ds_res; - - while (fgets(buf, sizeof(buf), res_file)) - replace_dynstr_append_mem(ds, buf, strlen(buf)); - } - error= pclose(res_file); - if (error != 0) - { - uint status= WEXITSTATUS(error), i; - my_bool ok= 0; - - if (query->abort_on_error) - die("command \"%s\" failed", cmd); - - DBUG_PRINT("info", - ("error: %d, status: %d", error, status)); - for (i= 0; i < query->expected_errors; i++) - { - DBUG_PRINT("info", ("expected error: %d", - query->expected_errno[i].code.errnum)); - if ((query->expected_errno[i].type == ERR_ERRNO) && - (query->expected_errno[i].code.errnum == status)) - { - ok= 1; - DBUG_PRINT("info", ("command \"%s\" failed with expected error: %d", - cmd, status)); - } - } - if (!ok) - die("command \"%s\" failed with wrong error: %d", - cmd, status); - } - else if (query->expected_errno[0].type == ERR_ERRNO && - query->expected_errno[0].code.errnum != 0) - { - /* Error code we wanted was != 0, i.e. not an expected success */ - die("command \"%s\" succeeded - should have failed with errno %d...", - cmd, query->expected_errno[0].code.errnum); - } - - if (!disable_result_log) - { - if (glob_replace) - free_replace(); - - if (record) - { - if (!query->record_file[0] && !result_file) - die("Missing result file"); - if (!result_file) - str_to_file(query->record_file, ds->str, ds->length); - } - else if (query->record_file[0]) - { - error= check_result(ds, query->record_file, query->require_file); - } - if (ds == &ds_tmp) - dynstr_free(&ds_tmp); - } -} - - -int var_query_set(VAR* v, const char *p, const char** p_end) -{ - char* end = (char*)((p_end && *p_end) ? *p_end : p + strlen(p)); MYSQL_RES *res; MYSQL_ROW row; - MYSQL* mysql = &cur_con->mysql; - LINT_INIT(res); - - while (end > p && *end != '`') - --end; - if (p == end) - die("Syntax error in query, missing '`'"); - ++p; - - if (mysql_real_query(mysql, p, (int)(end - p)) || - !(res = mysql_store_result(mysql))) - { - *end = 0; - die("Error running query '%s': %s", p, mysql_error(mysql)); - } - - if ((row = mysql_fetch_row(res)) && row[0]) - { - /* - Concatenate all row results with tab in between to allow us to work - with results from many columns (for example from SHOW VARIABLES) - */ - DYNAMIC_STRING result; - uint i; - ulong *lengths; - char *end; - - init_dynamic_string(&result, "", 16384, 65536); - lengths= mysql_fetch_lengths(res); - for (i=0; i < mysql_num_fields(res); i++) - { - if (row[0]) - dynstr_append_mem(&result, row[i], lengths[i]); - dynstr_append_mem(&result, "\t", 1); - } - end= result.str + result.length-1; - eval_expr(v, result.str, (const char**) &end); - dynstr_free(&result); - } - else - eval_expr(v, "", 0); - - mysql_free_result(res); - return 0; -} - -void var_copy(VAR *dest, VAR *src) -{ - dest->int_val= src->int_val; - dest->int_dirty= src->int_dirty; - - /* Alloc/realloc data for str_val in dest */ - if (dest->alloced_len < src->alloced_len && - !(dest->str_val= dest->str_val - ? my_realloc(dest->str_val, src->alloced_len, MYF(MY_WME)) - : my_malloc(src->alloced_len, MYF(MY_WME)))) - die("Out of memory"); - else - dest->alloced_len= src->alloced_len; - - /* Copy str_val data to dest */ - dest->str_val_len= src->str_val_len; - if (src->str_val_len) - memcpy(dest->str_val, src->str_val, src->str_val_len); -} - -int eval_expr(VAR* v, const char *p, const char** p_end) -{ - VAR* vp; - if (*p == '$') - { - if ((vp = var_get(p,p_end,0,0))) - { - var_copy(v, vp); - return 0; - } - } - else if (*p == '`') - { - return var_query_set(v, p, p_end); - } - else - { - int new_val_len = (p_end && *p_end) ? - (int) (*p_end - p) : (int) strlen(p); - if (new_val_len + 1 >= v->alloced_len) - { - v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ? - MIN_VAR_ALLOC : new_val_len + 1; - if (!(v->str_val = - v->str_val ? my_realloc(v->str_val, v->alloced_len+1, - MYF(MY_WME)) : - my_malloc(v->alloced_len+1, MYF(MY_WME)))) - die("Out of memory"); - } - v->str_val_len = new_val_len; - memcpy(v->str_val, p, new_val_len); - v->str_val[new_val_len] = 0; - v->int_val=atoi(p); - v->int_dirty=0; - return 0; - } - - die("Invalid expr: %s", p); - return 1; -} - - -enum enum_operator -{ - DO_DEC, - DO_INC -}; - -/* - Decrease or increase the value of a variable - - SYNOPSIS - do_modify_var() - query called command - name human readable name of operator - operator operation to perform on the var - - DESCRIPTION - dec $var_name - inc $var_name - -*/ - -int do_modify_var(struct st_query *query, const char *name, - enum enum_operator operator) -{ - const char *p= query->first_argument; - VAR* v; - if (!*p) - die("Missing arguments to %s", name); - if (*p != '$') - die("First argument to %s must be a variable (start with $)", name); - v= var_get(p, &p, 1, 0); - switch (operator){ - case DO_DEC: - v->int_val--; - break; - case DO_INC: - v->int_val++; - break; - default: - die("Invalid operator to do_operator"); - break; - } - v->int_dirty= 1; - query->last_argument= (char*)++p; - return 0; -} - - -int do_system(struct st_query *q) -{ - char *p=q->first_argument; - VAR v; - var_init(&v, 0, 0, 0, 0); - eval_expr(&v, p, 0); /* NULL terminated */ - if (v.str_val_len) - { - char expr_buf[1024]; - if ((uint)v.str_val_len > sizeof(expr_buf) - 1) - v.str_val_len = sizeof(expr_buf) - 1; - memcpy(expr_buf, v.str_val, v.str_val_len); - expr_buf[v.str_val_len] = 0; - DBUG_PRINT("info", ("running system command '%s'", expr_buf)); - if (system(expr_buf)) - { - if (q->abort_on_error) - die("system command '%s' failed", expr_buf); - /* If ! abort_on_error, display message and continue */ - verbose_msg("system command '%s' failed", expr_buf); - } - } - else - die("Missing arguments to system, nothing to do!"); - var_free(&v); - q->last_argument= q->end; - return 0; -} - - -/* - Print the content between echo and to result file. - If content is a variable, the variable value will be retrieved - - SYNOPSIS - do_echo() - q called command - - DESCRIPTION - Usage 1: - echo text - Print the text after echo until end of command to result file - - Usage 2: - echo $ - Print the content of the variable to result file - -*/ - -int do_echo(struct st_query *q) -{ - char *p= q->first_argument; - DYNAMIC_STRING *ds; - DYNAMIC_STRING ds_tmp; - VAR v; - var_init(&v,0,0,0,0); - - if (q->record_file[0]) - { - init_dynamic_string(&ds_tmp, "", 256, 512); - ds= &ds_tmp; - } - else - ds= &ds_res; - - eval_expr(&v, p, 0); /* NULL terminated */ - if (v.str_val_len) - dynstr_append_mem(ds, v.str_val, v.str_val_len); - dynstr_append_mem(ds, "\n", 1); - var_free(&v); - if (ds == &ds_tmp) - dynstr_free(&ds_tmp); - q->last_argument= q->end; - return 0; -} - - -int do_sync_with_master2(long offset) -{ - MYSQL_RES* res; - MYSQL_ROW row; - MYSQL* mysql= &cur_con->mysql; + MYSQL *mysql= &cur_con->mysql; char query_buf[FN_REFLEN+128]; int tries= 0; int rpl_parse; @@ -1362,10 +2001,10 @@ int do_sync_with_master2(long offset) wait_for_position: if (mysql_query(mysql, query_buf)) - die("failed in %s: %d: %s", query_buf, mysql_errno(mysql), + die("failed in '%s': %d: %s", query_buf, mysql_errno(mysql), mysql_error(mysql)); - if (!(last_result= res= mysql_store_result(mysql))) + if (!(res= mysql_store_result(mysql))) die("mysql_store_result() returned NULL for '%s'", query_buf); if (!(row= mysql_fetch_row(res))) die("empty result in %s", query_buf); @@ -1375,24 +2014,24 @@ wait_for_position: It may be that the slave SQL thread has not started yet, though START SLAVE has been issued ? */ - if (tries++ == 3) + if (tries++ == 30) die("could not sync with master ('%s' returned NULL)", query_buf); - sleep(1); /* So at most we will wait 3 seconds and make 4 tries */ + sleep(1); /* So at most we will wait 30 seconds and make 31 tries */ mysql_free_result(res); goto wait_for_position; } mysql_free_result(res); - last_result=0; if (rpl_parse) mysql_enable_rpl_parse(mysql); - return 0; + return; } -int do_sync_with_master(struct st_query *query) + +void do_sync_with_master(struct st_command *command) { long offset= 0; - char *p= query->first_argument; + char *p= command->first_argument; const char *offset_start= p; if (*offset_start) { @@ -1401,33 +2040,177 @@ int do_sync_with_master(struct st_query *query) if(*p && !my_isspace(charset_info, *p)) die("Invalid integer argument \"%s\"", offset_start); - query->last_argument= p; + command->last_argument= p; } - return do_sync_with_master2(offset); + do_sync_with_master2(offset); + return; } + +/* + when ndb binlog is on, this call will wait until last updated epoch + (locally in the mysqld) has been received into the binlog +*/ int do_save_master_pos() { - MYSQL_RES* res; + MYSQL_RES *res; MYSQL_ROW row; - MYSQL* mysql = &cur_con->mysql; + MYSQL *mysql = &cur_con->mysql; const char *query; int rpl_parse; rpl_parse = mysql_rpl_parse_enabled(mysql); mysql_disable_rpl_parse(mysql); +#ifdef HAVE_NDB_BINLOG + /* + Wait for ndb binlog to be up-to-date with all changes + done on the local mysql server + */ + { + ulong have_ndbcluster; + if (mysql_query(mysql, query= "show variables like 'have_ndbcluster'")) + die("'%s' failed: %d %s", query, + mysql_errno(mysql), mysql_error(mysql)); + if (!(res= mysql_store_result(mysql))) + die("mysql_store_result() returned NULL for '%s'", query); + if (!(row= mysql_fetch_row(res))) + die("Query '%s' returned empty result", query); + + have_ndbcluster= strcmp("YES", row[1]) == 0; + mysql_free_result(res); + + if (have_ndbcluster) + { + ulonglong start_epoch= 0, applied_epoch= 0, + latest_epoch=0, latest_trans_epoch=0, + latest_handled_binlog_epoch= 0, latest_received_binlog_epoch= 0, + latest_applied_binlog_epoch= 0; + int count= 0; + int do_continue= 1; + while (do_continue) + { + const char binlog[]= "binlog"; + const char latest_epoch_str[]= + "latest_epoch="; + const char latest_trans_epoch_str[]= + "latest_trans_epoch="; + const char latest_received_binlog_epoch_str[]= + "latest_received_binlog_epoch"; + const char latest_handled_binlog_epoch_str[]= + "latest_handled_binlog_epoch="; + const char latest_applied_binlog_epoch_str[]= + "latest_applied_binlog_epoch="; + if (count) + sleep(1); + if (mysql_query(mysql, query= "show engine ndb status")) + die("failed in '%s': %d %s", query, + mysql_errno(mysql), mysql_error(mysql)); + if (!(res= mysql_store_result(mysql))) + die("mysql_store_result() returned NULL for '%s'", query); + while ((row= mysql_fetch_row(res))) + { + if (strcmp(row[1], binlog) == 0) + { + const char *status= row[2]; + + /* latest_epoch */ + while (*status && strncmp(status, latest_epoch_str, + sizeof(latest_epoch_str)-1)) + status++; + if (*status) + { + status+= sizeof(latest_epoch_str)-1; + latest_epoch= strtoull(status, (char**) 0, 10); + } + else + die("result does not contain '%s' in '%s'", + latest_epoch_str, query); + /* latest_trans_epoch */ + while (*status && strncmp(status, latest_trans_epoch_str, + sizeof(latest_trans_epoch_str)-1)) + status++; + if (*status) + { + status+= sizeof(latest_trans_epoch_str)-1; + latest_trans_epoch= strtoull(status, (char**) 0, 10); + } + else + die("result does not contain '%s' in '%s'", + latest_trans_epoch_str, query); + /* latest_received_binlog_epoch */ + while (*status && + strncmp(status, latest_received_binlog_epoch_str, + sizeof(latest_received_binlog_epoch_str)-1)) + status++; + if (*status) + { + status+= sizeof(latest_received_binlog_epoch_str)-1; + latest_received_binlog_epoch= strtoull(status, (char**) 0, 10); + } + else + die("result does not contain '%s' in '%s'", + latest_received_binlog_epoch_str, query); + /* latest_handled_binlog */ + while (*status && + strncmp(status, latest_handled_binlog_epoch_str, + sizeof(latest_handled_binlog_epoch_str)-1)) + status++; + if (*status) + { + status+= sizeof(latest_handled_binlog_epoch_str)-1; + latest_handled_binlog_epoch= strtoull(status, (char**) 0, 10); + } + else + die("result does not contain '%s' in '%s'", + latest_handled_binlog_epoch_str, query); + /* latest_applied_binlog_epoch */ + while (*status && + strncmp(status, latest_applied_binlog_epoch_str, + sizeof(latest_applied_binlog_epoch_str)-1)) + status++; + if (*status) + { + status+= sizeof(latest_applied_binlog_epoch_str)-1; + latest_applied_binlog_epoch= strtoull(status, (char**) 0, 10); + } + else + die("result does not contain '%s' in '%s'", + latest_applied_binlog_epoch_str, query); + if (count == 0) + start_epoch= latest_trans_epoch; + break; + } + } + if (!row) + die("result does not contain '%s' in '%s'", + binlog, query); + if (latest_applied_binlog_epoch > applied_epoch) + count= 0; + applied_epoch= latest_applied_binlog_epoch; + count++; + if (latest_handled_binlog_epoch >= start_epoch) + do_continue= 0; + else if (count > 30) + { + break; + } + mysql_free_result(res); + } + } + } +#endif if (mysql_query(mysql, query= "show master status")) - die("failed in show master status: %d: %s", + die("failed in 'show master status': %d %s", mysql_errno(mysql), mysql_error(mysql)); - if (!(last_result =res = mysql_store_result(mysql))) + if (!(res = mysql_store_result(mysql))) die("mysql_store_result() retuned NULL for '%s'", query); if (!(row = mysql_fetch_row(res))) die("empty result in show master status"); strnmov(master_pos.file, row[0], sizeof(master_pos.file)-1); master_pos.pos = strtoul(row[1], (char**) 0, 10); - mysql_free_result(res); last_result=0; + mysql_free_result(res); if (rpl_parse) mysql_enable_rpl_parse(mysql); @@ -1440,25 +2223,30 @@ int do_save_master_pos() Assign the variable with SYNOPSIS - do_let() - query called command + do_let() + query called command DESCRIPTION - let $= + let $= - - is the string string found between the $ and = - - is the content between the = and , it may span - multiple line and contain any characters except - - is a string containing of one or more chars, default is ; + - is the string string found between the $ and = + - is the content between the = and , it may span + multiple line and contain any characters except + - is a string containing of one or more chars, default is ; RETURN VALUES - Program will die if error detected + Program will die if error detected */ -int do_let(struct st_query *query) +void do_let(struct st_command *command) { - char *p= query->first_argument; - char *var_name, *var_name_end, *var_val_start; + char *p= command->first_argument; + char *var_name, *var_name_end; + DYNAMIC_STRING let_rhs_expr; + + DBUG_ENTER("do_let"); + + init_dynamic_string(&let_rhs_expr, "", 512, 2048); /* Find */ if (!*p) @@ -1467,7 +2255,8 @@ int do_let(struct st_query *query) while (*p && (*p != '=') && !my_isspace(charset_info,*p)) p++; var_name_end= p; - if (var_name+1 == var_name_end) + if (var_name == var_name_end || + (var_name+1 == var_name_end && *var_name == '$')) die("Missing variable name in let"); while (my_isspace(charset_info,*p)) p++; @@ -1477,29 +2266,19 @@ int do_let(struct st_query *query) /* Find start of */ while (*p && my_isspace(charset_info,*p)) p++; - var_val_start= p; - query->last_argument= query->end; + + do_eval(&let_rhs_expr, p, command->end, FALSE); + + command->last_argument= command->end; /* Assign var_val to var_name */ - return var_set(var_name, var_name_end, var_val_start, query->end); + var_set(var_name, var_name_end, let_rhs_expr.str, + (let_rhs_expr.str + let_rhs_expr.length)); + dynstr_free(&let_rhs_expr); + DBUG_VOID_RETURN; } -/* - Store an integer (typically the returncode of the last SQL) - statement in the mysqltest builtin variable $mysql_errno, by - simulating of a user statement "let $mysql_errno= " -*/ - -int var_set_errno(int sql_errno) -{ - const char *var_name= "$mysql_errno"; - char var_val[21]; - uint length= my_sprintf(var_val, (var_val, "%d", sql_errno)); - return var_set(var_name, var_name + 12, var_val, var_val + length); -} - - -int do_rpl_probe(struct st_query *query __attribute__((unused))) +int do_rpl_probe(struct st_command *command __attribute__((unused))) { DBUG_ENTER("do_rpl_probe"); if (mysql_rpl_probe(&cur_con->mysql)) @@ -1508,14 +2287,14 @@ int do_rpl_probe(struct st_query *query __attribute__((unused))) } -int do_enable_rpl_parse(struct st_query *query __attribute__((unused))) +int do_enable_rpl_parse(struct st_command *command __attribute__((unused))) { mysql_enable_rpl_parse(&cur_con->mysql); return 0; } -int do_disable_rpl_parse(struct st_query *query __attribute__((unused))) +int do_disable_rpl_parse(struct st_command *command __attribute__((unused))) { mysql_disable_rpl_parse(&cur_con->mysql); return 0; @@ -1523,17 +2302,17 @@ int do_disable_rpl_parse(struct st_query *query __attribute__((unused))) /* - Sleep the number of specifed seconds + Sleep the number of specified seconds SYNOPSIS - do_sleep() - q called command - real_sleep use the value from opt_sleep as number of seconds to sleep - if real_sleep is false + do_sleep() + q called command + real_sleep use the value from opt_sleep as number of seconds to sleep + if real_sleep is false DESCRIPTION - sleep - real_sleep + sleep + real_sleep The difference between the sleep and real_sleep commands is that sleep uses the delay from the --sleep command-line option if there is one. @@ -1544,39 +2323,42 @@ int do_disable_rpl_parse(struct st_query *query __attribute__((unused))) used for cpu-independent delays. */ -int do_sleep(struct st_query *query, my_bool real_sleep) +int do_sleep(struct st_command *command, my_bool real_sleep) { int error= 0; - char *p= query->first_argument; - char *sleep_start, *sleep_end= query->end; + char *p= command->first_argument; + char *sleep_start, *sleep_end= command->end; double sleep_val; while (my_isspace(charset_info, *p)) p++; if (!*p) - die("Missing argument to %.*s", query->first_word_len, query->query); + die("Missing argument to %.*s", command->first_word_len, command->query); sleep_start= p; /* Check that arg starts with a digit, not handled by my_strtod */ if (!my_isdigit(charset_info, *sleep_start)) - die("Invalid argument to %.*s \"%s\"", query->first_word_len, query->query, - query->first_argument); + die("Invalid argument to %.*s \"%s\"", command->first_word_len, + command->query,command->first_argument); sleep_val= my_strtod(sleep_start, &sleep_end, &error); if (error) - die("Invalid argument to %.*s \"%s\"", query->first_word_len, query->query, - query->first_argument); + die("Invalid argument to %.*s \"%s\"", command->first_word_len, + command->query, command->first_argument); /* Fixed sleep time selected by --sleep option */ - if (opt_sleep && !real_sleep) + if (opt_sleep >= 0 && !real_sleep) sleep_val= opt_sleep; - my_sleep((ulong) (sleep_val * 1000000L)); - query->last_argument= sleep_end; + DBUG_PRINT("info", ("sleep_val: %f", sleep_val)); + if (sleep_val) + my_sleep((ulong) (sleep_val * 1000000L)); + command->last_argument= sleep_end; return 0; } -static void get_file_name(char *filename, struct st_query *q) + +void do_get_file_name(char *filename, struct st_command *command) { - char *p= q->first_argument, *name; + char *p= command->first_argument, *name; if (!*p) die("Missing file name argument"); name= p; @@ -1584,13 +2366,14 @@ static void get_file_name(char *filename, struct st_query *q) p++; if (*p) *p++= 0; - q->last_argument= p; + command->last_argument= p; strmake(filename, name, FN_REFLEN); } -static void set_charset(struct st_query *q) + +void do_set_charset(struct st_command *command) { - char *charset_name= q->first_argument; + char *charset_name= command->first_argument; char *p; if (!charset_name || !*charset_name) @@ -1601,49 +2384,182 @@ static void set_charset(struct st_query *q) p++; if(*p) *p++= 0; - q->last_argument= p; + command->last_argument= p; charset_info= get_charset_by_csname(charset_name,MY_CS_PRIMARY,MYF(MY_WME)); if (!charset_info) - abort_not_supported_test(); + abort_not_supported_test("Test requires charset '%s'", charset_name); } -static uint get_errcodes(match_err *to,struct st_query *q) + +#if MYSQL_VERSION_ID >= 50000 +/* List of error names to error codes, available from 5.0 */ +typedef struct { - char *p= q->first_argument; + const char *name; + long code; +} st_error; + +static st_error global_error_names[] = +{ +#include + { 0, 0 } +}; +#define HAVE_MYSQLD_ERNAME +#endif + + +uint get_errcode_from_name(char *error_name, char *error_end) +{ +#ifdef HAVE_MYSQLD_ERNAME + DBUG_ENTER("get_errcode_from_name"); + + /* SQL error as string */ + st_error *e= global_error_names; + DBUG_PRINT("enter", ("error_name: %s", error_name)); + + /* Loop through the array of known error names */ + for (; e->name; e++) + { + /* + If we get a match, we need to check the length of the name we + matched against in case it was longer than what we are checking + (as in ER_WRONG_VALUE vs. ER_WRONG_VALUE_COUNT). + */ + if (!strncmp(error_name, e->name, (int) (error_end - error_name)) && + (uint) strlen(e->name) == (uint) (error_end - error_name)) + { + return (uint) e->code; + break; + } + } + if (!e->name) + die("Unknown SQL error name '%s'", error_name); +#else + LINT_INIT(error_name); + LINT_INIT(error_end); + abort_not_in_this_version(); +#endif + return 0; +} + + +void do_get_errcodes(struct st_command *command) +{ + struct st_match_err *to= saved_expected_errors.err; + char *p= command->first_argument; uint count= 0; - DBUG_ENTER("get_errcodes"); + + DBUG_ENTER("do_get_errcodes"); if (!*p) - die("Missing argument in %s", q->query); + die("Missing argument(s) to 'error'"); do { + char *end; + + /* Skip leading spaces */ + while (*p && *p == ' ') + p++; + + /* Find end */ + end= p; + while (*end && *end != ',' && *end != ' ') + end++; + if (*p == 'S') { - /* SQLSTATE string */ - int i; - p++; - for (i = 0; my_isalnum(charset_info, *p) && i < SQLSTATE_LENGTH; p++, i++) - to[count].code.sqlstate[i]= *p; - to[count].code.sqlstate[i]= '\0'; - to[count].type= ERR_SQLSTATE; + /* + SQLSTATE string + - Must be SQLSTATE_LENGTH long + - May contain only digits[0-9] and _uppercase_ letters + */ + p++; /* Step past the S */ + if (end - p != SQLSTATE_LENGTH) + die("The sqlstate must be exactly %d chars long", SQLSTATE_LENGTH); + + /* Check sqlstate string validity */ + char *to_ptr= to->code.sqlstate; + while (*p && p != end) + { + if (my_isdigit(charset_info, *p) || my_isupper(charset_info, *p)) + *to_ptr++= *p++; + else + die("The sqlstate may only consist of digits[0-9] " \ + "and _uppercase_ letters"); + } + + *to_ptr= 0; + to->type= ERR_SQLSTATE; + DBUG_PRINT("info", ("ERR_SQLSTATE: %d", to->code.sqlstate)); + } + else if (*p == 's') + { + die("The sqlstate definition must start with an uppercase S"); + } + else if (*p == 'E') + { + /* Error name string */ + + DBUG_PRINT("info", ("Error name: %s", p)); + to->code.errnum= get_errcode_from_name(p, end); + to->type= ERR_ERRNO; + DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum)); + } + else if (*p == 'e') + { + die("The error name definition must start with an uppercase E"); } else { long val; - p=str2int(p,10,(long) INT_MIN, (long) INT_MAX, &val); - if (p == NULL) - die("Invalid argument in %s", q->query); - to[count].code.errnum= (uint) val; - to[count].type= ERR_ERRNO; + char *start= p; + /* Check that the string passed to str2int only contain digits */ + while (*p && p != end) + { + if (!my_isdigit(charset_info, *p)) + die("Invalid argument to error: '%s' - "\ + "the errno may only consist of digits[0-9]", + command->first_argument); + p++; + } + + /* Convert the sting to int */ + if (!str2int(start, 10, (long) INT_MIN, (long) INT_MAX, &val)) + die("Invalid argument to error: '%s'", command->first_argument); + + to->code.errnum= (uint) val; + to->type= ERR_ERRNO; + DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum)); } + to++; count++; - } while (*(p++) == ','); - q->last_argument= (p - 1); - to[count].type= ERR_EMPTY; /* End of data */ - DBUG_RETURN(count); + + if (count >= (sizeof(saved_expected_errors.err) / + sizeof(struct st_match_err))) + die("Too many errorcodes specified"); + + /* Set pointer to the end of the last error code */ + p= end; + + /* Find next ',' */ + while (*p && *p != ',') + p++; + + if (*p) + p++; /* Step past ',' */ + + } while (*p); + + command->last_argument= p; + to->type= ERR_EMPTY; /* End of data */ + + DBUG_PRINT("info", ("Expected errors: %d", count)); + saved_expected_errors.count= count; + DBUG_VOID_RETURN; } + /* Get a string; Return ptr to end of string Strings may be surrounded by " or ' @@ -1651,11 +2567,10 @@ static uint get_errcodes(match_err *to,struct st_query *q) If string is a '$variable', return the value of the variable. */ - -static char *get_string(char **to_ptr, char **from_ptr, - struct st_query *q) +char *get_string(char **to_ptr, char **from_ptr, + struct st_command *command) { - reg1 char c,sep; + char c, sep; char *to= *to_ptr, *from= *from_ptr, *start=to; DBUG_ENTER("get_string"); @@ -1701,7 +2616,7 @@ static char *get_string(char **to_ptr, char **from_ptr, *to++=c; } if (*from != ' ' && *from) - die("Wrong string argument in %s", q->query); + die("Wrong string argument in %s", command->query); while (my_isspace(charset_info,*from)) /* Point to next string */ from++; @@ -1725,93 +2640,46 @@ static char *get_string(char **to_ptr, char **from_ptr, } -/* - Get arguments for replace. The syntax is: - replace from to [from to ...] - Where each argument may be quoted with ' or " - A argument may also be a variable, in which case the value of the - variable is replaced. -*/ - -static void get_replace(struct st_query *q) +void set_reconnect(MYSQL* mysql, int val) { - uint i; - char *from= q->first_argument; - char *buff,*start; - char word_end_chars[256],*pos; - POINTER_ARRAY to_array,from_array; - DBUG_ENTER("get_replace"); - - free_replace(); - - bzero((char*) &to_array,sizeof(to_array)); - bzero((char*) &from_array,sizeof(from_array)); - if (!*from) - die("Missing argument in %s", q->query); - start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE)); - while (*from) - { - char *to=buff; - to=get_string(&buff, &from, q); - if (!*from) - die("Wrong number of arguments to replace_result in '%s'", q->query); - insert_pointer_name(&from_array,to); - to=get_string(&buff, &from, q); - insert_pointer_name(&to_array,to); - } - for (i=1,pos=word_end_chars ; i < 256 ; i++) - if (my_isspace(charset_info,i)) - *pos++= i; - *pos=0; /* End pointer */ - if (!(glob_replace=init_replace((char**) from_array.typelib.type_names, - (char**) to_array.typelib.type_names, - (uint) from_array.typelib.count, - word_end_chars)) || - initialize_replace_buffer()) - die("Can't initialize replace from '%s'", q->query); - free_pointer_array(&from_array); - free_pointer_array(&to_array); - my_free(start, MYF(0)); - q->last_argument= q->end; - DBUG_VOID_RETURN; +#if MYSQL_VERSION_ID < 50000 + mysql->reconnect= val; +#else + int reconnect= val; + mysql_options(mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect); +#endif } -void free_replace() + +struct st_connection * find_connection_by_name(const char *name) { - DBUG_ENTER("free_replace"); - if (glob_replace) + struct st_connection *con; + for (con= connections; con < next_con; con++) { - my_free((char*) glob_replace,MYF(0)); - glob_replace=0; - free_replace_buffer(); + if (!strcmp(con->name, name)) + { + return con; + } } - DBUG_VOID_RETURN; + return 0; /* Connection not found */ } int select_connection_name(const char *name) { - struct connection *con; DBUG_ENTER("select_connection2"); DBUG_PRINT("enter",("name: '%s'", name)); - for (con= cons; con < next_con; con++) - { - if (!strcmp(con->name, name)) - { - cur_con= con; - DBUG_RETURN(0); - } - } - die("connection '%s' not found in connection pool", name); - DBUG_RETURN(1); /* Never reached */ + if (!(cur_con= find_connection_by_name(name))) + die("connection '%s' not found in connection pool", name); + DBUG_RETURN(0); } -int select_connection(struct st_query *query) +int select_connection(struct st_command *command) { char *name; - char *p= query->first_argument; + char *p= command->first_argument; DBUG_ENTER("select_connection"); if (!*p) @@ -1821,33 +2689,38 @@ int select_connection(struct st_query *query) p++; if (*p) *p++= 0; - query->last_argument= p; + command->last_argument= p; return select_connection_name(name); } -int close_connection(struct st_query *q) +void do_close_connection(struct st_command *command) { - char *p= q->first_argument, *name; - struct connection *con; + char *p= command->first_argument, *name; + struct st_connection *con; + DBUG_ENTER("close_connection"); DBUG_PRINT("enter",("name: '%s'",p)); if (!*p) - die("Missing connection name in connect"); + die("Missing connection name in disconnect"); name= p; while (*p && !my_isspace(charset_info,*p)) p++; if (*p) *p++= 0; - q->last_argument= p; - for (con= cons; con < next_con; con++) + command->last_argument= p; + + /* Loop through connection pool for connection to close */ + for (con= connections; con < next_con; con++) { + DBUG_PRINT("info", ("con->name: %s", con->name)); if (!strcmp(con->name, name)) { + DBUG_PRINT("info", ("Closing connection %s", con->name)); #ifndef EMBEDDED_LIBRARY - if (q->type == Q_DIRTY_CLOSE) + if (command->type == Q_DIRTY_CLOSE) { if (con->mysql.net.vio) { @@ -1857,172 +2730,353 @@ int close_connection(struct st_query *q) } #endif mysql_close(&con->mysql); - DBUG_RETURN(0); + if (con->util_mysql) + mysql_close(con->util_mysql); + con->util_mysql= 0; + my_free(con->name, MYF(0)); + + /* + When the connection is closed set name to "closed_connection" + to make it possible to reuse the connection name. + The connection slot will not be reused + */ + if (!(con->name = my_strdup("closed_connection", MYF(MY_WME)))) + die("Out of memory"); + + DBUG_VOID_RETURN; } } die("connection '%s' not found in connection pool", name); - DBUG_RETURN(1); /* Never reached */ } /* - This one now is a hack - we may want to improve in in the - future to handle quotes. For now we assume that anything that is not - a comma, a space or ) belongs to the argument. space is a chopper, comma or - ) are delimiters/terminators + Connect to a server doing several retries if needed. + + SYNOPSIS + safe_connect() + con - connection structure to be used + host, user, pass, - connection parameters + db, port, sock + + NOTE + + Sometimes in a test the client starts before + the server - to solve the problem, we try again + after some sleep if connection fails the first + time + + This function will try to connect to the given server + "opt_max_connect_retries" times and sleep "connection_retry_sleep" + seconds between attempts before finally giving up. + This helps in situation when the client starts + before the server (which happens sometimes). + It will only ignore connection errors during these retries. + */ -char* safe_get_param(char *str, char** arg, const char *msg) +void safe_connect(MYSQL* mysql, const char *name, const char *host, + const char *user, const char *pass, const char *db, + int port, const char *sock) { - DBUG_ENTER("safe_get_param"); - while (*str && my_isspace(charset_info,*str)) - str++; - *arg= str; - for (; *str && *str != ',' && *str != ')' ; str++) + int failed_attempts= 0; + static ulong connection_retry_sleep= 100000; /* Microseconds */ + + DBUG_ENTER("safe_connect"); + while(!mysql_real_connect(mysql, host,user, pass, db, port, sock, + CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS)) { - if (my_isspace(charset_info,*str)) - *str= 0; - } - if (!*str) - die(msg); + /* + Connect failed - *str++= 0; - DBUG_RETURN(str); -} + Only allow retry if this was an error indicating the server + could not be contacted + */ -#ifndef EMBEDDED_LIBRARY -void init_manager() -{ - if (!(manager=mysql_manager_init(0))) - die("Failed in mysql_manager_init()"); - if (!mysql_manager_connect(manager,manager_host,manager_user, - manager_pass,manager_port)) - die("Could not connect to MySQL manager: %s(%d)",manager->last_error, - manager->last_errno); - -} -#endif - -int safe_connect(MYSQL* con, const char *host, const char *user, - const char *pass, - const char *db, int port, const char *sock) -{ - int con_error = 1; - int i; - for (i = 0; i < MAX_CON_TRIES; ++i) - { - if (mysql_real_connect(con, host,user, pass, db, port, sock, - CLIENT_MULTI_STATEMENTS)) + if (mysql_errno(mysql) == CR_CONNECTION_ERROR && + failed_attempts < opt_max_connect_retries) + my_sleep(connection_retry_sleep); + else { - con_error = 0; - break; + if (failed_attempts > 0) + die("Could not open connection '%s' after %d attempts: %d %s", name, + failed_attempts, mysql_errno(mysql), mysql_error(mysql)); + else + die("Could not open connection '%s': %d %s", name, + mysql_errno(mysql), mysql_error(mysql)); } - sleep(CON_RETRY_SLEEP); + failed_attempts++; } - return con_error; + DBUG_VOID_RETURN; } -int do_connect(struct st_query *q) +/* + Connect to a server and handle connection errors in case they occur. + + SYNOPSIS + connect_n_handle_errors() + q - context of connect "query" (command) + con - connection structure to be used + host, user, pass, - connection parameters + db, port, sock + + DESCRIPTION + This function will try to establish a connection to server and handle + possible errors in the same manner as if "connect" was usual SQL-statement + (If error is expected it will ignore it once it occurs and log the + "statement" to the query log). + Unlike safe_connect() it won't do several attempts. + + RETURN VALUES + 1 - Connected + 0 - Not connected + +*/ + +int connect_n_handle_errors(struct st_command *command, + MYSQL* con, const char* host, + const char* user, const char* pass, + const char* db, int port, const char* sock) { - char *con_name, *con_user,*con_pass, *con_host, *con_port_str, - *con_db, *con_sock; - char *p= q->first_argument; - char buff[FN_REFLEN]; - int con_port; - int free_con_sock= 0; + DYNAMIC_STRING *ds; + + ds= &ds_res; + + /* Only log if an error is expected */ + if (!command->abort_on_error && + !disable_query_log) + { + /* + Log the connect to result log + */ + dynstr_append_mem(ds, "connect(", 8); + replace_dynstr_append(ds, host); + dynstr_append_mem(ds, ",", 1); + replace_dynstr_append(ds, user); + dynstr_append_mem(ds, ",", 1); + replace_dynstr_append(ds, pass); + dynstr_append_mem(ds, ",", 1); + if (db) + replace_dynstr_append(ds, db); + dynstr_append_mem(ds, ",", 1); + replace_dynstr_append_uint(ds, port); + dynstr_append_mem(ds, ",", 1); + if (sock) + replace_dynstr_append(ds, sock); + dynstr_append_mem(ds, ")", 1); + dynstr_append_mem(ds, delimiter, delimiter_length); + dynstr_append_mem(ds, "\n", 1); + } + if (!mysql_real_connect(con, host, user, pass, db, port, sock ? sock: 0, + CLIENT_MULTI_STATEMENTS)) + { + handle_error(command, mysql_errno(con), mysql_error(con), + mysql_sqlstate(con), ds); + return 0; /* Not connected */ + } + + handle_no_error(command); + return 1; /* Connected */ +} + + +/* + Open a new connection to MySQL Server with the parameters + specified. Make the new connection the current connection. + + SYNOPSIS + do_connect() + q called command + + DESCRIPTION + connect(,,,[,[,[,[]]]]); + connect ,,,[,[,[,[]]]]; + + - name of the new connection + - hostname of server + - user to connect as + - password used when connecting + - initial db when connected + - server port + - server socket + - options to use for the connection + * SSL - use SSL if available + * COMPRESS - use compression if available + +*/ + +void do_connect(struct st_command *command) +{ + int con_port= port; + char *con_options; + bool con_ssl= 0, con_compress= 0; + char *ptr; + + DYNAMIC_STRING ds_connection_name; + DYNAMIC_STRING ds_host; + DYNAMIC_STRING ds_user; + DYNAMIC_STRING ds_password; + DYNAMIC_STRING ds_database; + DYNAMIC_STRING ds_port; + DYNAMIC_STRING ds_sock; + DYNAMIC_STRING ds_options; + const struct command_arg connect_args[] = { + "connection name", ARG_STRING, TRUE, &ds_connection_name, + "Name of the connection", + "host", ARG_STRING, TRUE, &ds_host, "Host to connect to", + "user", ARG_STRING, FALSE, &ds_user, "User to connect as", + "passsword", ARG_STRING, FALSE, &ds_password, + "Password used when connecting", + "database", ARG_STRING, FALSE, &ds_database, + "Dtabase to select after connect", + "port", ARG_STRING, FALSE, &ds_port, "Port to connect to", + "socket", ARG_STRING, FALSE, &ds_sock, "Socket to connect with", + "options", ARG_STRING, FALSE, &ds_options, + "Options to use while connecting" + }; DBUG_ENTER("do_connect"); - DBUG_PRINT("enter",("connect: %s",p)); + DBUG_PRINT("enter",("connect: %s", command->first_argument)); - if (*p != '(') - die("Syntax error in connect - expected '(' found '%c'", *p); - p++; - p= safe_get_param(p, &con_name, "missing connection name"); - p= safe_get_param(p, &con_host, "missing connection host"); - p= safe_get_param(p, &con_user, "missing connection user"); - p= safe_get_param(p, &con_pass, "missing connection password"); - p= safe_get_param(p, &con_db, "missing connection db"); - if (!*p || *p == ';') /* Default port and sock */ + /* Remove parenteses around connect arguments */ + if ((ptr= strstr(command->first_argument, "("))) { - con_port= port; - con_sock= (char*) unix_sock; + /* Replace it with a space */ + *ptr= ' '; + if ((ptr= strstr(command->first_argument, ")"))) + { + /* Replace it with \0 */ + *ptr= 0; + } + else + die("connect - argument list started with '(' must be ended with ')'"); + } + + check_command_args(command, command->first_argument, connect_args, + sizeof(connect_args)/sizeof(struct command_arg), + ','); + + /* Port */ + if (ds_port.length) + { + con_port= atoi(ds_port.str); + if (con_port == 0) + die("Illegal argument for port: '%s'", ds_port.str); + } + + /* Sock */ + if (ds_sock.length) + { + /* + If the socket is specified just as a name without path + append tmpdir in front + */ + if (*ds_sock.str != FN_LIBCHAR) + { + char buff[FN_REFLEN]; + fn_format(buff, ds_sock.str, TMPDIR, "", 0); + dynstr_set(&ds_sock, buff); + } } else { - VAR* var_port, *var_sock; - p= safe_get_param(p, &con_port_str, "missing connection port"); - if (*con_port_str == '$') - { - if (!(var_port= var_get(con_port_str, 0, 0, 0))) - die("Unknown variable '%s'", con_port_str+1); - con_port= var_port->int_val; - } - else - con_port= atoi(con_port_str); - p= safe_get_param(p, &con_sock, "missing connection socket"); - if (*con_sock == '$') - { - if (!(var_sock= var_get(con_sock, 0, 0, 0))) - die("Unknown variable '%s'", con_sock+1); - if (!(con_sock= (char*)my_malloc(var_sock->str_val_len+1, MYF(0)))) - die("Out of memory"); - free_con_sock= 1; - memcpy(con_sock, var_sock->str_val, var_sock->str_val_len); - con_sock[var_sock->str_val_len]= 0; - } + /* No socket specified, use default */ + dynstr_set(&ds_sock, unix_sock); } - q->last_argument= p; + DBUG_PRINT("info", ("socket: %s", ds_sock.str)); - if (next_con == cons_end) - die("Connection limit exhausted - increase MAX_CONS in mysqltest.c"); + + /* Options */ + con_options= ds_options.str; + while (*con_options) + { + char* end= con_options; + while (*end && !my_isspace(charset_info, *end)) + end++; + if (!strncmp(con_options, "SSL", 3)) + con_ssl= 1; + else if (!strncmp(con_options, "COMPRESS", 8)) + con_compress= 1; + else + die("Illegal option to connect: %.*s", end - con_options, con_options); + /* Process next option */ + con_options= end; + } + + if (next_con == connections_end) + die("Connection limit exhausted, you can have max %d connections", + (sizeof(connections)/sizeof(struct st_connection))); + + if (find_connection_by_name(ds_connection_name.str)) + die("Connection %s already exists", ds_connection_name.str); if (!mysql_init(&next_con->mysql)) die("Failed on mysql_init()"); - if (opt_compress) - mysql_options(&next_con->mysql,MYSQL_OPT_COMPRESS,NullS); + if (opt_compress || con_compress) + mysql_options(&next_con->mysql, MYSQL_OPT_COMPRESS, NullS); mysql_options(&next_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0); mysql_options(&next_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name); #ifdef HAVE_OPENSSL - if (opt_use_ssl) + if (opt_use_ssl || con_ssl) + { mysql_ssl_set(&next_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); +#if MYSQL_VERSION_ID >= 50000 + /* Turn on ssl_verify_server_cert only if host is "localhost" */ + opt_ssl_verify_server_cert= !strcmp(con_host, "localhost"); + mysql_options(&next_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, + &opt_ssl_verify_server_cert); +#endif + } #endif - if (con_sock && !free_con_sock && *con_sock && *con_sock != FN_LIBCHAR) - con_sock=fn_format(buff, con_sock, TMPDIR, "",0); - if (!con_db[0]) - con_db= db; - /* Special database to allow one to connect without a database name */ - if (con_db && !strcmp(con_db,"*NO-ONE*")) - con_db= 0; - if ((safe_connect(&next_con->mysql, con_host, - con_user, con_pass, - con_db, con_port, con_sock ? con_sock: 0))) - die("Could not open connection '%s': %s", con_name, - mysql_error(&next_con->mysql)); - if (!(next_con->name= my_strdup(con_name, MYF(MY_WME)))) - die(NullS); - cur_con= next_con++; - if (free_con_sock) - my_free(con_sock, MYF(MY_WME)); - DBUG_RETURN(0); + /* Use default db name */ + if (ds_database.length == 0) + dynstr_set(&ds_database, db); + + /* Special database to allow one to connect without a database name */ + if (ds_database.length && !strcmp(ds_database.str,"*NO-ONE*")) + dynstr_set(&ds_database, ""); + + + if (connect_n_handle_errors(command, &next_con->mysql, + ds_host.str,ds_user.str, + ds_password.str, ds_database.str, + con_port, ds_sock.str)) + { + DBUG_PRINT("info", ("Inserting connection %s in connection pool", + ds_connection_name.str)); + if (!(next_con->name= my_strdup(ds_connection_name.str, MYF(MY_WME)))) + die("Out of memory"); + cur_con= next_con++; + } + + dynstr_free(&ds_connection_name); + dynstr_free(&ds_host); + dynstr_free(&ds_user); + dynstr_free(&ds_password); + dynstr_free(&ds_database); + dynstr_free(&ds_port); + dynstr_free(&ds_sock); + dynstr_free(&ds_options); + DBUG_VOID_RETURN; } -int do_done(struct st_query *q) +int do_done(struct st_command *command) { /* Check if empty block stack */ if (cur_block == block_stack) { - if (*q->query != '}') + if (*command->query != '}') die("Stray 'end' command - end of block before beginning"); die("Stray '}' - end of block before beginning"); } /* Test if inner block has been executed */ - if (cur_block->ok && cur_block->cmd == Q_WHILE) + if (cur_block->ok && cur_block->cmd == cmd_while) { /* Pop block from stack, re-execute outer block */ cur_block--; @@ -2038,12 +3092,41 @@ int do_done(struct st_query *q) } -int do_block(enum enum_commands cmd, struct st_query *q) +/* + Process start of a "if" or "while" statement + + SYNOPSIS + do_block() + cmd Type of block + q called command + + DESCRIPTION + if ([!]) + { + + } + + while ([!]) + { + + } + + Evaluates the and if it evaluates to + greater than zero executes the following code block. + A '!' can be used before the to indicate it should + be executed if it evaluates to zero. + +*/ + +void do_block(enum block_cmd cmd, struct st_command* command) { - char *p= q->first_argument; + char *p= command->first_argument; const char *expr_start, *expr_end; VAR v; - const char *cmd_name= (cmd == Q_WHILE ? "while" : "if"); + const char *cmd_name= (cmd == cmd_while ? "while" : "if"); + my_bool not_expr= FALSE; + DBUG_ENTER("do_block"); + DBUG_PRINT("enter", ("%s", cmd_name)); /* Check stack overflow */ if (cur_block == block_stack_end) @@ -2059,13 +3142,21 @@ int do_block(enum enum_commands cmd, struct st_query *q) cur_block++; cur_block->cmd= cmd; cur_block->ok= FALSE; - return 0; + DBUG_VOID_RETURN; } /* Parse and evaluate test expression */ expr_start= strchr(p, '('); - if (!expr_start) + if (!expr_start++) die("missing '(' in %s", cmd_name); + + /* Check for ! */ + if (*expr_start == '!') + { + not_expr= TRUE; + expr_start++; /* Step past the '!' */ + } + /* Find ending ')' */ expr_end= strrchr(expr_start, ')'); if (!expr_end) die("missing ')' in %s", cmd_name); @@ -2073,61 +3164,62 @@ int do_block(enum enum_commands cmd, struct st_query *q) while (*p && my_isspace(charset_info, *p)) p++; - if (*p == '{') - die("Missing newline between %s and '{'", cmd_name); - if (*p) + if (*p && *p != '{') die("Missing '{' after %s. Found \"%s\"", cmd_name, p); var_init(&v,0,0,0,0); - eval_expr(&v, ++expr_start, &expr_end); + eval_expr(&v, expr_start, &expr_end); /* Define inner block */ cur_block++; cur_block->cmd= cmd; cur_block->ok= (v.int_val ? TRUE : FALSE); + if (not_expr) + cur_block->ok = !cur_block->ok; + + DBUG_PRINT("info", ("OK: %d", cur_block->ok)); + var_free(&v); - return 0; + DBUG_VOID_RETURN; } -/* - Read characters from line buffer or file. This is needed to allow - my_ungetc() to buffer MAX_DELIMITER characters for a file - - NOTE: - This works as long as one doesn't change files (with 'source file_name') - when there is things pushed into the buffer. This should however not - happen for any tests in the test suite. -*/ - -int my_getc(FILE *file) +void do_delimiter(struct st_command* command) { - if (line_buffer_pos == line_buffer) - return fgetc(file); - return *--line_buffer_pos; -} + char* p= command->first_argument; + DBUG_ENTER("do_delimiter"); + DBUG_PRINT("enter", ("first_argument: %s", command->first_argument)); -void my_ungetc(int c) -{ - *line_buffer_pos++= (char) c; + while (*p && my_isspace(charset_info, *p)) + p++; + + if (!(*p)) + die("Can't set empty delimiter"); + + strmake(delimiter, p, sizeof(delimiter) - 1); + delimiter_length= strlen(delimiter); + + DBUG_PRINT("exit", ("delimiter: %s", delimiter)); + command->last_argument= p + delimiter_length; + DBUG_VOID_RETURN; } -my_bool end_of_query(int c) +my_bool match_delimiter(int c, const char *delim, uint length) { uint i; - char tmp[MAX_DELIMITER]; + char tmp[MAX_DELIMITER_LENGTH]; - if (c != *delimiter) + if (c != *delim) return 0; - for (i= 1; i < delimiter_length && - (c= my_getc(cur_file->file)) == *(delimiter + i); + for (i= 1; i < length && + (c= my_getc(cur_file->file)) == *(delim + i); i++) tmp[i]= c; - if (i == delimiter_length) + if (i == length) return 1; /* Found delimiter */ /* didn't find delimiter, push back things that we read */ @@ -2138,44 +3230,51 @@ my_bool end_of_query(int c) } +my_bool end_of_query(int c) +{ + return match_delimiter(c, delimiter, delimiter_length); +} + + /* Read one "line" from the file SYNOPSIS - read_line - buf buffer for the read line - size size of the buffer i.e max size to read + read_line + buf buffer for the read line + size size of the buffer i.e max size to read DESCRIPTION - This function actually reads several lines an adds them to the - buffer buf. It will continue to read until it finds what it believes - is a complete query. + This function actually reads several lines and adds them to the + buffer buf. It continues to read until it finds what it believes + is a complete query. - Normally that means it will read lines until it reaches the - "delimiter" that marks end of query. Default delimiter is ';' - The function should be smart enough not to detect delimiter's - found inside strings surrounded with '"' and '\'' escaped strings. + Normally that means it will read lines until it reaches the + "delimiter" that marks end of query. Default delimiter is ';' + The function should be smart enough not to detect delimiter's + found inside strings surrounded with '"' and '\'' escaped strings. - If the first line in a query starts with '#' or '-' this line is treated - as a comment. A comment is always terminated when end of line '\n' is - reached. + If the first line in a query starts with '#' or '-' this line is treated + as a comment. A comment is always terminated when end of line '\n' is + reached. */ int read_line(char *buf, int size) { - int c; + char c, last_quote; char *p= buf, *buf_end= buf + size - 1; - int no_save= 0; - enum {R_NORMAL, R_Q1, R_ESC_Q_Q1, R_ESC_Q_Q2, - R_ESC_SLASH_Q1, R_ESC_SLASH_Q2, - R_Q2, R_COMMENT, R_LINE_START} state= R_LINE_START; + int skip_char= 0; + enum {R_NORMAL, R_Q, R_SLASH_IN_Q, + R_COMMENT, R_LINE_START} state= R_LINE_START; DBUG_ENTER("read_line"); + LINT_INIT(last_quote); - start_lineno= *lineno; + start_lineno= cur_file->lineno; + DBUG_PRINT("info", ("start_lineno: %d", start_lineno)); for (; p < buf_end ;) { - no_save= 0; + skip_char= 0; c= my_getc(cur_file->file); if (feof(cur_file->file)) { @@ -2187,127 +3286,118 @@ int read_line(char *buf, int size) } my_free((gptr)cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR)); cur_file->file_name= 0; - lineno--; - start_lineno= *lineno; if (cur_file == file_stack) { /* We're back at the first file, check if all { have matching } - */ + */ if (cur_block != block_stack) - { - start_lineno= *(lineno+1); die("Missing end of block"); - } - DBUG_RETURN(1); + + *p= 0; + DBUG_PRINT("info", ("end of file")); + DBUG_RETURN(1); } cur_file--; + start_lineno= cur_file->lineno; continue; } - /* Line counting is independent of state */ if (c == '\n') - (*lineno)++; + { + /* Line counting is independent of state */ + cur_file->lineno++; + + /* Convert cr/lf to lf */ + if (p != buf && *(p-1) == '\r') + p--; + } switch(state) { case R_NORMAL: - /* Only accept '{' in the beginning of a line */ if (end_of_query(c)) { *p= 0; + DBUG_PRINT("exit", ("Found delimiter '%s'", delimiter)); DBUG_RETURN(0); } - else if (c == '\'') - state = R_Q1; - else if (c == '"') - state = R_Q2; - else if (c == '\n') + else if ((c == '{' && + (!strncasecmp(buf, "while", min(5, p - buf)) || + !strncasecmp(buf, "if", min(2, p - buf))))) { - state = R_LINE_START; + /* Only if and while commands can be terminated by { */ + *p++= c; + *p= 0; + DBUG_PRINT("exit", ("Found '{' indicating begining of block")); + DBUG_RETURN(0); + } + else if (c == '\'' || c == '"' || c == '`') + { + last_quote= c; + state= R_Q; } break; + case R_COMMENT: if (c == '\n') { + /* Comments are terminated by newline */ *p= 0; + DBUG_PRINT("exit", ("Found newline in comment")); DBUG_RETURN(0); } break; + case R_LINE_START: - /* Only accept start of comment if this is the first line in query */ - if ((*lineno == start_lineno) && (c == '#' || c == '-')) + if (c == '#' || c == '-') { + /* A # or - in the first position of the line - this is a comment */ state = R_COMMENT; } else if (my_isspace(charset_info, c)) { + /* Skip all space at begining of line */ if (c == '\n') - start_lineno= *lineno; /* Query hasn't started yet */ - no_save= 1; + start_lineno= cur_file->lineno; /* Query hasn't started yet */ + skip_char= 1; + } + else if (end_of_query(c)) + { + *p= 0; + DBUG_PRINT("exit", ("Found delimiter '%s'", delimiter)); + DBUG_RETURN(0); } else if (c == '}') { - *buf++= '}'; - *buf= 0; - DBUG_RETURN(0); - } - else if (end_of_query(c) || c == '{') - { + /* A "}" need to be by itself in the begining of a line to terminate */ + *p++= c; *p= 0; + DBUG_PRINT("exit", ("Found '}' in begining of a line")); DBUG_RETURN(0); } - else if (c == '\'') - state= R_Q1; - else if (c == '"') - state= R_Q2; + else if (c == '\'' || c == '"' || c == '`') + { + last_quote= c; + state= R_Q; + } else state= R_NORMAL; break; - case R_Q1: - if (c == '\'') - state= R_ESC_Q_Q1; - else if (c == '\\') - state= R_ESC_SLASH_Q1; - break; - case R_ESC_Q_Q1: - if (end_of_query(c)) - { - *p= 0; - DBUG_RETURN(0); - } - if (c != '\'') + case R_Q: + if (c == last_quote) state= R_NORMAL; - else - state= R_Q1; - break; - case R_ESC_SLASH_Q1: - state= R_Q1; + else if (c == '\\') + state= R_SLASH_IN_Q; break; - case R_Q2: - if (c == '"') - state= R_ESC_Q_Q2; - else if (c == '\\') - state= R_ESC_SLASH_Q2; - break; - case R_ESC_Q_Q2: - if (end_of_query(c)) - { - *p= 0; - DBUG_RETURN(0); - } - if (c != '"') - state= R_NORMAL; - else - state= R_Q2; - break; - case R_ESC_SLASH_Q2: - state= R_Q2; + case R_SLASH_IN_Q: + state= R_Q; break; + } - if (!no_save) + if (!skip_char) { /* Could be a multibyte character */ /* This code is based on the code in "sql_load.cc" */ @@ -2325,7 +3415,7 @@ int read_line(char *buf, int size) for (i= 1; i < charlen; i++) { if (feof(cur_file->file)) - goto found_eof; /* FIXME: could we just break here?! */ + goto found_eof; c= my_getc(cur_file->file); *p++ = c; } @@ -2342,102 +3432,191 @@ int read_line(char *buf, int size) *p++= c; } } - *p= 0; /* Always end with \0 */ - DBUG_RETURN(feof(cur_file->file)); + die("The input buffer is too small for this query.x\n" \ + "check your query or increase MAX_QUERY and recompile"); + DBUG_RETURN(0); } + /* - Create a query from a set of lines + Convert the read query to result format version 1 - SYNOPSIS - read_query() - q_ptr pointer where to return the new query + That is: After newline, all spaces need to be skipped + unless the previous char was a quote - DESCRIPTION - Converts lines returned by read_line into a query, this involves - parsing the first word in the read line to find the query type. + This is due to an old bug that has now been fixed, but the + version 1 output format is preserved by using this function - - A -- comment may contain a valid query as the first word after the - comment start. Thus it's always checked to see if that is the case. - The advantage with this approach is to be able to execute commands - terminated by new line '\n' regardless how many "delimiter" it contain. - - If query starts with @ this will specify a file to .... */ -static char read_query_buf[MAX_QUERY]; - -int read_query(struct st_query** q_ptr) +void convert_to_format_v1(char* query) { - char *p= read_query_buf, *p1; - struct st_query* q; - DBUG_ENTER("read_query"); + int last_c_was_quote= 0; + char *p= query, *write= query; + char *end= strend(query); + char last_c; + + while (p <= end) + { + if (*p == '\n' && !last_c_was_quote) + { + *write++ = *p++; /* Save the newline */ + + /* Skip any spaces on next line */ + while (*p && my_isspace(charset_info, *p)) + p++; + + last_c_was_quote= 0; + } + else if (*p == '\'' || *p == '"' || *p == '`') + { + last_c= *p; + *write++ = *p++; + + /* Copy anything until the next quote of same type */ + while (*p && *p != last_c) + *write++ = *p++; + + *write++ = *p++; + + last_c_was_quote= 1; + } + else + { + *write++ = *p++; + last_c_was_quote= 0; + } + } +} + + +/* + Check for unexpected "junk" after the end of query + This is normally caused by missing delimiters +*/ + +void check_eol_junk(const char *eol) +{ + const char *p= eol; + DBUG_ENTER("check_eol_junk"); + DBUG_PRINT("enter", ("eol: %s", eol)); + /* Remove all spacing chars except new line */ + while (*p && my_isspace(charset_info, *p) && (*p != '\n')) + p++; + + /* Check for extra delimiter */ + if (*p && !strncmp(p, delimiter, delimiter_length)) + die("Extra delimiter \"%s\" found", delimiter); + + /* Allow trailing # comment */ + if (*p && *p != '#') + { + if (*p == '\n') + die("Missing delimiter"); + die("End of line junk detected: \"%s\"", p); + } + DBUG_VOID_RETURN; +} + + + +/* + Create a command from a set of lines + + SYNOPSIS + read_command() + command_ptr pointer where to return the new query + + DESCRIPTION + Converts lines returned by read_line into a command, this involves + parsing the first word in the read line to find the command type. + + + A -- comment may contain a valid query as the first word after the + comment start. Thus it's always checked to see if that is the case. + The advantage with this approach is to be able to execute commands + terminated by new line '\n' regardless how many "delimiter" it contain. + +*/ + +#define MAX_QUERY (256*1024) /* 256K -- a test in sp-big is >128K */ +static char read_command_buf[MAX_QUERY]; + +int read_command(struct st_command** command_ptr) +{ + char *p= read_command_buf; + struct st_command* command; + DBUG_ENTER("read_command"); if (parser.current_line < parser.read_lines) { - get_dynamic(&q_lines, (gptr) q_ptr, parser.current_line) ; + get_dynamic(&q_lines, (gptr) command_ptr, parser.current_line) ; DBUG_RETURN(0); } - if (!(*q_ptr= q= (struct st_query*) my_malloc(sizeof(*q), MYF(MY_WME))) || - insert_dynamic(&q_lines, (gptr) &q)) + if (!(*command_ptr= command= + (struct st_command*) my_malloc(sizeof(*command), MYF(MY_WME))) || + insert_dynamic(&q_lines, (gptr) &command)) die(NullS); - q->record_file[0]= 0; - q->require_file= 0; - q->first_word_len= 0; + command->record_file[0]= 0; + command->require_file= 0; + command->first_word_len= 0; + command->query_len= 0; - q->type= Q_UNKNOWN; - q->query_buf= q->query= 0; - if (read_line(read_query_buf, sizeof(read_query_buf))) + command->type= Q_UNKNOWN; + command->query_buf= command->query= 0; + read_command_buf[0]= 0; + if (read_line(read_command_buf, sizeof(read_command_buf))) { - DBUG_PRINT("warning",("too long query")); + check_eol_junk(read_command_buf); DBUG_RETURN(1); } - DBUG_PRINT("info", ("query: %s", read_query_buf)); + + convert_to_format_v1(read_command_buf); + + DBUG_PRINT("info", ("query: %s", read_command_buf)); if (*p == '#') { - q->type= Q_COMMENT; + command->type= Q_COMMENT; /* This goto is to avoid losing the "expected error" info. */ goto end; } - memcpy((gptr) q->expected_errno, (gptr) global_expected_errno, - sizeof(global_expected_errno)); - q->expected_errors= global_expected_errors; - q->abort_on_error= (global_expected_errors == 0 && abort_on_error); + if (!parsing_disabled) + { + memcpy(&command->expected_errors, &saved_expected_errors, + sizeof(saved_expected_errors)); + DBUG_PRINT("info", ("There are %d expected errors", + command->expected_errors.count)); + command->abort_on_error= (command->expected_errors.count == 0 && + abort_on_error); + } + if (p[0] == '-' && p[1] == '-') { - q->type= Q_COMMENT_WITH_COMMAND; + command->type= Q_COMMENT_WITH_COMMAND; p+= 2; /* To calculate first word */ } - else + else if (!parsing_disabled) { while (*p && my_isspace(charset_info, *p)) p++ ; - if (*p == '@') - { - p++; - p1 = q->record_file; - while (!my_isspace(charset_info, *p) && - p1 < q->record_file + sizeof(q->record_file) - 1) - *p1++ = *p++; - *p1 = 0; - } } end: while (*p && my_isspace(charset_info, *p)) p++; - if (!(q->query_buf= q->query= my_strdup(p, MYF(MY_WME)))) + + if (!(command->query_buf= command->query= my_strdup(p, MYF(MY_WME)))) die(NullS); /* Calculate first word and first argument */ - for (p= q->query; *p && !my_isspace(charset_info, *p) ; p++) ; - q->first_word_len= (uint) (p - q->query); + for (p= command->query; *p && !my_isspace(charset_info, *p) ; p++) ; + command->first_word_len= (uint) (p - command->query); while (*p && my_isspace(charset_info, *p)) p++; - q->first_argument= p; - q->end= strend(q->query); + command->first_argument= p; + command->end= strend(command->query); + command->query_len= (command->end - command->query); parser.read_lines++; DBUG_RETURN(0); } @@ -2445,39 +3624,41 @@ end: static struct my_option my_long_options[] = { - {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.", - 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, - {"database", 'D', "Database to use.", (gptr*) &db, (gptr*) &db, 0, - GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, + 0, 0, 0, 0, 0, 0}, {"basedir", 'b', "Basedir for tests.", (gptr*) &opt_basedir, (gptr*) &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"big-test", 'B', "Define BIG_TEST to 1.", (gptr*) &opt_big_test, - (gptr*) &opt_big_test, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"compress", 'C', "Use the compressed server/client protocol.", (gptr*) &opt_compress, (gptr*) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, - 0, 0, 0, 0, 0, 0}, + {"cursor-protocol", OPT_CURSOR_PROTOCOL, "Use cursors for prepared statements.", + (gptr*) &cursor_protocol, (gptr*) &cursor_protocol, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"database", 'D', "Database to use.", (gptr*) &db, (gptr*) &db, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#ifdef DBUG_OFF + {"debug", '#', "This is a non-debug version. Catch this and exit", + 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#else + {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.", + 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#endif {"host", 'h', "Connect to host.", (gptr*) &host, (gptr*) &host, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"manager-user", OPT_MANAGER_USER, "Undocumented: Used for debugging.", - (gptr*) &manager_user, (gptr*) &manager_user, 0, GET_STR, REQUIRED_ARG, 0, - 0, 0, 0, 0, 0}, - {"manager-host", OPT_MANAGER_HOST, "Undocumented: Used for debugging.", - (gptr*) &manager_host, (gptr*) &manager_host, 0, GET_STR, REQUIRED_ARG, - 0, 0, 0, 0, 0, 0}, - {"manager-password", OPT_MANAGER_PASSWD, "Undocumented: Used for debugging.", - 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"manager-port", OPT_MANAGER_PORT, "Undocumented: Used for debugging.", - (gptr*) &manager_port, (gptr*) &manager_port, 0, GET_INT, REQUIRED_ARG, - MYSQL_MANAGER_PORT, 0, 0, 0, 0, 0}, - {"manager-wait-timeout", OPT_MANAGER_WAIT_TIMEOUT, - "Undocumented: Used for debugging.", (gptr*) &manager_wait_timeout, - (gptr*) &manager_wait_timeout, 0, GET_INT, REQUIRED_ARG, 3, 0, 0, 0, 0, 0}, + {"include", 'i', "Include SQL before each test case.", (gptr*) &opt_include, + (gptr*) &opt_include, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"mark-progress", OPT_MARK_PROGRESS, + "Write linenumber and elapsed time to .progress ", + (gptr*) &opt_mark_progress, (gptr*) &opt_mark_progress, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"max-connect-retries", OPT_MAX_CONNECT_RETRIES, + "Max number of connection attempts when connecting to server", + (gptr*) &opt_max_connect_retries, (gptr*) &opt_max_connect_retries, 0, + GET_INT, REQUIRED_ARG, 500, 1, 10000, 0, 0, 0}, {"password", 'p', "Password to use when connecting to server.", 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"port", 'P', "Port number to use for connection.", (gptr*) &port, - (gptr*) &port, 0, GET_INT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0, 0}, + (gptr*) &port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"ps-protocol", OPT_PS_PROTOCOL, "Use prepared statements protocol for communication", (gptr*) &ps_protocol, (gptr*) &ps_protocol, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -2498,11 +3679,14 @@ static struct my_option my_long_options[] = "Don't use the memory allocation checking.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"sleep", 'T', "Sleep always this many seconds on sleep commands.", - (gptr*) &opt_sleep, (gptr*) &opt_sleep, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, + (gptr*) &opt_sleep, (gptr*) &opt_sleep, 0, GET_INT, REQUIRED_ARG, -1, 0, 0, 0, 0, 0}, {"socket", 'S', "Socket file to use for connection.", (gptr*) &unix_sock, (gptr*) &unix_sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"sp-protocol", OPT_SP_PROTOCOL, "Use stored procedures for select", + (gptr*) &sp_protocol, (gptr*) &sp_protocol, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #include "sslopt-longopts.h" {"test-file", 'x', "Read test from/in this file (default stdin).", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -2516,13 +3700,16 @@ static struct my_option my_long_options[] = GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"view-protocol", OPT_VIEW_PROTOCOL, "Use views for select", + (gptr*) &view_protocol, (gptr*) &view_protocol, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; #include -static void print_version(void) +void print_version(void) { printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname,MTEST_VERSION, MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE); @@ -2543,6 +3730,52 @@ void usage() #include +/* + Read arguments for embedded server and put them into + embedded_server_args[] +*/ + +void read_embedded_server_arguments(const char *name) +{ + char argument[1024],buff[FN_REFLEN], *str=0; + FILE *file; + + if (!test_if_hard_path(name)) + { + strxmov(buff, opt_basedir, name, NullS); + name=buff; + } + fn_format(buff, name, "", "", MY_UNPACK_FILENAME); + + if (!embedded_server_arg_count) + { + embedded_server_arg_count=1; + embedded_server_args[0]= (char*) ""; /* Progname */ + } + if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME)))) + die("Failed to open file %s", buff); + + while (embedded_server_arg_count < MAX_EMBEDDED_SERVER_ARGS && + (str=fgets(argument,sizeof(argument), file))) + { + *(strend(str)-1)=0; /* Remove end newline */ + if (!(embedded_server_args[embedded_server_arg_count]= + (char*) my_strdup(str,MYF(MY_WME)))) + { + my_fclose(file,MYF(0)); + die("Out of memory"); + + } + embedded_server_arg_count++; + } + my_fclose(file,MYF(0)); + if (str) + die("Too many arguments in option file: %s",name); + + return; +} + + static my_bool get_one_option(int optid, const struct my_option *opt __attribute__((unused)), char *argument) @@ -2556,40 +3789,36 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case 'r': record = 1; break; - case (int)OPT_MANAGER_PASSWD: - my_free(manager_pass,MYF(MY_ALLOW_ZERO_PTR)); - manager_pass=my_strdup(argument, MYF(MY_FAE)); - while (*argument) *argument++= 'x'; /* Destroy argument */ - break; case 'x': + { + char buff[FN_REFLEN]; + if (!test_if_hard_path(argument)) { - char buff[FN_REFLEN]; - if (!test_if_hard_path(argument)) - { - strxmov(buff, opt_basedir, argument, NullS); - argument= buff; - } - fn_format(buff, argument, "", "", 4); - DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0); - if (!(cur_file->file= - my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0)))) - die("Could not open %s: errno = %d", buff, errno); - cur_file->file_name= my_strdup(buff, MYF(MY_FAE)); - break; + strxmov(buff, opt_basedir, argument, NullS); + argument= buff; } + fn_format(buff, argument, "", "", MY_UNPACK_FILENAME); + DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0); + if (!(cur_file->file= + my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0)))) + die("Could not open %s: errno = %d", buff, errno); + cur_file->file_name= my_strdup(buff, MYF(MY_FAE)); + cur_file->lineno= 1; + break; + } case 'm': + { + static char buff[FN_REFLEN]; + if (!test_if_hard_path(argument)) { - static char buff[FN_REFLEN]; - if (!test_if_hard_path(argument)) - { - strxmov(buff, opt_basedir, argument, NullS); - argument= buff; - } - fn_format(buff, argument, "", "", 4); - timer_file= buff; - unlink(timer_file); /* Ignore error, may not exist */ - break; + strxmov(buff, opt_basedir, argument, NullS); + argument= buff; } + fn_format(buff, argument, "", "", MY_UNPACK_FILENAME); + timer_file= buff; + unlink(timer_file); /* Ignore error, may not exist */ + break; + } case 'p': if (argument) { @@ -2611,7 +3840,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), embedded_server_arg_count=1; embedded_server_args[0]= (char*) ""; } - if (embedded_server_arg_count == MAX_SERVER_ARGS-1 || + if (embedded_server_arg_count == MAX_EMBEDDED_SERVER_ARGS-1 || !(embedded_server_args[embedded_server_arg_count++]= my_strdup(argument, MYF(MY_FAE)))) { @@ -2619,8 +3848,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), } break; case 'F': - if (read_server_arguments(argument)) - die(NullS); + read_embedded_server_arguments(argument); break; case OPT_SKIP_SAFEMALLOC: #ifdef SAFEMALLOC @@ -2659,14 +3887,16 @@ int parse_args(int argc, char **argv) return 0; } -char* safe_str_append(char *buf, const char *str, int size) -{ - int i,c ; - for (i = 0; (c = *str++) && i < size - 1; i++) - *buf++ = c; - *buf = 0; - return buf; -} + +/* + Write the content of str into file + + SYNOPSIS + str_to_file + fname - name of file to truncate/create and write to + str - content to write to file + size - size of content witten to file +*/ void str_to_file(const char *fname, char *str, int size) { @@ -2675,38 +3905,213 @@ void str_to_file(const char *fname, char *str, int size) if (!test_if_hard_path(fname)) { strxmov(buff, opt_basedir, fname, NullS); - fname=buff; + fname= buff; } - fn_format(buff,fname,"","",4); - - if ((fd = my_open(buff, O_WRONLY | O_CREAT | O_TRUNC, - MYF(MY_WME | MY_FFNF))) < 0) + fn_format(buff, fname, "", "", MY_UNPACK_FILENAME); + + if ((fd= my_open(buff, O_WRONLY | O_CREAT | O_TRUNC, + MYF(MY_WME | MY_FFNF))) < 0) die("Could not open %s: errno = %d", buff, errno); if (my_write(fd, (byte*)str, size, MYF(MY_WME|MY_FNABP))) die("write failed"); my_close(fd, MYF(0)); } -void reject_dump(const char *record_file, char *buf, int size) + +void dump_result_to_reject_file(const char *record_file, char *buf, int size) { char reject_file[FN_REFLEN]; - str_to_file(fn_format(reject_file, record_file,"",".reject",2), buf, size); + str_to_file(fn_format(reject_file, record_file, "", ".reject", + MY_REPLACE_EXT), + buf, size); +} + +void dump_result_to_log_file(const char *record_file, char *buf, int size) +{ + char log_file[FN_REFLEN]; + str_to_file(fn_format(log_file, record_file, "", ".log", + MY_REPLACE_EXT), + buf, size); +} + +void dump_progress(const char *record_file) +{ + char log_file[FN_REFLEN]; + str_to_file(fn_format(log_file, record_file, "", ".progress", + MY_REPLACE_EXT), + ds_progress.str, ds_progress.length); +} + +void check_regerr(my_regex_t* r, int err) +{ + char err_buf[1024]; + + if (err) + { + my_regerror(err,r,err_buf,sizeof(err_buf)); + die("Regex error: %s\n", err_buf); + } } -/* Append the string to ds, with optional replace */ +#ifdef __WIN__ -static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val, - int len) +DYNAMIC_ARRAY patterns; + +/* + init_win_path_patterns + + DESCRIPTION + Setup string patterns that will be used to detect filenames that + needs to be converted from Win to Unix format + +*/ + +void init_win_path_patterns() { - if (glob_replace) + /* List of string patterns to match in order to find paths */ + const char* paths[] = { "$MYSQL_TEST_DIR", + "$MYSQL_TMP_DIR", + "./test/", 0 }; + int num_paths= 3; + int i; + char* p; + + DBUG_ENTER("init_win_path_patterns"); + + my_init_dynamic_array(&patterns, sizeof(const char*), 16, 16); + + /* Loop through all paths in the array */ + for (i= 0; i < num_paths; i++) { - len=(int) replace_strings(glob_replace, &out_buff, &out_length, val); - if (len == -1) - die("Out of memory in replace"); - val=out_buff; + VAR* v; + if (*(paths[i]) == '$') + { + v= var_get(paths[i], 0, 0, 0); + p= my_strdup(v->str_val, MYF(MY_FAE)); + } + else + p= my_strdup(paths[i], MYF(MY_FAE)); + + if (insert_dynamic(&patterns, (gptr) &p)) + die(NullS); + + DBUG_PRINT("info", ("p: %s", p)); + while (*p) + { + if (*p == '/') + *p='\\'; + p++; + } + } + DBUG_VOID_RETURN; +} + +void free_win_path_patterns() +{ + uint i= 0; + for (i=0 ; i < patterns.elements ; i++) + { + const char** pattern= dynamic_element(&patterns, i, const char**); + my_free((gptr) *pattern, MYF(0)); + } + delete_dynamic(&patterns); +} + +/* + fix_win_paths + + DESCRIPTION + Search the string 'val' for the patterns that are known to be + strings that contain filenames. Convert all \ to / in the + filenames that are found. + + Ex: + val = 'Error "c:\mysql\mysql-test\var\test\t1.frm" didn't exist' + => $MYSQL_TEST_DIR is found by strstr + => all \ from c:\mysql\m... until next space is converted into / +*/ + +void fix_win_paths(const char *val, int len) +{ + uint i; + char *p; + + DBUG_ENTER("fix_win_paths"); + for (i= 0; i < patterns.elements; i++) + { + const char** pattern= dynamic_element(&patterns, i, const char**); + DBUG_PRINT("info", ("pattern: %s", *pattern)); + if (strlen(*pattern) == 0) continue; + /* Search for the path in string */ + while ((p= strstr(val, *pattern))) + { + DBUG_PRINT("info", ("Found %s in val p: %s", *pattern, p)); + + while (*p && !my_isspace(charset_info, *p)) + { + if (*p == '\\') + *p= '/'; + p++; + } + DBUG_PRINT("info", ("Converted \\ to /, p: %s", p)); + } + } + DBUG_PRINT("exit", (" val: %s, len: %d", val, len)); + DBUG_VOID_RETURN; +} +#endif + + + +/* + Append the result for one field to the dynamic string ds +*/ + +void append_field(DYNAMIC_STRING *ds, uint col_idx, MYSQL_FIELD* field, + const char* val, ulonglong len, bool is_null) +{ + if (col_idx < max_replace_column && replace_column[col_idx]) + { + val= replace_column[col_idx]; + len= strlen(val); + } + else if (is_null) + { + val= "NULL"; + len= 4; + } +#ifdef __WIN__ + else if ((field->type == MYSQL_TYPE_DOUBLE || + field->type == MYSQL_TYPE_FLOAT ) && + field->decimals >= 31) + { + /* Convert 1.2e+018 to 1.2e+18 and 1.2e-018 to 1.2e-18 */ + char *start= strchr(val, 'e'); + if (start && strlen(start) >= 5 && + (start[1] == '-' || start[1] == '+') && start[2] == '0') + { + start+=2; /* Now points at first '0' */ + /* Move all chars after the first '0' one step left */ + memmove(start, start + 1, strlen(start)); + len--; + } + } +#endif + + if (!display_result_vertically) + { + if (col_idx) + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append_mem(ds, val, (int)len); + } + else + { + dynstr_append(ds, field->name); + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append_mem(ds, val, (int)len); + dynstr_append_mem(ds, "\n", 1); } - dynstr_append_mem(ds, val, len); } @@ -2715,676 +4120,97 @@ static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val, Values may be converted with 'replace_column' */ -static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res) +void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res) { MYSQL_ROW row; uint num_fields= mysql_num_fields(res); - MYSQL_FIELD *fields= !display_result_vertically ? 0 : mysql_fetch_fields(res); - unsigned long *lengths; + MYSQL_FIELD *fields= mysql_fetch_fields(res); + ulong *lengths; + while ((row = mysql_fetch_row(res))) { uint i; lengths = mysql_fetch_lengths(res); for (i = 0; i < num_fields; i++) - { - const char *val= row[i]; - ulonglong len= lengths[i]; - - if (i < max_replace_column && replace_column[i]) - { - val= replace_column[i]; - len= strlen(val); - } - if (!val) - { - val= "NULL"; - len= 4; - } - if (!display_result_vertically) - { - if (i) - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_mem(ds, val, len); - } - else - { - dynstr_append(ds, fields[i].name); - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_mem(ds, val, len); - dynstr_append_mem(ds, "\n", 1); - } - } + append_field(ds, i, &fields[i], + (const char*)row[i], lengths[i], !row[i]); if (!display_result_vertically) dynstr_append_mem(ds, "\n", 1); } - free_replace_column(); } /* -* flags control the phased/stages of query execution to be performed -* if QUERY_SEND bit is on, the query will be sent. If QUERY_REAP is on -* the result will be read - for regular query, both bits must be on + Append all results from ps execution to the dynamic string separated + with '\t'. Values may be converted with 'replace_column' */ -static int run_query_normal(MYSQL *mysql, struct st_query *q, int flags); -static int run_query_stmt (MYSQL *mysql, struct st_query *q, int flags); -static void run_query_stmt_handle_warnings(MYSQL *mysql, DYNAMIC_STRING *ds); -static int run_query_stmt_handle_error(char *query, struct st_query *q, - MYSQL_STMT *stmt, DYNAMIC_STRING *ds); -static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields, - DYNAMIC_STRING *ds); - -static int run_query(MYSQL *mysql, struct st_query *q, int flags) +void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, + MYSQL_FIELD *fields, uint num_fields) { - - /* - Try to find out if we can run this statement using the prepared - statement protocol. - - We don't have a mysql_stmt_send_execute() so we only handle - complete SEND+REAP. - - If it is a '?' in the query it may be a SQL level prepared - statement already and we can't do it twice - */ - - if (ps_protocol_enabled && disable_info && - (flags & QUERY_SEND) && (flags & QUERY_REAP) && ps_match_re(q->query)) - return run_query_stmt(mysql, q, flags); - return run_query_normal(mysql, q, flags); -} - - -static int run_query_normal(MYSQL* mysql, struct st_query* q, int flags) -{ - MYSQL_RES* res= 0; + MYSQL_BIND *bind; + my_bool *is_null; + ulong *length; uint i; - int error= 0, err= 0, counter= 0; - DYNAMIC_STRING *ds; - DYNAMIC_STRING ds_tmp; - DYNAMIC_STRING eval_query; - char* query; - int query_len, got_error_on_send= 0; - DBUG_ENTER("run_query_normal"); - DBUG_PRINT("enter",("flags: %d", flags)); - - if (q->type != Q_EVAL) - { - query = q->query; - query_len = strlen(query); - } - else - { - init_dynamic_string(&eval_query, "", 16384, 65536); - do_eval(&eval_query, q->query); - query = eval_query.str; - query_len = eval_query.length; - } - DBUG_PRINT("enter", ("query: '%-.60s'", query)); - if (q->record_file[0]) - { - init_dynamic_string(&ds_tmp, "", 16384, 65536); - ds = &ds_tmp; - } - else - ds= &ds_res; + /* Allocate array with bind structs, lengths and NULL flags */ + bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND), + MYF(MY_WME | MY_FAE | MY_ZEROFILL)); + length= (ulong*) my_malloc(num_fields * sizeof(ulong), + MYF(MY_WME | MY_FAE)); + is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool), + MYF(MY_WME | MY_FAE)); - if (flags & QUERY_SEND) + /* Allocate data for the result of each field */ + for (i= 0; i < num_fields; i++) { - got_error_on_send= mysql_send_query(mysql, query, query_len); - if (got_error_on_send && q->expected_errno[0].type == ERR_EMPTY) - die("unable to send query '%s' (mysql_errno=%d , errno=%d)", - query, mysql_errno(mysql), errno); + uint max_length= fields[i].max_length + 1; + bind[i].buffer_type= MYSQL_TYPE_STRING; + bind[i].buffer= (char *)my_malloc(max_length, MYF(MY_WME | MY_FAE)); + bind[i].buffer_length= max_length; + bind[i].is_null= &is_null[i]; + bind[i].length= &length[i]; + + DBUG_PRINT("bind", ("col[%d]: buffer_type: %d, buffer_length: %d", + i, bind[i].buffer_type, bind[i].buffer_length)); } - do + if (mysql_stmt_bind_result(stmt, bind)) + die("mysql_stmt_bind_result failed: %d: %s", + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); + + while (mysql_stmt_fetch(stmt) == 0) { - if ((flags & QUERY_SEND) && !disable_query_log && !counter) - { - replace_dynstr_append_mem(ds,query, query_len); - dynstr_append_mem(ds, delimiter, delimiter_length); + for (i= 0; i < num_fields; i++) + append_field(ds, i, &fields[i], (const char *) bind[i].buffer, + *bind[i].length, *bind[i].is_null); + if (!display_result_vertically) dynstr_append_mem(ds, "\n", 1); - } - if (!(flags & QUERY_REAP)) - DBUG_RETURN(0); + } - if (got_error_on_send || - (!counter && (*mysql->methods->read_query_result)(mysql)) || - (!(last_result= res= mysql_store_result(mysql)) && - mysql_field_count(mysql))) - { - if (q->require_file) - { - abort_not_supported_test(); - } - if (q->abort_on_error) - die("query '%s' failed: %d: %s", query, - mysql_errno(mysql), mysql_error(mysql)); + if (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + die("fetch didn't end with MYSQL_NO_DATA from statement: %d %s", + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); - for (i=0 ; (uint) i < q->expected_errors ; i++) - { - if (((q->expected_errno[i].type == ERR_ERRNO) && - (q->expected_errno[i].code.errnum == mysql_errno(mysql))) || - ((q->expected_errno[i].type == ERR_SQLSTATE) && - (strcmp(q->expected_errno[i].code.sqlstate,mysql_sqlstate(mysql)) == 0))) - { - if (i == 0 && q->expected_errors == 1) - { - /* Only log error if there is one possible error */ - dynstr_append_mem(ds,"ERROR ",6); - replace_dynstr_append_mem(ds, mysql_sqlstate(mysql), - strlen(mysql_sqlstate(mysql))); - dynstr_append_mem(ds, ": ", 2); - replace_dynstr_append_mem(ds,mysql_error(mysql), - strlen(mysql_error(mysql))); - dynstr_append_mem(ds,"\n",1); - } - /* Don't log error if we may not get an error */ - else if (q->expected_errno[0].type == ERR_SQLSTATE || - (q->expected_errno[0].type == ERR_ERRNO && - q->expected_errno[0].code.errnum != 0)) - dynstr_append(ds,"Got one of the listed errors\n"); - goto end; /* Ok */ - } - } - DBUG_PRINT("info",("i: %d expected_errors: %d", i, - q->expected_errors)); - dynstr_append_mem(ds, "ERROR ",6); - replace_dynstr_append_mem(ds, mysql_sqlstate(mysql), - strlen(mysql_sqlstate(mysql))); - dynstr_append_mem(ds,": ",2); - replace_dynstr_append_mem(ds, mysql_error(mysql), - strlen(mysql_error(mysql))); - dynstr_append_mem(ds,"\n",1); - if (i) - { - if (q->expected_errno[0].type == ERR_ERRNO) - verbose_msg("query '%s' failed with wrong errno %d instead of %d...", - q->query, mysql_errno(mysql), q->expected_errno[0].code.errnum); - else - verbose_msg("query '%s' failed with wrong sqlstate %s instead of %s...", - q->query, mysql_sqlstate(mysql), q->expected_errno[0].code.sqlstate); - error= 1; - goto end; - } - verbose_msg("query '%s' failed: %d: %s", q->query, mysql_errno(mysql), - mysql_error(mysql)); - /* - if we do not abort on error, failure to run the query does - not fail the whole test case - */ - goto end; - - } - - if (handle_no_error(q)) - { - error= 1; - goto end; - } - - if (!disable_result_log) - { - ulong affected_rows; /* Ok to be undef if 'disable_info' is set */ - LINT_INIT(affected_rows); - - if (res) - { - MYSQL_FIELD *field= mysql_fetch_fields(res); - uint num_fields= mysql_num_fields(res); - - if (display_metadata) - run_query_display_metadata(field, num_fields, ds); - - if (!display_result_vertically) - { - for (i = 0; i < num_fields; i++) - { - if (i) - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_mem(ds, field[i].name, - strlen(field[i].name)); - } - dynstr_append_mem(ds, "\n", 1); - } - append_result(ds, res); - } - - /* - Need to call mysql_affected_rows() before the new - query to find the warnings - */ - if (!disable_info) - affected_rows= (ulong)mysql_affected_rows(mysql); - - /* Add all warnings to the result */ - if (!disable_warnings && mysql_warning_count(mysql)) - { - MYSQL_RES *warn_res=0; - uint count= mysql_warning_count(mysql); - if (!mysql_real_query(mysql, "SHOW WARNINGS", 13)) - { - warn_res= mysql_store_result(mysql); - } - if (!warn_res) - die("Warning count is %u but didn't get any warnings\n", - count); - else - { - dynstr_append_mem(ds, "Warnings:\n", 10); - append_result(ds, warn_res); - mysql_free_result(warn_res); - } - } - if (!disable_info) - { - char buf[40]; - sprintf(buf,"affected rows: %lu\n", affected_rows); - dynstr_append(ds, buf); - if (mysql_info(mysql)) - { - dynstr_append(ds, "info: "); - dynstr_append(ds, mysql_info(mysql)); - dynstr_append_mem(ds, "\n", 1); - } - } - } - - if (record) - { - if (!q->record_file[0] && !result_file) - die("Missing result file"); - if (!result_file) - str_to_file(q->record_file, ds->str, ds->length); - } - else if (q->record_file[0]) - { - error = check_result(ds, q->record_file, q->require_file); - } - if (res) - mysql_free_result(res); - last_result= 0; - counter++; - } while (!(err= mysql_next_result(mysql))); - if (err >= 1) - mysql_error(mysql); - -end: - free_replace(); - last_result=0; - if (ds == &ds_tmp) - dynstr_free(&ds_tmp); - if (q->type == Q_EVAL) - dynstr_free(&eval_query); - - /* - We save the return code (mysql_errno(mysql)) from the last call sent - to the server into the mysqltest builtin variable $mysql_errno. This - variable then can be used from the test case itself. - */ - var_set_errno(mysql_errno(mysql)); - DBUG_RETURN(error); + for (i= 0; i < num_fields; i++) + { + /* Free data for output */ + my_free((gptr)bind[i].buffer, MYF(MY_WME | MY_FAE)); + } + /* Free array with bind structs, lengths and NULL flags */ + my_free((gptr)bind , MYF(MY_WME | MY_FAE)); + my_free((gptr)length , MYF(MY_WME | MY_FAE)); + my_free((gptr)is_null , MYF(MY_WME | MY_FAE)); } -/****************************************************************************\ - * If --ps-protocol run ordinary statements using prepared statemnt C API -\****************************************************************************/ - /* - We don't have a mysql_stmt_send_execute() so we only handle - complete SEND+REAP + Append metadata for fields to output */ -static int run_query_stmt(MYSQL *mysql, struct st_query *q, int flags) -{ - int error= 0; /* Function return code if "goto end;" */ - int err; /* Temporary storage of return code from calls */ - int query_len, got_error_on_execute; - uint num_rows; - char *query; - MYSQL_RES *res= NULL; /* Note that here 'res' is meta data result set */ - DYNAMIC_STRING *ds; - DYNAMIC_STRING ds_tmp; - DYNAMIC_STRING eval_query; - MYSQL_STMT *stmt; - DBUG_ENTER("run_query_stmt"); - - /* - We must allocate a new stmt for each query in this program becasue this - may be a new connection. - */ - if (!(stmt= mysql_stmt_init(mysql))) - die("unable init stmt structure"); - - if (q->type != Q_EVAL) - { - query= q->query; - query_len= strlen(query); - } - else - { - init_dynamic_string(&eval_query, "", 16384, 65536); - do_eval(&eval_query, q->query); - query= eval_query.str; - query_len= eval_query.length; - } - DBUG_PRINT("query", ("'%-.60s'", query)); - - if (q->record_file[0]) - { - init_dynamic_string(&ds_tmp, "", 16384, 65536); - ds= &ds_tmp; - } - else - ds= &ds_res; - - /* Store the query into the output buffer if not disabled */ - if (!disable_query_log) - { - replace_dynstr_append_mem(ds,query, query_len); - dynstr_append_mem(ds, delimiter, delimiter_length); - dynstr_append_mem(ds, "\n", 1); - } - - /* - We use the prepared statement interface but there is actually no - '?' in the query. If unpreparable we fall back to use normal - C API. - */ - if ((err= mysql_stmt_prepare(stmt, query, query_len)) == CR_NO_PREPARE_STMT) - return run_query_normal(mysql, q, flags); - - if (err != 0) - { - if (q->abort_on_error) - { - die("query '%s' failed: %d: %s", query, - mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); - } - else - { - /* - Preparing is part of normal execution and some errors may be expected - */ - error= run_query_stmt_handle_error(query, q, stmt, ds); - goto end; - } - } - - /* We may have got warnings already, collect them if any */ - /* FIXME we only want this if the statement succeeds I think */ - run_query_stmt_handle_warnings(mysql, ds); - - /* - No need to call mysql_stmt_bind_param() because we have no - parameter markers. - - To optimize performance we use a global 'stmt' that is initiated - once. A new prepare will implicitely close the old one. When we - terminate we will lose the connection, this also closes the last - prepared statement. - */ - - if ((got_error_on_execute= mysql_stmt_execute(stmt)) != 0) /* 0 == Success */ - { - if (q->abort_on_error) - { - /* We got an error, unexpected */ - die("unable to execute statement '%s': " - "%s (mysql_stmt_errno=%d returned=%d)", - query, mysql_stmt_error(stmt), - mysql_stmt_errno(stmt), got_error_on_execute); - } - else - { - /* We got an error, maybe expected */ - error= run_query_stmt_handle_error(query, q, stmt, ds); - goto end; - } - } - - /* - We instruct that we want to update the "max_length" field in - mysql_stmt_store_result(), this is our only way to know how much - buffer to allocate for result data - */ - { - my_bool one= 1; - if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, - (void*) &one) != 0) - die("unable to set stmt attribute " - "'STMT_ATTR_UPDATE_MAX_LENGTH': %s (returned=%d)", - query, err); - } - - /* - If we got here the statement succeeded and was expected to do so, - get data. Note that this can still give errors found during execution! - */ - if ((err= mysql_stmt_store_result(stmt)) != 0) - { - if (q->abort_on_error) - { - /* We got an error, unexpected */ - die("unable to execute statement '%s': " - "%s (mysql_stmt_errno=%d returned=%d)", - query, mysql_stmt_error(stmt), - mysql_stmt_errno(stmt), got_error_on_execute); - } - else - { - /* We got an error, maybe expected */ - error= run_query_stmt_handle_error(query, q, stmt, ds); - goto end; - } - } - - /* If we got here the statement was both executed and read succeesfully */ - if (handle_no_error(q)) - { - error= 1; - goto end; - } - - num_rows= mysql_stmt_num_rows(stmt); - - /* - Not all statements creates a result set. If there is one we can - now create another normal result set that contains the meta - data. This set can be handled almost like any other non prepared - statement result set. - */ - if (!disable_result_log && ((res= mysql_stmt_result_metadata(stmt)) != NULL)) - { - /* Take the column count from meta info */ - MYSQL_FIELD *field= mysql_fetch_fields(res); - uint num_fields= mysql_num_fields(res); - - /* FIXME check error from the above? */ - - if (display_metadata) - run_query_display_metadata(field, num_fields, ds); - - if (!display_result_vertically) - { - /* Display the table heading with the names tab separated */ - uint col_idx; - for (col_idx= 0; col_idx < num_fields; col_idx++) - { - if (col_idx) - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_mem(ds, field[col_idx].name, - strlen(field[col_idx].name)); - } - dynstr_append_mem(ds, "\n", 1); - } - - /* Now we are to put the real result into the output buffer */ - /* FIXME when it works, create function append_stmt_result() */ - { - MYSQL_BIND *bind; - my_bool *is_null; - unsigned long *length; - /* FIXME we don't handle vertical display ..... */ - uint col_idx, row_idx; - - /* Allocate array with bind structs, lengths and NULL flags */ - bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND), - MYF(MY_WME | MY_FAE)); - length= (unsigned long*) my_malloc(num_fields * sizeof(unsigned long), - MYF(MY_WME | MY_FAE)); - is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool), - MYF(MY_WME | MY_FAE)); - - for (col_idx= 0; col_idx < num_fields; col_idx++) - { - /* Allocate data for output */ - /* - FIXME it may be a bug that for non string/blob types - 'max_length' is 0, should try out 'length' in that case - */ - uint max_length= max(field[col_idx].max_length + 1, 1024); - char *str_data= (char *) my_malloc(max_length, MYF(MY_WME | MY_FAE)); - - bind[col_idx].buffer_type= MYSQL_TYPE_STRING; - bind[col_idx].buffer= (char *)str_data; - bind[col_idx].buffer_length= max_length; - bind[col_idx].is_null= &is_null[col_idx]; - bind[col_idx].length= &length[col_idx]; - } - - /* Fill in the data into the structures created above */ - if ((err= mysql_stmt_bind_result(stmt, bind)) != 0) - die("unable to bind result to statement '%s': " - "%s (mysql_stmt_errno=%d returned=%d)", - query, - mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err); - - /* Read result from each row */ - for (row_idx= 0; row_idx < num_rows; row_idx++) - { - if ((err= mysql_stmt_fetch(stmt)) != 0) - die("unable to fetch all rows from statement '%s': " - "%s (mysql_stmt_errno=%d returned=%d)", - query, - mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err); - - /* Read result from each column */ - for (col_idx= 0; col_idx < num_fields; col_idx++) - { - const char *val; - ulonglong len; - if (col_idx < max_replace_column && replace_column[col_idx]) - { - val= replace_column[col_idx]; - len= strlen(val); - } - else if (*bind[col_idx].is_null) - { - val= "NULL"; - len= 4; - } - else - { - /* FIXME is string terminated? */ - val= (const char *) bind[col_idx].buffer; - len= *bind[col_idx].length; - } - if (!display_result_vertically) - { - if (col_idx) /* No tab before first col */ - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_mem(ds, val, len); - } - else - { - dynstr_append(ds, field[col_idx].name); - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_mem(ds, val, len); - dynstr_append_mem(ds, "\n", 1); - } - } - if (!display_result_vertically) - dynstr_append_mem(ds, "\n", 1); - } - - if ((err= mysql_stmt_fetch(stmt)) != MYSQL_NO_DATA) - die("fetch didn't end with MYSQL_NO_DATA from statement " - "'%s': %s (mysql_stmt_errno=%d returned=%d)", - query, - mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err); - - free_replace_column(); - - for (col_idx= 0; col_idx < num_fields; col_idx++) - { - /* Free data for output */ - my_free((gptr)bind[col_idx].buffer, MYF(MY_WME | MY_FAE)); - } - /* Free array with bind structs, lengths and NULL flags */ - my_free((gptr)bind , MYF(MY_WME | MY_FAE)); - my_free((gptr)length , MYF(MY_WME | MY_FAE)); - my_free((gptr)is_null , MYF(MY_WME | MY_FAE)); - } - - /* Add all warnings to the result */ - run_query_stmt_handle_warnings(mysql, ds); - - if (!disable_info) - { - char buf[40]; - sprintf(buf,"affected rows: %lu\n",(ulong) mysql_affected_rows(mysql)); - dynstr_append(ds, buf); - if (mysql_info(mysql)) - { - dynstr_append(ds, "info: "); - dynstr_append(ds, mysql_info(mysql)); - dynstr_append_mem(ds, "\n", 1); - } - } - } - run_query_stmt_handle_warnings(mysql, ds); - - if (record) - { - if (!q->record_file[0] && !result_file) - die("Missing result file"); - if (!result_file) - str_to_file(q->record_file, ds->str, ds->length); - } - else if (q->record_file[0]) - { - error= check_result(ds, q->record_file, q->require_file); - } - if (res) - mysql_free_result(res); /* Free normal result set with meta data */ - last_result= 0; /* FIXME have no idea what this is about... */ - - if (err >= 1) - mysql_error(mysql); /* FIXME strange, has no effect... */ - -end: - free_replace(); - last_result=0; - if (ds == &ds_tmp) - dynstr_free(&ds_tmp); - if (q->type == Q_EVAL) - dynstr_free(&eval_query); - var_set_errno(mysql_stmt_errno(stmt)); - mysql_stmt_close(stmt); - DBUG_RETURN(error); -} - - -/****************************************************************************\ - * Broken out sub functions to run_query_stmt() -\****************************************************************************/ - -static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields, - DYNAMIC_STRING *ds) +void append_metadata(DYNAMIC_STRING *ds, + MYSQL_FIELD *field, + uint num_fields) { MYSQL_FIELD *field_end; dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\t" @@ -3395,147 +4221,360 @@ static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields, field < field_end ; field++) { - char buff[22]; dynstr_append_mem(ds, field->catalog, - field->catalog_length); + field->catalog_length); dynstr_append_mem(ds, "\t", 1); dynstr_append_mem(ds, field->db, field->db_length); dynstr_append_mem(ds, "\t", 1); dynstr_append_mem(ds, field->org_table, - field->org_table_length); + field->org_table_length); dynstr_append_mem(ds, "\t", 1); dynstr_append_mem(ds, field->table, - field->table_length); + field->table_length); dynstr_append_mem(ds, "\t", 1); dynstr_append_mem(ds, field->org_name, - field->org_name_length); + field->org_name_length); dynstr_append_mem(ds, "\t", 1); dynstr_append_mem(ds, field->name, field->name_length); dynstr_append_mem(ds, "\t", 1); - int10_to_str((int) field->type, buff, 10); - dynstr_append(ds, buff); + replace_dynstr_append_uint(ds, field->type); dynstr_append_mem(ds, "\t", 1); - longlong10_to_str((unsigned int) field->length, buff, 10); - dynstr_append(ds, buff); + replace_dynstr_append_uint(ds, field->length); dynstr_append_mem(ds, "\t", 1); - longlong10_to_str((unsigned int) field->max_length, buff, 10); - dynstr_append(ds, buff); + replace_dynstr_append_uint(ds, field->max_length); dynstr_append_mem(ds, "\t", 1); dynstr_append_mem(ds, (char*) (IS_NOT_NULL(field->flags) ? "N" : "Y"), 1); dynstr_append_mem(ds, "\t", 1); - - int10_to_str((int) field->flags, buff, 10); - dynstr_append(ds, buff); + replace_dynstr_append_uint(ds, field->flags); dynstr_append_mem(ds, "\t", 1); - int10_to_str((int) field->decimals, buff, 10); - dynstr_append(ds, buff); + replace_dynstr_append_uint(ds, field->decimals); dynstr_append_mem(ds, "\t", 1); - int10_to_str((int) field->charsetnr, buff, 10); - dynstr_append(ds, buff); + replace_dynstr_append_uint(ds, field->charsetnr); dynstr_append_mem(ds, "\n", 1); } } -static void run_query_stmt_handle_warnings(MYSQL *mysql, DYNAMIC_STRING *ds) +/* + Append affected row count and other info to output +*/ + +void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows, + const char *info) +{ + char buf[40], buff2[21]; + sprintf(buf,"affected rows: %s\n", llstr(affected_rows, buff2)); + dynstr_append(ds, buf); + if (info) + { + dynstr_append(ds, "info: "); + dynstr_append(ds, info); + dynstr_append_mem(ds, "\n", 1); + } +} + + +/* + Display the table headings with the names tab separated +*/ + +void append_table_headings(DYNAMIC_STRING *ds, + MYSQL_FIELD *field, + uint num_fields) +{ + uint col_idx; + for (col_idx= 0; col_idx < num_fields; col_idx++) + { + if (col_idx) + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append(ds, field[col_idx].name); + } + dynstr_append_mem(ds, "\n", 1); +} + +/* + Fetch warnings from server and append to ds + + RETURN VALUE + Number of warnings appended to ds +*/ + +int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql) { uint count; - DBUG_ENTER("run_query_stmt_handle_warnings"); + MYSQL_RES *warn_res; + DBUG_ENTER("append_warnings"); - if (!disable_warnings && (count= mysql_warning_count(mysql))) + if (!(count= mysql_warning_count(mysql))) + DBUG_RETURN(0); + + /* + If one day we will support execution of multi-statements + through PS API we should not issue SHOW WARNINGS until + we have not read all results... + */ + DBUG_ASSERT(!mysql_more_results(mysql)); + + if (mysql_real_query(mysql, "SHOW WARNINGS", 13)) + die("Error running query \"SHOW WARNINGS\": %s", mysql_error(mysql)); + + if (!(warn_res= mysql_store_result(mysql))) + die("Warning count is %u but didn't get any warnings", + count); + + append_result(ds, warn_res); + mysql_free_result(warn_res); + + DBUG_PRINT("warnings", ("%s", ds->str)); + + DBUG_RETURN(count); +} + + + +/* + Run query using MySQL C API + + SYNPOSIS + run_query_normal + mysql - mysql handle + command - currrent command pointer + flags -flags indicating wheter to SEND and/or REAP + query - query string to execute + query_len - length query string to execute + ds - output buffer wherte to store result form query + + RETURN VALUE + error - function will not return +*/ + +void run_query_normal(MYSQL *mysql, struct st_command *command, + int flags, char *query, int query_len, + DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings) +{ + MYSQL_RES *res= 0; + int err= 0, counter= 0; + DBUG_ENTER("run_query_normal"); + DBUG_PRINT("enter",("flags: %d", flags)); + DBUG_PRINT("enter", ("query: '%-.60s'", query)); + + if (flags & QUERY_SEND_FLAG) { - if (mysql_real_query(mysql, "SHOW WARNINGS", 13) == 0) + /* + Send the query + */ + if (mysql_send_query(mysql, query, query_len)) { - MYSQL_RES *warn_res= mysql_store_result(mysql); - if (!warn_res) - die("Warning count is %u but didn't get any warnings\n", - count); - else - { - dynstr_append_mem(ds, "Warnings:\n", 10); - append_result(ds, warn_res); - mysql_free_result(warn_res); - } + handle_error(command, mysql_errno(mysql), mysql_error(mysql), + mysql_sqlstate(mysql), ds); + goto end; } } + + if (!(flags & QUERY_REAP_FLAG)) + DBUG_VOID_RETURN; + + do + { + /* + When on first result set, call mysql_read_query_result to retrieve + answer to the query sent earlier + */ + if ((counter==0) && mysql_read_query_result(mysql)) + { + handle_error(command, mysql_errno(mysql), mysql_error(mysql), + mysql_sqlstate(mysql), ds); + goto end; + + } + + /* + Store the result. If res is NULL, use mysql_field_count to + determine if that was expected + */ + if (!(res= mysql_store_result(mysql)) && mysql_field_count(mysql)) + { + handle_error(command, mysql_errno(mysql), mysql_error(mysql), + mysql_sqlstate(mysql), ds); + goto end; + } + + if (!disable_result_log) + { + ulonglong affected_rows; /* Ok to be undef if 'disable_info' is set */ + LINT_INIT(affected_rows); + + if (res) + { + MYSQL_FIELD *fields= mysql_fetch_fields(res); + uint num_fields= mysql_num_fields(res); + + if (display_metadata) + append_metadata(ds, fields, num_fields); + + if (!display_result_vertically) + append_table_headings(ds, fields, num_fields); + + append_result(ds, res); + } + + /* + Need to call mysql_affected_rows() before the "new" + query to find the warnings + */ + if (!disable_info) + affected_rows= mysql_affected_rows(mysql); + + /* + Add all warnings to the result. We can't do this if we are in + the middle of processing results from multi-statement, because + this will break protocol. + */ + if (!disable_warnings && !mysql_more_results(mysql)) + { + if (append_warnings(ds_warnings, mysql) || ds_warnings->length) + { + dynstr_append_mem(ds, "Warnings:\n", 10); + dynstr_append_mem(ds, ds_warnings->str, ds_warnings->length); + } + } + + if (!disable_info) + append_info(ds, affected_rows, mysql_info(mysql)); + } + + if (res) + mysql_free_result(res); + counter++; + } while (!(err= mysql_next_result(mysql))); + if (err > 0) + { + /* We got an error from mysql_next_result, maybe expected */ + handle_error(command, mysql_errno(mysql), mysql_error(mysql), + mysql_sqlstate(mysql), ds); + goto end; + } + DBUG_ASSERT(err == -1); /* Successful and there are no more results */ + + /* If we come here the query is both executed and read successfully */ + handle_no_error(command); + +end: + + /* + We save the return code (mysql_errno(mysql)) from the last call sent + to the server into the mysqltest builtin variable $mysql_errno. This + variable then can be used from the test case itself. + */ + var_set_errno(mysql_errno(mysql)); DBUG_VOID_RETURN; } -static int run_query_stmt_handle_error(char *query, struct st_query *q, - MYSQL_STMT *stmt, DYNAMIC_STRING *ds) +/* + Handle errors which occurred during execution + + SYNOPSIS + handle_error() + q - query context + err_errno - error number + err_error - error message + err_sqlstate - sql state + ds - dynamic string which is used for output buffer + + NOTE + If there is an unexpected error this function will abort mysqltest + immediately. + + RETURN VALUE + error - function will not return +*/ + +void handle_error(struct st_command *command, + unsigned int err_errno, const char *err_error, + const char *err_sqlstate, DYNAMIC_STRING *ds) { - if (q->require_file) /* FIXME don't understand this one */ + uint i; + + DBUG_ENTER("handle_error"); + + if (command->require_file) { - abort_not_supported_test(); + /* + The query after a "--require" failed. This is fine as long the server + returned a valid reponse. Don't allow 2013 or 2006 to trigger an + abort_not_supported_test + */ + if (err_errno == CR_SERVER_LOST || + err_errno == CR_SERVER_GONE_ERROR) + die("require query '%s' failed: %d: %s", command->query, err_errno, err_error); + + /* Abort the run of this test, pass the failed query as reason */ + abort_not_supported_test("Query '%s' failed, required functionality" \ + "not supported", command->query); } - if (q->abort_on_error) - die("query '%s' failed: %d: %s", query, - mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); - else - { - int i; + if (command->abort_on_error) + die("query '%s' failed: %d: %s", command->query, err_errno, err_error); - for (i=0 ; (uint) i < q->expected_errors ; i++) + DBUG_PRINT("info", ("expected_errors.count: %d", + command->expected_errors.count)); + for (i= 0 ; (uint) i < command->expected_errors.count ; i++) + { + if (((command->expected_errors.err[i].type == ERR_ERRNO) && + (command->expected_errors.err[i].code.errnum == err_errno)) || + ((command->expected_errors.err[i].type == ERR_SQLSTATE) && + (strcmp(command->expected_errors.err[i].code.sqlstate, + err_sqlstate) == 0))) { - if (((q->expected_errno[i].type == ERR_ERRNO) && - (q->expected_errno[i].code.errnum == mysql_stmt_errno(stmt))) || - ((q->expected_errno[i].type == ERR_SQLSTATE) && - (strcmp(q->expected_errno[i].code.sqlstate, - mysql_stmt_sqlstate(stmt)) == 0))) + if (!disable_result_log) { - if (i == 0 && q->expected_errors == 1) + if (command->expected_errors.count == 1) { /* Only log error if there is one possible error */ - dynstr_append_mem(ds,"ERROR ",6); - replace_dynstr_append_mem(ds, mysql_stmt_sqlstate(stmt), - strlen(mysql_stmt_sqlstate(stmt))); + dynstr_append_mem(ds, "ERROR ", 6); + replace_dynstr_append(ds, err_sqlstate); dynstr_append_mem(ds, ": ", 2); - replace_dynstr_append_mem(ds,mysql_stmt_error(stmt), - strlen(mysql_stmt_error(stmt))); + replace_dynstr_append(ds, err_error); dynstr_append_mem(ds,"\n",1); } /* Don't log error if we may not get an error */ - else if (q->expected_errno[0].type == ERR_SQLSTATE || - (q->expected_errno[0].type == ERR_ERRNO && - q->expected_errno[0].code.errnum != 0)) + else if (command->expected_errors.err[0].type == ERR_SQLSTATE || + (command->expected_errors.err[0].type == ERR_ERRNO && + command->expected_errors.err[0].code.errnum != 0)) dynstr_append(ds,"Got one of the listed errors\n"); - return 0; /* Ok */ } + /* OK */ + DBUG_VOID_RETURN; } - DBUG_PRINT("info",("i: %d expected_errors: %d", i, - q->expected_errors)); - dynstr_append_mem(ds, "ERROR ",6); - replace_dynstr_append_mem(ds, mysql_stmt_sqlstate(stmt), - strlen(mysql_stmt_sqlstate(stmt))); - dynstr_append_mem(ds,": ",2); - replace_dynstr_append_mem(ds, mysql_stmt_error(stmt), - strlen(mysql_stmt_error(stmt))); - dynstr_append_mem(ds,"\n",1); - if (i) - { - if (q->expected_errno[0].type == ERR_ERRNO) - verbose_msg("query '%s' failed with wrong errno %d instead of %d...", - q->query, mysql_stmt_errno(stmt), - q->expected_errno[0].code.errnum); - else - verbose_msg("query '%s' failed with wrong sqlstate %s instead of %s...", - q->query, mysql_stmt_sqlstate(stmt), - q->expected_errno[0].code.sqlstate); - return 1; /* Error */ - } - verbose_msg("query '%s' failed: %d: %s", q->query, mysql_stmt_errno(stmt), - mysql_stmt_error(stmt)); - /* - if we do not abort on error, failure to run the query does - not fail the whole test case - */ - return 0; } - return 0; + DBUG_PRINT("info",("i: %d expected_errors: %d", i, + command->expected_errors)); + + if (!disable_result_log) + { + dynstr_append_mem(ds, "ERROR ",6); + replace_dynstr_append(ds, err_sqlstate); + dynstr_append_mem(ds, ": ", 2); + replace_dynstr_append(ds, err_error); + dynstr_append_mem(ds, "\n", 1); + } + + if (i) + { + if (command->expected_errors.err[0].type == ERR_ERRNO) + die("query '%s' failed with wrong errno %d: '%s', instead of %d...", + command->query, err_errno, err_error, + command->expected_errors.err[0].code.errnum); + else + die("query '%s' failed with wrong sqlstate %s: '%s', instead of %s...", + command->query, err_sqlstate, err_error, + command->expected_errors.err[0].code.sqlstate); + } + + DBUG_VOID_RETURN; } @@ -3543,44 +4582,518 @@ static int run_query_stmt_handle_error(char *query, struct st_query *q, Handle absence of errors after execution SYNOPSIS - handle_no_error() - q - context of query + handle_no_error() + q - context of query RETURN VALUE - 0 - OK - 1 - Some error was expected from this query. + error - function will not return */ -static int handle_no_error(struct st_query *q) +void handle_no_error(struct st_command *command) { DBUG_ENTER("handle_no_error"); - if (q->expected_errno[0].type == ERR_ERRNO && - q->expected_errno[0].code.errnum != 0) + if (command->expected_errors.err[0].type == ERR_ERRNO && + command->expected_errors.err[0].code.errnum != 0) { /* Error code we wanted was != 0, i.e. not an expected success */ - verbose_msg("query '%s' succeeded - should have failed with errno %d...", - q->query, q->expected_errno[0].code.errnum); - DBUG_RETURN(1); + die("query '%s' succeeded - should have failed with errno %d...", + command->query, command->expected_errors.err[0].code.errnum); } - else if (q->expected_errno[0].type == ERR_SQLSTATE && - strcmp(q->expected_errno[0].code.sqlstate,"00000") != 0) + else if (command->expected_errors.err[0].type == ERR_SQLSTATE && + strcmp(command->expected_errors.err[0].code.sqlstate,"00000") != 0) { /* SQLSTATE we wanted was != "00000", i.e. not an expected success */ - verbose_msg("query '%s' succeeded - should have failed with sqlstate %s...", - q->query, q->expected_errno[0].code.sqlstate); - DBUG_RETURN(1); + die("query '%s' succeeded - should have failed with sqlstate %s...", + command->query, command->expected_errors.err[0].code.sqlstate); } - DBUG_RETURN(0); + DBUG_VOID_RETURN; } -/****************************************************************************\ - * Functions to match SQL statements that can be prepared -\****************************************************************************/ -static void ps_init_re(void) +/* + Run query using prepared statement C API + + SYNPOSIS + run_query_stmt + mysql - mysql handle + command - currrent command pointer + query - query string to execute + query_len - length query string to execute + ds - output buffer where to store result form query + + RETURN VALUE + error - function will not return +*/ + +void run_query_stmt(MYSQL *mysql, struct st_command *command, + char *query, int query_len, DYNAMIC_STRING *ds, + DYNAMIC_STRING *ds_warnings) { + MYSQL_RES *res= NULL; /* Note that here 'res' is meta data result set */ + MYSQL_STMT *stmt; + DYNAMIC_STRING ds_prepare_warnings; + DYNAMIC_STRING ds_execute_warnings; + DBUG_ENTER("run_query_stmt"); + DBUG_PRINT("query", ("'%-.60s'", query)); + + /* + Init a new stmt if it's not already one created for this connection + */ + if(!(stmt= cur_con->stmt)) + { + if (!(stmt= mysql_stmt_init(mysql))) + die("unable to init stmt structure"); + cur_con->stmt= stmt; + } + + /* Init dynamic strings for warnings */ + if (!disable_warnings) + { + init_dynamic_string(&ds_prepare_warnings, NULL, 0, 256); + init_dynamic_string(&ds_execute_warnings, NULL, 0, 256); + } + + /* + Prepare the query + */ + if (mysql_stmt_prepare(stmt, query, query_len)) + { + handle_error(command, mysql_stmt_errno(stmt), + mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); + goto end; + } + + /* + Get the warnings from mysql_stmt_prepare and keep them in a + separate string + */ + if (!disable_warnings) + append_warnings(&ds_prepare_warnings, mysql); + + /* + No need to call mysql_stmt_bind_param() because we have no + parameter markers. + */ + +#if MYSQL_VERSION_ID >= 50000 + if (cursor_protocol_enabled) + { + /* + Use cursor when retrieving result + */ + ulong type= CURSOR_TYPE_READ_ONLY; + if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type)) + die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s", + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); + } +#endif + + /* + Execute the query + */ + if (mysql_stmt_execute(stmt)) + { + handle_error(command, mysql_stmt_errno(stmt), + mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); + goto end; + } + + /* + When running in cursor_protocol get the warnings from execute here + and keep them in a separate string for later. + */ + if (cursor_protocol_enabled && !disable_warnings) + append_warnings(&ds_execute_warnings, mysql); + + /* + We instruct that we want to update the "max_length" field in + mysql_stmt_store_result(), this is our only way to know how much + buffer to allocate for result data + */ + { + my_bool one= 1; + if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one)) + die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s", + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); + } + + /* + If we got here the statement succeeded and was expected to do so, + get data. Note that this can still give errors found during execution! + */ + if (mysql_stmt_store_result(stmt)) + { + handle_error(command, mysql_stmt_errno(stmt), + mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); + goto end; + } + + /* If we got here the statement was both executed and read successfully */ + handle_no_error(command); + if (!disable_result_log) + { + /* + Not all statements creates a result set. If there is one we can + now create another normal result set that contains the meta + data. This set can be handled almost like any other non prepared + statement result set. + */ + if ((res= mysql_stmt_result_metadata(stmt)) != NULL) + { + /* Take the column count from meta info */ + MYSQL_FIELD *fields= mysql_fetch_fields(res); + uint num_fields= mysql_num_fields(res); + + if (display_metadata) + append_metadata(ds, fields, num_fields); + + if (!display_result_vertically) + append_table_headings(ds, fields, num_fields); + + append_stmt_result(ds, stmt, fields, num_fields); + + mysql_free_result(res); /* Free normal result set with meta data */ + + /* Clear prepare warnings */ + dynstr_set(&ds_prepare_warnings, NULL); + } + else + { + /* + This is a query without resultset + */ + } + + if (!disable_warnings) + { + /* Get the warnings from execute */ + + /* Append warnings to ds - if there are any */ + if (append_warnings(&ds_execute_warnings, mysql) || + ds_execute_warnings.length || + ds_prepare_warnings.length || + ds_warnings->length) + { + dynstr_append_mem(ds, "Warnings:\n", 10); + if (ds_warnings->length) + dynstr_append_mem(ds, ds_warnings->str, + ds_warnings->length); + if (ds_prepare_warnings.length) + dynstr_append_mem(ds, ds_prepare_warnings.str, + ds_prepare_warnings.length); + if (ds_execute_warnings.length) + dynstr_append_mem(ds, ds_execute_warnings.str, + ds_execute_warnings.length); + } + } + + if (!disable_info) + append_info(ds, mysql_affected_rows(mysql), mysql_info(mysql)); + + } + +end: + if (!disable_warnings) + { + dynstr_free(&ds_prepare_warnings); + dynstr_free(&ds_execute_warnings); + } + + /* + We save the return code (mysql_stmt_errno(stmt)) from the last call sent + to the server into the mysqltest builtin variable $mysql_errno. This + variable then can be used from the test case itself. + */ + + var_set_errno(mysql_stmt_errno(stmt)); +#ifndef BUG15518_FIXED + mysql_stmt_close(stmt); + cur_con->stmt= NULL; +#endif + DBUG_VOID_RETURN; +} + + + +/* + Create a util connection if one does not already exists + and use that to run the query + This is done to avoid implict commit when creating/dropping objects such + as view, sp etc. +*/ + +int util_query(MYSQL* org_mysql, const char* query){ + + MYSQL* mysql; + DBUG_ENTER("util_query"); + + if(!(mysql= cur_con->util_mysql)) + { + DBUG_PRINT("info", ("Creating util_mysql")); + if (!(mysql= mysql_init(mysql))) + die("Failed in mysql_init()"); + + safe_connect(mysql, "util", org_mysql->host, org_mysql->user, + org_mysql->passwd, org_mysql->db, org_mysql->port, + org_mysql->unix_socket); + + cur_con->util_mysql= mysql; + } + + return mysql_query(mysql, query); +} + + + +/* + Run query + + flags control the phased/stages of query execution to be performed + if QUERY_SEND_FLAG bit is on, the query will be sent. If QUERY_REAP_FLAG + is on the result will be read - for regular query, both bits must be on + + SYNPOSIS + run_query + mysql - mysql handle + command - currrent command pointer + +*/ + +void run_query(MYSQL *mysql, struct st_command *command, int flags) +{ + DYNAMIC_STRING *ds; + DYNAMIC_STRING ds_result; + DYNAMIC_STRING ds_warnings; + DYNAMIC_STRING eval_query; + char *query; + int query_len; + my_bool view_created= 0, sp_created= 0; + my_bool complete_query= ((flags & QUERY_SEND_FLAG) && + (flags & QUERY_REAP_FLAG)); + + init_dynamic_string(&ds_warnings, NULL, 0, 256); + + /* + Evaluate query if this is an eval command + */ + if (command->type == Q_EVAL) + { + init_dynamic_string(&eval_query, "", command->query_len+256, 1024); + do_eval(&eval_query, command->query, command->end, FALSE); + query = eval_query.str; + query_len = eval_query.length; + } + else + { + query = command->query; + query_len = strlen(query); + } + + /* + When command->record_file is set the output of _this_ query + should be compared with an already existing file + Create a temporary dynamic string to contain the output from + this query. + */ + if (command->record_file[0]) + { + init_dynamic_string(&ds_result, "", 1024, 1024); + ds= &ds_result; + } + else + ds= &ds_res; + + /* + Log the query into the output buffer + */ + if (!disable_query_log && (flags & QUERY_SEND_FLAG)) + { + replace_dynstr_append_mem(ds, query, query_len); + dynstr_append_mem(ds, delimiter, delimiter_length); + dynstr_append_mem(ds, "\n", 1); + } + + if (view_protocol_enabled && + complete_query && + match_re(&view_re, query)) + { + /* + Create the query as a view. + Use replace since view can exist from a failed mysqltest run + */ + DYNAMIC_STRING query_str; + init_dynamic_string(&query_str, + "CREATE OR REPLACE VIEW mysqltest_tmp_v AS ", + query_len+64, 256); + dynstr_append_mem(&query_str, query, query_len); + if (util_query(mysql, query_str.str)) + { + /* + Failed to create the view, this is not fatal + just run the query the normal way + */ + DBUG_PRINT("view_create_error", + ("Failed to create view '%s': %d: %s", query_str.str, + mysql_errno(mysql), mysql_error(mysql))); + + /* Log error to create view */ + verbose_msg("Failed to create view '%s' %d: %s", query_str.str, + mysql_errno(mysql), mysql_error(mysql)); + } + else + { + /* + Yes, it was possible to create this query as a view + */ + view_created= 1; + query= (char*)"SELECT * FROM mysqltest_tmp_v"; + query_len = strlen(query); + + /* + Collect warnings from create of the view that should otherwise + have been produced when the SELECT was executed + */ + append_warnings(&ds_warnings, cur_con->util_mysql); + } + + dynstr_free(&query_str); + + } + + if (sp_protocol_enabled && + complete_query && + match_re(&sp_re, query)) + { + /* + Create the query as a stored procedure + Drop first since sp can exist from a failed mysqltest run + */ + DYNAMIC_STRING query_str; + init_dynamic_string(&query_str, + "DROP PROCEDURE IF EXISTS mysqltest_tmp_sp;", + query_len+64, 256); + util_query(mysql, query_str.str); + dynstr_set(&query_str, "CREATE PROCEDURE mysqltest_tmp_sp()\n"); + dynstr_append_mem(&query_str, query, query_len); + if (util_query(mysql, query_str.str)) + { + /* + Failed to create the stored procedure for this query, + this is not fatal just run the query the normal way + */ + DBUG_PRINT("sp_create_error", + ("Failed to create sp '%s': %d: %s", query_str.str, + mysql_errno(mysql), mysql_error(mysql))); + + /* Log error to create sp */ + verbose_msg("Failed to create sp '%s' %d: %s", query_str.str, + mysql_errno(mysql), mysql_error(mysql)); + + } + else + { + sp_created= 1; + + query= (char*)"CALL mysqltest_tmp_sp()"; + query_len = strlen(query); + } + dynstr_free(&query_str); + } + + /* + Find out how to run this query + + Always run with normal C API if it's not a complete + SEND + REAP + + If it is a '?' in the query it may be a SQL level prepared + statement already and we can't do it twice + */ + if (ps_protocol_enabled && + complete_query && + match_re(&ps_re, query)) + run_query_stmt(mysql, command, query, query_len, ds, &ds_warnings); + else + run_query_normal(mysql, command, flags, query, query_len, + ds, &ds_warnings); + + if (sp_created) + { + if (util_query(mysql, "DROP PROCEDURE mysqltest_tmp_sp ")) + die("Failed to drop sp: %d: %s", mysql_errno(mysql), mysql_error(mysql)); + } + + if (view_created) + { + if (util_query(mysql, "DROP VIEW mysqltest_tmp_v ")) + die("Failed to drop view: %d: %s", + mysql_errno(mysql), mysql_error(mysql)); + } + + if (command->record_file[0]) + { + + /* A result file was specified for _this_ query */ + if (record) + { + /* + Recording in progress + Dump the output from _this_ query to the specified record_file + */ + str_to_file(command->record_file, ds->str, ds->length); + + } else { + + /* + The output from _this_ query should be checked against an already + existing file which has been specified using --require or --result + */ + check_result(ds, command->record_file, command->require_file); + } + } + + dynstr_free(&ds_warnings); + if (ds == &ds_result) + dynstr_free(&ds_result); + if (command->type == Q_EVAL) + dynstr_free(&eval_query); +} + +/****************************************************************************/ +/* + Functions to detect different SQL statements +*/ + +char *re_eprint(int err) +{ + static char epbuf[100]; + size_t len= my_regerror(REG_ITOA|err, (my_regex_t *)NULL, + epbuf, sizeof(epbuf)); + assert(len <= sizeof(epbuf)); + return(epbuf); +} + +void init_re_comp(my_regex_t *re, const char* str) +{ + int err= my_regcomp(re, str, (REG_EXTENDED | REG_ICASE | REG_NOSUB), + &my_charset_latin1); + if (err) + { + char erbuf[100]; + int len= my_regerror(err, re, erbuf, sizeof(erbuf)); + die("error %s, %d/%d `%s'\n", + re_eprint(err), len, (int)sizeof(erbuf), erbuf); + } +} + +void init_re(void) +{ + /* + Filter for queries that can be run using the + MySQL Prepared Statements C API + */ const char *ps_re_str = "^(" "[[:space:]]*REPLACE[[:space:]]|" @@ -3595,199 +5108,210 @@ static void ps_init_re(void) "[[:space:]]*UPDATE[[:space:]]+MULTI[[:space:]]|" "[[:space:]]*INSERT[[:space:]]+SELECT[[:space:]])"; - int err= my_regcomp(&ps_re, ps_re_str, - (REG_EXTENDED | REG_ICASE | REG_NOSUB), - &my_charset_latin1); - if (err) - { - char erbuf[100]; - int len= my_regerror(err, &ps_re, erbuf, sizeof(erbuf)); - fprintf(stderr, "error %s, %d/%d `%s'\n", - ps_eprint(err), len, (int)sizeof(erbuf), erbuf); - exit(1); - } + /* + Filter for queries that can be run using the + Stored procedures + */ + const char *sp_re_str =ps_re_str; + + /* + Filter for queries that can be run as views + */ + const char *view_re_str = + "^(" + "[[:space:]]*SELECT[[:space:]])"; + + init_re_comp(&ps_re, ps_re_str); + init_re_comp(&sp_re, sp_re_str); + init_re_comp(&view_re, view_re_str); } -static int ps_match_re(char *stmt_str) +int match_re(my_regex_t *re, char *str) { - int err= my_regexec(&ps_re, stmt_str, (size_t)0, NULL, 0); + int err= my_regexec(re, str, (size_t)0, NULL, 0); if (err == 0) return 1; else if (err == REG_NOMATCH) return 0; - else + { char erbuf[100]; - int len= my_regerror(err, &ps_re, erbuf, sizeof(erbuf)); - fprintf(stderr, "error %s, %d/%d `%s'\n", - ps_eprint(err), len, (int)sizeof(erbuf), erbuf); - exit(1); + int len= my_regerror(err, re, erbuf, sizeof(erbuf)); + die("error %s, %d/%d `%s'\n", + re_eprint(err), len, (int)sizeof(erbuf), erbuf); } + return 0; } -static char *ps_eprint(int err) -{ - static char epbuf[100]; - size_t len= my_regerror(REG_ITOA|err, (my_regex_t *)NULL, epbuf, sizeof(epbuf)); - assert(len <= sizeof(epbuf)); - return(epbuf); -} - - -static void ps_free_reg(void) +void free_re(void) { my_regfree(&ps_re); + my_regfree(&sp_re); + my_regfree(&view_re); + my_regex_end(); } /****************************************************************************/ -void get_query_type(struct st_query* q) +void get_command_type(struct st_command* command) { char save; uint type; - DBUG_ENTER("get_query_type"); + DBUG_ENTER("get_command_type"); - if (*q->query == '}') + if (!parsing_disabled && *command->query == '}') { - q->type = Q_END_BLOCK; + command->type = Q_END_BLOCK; DBUG_VOID_RETURN; } - if (q->type != Q_COMMENT_WITH_COMMAND) - q->type = Q_QUERY; + if (command->type != Q_COMMENT_WITH_COMMAND) + command->type= parsing_disabled ? Q_COMMENT : Q_QUERY; - save=q->query[q->first_word_len]; - q->query[q->first_word_len]=0; - type=find_type(q->query, &command_typelib, 1+2); - q->query[q->first_word_len]=save; + save=command->query[command->first_word_len]; + command->query[command->first_word_len]=0; + type=find_type(command->query, &command_typelib, 1+2); + command->query[command->first_word_len]=save; if (type > 0) - q->type=(enum enum_commands) type; /* Found command */ - DBUG_VOID_RETURN; -} - - -static byte *get_var_key(const byte* var, uint* len, - my_bool __attribute__((unused)) t) -{ - register char* key; - key = ((VAR*)var)->name; - *len = ((VAR*)var)->name_len; - return (byte*)key; -} - -static VAR *var_init(VAR *v, const char *name, int name_len, const char *val, - int val_len) -{ - int val_alloc_len; - VAR *tmp_var; - if (!name_len && name) - name_len = strlen(name); - if (!val_len && val) - val_len = strlen(val) ; - val_alloc_len = val_len + 16; /* room to grow */ - if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var) - + name_len, MYF(MY_WME)))) - die("Out of memory"); - - tmp_var->name = (name) ? (char*) tmp_var + sizeof(*tmp_var) : 0; - tmp_var->alloced = (v == 0); - - if (!(tmp_var->str_val = my_malloc(val_alloc_len+1, MYF(MY_WME)))) - die("Out of memory"); - - memcpy(tmp_var->name, name, name_len); - if (val) { - memcpy(tmp_var->str_val, val, val_len); - tmp_var->str_val[val_len]=0; + command->type=(enum enum_commands) type; /* Found command */ + /* + If queries are disabled, only recognize + --enable_parsing and --disable_parsing + */ + if (parsing_disabled && command->type != Q_ENABLE_PARSING && + command->type != Q_DISABLE_PARSING) + command->type= Q_COMMENT; + + /* + Look for case where "query" was explicitly specified to + force command being sent to server + */ + if (type == Q_QUERY) + { + /* Skip the "query" part */ + command->query= command->first_argument; + } + } + else if (command->type == Q_COMMENT_WITH_COMMAND && + command->first_word_len && + strcmp(command->query + command->first_word_len - 1, delimiter) == 0) + { + /* + Detect comment with command using extra delimiter + Ex --disable_query_log; + ^ Extra delimiter causing the command + to be skipped + */ + save= command->query[command->first_word_len-1]; + command->query[command->first_word_len-1]= 0; + type= find_type(command->query, &command_typelib, 1+2); + command->query[command->first_word_len-1]= save; + if (type > 0) + die("Extra delimiter \";\" found"); } - tmp_var->name_len = name_len; - tmp_var->str_val_len = val_len; - tmp_var->alloced_len = val_alloc_len; - tmp_var->int_val = (val) ? atoi(val) : 0; - tmp_var->int_dirty = 0; - return tmp_var; -} - -static void var_free(void *v) -{ - my_free(((VAR*) v)->str_val, MYF(MY_WME)); - if (((VAR*)v)->alloced) - my_free((char*) v, MYF(MY_WME)); -} - - -static VAR* var_from_env(const char *name, const char *def_val) -{ - const char *tmp; - VAR *v; - if (!(tmp = getenv(name))) - tmp = def_val; - - v = var_init(0, name, strlen(name), tmp, strlen(tmp)); - my_hash_insert(&var_hash, (byte*)v); - return v; -} - - -static void init_var_hash(MYSQL *mysql) -{ - VAR *v; - DBUG_ENTER("init_var_hash"); - if (hash_init(&var_hash, charset_info, - 1024, 0, 0, get_var_key, var_free, MYF(0))) - die("Variable hash initialization failed"); - if (opt_big_test) - my_hash_insert(&var_hash, (byte*) var_init(0,"BIG_TEST", 0, "1",0)); - v= var_init(0,"MAX_TABLES", 0, (sizeof(ulong) == 4) ? "31" : "62",0); - my_hash_insert(&var_hash, (byte*) v); - v= var_init(0,"SERVER_VERSION", 0, mysql_get_server_info(mysql), 0); - my_hash_insert(&var_hash, (byte*) v); - v= var_init(0,"DB", 2, db, 0); - my_hash_insert(&var_hash, (byte*) v); DBUG_VOID_RETURN; } + +/* + Record how many milliseconds it took to execute the test file + up until the current line and save it in the dynamic string ds_progress. + + The ds_progress will be dumped to .progress when + test run completes + +*/ + +void mark_progress(struct st_command* command __attribute__((unused)), + int line) +{ + char buf[32], *end; + ulonglong timer= timer_now(); + if (!progress_start) + progress_start= timer; + timer-= progress_start; + + /* Milliseconds since start */ + end= longlong2str(timer, buf, 10); + dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); + dynstr_append_mem(&ds_progress, "\t", 1); + + /* Parser line number */ + end= int10_to_str(line, buf, 10); + dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); + dynstr_append_mem(&ds_progress, "\t", 1); + + /* Filename */ + dynstr_append(&ds_progress, cur_file->file_name); + dynstr_append_mem(&ds_progress, ":", 1); + + /* Line in file */ + end= int10_to_str(cur_file->lineno, buf, 10); + dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); + + + dynstr_append_mem(&ds_progress, "\n", 1); + +} + + int main(int argc, char **argv) { - int error = 0; - struct st_query *q; - my_bool require_file=0, q_send_flag=0, query_executed= 0, abort_flag= 0; + struct st_command *command; + my_bool require_file= 0, q_send_flag= 0; + uint command_executed= 0, last_command_executed= 0; char save_file[FN_REFLEN]; MY_STAT res_info; MY_INIT(argv[0]); - { - DBUG_ENTER("main"); - DBUG_PROCESS(argv[0]); - /* Use all time until exit if no explicit 'start_timer' */ - timer_start= timer_now(); + save_file[0]= 0; + TMPDIR[0]= 0; - save_file[0]=0; - TMPDIR[0]=0; - memset(cons, 0, sizeof(cons)); - cons_end = cons + MAX_CONS; - next_con = cons + 1; - cur_con = cons; + /* Init connections */ + memset(connections, 0, sizeof(connections)); + connections_end= connections + + (sizeof(connections)/sizeof(struct st_connection)) - 1; + next_con= connections + 1; + cur_con= connections; + /* Init file stack */ memset(file_stack, 0, sizeof(file_stack)); - memset(&master_pos, 0, sizeof(master_pos)); - file_stack_end= file_stack + MAX_INCLUDE_DEPTH - 1; + file_stack_end= + file_stack + (sizeof(file_stack)/sizeof(struct st_test_file)) - 1; cur_file= file_stack; - lineno = lineno_stack; - my_init_dynamic_array(&q_lines, sizeof(struct st_query*), INIT_Q_LINES, - INIT_Q_LINES); + /* Init block stack */ memset(block_stack, 0, sizeof(block_stack)); - block_stack_end= block_stack + BLOCK_STACK_DEPTH - 1; + block_stack_end= + block_stack + (sizeof(block_stack)/sizeof(struct st_block)) - 1; cur_block= block_stack; cur_block->ok= TRUE; /* Outer block should always be executed */ - cur_block->cmd= Q_UNKNOWN; + cur_block->cmd= cmd_none; - init_dynamic_string(&ds_res, "", 0, 65536); + my_init_dynamic_array(&q_lines, sizeof(struct st_command*), 1024, 1024); + + if (hash_init(&var_hash, charset_info, + 1024, 0, 0, get_var_key, var_free, MYF(0))) + die("Variable hash initialization failed"); + + memset(&master_pos, 0, sizeof(master_pos)); + + parser.current_line= parser.read_lines= 0; + memset(&var_reg, 0, sizeof(var_reg)); + +#ifdef __WIN__ + init_tmp_sh_file(); + init_win_path_patterns(); +#endif + + init_dynamic_string(&ds_res, "", 65536, 65536); + init_dynamic_string(&ds_progress, "", 0, 2048); parse_args(argc, argv); + + DBUG_PRINT("info",("result_file: '%s'", result_file ? result_file : "")); if (mysql_server_init(embedded_server_arg_count, embedded_server_args, (char**) embedded_server_groups)) @@ -3796,17 +5320,17 @@ int main(int argc, char **argv) { cur_file->file= stdin; cur_file->file_name= my_strdup("", MYF(MY_WME)); + cur_file->lineno= 1; } - *lineno=1; -#ifndef EMBEDDED_LIBRARY - if (manager_host) - init_manager(); -#endif - if (ps_protocol) - { + init_re(); + ps_protocol_enabled= ps_protocol; + sp_protocol_enabled= sp_protocol; + view_protocol_enabled= view_protocol; + cursor_protocol_enabled= cursor_protocol; + /* Cursor protcol implies ps protocol */ + if (cursor_protocol_enabled) ps_protocol_enabled= 1; - ps_init_re(); - } + if (!( mysql_init(&cur_con->mysql))) die("Failed in mysql_init()"); if (opt_compress) @@ -3815,18 +5339,30 @@ int main(int argc, char **argv) mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name); #ifdef HAVE_OPENSSL + +#if MYSQL_VERSION_ID >= 50000 + opt_ssl_verify_server_cert= TRUE; /* Always on in mysqltest */ +#endif + if (opt_use_ssl) + { mysql_ssl_set(&cur_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); +#if MYSQL_VERSION_ID >= 50000 + mysql_options(&cur_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, + &opt_ssl_verify_server_cert); +#endif + } #endif if (!(cur_con->name = my_strdup("default", MYF(MY_WME)))) die("Out of memory"); - if (safe_connect(&cur_con->mysql, host, user, pass, db, port, unix_sock)) - die("Failed in mysql_real_connect(): %s", mysql_error(&cur_con->mysql)); + safe_connect(&cur_con->mysql, cur_con->name, host, user, pass, + db, port, unix_sock); - init_var_hash(&cur_con->mysql); + /* Use all time until exit if no explicit 'start_timer' */ + timer_start= timer_now(); /* Initialize $mysql_errno with -1, so we can @@ -3835,24 +5371,31 @@ int main(int argc, char **argv) */ var_set_errno(-1); - while (!abort_flag && !read_query(&q)) + if (opt_include) + { + open_file(opt_include); + } + + while (!read_command(&command)) { int current_line_inc = 1, processed = 0; - if (q->type == Q_UNKNOWN || q->type == Q_COMMENT_WITH_COMMAND) - get_query_type(q); + if (command->type == Q_UNKNOWN || command->type == Q_COMMENT_WITH_COMMAND) + get_command_type(command); if (cur_block->ok) { - q->last_argument= q->first_argument; + command->last_argument= command->first_argument; processed = 1; - switch (q->type) { - case Q_CONNECT: do_connect(q); break; - case Q_CONNECTION: select_connection(q); break; + switch (command->type) { + case Q_CONNECT: + do_connect(command); + break; + case Q_CONNECTION: select_connection(command); break; case Q_DISCONNECT: case Q_DIRTY_CLOSE: - close_connection(q); break; - case Q_RPL_PROBE: do_rpl_probe(q); break; - case Q_ENABLE_RPL_PARSE: do_enable_rpl_parse(q); break; - case Q_DISABLE_RPL_PARSE: do_disable_rpl_parse(q); break; + do_close_connection(command); break; + case Q_RPL_PROBE: do_rpl_probe(command); break; + case Q_ENABLE_RPL_PARSE: do_enable_rpl_parse(command); break; + case Q_DISABLE_RPL_PARSE: do_disable_rpl_parse(command); break; case Q_ENABLE_QUERY_LOG: disable_query_log=0; break; case Q_DISABLE_QUERY_LOG: disable_query_log=1; break; case Q_ENABLE_ABORT_ON_ERROR: abort_on_error=1; break; @@ -3861,27 +5404,27 @@ int main(int argc, char **argv) case Q_DISABLE_RESULT_LOG: disable_result_log=1; break; case Q_ENABLE_WARNINGS: disable_warnings=0; break; case Q_DISABLE_WARNINGS: disable_warnings=1; break; + case Q_ENABLE_PS_WARNINGS: disable_ps_warnings=0; break; + case Q_DISABLE_PS_WARNINGS: disable_ps_warnings=1; break; case Q_ENABLE_INFO: disable_info=0; break; case Q_DISABLE_INFO: disable_info=1; break; case Q_ENABLE_METADATA: display_metadata=1; break; case Q_DISABLE_METADATA: display_metadata=0; break; - case Q_SOURCE: do_source(q); break; - case Q_SLEEP: do_sleep(q, 0); break; - case Q_REAL_SLEEP: do_sleep(q, 1); break; - case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(q); break; - case Q_REQUIRE_MANAGER: do_require_manager(q); break; -#ifndef EMBEDDED_LIBRARY - case Q_SERVER_START: do_server_start(q); break; - case Q_SERVER_STOP: do_server_stop(q); break; -#endif - case Q_INC: do_modify_var(q, "inc", DO_INC); break; - case Q_DEC: do_modify_var(q, "dec", DO_DEC); break; - case Q_ECHO: do_echo(q); break; - case Q_SYSTEM: do_system(q); break; + case Q_SOURCE: do_source(command); break; + case Q_SLEEP: do_sleep(command, 0); break; + case Q_REAL_SLEEP: do_sleep(command, 1); break; + case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(command); break; + case Q_INC: do_modify_var(command, DO_INC); break; + case Q_DEC: do_modify_var(command, DO_DEC); break; + case Q_ECHO: do_echo(command); command_executed++; break; + case Q_SYSTEM: do_system(command); break; + case Q_REMOVE_FILE: do_remove_file(command); break; + case Q_FILE_EXIST: do_file_exist(command); break; + case Q_WRITE_FILE: do_write_file(command); break; + case Q_COPY_FILE: do_copy_file(command); break; + case Q_PERL: do_perl(command); break; case Q_DELIMITER: - strmake(delimiter, q->first_argument, sizeof(delimiter) - 1); - delimiter_length= strlen(delimiter); - q->last_argument= q->first_argument+delimiter_length; + do_delimiter(command); break; case Q_DISPLAY_VERTICAL_RESULTS: display_result_vertically= TRUE; @@ -3889,115 +5432,121 @@ int main(int argc, char **argv) case Q_DISPLAY_HORIZONTAL_RESULTS: display_result_vertically= FALSE; break; - case Q_LET: do_let(q); break; + case Q_LET: do_let(command); break; case Q_EVAL_RESULT: eval_result = 1; break; case Q_EVAL: - if (q->query == q->query_buf) + if (command->query == command->query_buf) { - q->query= q->first_argument; - q->first_word_len= 0; + command->query= command->first_argument; + command->first_word_len= 0; } /* fall through */ case Q_QUERY_VERTICAL: case Q_QUERY_HORIZONTAL: { my_bool old_display_result_vertically= display_result_vertically; - if (!q->query[q->first_word_len]) - { - /* This happens when we use 'query_..' on it's own line */ - q_send_flag=1; - DBUG_PRINT("info", - ("query: '%s' first_word_len: %d send_flag=1", - q->query, q->first_word_len)); - break; - } - /* fix up query pointer if this is * first iteration for this line */ - if (q->query == q->query_buf) - q->query += q->first_word_len + 1; - display_result_vertically= (q->type==Q_QUERY_VERTICAL); + + /* Remove "query_*" if this is first iteration */ + if (command->query == command->query_buf) + command->query= command->first_argument; + + display_result_vertically= (command->type == Q_QUERY_VERTICAL); if (save_file[0]) { - strmov(q->record_file,save_file); - q->require_file=require_file; + strmov(command->record_file,save_file); + command->require_file=require_file; save_file[0]=0; } - error|= run_query(&cur_con->mysql, q, QUERY_REAP|QUERY_SEND); + run_query(&cur_con->mysql, command, QUERY_REAP_FLAG|QUERY_SEND_FLAG); display_result_vertically= old_display_result_vertically; - q->last_argument= q->end; - query_executed= 1; + command->last_argument= command->end; + command_executed++; break; } case Q_QUERY: case Q_REAP: { - /* - We read the result always regardless of the mode for both full - query and read-result only (reap) - */ - int flags = QUERY_REAP; - if (q->type != Q_REAP) /* for a full query, enable the send stage */ - flags |= QUERY_SEND; - if (q_send_flag) - { - flags= QUERY_SEND; - q_send_flag=0; - } + int flags; + if (q_send_flag) + { + /* Last command was an empty 'send' */ + flags= QUERY_SEND_FLAG; + q_send_flag= 0; + } + else if (command->type == Q_REAP) + { + flags= QUERY_REAP_FLAG; + } + else + { + /* full query, both reap and send */ + flags= QUERY_REAP_FLAG | QUERY_SEND_FLAG; + } + if (save_file[0]) { - strmov(q->record_file,save_file); - q->require_file=require_file; + strmov(command->record_file,save_file); + command->require_file=require_file; save_file[0]=0; } - error |= run_query(&cur_con->mysql, q, flags); - query_executed= 1; - q->last_argument= q->end; + run_query(&cur_con->mysql, command, flags); + command_executed++; + command->last_argument= command->end; break; } case Q_SEND: - if (!q->query[q->first_word_len]) - { - /* This happens when we use 'send' on it's own line */ - q_send_flag=1; - break; - } - /* fix up query pointer if this is * first iteration for this line */ - if (q->query == q->query_buf) - q->query += q->first_word_len; + if (!*command->first_argument) + { + /* + This is a send without arguments, it indicates that _next_ query + should be send only + */ + q_send_flag= 1; + break; + } + + /* Remove "send" if this is first iteration */ + if (command->query == command->query_buf) + command->query= command->first_argument; + /* - run_query() can execute a query partially, depending on the flags - QUERY_SEND flag without QUERY_REAP tells it to just send the - query and read the result some time later when reap instruction + run_query() can execute a query partially, depending on the flags. + QUERY_SEND_FLAG flag without QUERY_REAP_FLAG tells it to just send + the query and read the result some time later when reap instruction is given on this connection. - */ - error |= run_query(&cur_con->mysql, q, QUERY_SEND); - query_executed= 1; - q->last_argument= q->end; + */ + run_query(&cur_con->mysql, command, QUERY_SEND_FLAG); + command_executed++; + command->last_argument= command->end; break; case Q_RESULT: - get_file_name(save_file,q); + do_get_file_name(save_file, command); require_file=0; break; case Q_ERROR: - global_expected_errors=get_errcodes(global_expected_errno,q); + do_get_errcodes(command); break; case Q_REQUIRE: - get_file_name(save_file,q); + do_get_file_name(save_file, command); require_file=1; break; case Q_REPLACE: - get_replace(q); + do_get_replace(command); break; + case Q_REPLACE_REGEX: + do_get_replace_regex(command); + break; case Q_REPLACE_COLUMN: - get_replace_column(q); + do_get_replace_column(command); break; case Q_SAVE_MASTER_POS: do_save_master_pos(); break; - case Q_SYNC_WITH_MASTER: do_sync_with_master(q); break; + case Q_SYNC_WITH_MASTER: do_sync_with_master(command); break; case Q_SYNC_SLAVE_WITH_MASTER: { do_save_master_pos(); - if (*q->first_argument) - select_connection(q); + if (*command->first_argument) + select_connection(command); else select_connection_name("slave"); do_sync_with_master2(0); @@ -4005,14 +5554,14 @@ int main(int argc, char **argv) } case Q_COMMENT: /* Ignore row */ case Q_COMMENT_WITH_COMMAND: - q->last_argument= q->end; + command->last_argument= command->end; break; case Q_PING: (void) mysql_ping(&cur_con->mysql); break; case Q_EXEC: - do_exec(q); - query_executed= 1; + do_exec(command); + command_executed++; break; case Q_START_TIMER: /* Overwrite possible earlier start of timer */ @@ -4021,10 +5570,9 @@ int main(int argc, char **argv) case Q_END_TIMER: /* End timer before ending mysqltest */ timer_output(); - got_end_timer= TRUE; break; case Q_CHARACTER_SET: - set_charset(q); + do_set_charset(command); break; case Q_DISABLE_PS_PROTOCOL: ps_protocol_enabled= 0; @@ -4033,48 +5581,117 @@ int main(int argc, char **argv) ps_protocol_enabled= ps_protocol; break; case Q_DISABLE_RECONNECT: - cur_con->mysql.reconnect= 0; + set_reconnect(&cur_con->mysql, 0); break; case Q_ENABLE_RECONNECT: - cur_con->mysql.reconnect= 1; + set_reconnect(&cur_con->mysql, 1); break; - case Q_EXIT: - abort_flag= 1; + case Q_DISABLE_PARSING: + if (parsing_disabled == 0) + parsing_disabled= 1; + else + die("Parsing is already disabled"); + break; + case Q_ENABLE_PARSING: + /* + Ensure we don't get parsing_disabled < 0 as this would accidentally + disable code we don't want to have disabled + */ + if (parsing_disabled == 1) + parsing_disabled= 0; + else + die("Parsing is already enabled"); break; - default: processed = 0; break; + case Q_DIE: + die("%s", command->first_argument); + break; + + default: + processed= 0; + break; } } if (!processed) { - current_line_inc = 0; - switch (q->type) { - case Q_WHILE: do_block(Q_WHILE, q); break; - case Q_IF: do_block(Q_IF, q); break; - case Q_END_BLOCK: do_done(q); break; + current_line_inc= 0; + switch (command->type) { + case Q_WHILE: do_block(cmd_while, command); break; + case Q_IF: do_block(cmd_if, command); break; + case Q_END_BLOCK: do_done(command); break; default: current_line_inc = 1; break; } } else - check_eol_junk(q->last_argument); + check_eol_junk(command->last_argument); - if (q->type != Q_ERROR) + if (command->type != Q_ERROR) { /* As soon as any non "error" command has been executed, the array with expected errors should be cleared */ - global_expected_errors= 0; - bzero((gptr) global_expected_errno, sizeof(global_expected_errno)); + memset(&saved_expected_errors, 0, sizeof(saved_expected_errors)); } + if (command_executed != last_command_executed) + { + /* + As soon as any command has been executed, + the replace structures should be cleared + */ + free_all_replace(); + } + last_command_executed= command_executed; + + + parser.current_line += current_line_inc; + if ( opt_mark_progress ) + mark_progress(command, parser.current_line); } start_lineno= 0; - if (!query_executed && result_file && my_stat(result_file, &res_info, 0)) + if (parsing_disabled) + die("Test ended with parsing disabled"); + + /* + The whole test has been executed _sucessfully_. + Time to compare result or save it to record file. + The entire output from test is now kept in ds_res. + */ + if (ds_res.length) + { + if (result_file) + { + if (record) + { + /* Dump the output from test to result file */ + str_to_file(result_file, ds_res.str, ds_res.length); + } + else + { + /* Check that the output from test is equal to result file + - detect missing result file + - detect zero size result file + */ + check_result(&ds_res, result_file, 0); + } + } + else + { + /* No result_file specified to compare with, print to stdout */ + printf("%s", ds_res.str); + } + } + else + { + die("The test didn't produce any output"); + } + + if (!command_executed && result_file && my_stat(result_file, &res_info, 0)) { /* my_stat() successful on result file. Check if we have not run a @@ -4083,114 +5700,49 @@ int main(int argc, char **argv) non-existing or non-readable file, we assume it's fine to have no query output from the test file, e.g. regarded as no error. */ - if (res_info.st_size) - error|= (RESULT_CONTENT_MISMATCH | RESULT_LENGTH_MISMATCH); - } - if (ds_res.length && !error) - { - if (result_file) - { - if (!record) - error |= check_result(&ds_res, result_file, q->require_file); - else - str_to_file(result_file, ds_res.str, ds_res.length); - } - else - { - /* Print the result to stdout */ - printf("%s", ds_res.str); - } + die("No queries executed but result file found!"); } + + if ( opt_mark_progress ) + dump_progress(result_file); + dynstr_free(&ds_progress); + dynstr_free(&ds_res); - if (!silent) - { - if (error) - printf("not ok\n"); - else - printf("ok\n"); - } - - if (!got_end_timer) - timer_output(); /* No end_timer cmd, end it */ + timer_output(); free_used_memory(); my_end(MY_CHECK_ERROR); - exit(error ? 1 : 0); - return error ? 1 : 0; /* Keep compiler happy */ - } + + /* Yes, if we got this far the test has suceeded! Sakila smiles */ + if (!silent) + printf("ok\n"); + exit(0); + return 0; /* Keep compiler happy */ } /* - Read arguments for embedded server and put them into - embedded_server_args_count and embedded_server_args[] + A primitive timer that give results in milliseconds if the + --timer-file= is given. The timer result is written + to that file when the result is available. To not confuse + mysql-test-run with an old obsolete result, we remove the file + before executing any commands. The time we measure is + + - If no explicit 'start_timer' or 'end_timer' is given in the + test case, the timer measure how long we execute in mysqltest. + + - If only 'start_timer' is given we measure how long we execute + from that point until we terminate mysqltest. + + - If only 'end_timer' is given we measure how long we execute + from that we enter mysqltest to the 'end_timer' is command is + executed. + + - If both 'start_timer' and 'end_timer' are given we measure + the time between executing the two commands. */ - -static int read_server_arguments(const char *name) -{ - char argument[1024],buff[FN_REFLEN], *str=0; - FILE *file; - - if (!test_if_hard_path(name)) - { - strxmov(buff, opt_basedir, name, NullS); - name=buff; - } - fn_format(buff,name,"","",4); - - if (!embedded_server_arg_count) - { - embedded_server_arg_count=1; - embedded_server_args[0]= (char*) ""; /* Progname */ - } - if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME)))) - return 1; - while (embedded_server_arg_count < MAX_SERVER_ARGS && - (str=fgets(argument,sizeof(argument), file))) - { - *(strend(str)-1)=0; /* Remove end newline */ - if (!(embedded_server_args[embedded_server_arg_count]= - (char*) my_strdup(str,MYF(MY_WME)))) - { - my_fclose(file,MYF(0)); - return 1; - } - embedded_server_arg_count++; - } - my_fclose(file,MYF(0)); - if (str) - { - fprintf(stderr,"Too many arguments in option file: %s\n",name); - return 1; - } - return 0; -} - -/****************************************************************************\ - * - * A primitive timer that give results in milliseconds if the - * --timer-file= is given. The timer result is written - * to that file when the result is available. To not confuse - * mysql-test-run with an old obsolete result, we remove the file - * before executing any commands. The time we measure is - * - * - If no explicit 'start_timer' or 'end_timer' is given in the - * test case, the timer measure how long we execute in mysqltest. - * - * - If only 'start_timer' is given we measure how long we execute - * from that point until we terminate mysqltest. - * - * - If only 'end_timer' is given we measure how long we execute - * from that we enter mysqltest to the 'end_timer' is command is - * executed. - * - * - If both 'start_timer' and 'end_timer' are given we measure - * the time between executing the two commands. - * -\****************************************************************************/ - -static void timer_output(void) +void timer_output(void) { if (timer_file) { @@ -4198,25 +5750,160 @@ static void timer_output(void) ulonglong timer= timer_now() - timer_start; end= longlong2str(timer, buf, 10); str_to_file(timer_file,buf, (int) (end-buf)); + /* Timer has been written to the file, don't use it anymore */ + timer_file= 0; } } -static ulonglong timer_now(void) + +ulonglong timer_now(void) { return my_getsystime() / 10000; } -/**************************************************************************** -* Handle replacement of strings -****************************************************************************/ -#define PC_MALLOC 256 /* Bytes for pointers */ -#define PS_MALLOC 512 /* Bytes for data */ +/* + Get arguments for replace_columns. The syntax is: + replace-column column_number to_string [column_number to_string ...] + Where each argument may be quoted with ' or " + A argument may also be a variable, in which case the value of the + variable is replaced. +*/ + +void do_get_replace_column(struct st_command *command) +{ + char *from= command->first_argument; + char *buff, *start; + DBUG_ENTER("get_replace_columns"); + + free_replace_column(); + if (!*from) + die("Missing argument in %s", command->query); + + /* Allocate a buffer for results */ + start= buff= my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE)); + while (*from) + { + char *to; + uint column_number; + + to= get_string(&buff, &from, command); + if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS) + die("Wrong column number to replace_column in '%s'", command->query); + if (!*from) + die("Wrong number of arguments to replace_column in '%s'", command->query); + to= get_string(&buff, &from, command); + my_free(replace_column[column_number-1], MY_ALLOW_ZERO_PTR); + replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE)); + set_if_bigger(max_replace_column, column_number); + } + my_free(start, MYF(0)); + command->last_argument= command->end; +} + + +void free_replace_column() +{ + uint i; + for (i=0 ; i < max_replace_column ; i++) + { + if (replace_column[i]) + { + my_free(replace_column[i], 0); + replace_column[i]= 0; + } + } + max_replace_column= 0; +} + + +/****************************************************************************/ +/* + Replace functions +*/ + +/* Definitions for replace result */ + +typedef struct st_pointer_array { /* when using array-strings */ + TYPELIB typelib; /* Pointer to strings */ + byte *str; /* Strings is here */ + int7 *flag; /* Flag about each var. */ + uint array_allocs,max_count,length,max_length; +} POINTER_ARRAY; + +struct st_replace; +struct st_replace *init_replace(my_string *from, my_string *to, uint count, + my_string word_end_chars); +int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name); +void replace_strings_append(struct st_replace *rep, DYNAMIC_STRING* ds, + const char *from, int len); +void free_pointer_array(POINTER_ARRAY *pa); + +struct st_replace *glob_replace; + +/* + Get arguments for replace. The syntax is: + replace from to [from to ...] + Where each argument may be quoted with ' or " + A argument may also be a variable, in which case the value of the + variable is replaced. +*/ + +void do_get_replace(struct st_command *command) +{ + uint i; + char *from= command->first_argument; + char *buff, *start; + char word_end_chars[256], *pos; + POINTER_ARRAY to_array, from_array; + DBUG_ENTER("get_replace"); + + free_replace(); + + bzero((char*) &to_array,sizeof(to_array)); + bzero((char*) &from_array,sizeof(from_array)); + if (!*from) + die("Missing argument in %s", command->query); + start= buff= my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE)); + while (*from) + { + char *to= buff; + to= get_string(&buff, &from, command); + if (!*from) + die("Wrong number of arguments to replace_result in '%s'", + command->query); + insert_pointer_name(&from_array,to); + to= get_string(&buff, &from, command); + insert_pointer_name(&to_array,to); + } + for (i= 1,pos= word_end_chars ; i < 256 ; i++) + if (my_isspace(charset_info,i)) + *pos++= i; + *pos=0; /* End pointer */ + if (!(glob_replace= init_replace((char**) from_array.typelib.type_names, + (char**) to_array.typelib.type_names, + (uint) from_array.typelib.count, + word_end_chars))) + die("Can't initialize replace from '%s'", command->query); + free_pointer_array(&from_array); + free_pointer_array(&to_array); + my_free(start, MYF(0)); + command->last_argument= command->end; + DBUG_VOID_RETURN; +} + + +void free_replace() +{ + DBUG_ENTER("free_replace"); + if (glob_replace) + { + my_free((char*) glob_replace,MYF(0)); + glob_replace=0; + } + DBUG_VOID_RETURN; +} -#define SPACE_CHAR 256 -#define START_OF_LINE 257 -#define END_OF_LINE 258 -#define LAST_CHAR_CODE 259 typedef struct st_replace { bool found; @@ -4230,99 +5917,514 @@ typedef struct st_replace_found { int from_offset; } REPLACE_STRING; + +void replace_strings_append(REPLACE *rep, DYNAMIC_STRING* ds, + const char *str, int len) +{ + reg1 REPLACE *rep_pos; + reg2 REPLACE_STRING *rep_str; + const char *start, *from; + DBUG_ENTER("replace_strings_append"); + + start= from= str; + rep_pos=rep+1; + for (;;) + { + /* Loop through states */ + DBUG_PRINT("info", ("Looping through states")); + while (!rep_pos->found) + rep_pos= rep_pos->next[(uchar) *from++]; + + /* Does this state contain a string to be replaced */ + if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string) + { + /* No match found */ + dynstr_append_mem(ds, start, from - start - 1); + DBUG_PRINT("exit", ("Found no more string to replace, appended: %s", start)); + DBUG_VOID_RETURN; + } + + /* Found a string that needs to be replaced */ + DBUG_PRINT("info", ("found: %d, to_offset: %d, from_offset: %d, string: %s", + rep_str->found, rep_str->to_offset, + rep_str->from_offset, rep_str->replace_string)); + + /* Append part of original string before replace string */ + dynstr_append_mem(ds, start, (from - rep_str->to_offset) - start); + + /* Append replace string */ + dynstr_append_mem(ds, rep_str->replace_string, + strlen(rep_str->replace_string)); + + if (!*(from-=rep_str->from_offset) && rep_pos->found != 2) + { + /* End of from string */ + DBUG_PRINT("exit", ("Found end of from string")); + DBUG_VOID_RETURN; + } + DBUG_ASSERT(from <= str+len); + start= from; + rep_pos=rep; + } +} + + +/* + Regex replace functions +*/ + + +/* Stores regex substitutions */ + +struct st_regex +{ + char* pattern; /* Pattern to be replaced */ + char* replace; /* String or expression to replace the pattern with */ + int icase; /* true if the match is case insensitive */ +}; + +struct st_replace_regex +{ + DYNAMIC_ARRAY regex_arr; /* stores a list of st_regex subsitutions */ + + /* + Temporary storage areas for substitutions. To reduce unnessary copying + and memory freeing/allocation, we pre-allocate two buffers, and alternate + their use, one for input/one for output, the roles changing on the next + st_regex substition. At the end of substitutions buf points to the + one containing the final result. + */ + char* buf; + char* even_buf; + char* odd_buf; + int even_buf_len; + int odd_buf_len; +}; + +struct st_replace_regex *glob_replace_regex= 0; + +int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace, + char *string, int icase); + + + +/* + Finds the next (non-escaped) '/' in the expression. + (If the character '/' is needed, it can be escaped using '\'.) +*/ + +#define PARSE_REGEX_ARG \ + while (p < expr_end) \ + { \ + char c= *p; \ + if (c == '/') \ + { \ + if (last_c == '\\') \ + { \ + buf_p[-1]= '/'; \ + } \ + else \ + { \ + *buf_p++ = 0; \ + break; \ + } \ + } \ + else \ + *buf_p++ = c; \ + \ + last_c= c; \ + p++; \ + } \ + \ +/* + Initializes the regular substitution expression to be used in the + result output of test. + + Returns: st_replace_regex struct with pairs of substitutions +*/ + +struct st_replace_regex* init_replace_regex(char* expr) +{ + struct st_replace_regex* res; + char* buf,*expr_end; + char* p; + char* buf_p; + uint expr_len= strlen(expr); + char last_c = 0; + struct st_regex reg; + + /* my_malloc() will die on fail with MY_FAE */ + res=(struct st_replace_regex*)my_malloc( + sizeof(*res)+expr_len ,MYF(MY_FAE+MY_WME)); + my_init_dynamic_array(&res->regex_arr,sizeof(struct st_regex),128,128); + + buf= (char*)res + sizeof(*res); + expr_end= expr + expr_len; + p= expr; + buf_p= buf; + + /* for each regexp substitution statement */ + while (p < expr_end) + { + bzero(®,sizeof(reg)); + /* find the start of the statement */ + while (p < expr_end) + { + if (*p == '/') + break; + p++; + } + + if (p == expr_end || ++p == expr_end) + { + if (res->regex_arr.elements) + break; + else + goto err; + } + /* we found the start */ + reg.pattern= buf_p; + + /* Find first argument -- pattern string to be removed */ + PARSE_REGEX_ARG + + if (p == expr_end || ++p == expr_end) + goto err; + + /* buf_p now points to the replacement pattern terminated with \0 */ + reg.replace= buf_p; + + /* Find second argument -- replace string to replace pattern */ + PARSE_REGEX_ARG + + if (p == expr_end) + goto err; + + /* skip the ending '/' in the statement */ + p++; + + /* Check if we should do matching case insensitive */ + if (p < expr_end && *p == 'i') + reg.icase= 1; + + /* done parsing the statement, now place it in regex_arr */ + if (insert_dynamic(&res->regex_arr,(gptr) ®)) + die("Out of memory"); + } + res->odd_buf_len= res->even_buf_len= 8192; + res->even_buf= (char*)my_malloc(res->even_buf_len,MYF(MY_WME+MY_FAE)); + res->odd_buf= (char*)my_malloc(res->odd_buf_len,MYF(MY_WME+MY_FAE)); + res->buf= res->even_buf; + + return res; + +err: + my_free((gptr)res,0); + die("Error parsing replace_regex \"%s\"", expr); + return 0; +} + +/* + Execute all substitutions on val. + + Returns: true if substituition was made, false otherwise + Side-effect: Sets r->buf to be the buffer with all substitutions done. + + IN: + struct st_replace_regex* r + char* val + Out: + struct st_replace_regex* r + r->buf points at the resulting buffer + r->even_buf and r->odd_buf might have been reallocated + r->even_buf_len and r->odd_buf_len might have been changed + + TODO: at some point figure out if there is a way to do everything + in one pass +*/ + +int multi_reg_replace(struct st_replace_regex* r,char* val) +{ + uint i; + char* in_buf, *out_buf; + int* buf_len_p; + + in_buf= val; + out_buf= r->even_buf; + buf_len_p= &r->even_buf_len; + r->buf= 0; + + /* For each substitution, do the replace */ + for (i= 0; i < r->regex_arr.elements; i++) + { + struct st_regex re; + char* save_out_buf= out_buf; + + get_dynamic(&r->regex_arr,(gptr)&re,i); + + if (!reg_replace(&out_buf, buf_len_p, re.pattern, re.replace, + in_buf, re.icase)) + { + /* if the buffer has been reallocated, make adjustements */ + if (save_out_buf != out_buf) + { + if (save_out_buf == r->even_buf) + r->even_buf= out_buf; + else + r->odd_buf= out_buf; + } + + r->buf= out_buf; + if (in_buf == val) + in_buf= r->odd_buf; + + swap_variables(char*,in_buf,out_buf); + + buf_len_p= (out_buf == r->even_buf) ? &r->even_buf_len : + &r->odd_buf_len; + } + } + + return (r->buf == 0); +} + +/* + Parse the regular expression to be used in all result files + from now on. + + The syntax is --replace_regex /from/to/i /from/to/i ... + i means case-insensitive match. If omitted, the match is + case-sensitive + +*/ +void do_get_replace_regex(struct st_command *command) +{ + char *expr= command->first_argument; + free_replace_regex(); + if (!(glob_replace_regex=init_replace_regex(expr))) + die("Could not init replace_regex"); + command->last_argument= command->end; +} + +void free_replace_regex() +{ + if (glob_replace_regex) + { + delete_dynamic(&glob_replace_regex->regex_arr); + my_free(glob_replace_regex->even_buf,MYF(MY_ALLOW_ZERO_PTR)); + my_free(glob_replace_regex->odd_buf,MYF(MY_ALLOW_ZERO_PTR)); + my_free((char*) glob_replace_regex,MYF(0)); + glob_replace_regex=0; + } +} + + + +/* + auxiluary macro used by reg_replace + makes sure the result buffer has sufficient length +*/ +#define SECURE_REG_BUF if (buf_len < need_buf_len) \ + { \ + int off= res_p - buf; \ + buf= (char*)my_realloc(buf,need_buf_len,MYF(MY_WME+MY_FAE)); \ + res_p= buf + off; \ + buf_len= need_buf_len; \ + } \ + \ +/* + Performs a regex substitution + + IN: + + buf_p - result buffer pointer. Will change if reallocated + buf_len_p - result buffer length. Will change if the buffer is reallocated + pattern - regexp pattern to match + replace - replacement expression + string - the string to perform substituions in + icase - flag, if set to 1 the match is case insensitive +*/ +int reg_replace(char** buf_p, int* buf_len_p, char *pattern, + char *replace, char *string, int icase) +{ + my_regex_t r; + my_regmatch_t *subs; + char *buf_end, *replace_end; + char *buf= *buf_p; + int len; + int buf_len, need_buf_len; + int cflags= REG_EXTENDED; + int err_code; + char *res_p,*str_p,*str_end; + + buf_len= *buf_len_p; + len= strlen(string); + str_end= string + len; + + /* start with a buffer of a reasonable size that hopefully will not + need to be reallocated + */ + need_buf_len= len * 2 + 1; + res_p= buf; + + SECURE_REG_BUF + + buf_end= buf + buf_len; + + if (icase) + cflags|= REG_ICASE; + + if ((err_code= my_regcomp(&r,pattern,cflags,&my_charset_latin1))) + { + check_regerr(&r,err_code); + return 1; + } + + subs= (my_regmatch_t*)my_malloc(sizeof(my_regmatch_t) * (r.re_nsub+1), + MYF(MY_WME+MY_FAE)); + + *res_p= 0; + str_p= string; + replace_end= replace + strlen(replace); + + /* for each pattern match instance perform a replacement */ + while (!err_code) + { + /* find the match */ + err_code= my_regexec(&r,str_p, r.re_nsub+1, subs, + (str_p == string) ? REG_NOTBOL : 0); + + /* if regular expression error (eg. bad syntax, or out of memory) */ + if (err_code && err_code != REG_NOMATCH) + { + check_regerr(&r,err_code); + my_regfree(&r); + return 1; + } + + /* if match found */ + if (!err_code) + { + char* expr_p= replace; + int c; + + /* + we need at least what we have so far in the buffer + the part + before this match + */ + need_buf_len= (res_p - buf) + subs[0].rm_so; + + /* on this pass, calculate the memory for the result buffer */ + while (expr_p < replace_end) + { + int back_ref_num= -1; + c= *expr_p; + + if (c == '\\' && expr_p + 1 < replace_end) + { + back_ref_num= expr_p[1] - '0'; + } + + /* found a valid back_ref (eg. \1)*/ + if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub) + { + int start_off,end_off; + if ((start_off=subs[back_ref_num].rm_so) > -1 && + (end_off=subs[back_ref_num].rm_eo) > -1) + { + need_buf_len += (end_off - start_off); + } + expr_p += 2; + } + else + { + expr_p++; + need_buf_len++; + } + } + need_buf_len++; + /* + now that we know the size of the buffer, + make sure it is big enough + */ + SECURE_REG_BUF + + /* copy the pre-match part */ + if (subs[0].rm_so) + { + memcpy(res_p, str_p, subs[0].rm_so); + res_p+= subs[0].rm_so; + } + + expr_p= replace; + + /* copy the match and expand back_refs */ + while (expr_p < replace_end) + { + int back_ref_num= -1; + c= *expr_p; + + if (c == '\\' && expr_p + 1 < replace_end) + { + back_ref_num= expr_p[1] - '0'; + } + + if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub) + { + int start_off,end_off; + if ((start_off=subs[back_ref_num].rm_so) > -1 && + (end_off=subs[back_ref_num].rm_eo) > -1) + { + int block_len= end_off - start_off; + memcpy(res_p,str_p + start_off, block_len); + res_p += block_len; + } + expr_p += 2; + } + else + { + *res_p++ = *expr_p++; + } + } + + /* handle the post-match part */ + if (subs[0].rm_so == subs[0].rm_eo) + { + if (str_p + subs[0].rm_so >= str_end) + break; + str_p += subs[0].rm_eo ; + *res_p++ = *str_p++; + } + else + { + str_p += subs[0].rm_eo; + } + } + else /* no match this time, just copy the string as is */ + { + int left_in_str= str_end-str_p; + need_buf_len= (res_p-buf) + left_in_str; + SECURE_REG_BUF + memcpy(res_p,str_p,left_in_str); + res_p += left_in_str; + str_p= str_end; + } + } + my_free((gptr)subs, MYF(0)); + my_regfree(&r); + *res_p= 0; + *buf_p= buf; + *buf_len_p= buf_len; + return 0; +} + + #ifndef WORD_BIT #define WORD_BIT (8*sizeof(uint)) #endif - -static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name) -{ - uint i,length,old_count; - byte *new_pos; - const char **new_array; - DBUG_ENTER("insert_pointer_name"); - - if (! pa->typelib.count) - { - if (!(pa->typelib.type_names=(const char **) - my_malloc(((PC_MALLOC-MALLOC_OVERHEAD)/ - (sizeof(my_string)+sizeof(*pa->flag))* - (sizeof(my_string)+sizeof(*pa->flag))),MYF(MY_WME)))) - DBUG_RETURN(-1); - if (!(pa->str= (byte*) my_malloc((uint) (PS_MALLOC-MALLOC_OVERHEAD), - MYF(MY_WME)))) - { - my_free((gptr) pa->typelib.type_names,MYF(0)); - DBUG_RETURN (-1); - } - pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(byte*)+ - sizeof(*pa->flag)); - pa->flag= (int7*) (pa->typelib.type_names+pa->max_count); - pa->length=0; - pa->max_length=PS_MALLOC-MALLOC_OVERHEAD; - pa->array_allocs=1; - } - length=(uint) strlen(name)+1; - if (pa->length+length >= pa->max_length) - { - if (!(new_pos= (byte*) my_realloc((gptr) pa->str, - (uint) (pa->max_length+PS_MALLOC), - MYF(MY_WME)))) - DBUG_RETURN(1); - if (new_pos != pa->str) - { - my_ptrdiff_t diff=PTR_BYTE_DIFF(new_pos,pa->str); - for (i=0 ; i < pa->typelib.count ; i++) - pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff, - char*); - pa->str=new_pos; - } - pa->max_length+=PS_MALLOC; - } - if (pa->typelib.count >= pa->max_count-1) - { - int len; - pa->array_allocs++; - len=(PC_MALLOC*pa->array_allocs - MALLOC_OVERHEAD); - if (!(new_array=(const char **) my_realloc((gptr) pa->typelib.type_names, - (uint) len/ - (sizeof(byte*)+sizeof(*pa->flag))* - (sizeof(byte*)+sizeof(*pa->flag)), - MYF(MY_WME)))) - DBUG_RETURN(1); - pa->typelib.type_names=new_array; - old_count=pa->max_count; - pa->max_count=len/(sizeof(byte*) + sizeof(*pa->flag)); - pa->flag= (int7*) (pa->typelib.type_names+pa->max_count); - memcpy((byte*) pa->flag,(my_string) (pa->typelib.type_names+old_count), - old_count*sizeof(*pa->flag)); - } - pa->flag[pa->typelib.count]=0; /* Reset flag */ - pa->typelib.type_names[pa->typelib.count++]= pa->str+pa->length; - pa->typelib.type_names[pa->typelib.count]= NullS; /* Put end-mark */ - VOID(strmov(pa->str+pa->length,name)); - pa->length+=length; - DBUG_RETURN(0); -} /* insert_pointer_name */ - - - /* free pointer array */ - -void free_pointer_array(POINTER_ARRAY *pa) -{ - if (pa->typelib.count) - { - pa->typelib.count=0; - my_free((gptr) pa->typelib.type_names,MYF(0)); - pa->typelib.type_names=0; - my_free((gptr) pa->str,MYF(0)); - } -} /* free_pointer_array */ - - - /* Code for replace rutines */ - #define SET_MALLOC_HUNC 64 +#define LAST_CHAR_CODE 259 typedef struct st_rep_set { uint *bits; /* Pointer to used sets */ @@ -4354,32 +6456,48 @@ typedef struct st_follow { } FOLLOWS; -static int init_sets(REP_SETS *sets,uint states); -static REP_SET *make_new_set(REP_SETS *sets); -static void make_sets_invisible(REP_SETS *sets); -static void free_last_set(REP_SETS *sets); -static void free_sets(REP_SETS *sets); -static void internal_set_bit(REP_SET *set, uint bit); -static void internal_clear_bit(REP_SET *set, uint bit); -static void or_bits(REP_SET *to,REP_SET *from); -static void copy_bits(REP_SET *to,REP_SET *from); -static int cmp_bits(REP_SET *set1,REP_SET *set2); -static int get_next_bit(REP_SET *set,uint lastpos); -static int find_set(REP_SETS *sets,REP_SET *find); -static int find_found(FOUND_SET *found_set,uint table_offset, - int found_offset); -static uint start_at_word(my_string pos); -static uint end_of_word(my_string pos); -static uint replace_len(my_string pos); +int init_sets(REP_SETS *sets,uint states); +REP_SET *make_new_set(REP_SETS *sets); +void make_sets_invisible(REP_SETS *sets); +void free_last_set(REP_SETS *sets); +void free_sets(REP_SETS *sets); +void internal_set_bit(REP_SET *set, uint bit); +void internal_clear_bit(REP_SET *set, uint bit); +void or_bits(REP_SET *to,REP_SET *from); +void copy_bits(REP_SET *to,REP_SET *from); +int cmp_bits(REP_SET *set1,REP_SET *set2); +int get_next_bit(REP_SET *set,uint lastpos); +int find_set(REP_SETS *sets,REP_SET *find); +int find_found(FOUND_SET *found_set,uint table_offset, + int found_offset); +uint start_at_word(my_string pos); +uint end_of_word(my_string pos); static uint found_sets=0; - /* Init a replace structure for further calls */ +uint replace_len(my_string str) +{ + uint len=0; + while (*str) + { + if (str[0] == '\\' && str[1]) + str++; + str++; + len++; + } + return len; +} + +/* Init a replace structure for further calls */ REPLACE *init_replace(my_string *from, my_string *to,uint count, my_string word_end_chars) { + static const int SPACE_CHAR= 256; + static const int START_OF_LINE= 257; + static const int END_OF_LINE= 258; + uint i,j,states,set_nr,len,result_len,max_length,found_end,bits_set,bit_nr; int used_sets,chr,default_state; char used_chars[LAST_CHAR_CODE],is_word_end[256]; @@ -4432,7 +6550,7 @@ REPLACE *init_replace(my_string *from, my_string *to,uint count, DBUG_RETURN(0); } - /* Init follow_ptr[] */ + /* Init follow_ptr[] */ for (i=0, states=1, follow_ptr=follow+1 ; i < count ; i++) { if (from[i][0] == '\\' && from[i][1] == '^') @@ -4615,7 +6733,7 @@ REPLACE *init_replace(my_string *from, my_string *to,uint count, } } - /* Alloc replace structure for the replace-state-machine */ + /* Alloc replace structure for the replace-state-machine */ if ((replace=(REPLACE*) my_malloc(sizeof(REPLACE)*(sets.count)+ sizeof(REPLACE_STRING)*(found_sets+1)+ @@ -4658,7 +6776,7 @@ REPLACE *init_replace(my_string *from, my_string *to,uint count, } -static int init_sets(REP_SETS *sets,uint states) +int init_sets(REP_SETS *sets,uint states) { bzero((char*) sets,sizeof(*sets)); sets->size_of_bits=((states+7)/8); @@ -4674,16 +6792,16 @@ static int init_sets(REP_SETS *sets,uint states) return 0; } - /* Make help sets invisible for nicer codeing */ +/* Make help sets invisible for nicer codeing */ -static void make_sets_invisible(REP_SETS *sets) +void make_sets_invisible(REP_SETS *sets) { sets->invisible=sets->count; sets->set+=sets->count; sets->count=0; } -static REP_SET *make_new_set(REP_SETS *sets) +REP_SET *make_new_set(REP_SETS *sets) { uint i,count,*bit_buffer; REP_SET *set; @@ -4701,7 +6819,7 @@ static REP_SET *make_new_set(REP_SETS *sets) } count=sets->count+sets->invisible+SET_MALLOC_HUNC; if (!(set=(REP_SET*) my_realloc((gptr) sets->set_buffer, - sizeof(REP_SET)*count, + sizeof(REP_SET)*count, MYF(MY_WME)))) return 0; sets->set_buffer=set; @@ -4720,34 +6838,34 @@ static REP_SET *make_new_set(REP_SETS *sets) return make_new_set(sets); } -static void free_last_set(REP_SETS *sets) +void free_last_set(REP_SETS *sets) { sets->count--; sets->extra++; return; } -static void free_sets(REP_SETS *sets) +void free_sets(REP_SETS *sets) { my_free((gptr)sets->set_buffer,MYF(0)); my_free((gptr)sets->bit_buffer,MYF(0)); return; } -static void internal_set_bit(REP_SET *set, uint bit) +void internal_set_bit(REP_SET *set, uint bit) { set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT); return; } -static void internal_clear_bit(REP_SET *set, uint bit) +void internal_clear_bit(REP_SET *set, uint bit) { set->bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT)); return; } -static void or_bits(REP_SET *to,REP_SET *from) +void or_bits(REP_SET *to,REP_SET *from) { reg1 uint i; for (i=0 ; i < to->size_of_bits ; i++) @@ -4755,22 +6873,22 @@ static void or_bits(REP_SET *to,REP_SET *from) return; } -static void copy_bits(REP_SET *to,REP_SET *from) +void copy_bits(REP_SET *to,REP_SET *from) { memcpy((byte*) to->bits,(byte*) from->bits, (size_t) (sizeof(uint) * to->size_of_bits)); } -static int cmp_bits(REP_SET *set1,REP_SET *set2) +int cmp_bits(REP_SET *set1,REP_SET *set2) { return bcmp((byte*) set1->bits,(byte*) set2->bits, sizeof(uint) * set1->size_of_bits); } - /* Get next set bit from set. */ +/* Get next set bit from set. */ -static int get_next_bit(REP_SET *set,uint lastpos) +int get_next_bit(REP_SET *set,uint lastpos) { uint pos,*start,*end,bits; @@ -4791,11 +6909,11 @@ static int get_next_bit(REP_SET *set,uint lastpos) return pos; } - /* find if there is a same set in sets. If there is, use it and - free given set, else put in given set in sets and return it's - position */ +/* find if there is a same set in sets. If there is, use it and + free given set, else put in given set in sets and return its + position */ -static int find_set(REP_SETS *sets,REP_SET *find) +int find_set(REP_SETS *sets,REP_SET *find) { uint i; for (i=0 ; i < sets->count-1 ; i++) @@ -4809,14 +6927,14 @@ static int find_set(REP_SETS *sets,REP_SET *find) return i; /* return new postion */ } - /* find if there is a found_set with same table_offset & found_offset - If there is return offset to it, else add new offset and return pos. - Pos returned is -offset-2 in found_set_structure because it's is - saved in set->next and set->next[] >= 0 points to next set and - set->next[] == -1 is reserved for end without replaces. - */ +/* find if there is a found_set with same table_offset & found_offset + If there is return offset to it, else add new offset and return pos. + Pos returned is -offset-2 in found_set_structure because it is + saved in set->next and set->next[] >= 0 points to next set and + set->next[] == -1 is reserved for end without replaces. +*/ -static int find_found(FOUND_SET *found_set,uint table_offset, int found_offset) +int find_found(FOUND_SET *found_set,uint table_offset, int found_offset) { int i; for (i=0 ; (uint) i < found_sets ; i++) @@ -4829,251 +6947,155 @@ static int find_found(FOUND_SET *found_set,uint table_offset, int found_offset) return -i-2; /* return new postion */ } - /* Return 1 if regexp starts with \b or ends with \b*/ +/* Return 1 if regexp starts with \b or ends with \b*/ -static uint start_at_word(my_string pos) +uint start_at_word(my_string pos) { return (((!bcmp(pos,"\\b",2) && pos[2]) || !bcmp(pos,"\\^",2)) ? 1 : 0); } -static uint end_of_word(my_string pos) +uint end_of_word(my_string pos) { my_string end=strend(pos); return ((end > pos+2 && !bcmp(end-2,"\\b",2)) || (end >= pos+2 && !bcmp(end-2,"\\$",2))) ? - 1 : 0; + 1 : 0; } - -static uint replace_len(my_string str) -{ - uint len=0; - while (*str) - { - if (str[0] == '\\' && str[1]) - str++; - str++; - len++; - } - return len; -} - - - /* Replace strings; Return length of result string */ - -uint replace_strings(REPLACE *rep, my_string *start,uint *max_length, - const char *from) -{ - reg1 REPLACE *rep_pos; - reg2 REPLACE_STRING *rep_str; - my_string to,end,pos,new_str; - - end=(to= *start) + *max_length-1; - rep_pos=rep+1; - for (;;) - { - while (!rep_pos->found) - { - rep_pos= rep_pos->next[(uchar) *from]; - if (to == end) - { - (*max_length)+=8192; - if (!(new_str=my_realloc(*start,*max_length,MYF(MY_WME)))) - return (uint) -1; - to=new_str+(to - *start); - end=(*start=new_str)+ *max_length-1; - } - *to++= *from++; - } - if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string) - return (uint) (to - *start)-1; - to-=rep_str->to_offset; - for (pos=rep_str->replace_string; *pos ; pos++) - { - if (to == end) - { - (*max_length)*=2; - if (!(new_str=my_realloc(*start,*max_length,MYF(MY_WME)))) - return (uint) -1; - to=new_str+(to - *start); - end=(*start=new_str)+ *max_length-1; - } - *to++= *pos; - } - if (!*(from-=rep_str->from_offset) && rep_pos->found != 2) - return (uint) (to - *start); - rep_pos=rep; - } -} - -static int initialize_replace_buffer(void) -{ - out_length=8192; - if (!(out_buff=my_malloc(out_length,MYF(MY_WME)))) - return(1); - return 0; -} - -static void free_replace_buffer(void) -{ - my_free(out_buff,MYF(MY_WME)); -} - - /**************************************************************************** - Replace results for a column -*****************************************************************************/ + * Handle replacement of strings + ****************************************************************************/ -static void free_replace_column() +#define PC_MALLOC 256 /* Bytes for pointers */ +#define PS_MALLOC 512 /* Bytes for data */ + +int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name) { - uint i; - for (i=0 ; i < max_replace_column ; i++) + uint i,length,old_count; + byte *new_pos; + const char **new_array; + DBUG_ENTER("insert_pointer_name"); + + if (! pa->typelib.count) { - if (replace_column[i]) + if (!(pa->typelib.type_names=(const char **) + my_malloc(((PC_MALLOC-MALLOC_OVERHEAD)/ + (sizeof(my_string)+sizeof(*pa->flag))* + (sizeof(my_string)+sizeof(*pa->flag))),MYF(MY_WME)))) + DBUG_RETURN(-1); + if (!(pa->str= (byte*) my_malloc((uint) (PS_MALLOC-MALLOC_OVERHEAD), + MYF(MY_WME)))) { - my_free(replace_column[i], 0); - replace_column[i]= 0; + my_free((gptr) pa->typelib.type_names,MYF(0)); + DBUG_RETURN (-1); } + pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(byte*)+ + sizeof(*pa->flag)); + pa->flag= (int7*) (pa->typelib.type_names+pa->max_count); + pa->length=0; + pa->max_length=PS_MALLOC-MALLOC_OVERHEAD; + pa->array_allocs=1; } - max_replace_column= 0; -} - -/* - Get arguments for replace_columns. The syntax is: - replace-column column_number to_string [column_number to_string ...] - Where each argument may be quoted with ' or " - A argument may also be a variable, in which case the value of the - variable is replaced. -*/ - -static void get_replace_column(struct st_query *q) -{ - char *from=q->first_argument; - char *buff,*start; - DBUG_ENTER("get_replace_columns"); - - free_replace_column(); - if (!*from) - die("Missing argument in %s", q->query); - - /* Allocate a buffer for results */ - start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE)); - while (*from) + length=(uint) strlen(name)+1; + if (pa->length+length >= pa->max_length) { - char *to; - uint column_number; - - to= get_string(&buff, &from, q); - if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS) - die("Wrong column number to replace_column in '%s'", q->query); - if (!*from) - die("Wrong number of arguments to replace_column in '%s'", q->query); - to= get_string(&buff, &from, q); - my_free(replace_column[column_number-1], MY_ALLOW_ZERO_PTR); - replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE)); - set_if_bigger(max_replace_column, column_number); - } - my_free(start, MYF(0)); - q->last_argument= q->end; -} - -#if defined(__NETWARE__) || defined(__WIN__) -/* - Substitute environment variables with text. - - SYNOPSIS - subst_env_var() - arg String that should be substitute - - DESCRIPTION - This function takes a string as an input and replaces the - environment variables, that starts with '$' character, with it value. - - NOTES - Return string must be freed with my_free() - - RETURN - String with environment variables replaced. -*/ - -static char *subst_env_var(const char *str) -{ - char *result; - char *pos; - - result= pos= my_malloc(MAX_QUERY, MYF(MY_FAE)); - while (*str) - { - /* - need this only when we want to provide the functionality of - escaping through \ 'backslash' - if ((result == pos && *str=='$') || - (result != pos && *str=='$' && str[-1] !='\\')) - */ - if (*str == '$') + if (!(new_pos= (byte*) my_realloc((gptr) pa->str, + (uint) (pa->max_length+PS_MALLOC), + MYF(MY_WME)))) + DBUG_RETURN(1); + if (new_pos != pa->str) { - char env_var[256], *env_pos= env_var, *subst; - - /* Search for end of environment variable */ - for (str++; - *str && !isspace(*str) && *str != '\\' && *str != '/' && - *str != '$'; - str++) - *env_pos++= *str; - *env_pos= 0; - - if (!(subst= getenv(env_var))) - { - my_free(result, MYF(0)); - die("MYSQLTEST.NLM: Environment variable %s is not defined", - env_var); - } - - /* get the string to be substitued for env_var */ - pos= strmov(pos, subst); - /* Process delimiter in *str again */ + my_ptrdiff_t diff=PTR_BYTE_DIFF(new_pos,pa->str); + for (i=0 ; i < pa->typelib.count ; i++) + pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff, + char*); + pa->str=new_pos; } - else - *pos++= *str++; + pa->max_length+=PS_MALLOC; } - *pos= 0; - return result; -} + if (pa->typelib.count >= pa->max_count-1) + { + int len; + pa->array_allocs++; + len=(PC_MALLOC*pa->array_allocs - MALLOC_OVERHEAD); + if (!(new_array=(const char **) my_realloc((gptr) pa->typelib.type_names, + (uint) len/ + (sizeof(byte*)+sizeof(*pa->flag))* + (sizeof(byte*)+sizeof(*pa->flag)), + MYF(MY_WME)))) + DBUG_RETURN(1); + pa->typelib.type_names=new_array; + old_count=pa->max_count; + pa->max_count=len/(sizeof(byte*) + sizeof(*pa->flag)); + pa->flag= (int7*) (pa->typelib.type_names+pa->max_count); + memcpy((byte*) pa->flag,(my_string) (pa->typelib.type_names+old_count), + old_count*sizeof(*pa->flag)); + } + pa->flag[pa->typelib.count]=0; /* Reset flag */ + pa->typelib.type_names[pa->typelib.count++]= pa->str+pa->length; + pa->typelib.type_names[pa->typelib.count]= NullS; /* Put end-mark */ + VOID(strmov(pa->str+pa->length,name)); + pa->length+=length; + DBUG_RETURN(0); +} /* insert_pointer_name */ -/* - popen replacement for Netware +/* free pointer array */ - SYNPOSIS - my_popen() - name Command to execute (with possible env variables) - mode Mode for popen. +void free_pointer_array(POINTER_ARRAY *pa) +{ + if (pa->typelib.count) + { + pa->typelib.count=0; + my_free((gptr) pa->typelib.type_names,MYF(0)); + pa->typelib.type_names=0; + my_free((gptr) pa->str,MYF(0)); + } +} /* free_pointer_array */ - NOTES - Environment variable expansion does not take place for popen function - on NetWare, so we use this function to wrap around popen to do this. - For the moment we ignore 'mode' and always use 'r0' +/* Functions that uses replace and replace_regex */ - RETURN - # >= 0 File handle - -1 Error -*/ - -#undef popen /* Remove wrapper */ -#ifdef __WIN__ -#define popen _popen /* redefine for windows */ +/* Append the string to ds, with optional replace */ +void replace_dynstr_append_mem(DYNAMIC_STRING *ds, + const char *val, int len) +{ +#ifdef __WIN__ + fix_win_paths(val, len); #endif -FILE *my_popen(const char *cmd, const char *mode __attribute__((unused))) -{ - char *subst_cmd; - FILE *res_file; + if (glob_replace_regex) + { + /* Regex replace */ + if (!multi_reg_replace(glob_replace_regex, (char*)val)) + { + val= glob_replace_regex->buf; + len= strlen(val); + } + } - subst_cmd= subst_env_var(cmd); - res_file= popen(subst_cmd, "r0"); - my_free(subst_cmd, MYF(0)); - return res_file; + if (glob_replace) + { + /* Normal replace */ + replace_strings_append(glob_replace, ds, val, len); + } + else + dynstr_append_mem(ds, val, len); } -#endif /* __NETWARE__ or __WIN__*/ + +/* Append zero-terminated string to ds, with optional replace */ +void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val) +{ + replace_dynstr_append_mem(ds, val, strlen(val)); +} + +/* Append uint to ds, with optional replace */ +void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val) +{ + char buff[22]; /* This should be enough for any int */ + char *end= longlong10_to_str(val, buff, 10); + replace_dynstr_append_mem(ds, buff, end - buff); +} + + diff --git a/mysql-test/include/have_multi_ndb.inc b/mysql-test/include/have_multi_ndb.inc index ec1a93311fb..844c4d78774 100644 --- a/mysql-test/include/have_multi_ndb.inc +++ b/mysql-test/include/have_multi_ndb.inc @@ -9,8 +9,10 @@ disable_query_log; drop table if exists t1, t2; --enable_warnings flush tables; -@r/have_ndb.require show variables like "have_ndbcluster"; -# @r/server_id.require show variables like "server_id"; +--require r/have_ndb.require +show variables like "have_ndbcluster"; +#--require r/server_id.require +#show variables like "server_id"; enable_query_log; # Check that server2 has NDB support @@ -20,8 +22,10 @@ disable_query_log; drop table if exists t1, t2; --enable_warnings flush tables; -@r/have_ndb.require show variables like "have_ndbcluster"; -# @r/server_id1.require show variables like "server_id"; +--require r/have_ndb.require +show variables like "have_ndbcluster"; +#--require r@r/server_id1.require +#show variables like "server_id"; enable_query_log; # Set the default connection to 'server1' diff --git a/mysql-test/include/master-slave.inc b/mysql-test/include/master-slave.inc index 5ec4b4379f8..9ef5489f346 100644 --- a/mysql-test/include/master-slave.inc +++ b/mysql-test/include/master-slave.inc @@ -8,7 +8,8 @@ connection slave; --disable_warnings stop slave; --enable_warnings -@r/slave-stopped.result show status like 'Slave_running'; +--require r/slave-stopped.result +show status like 'Slave_running'; connection master; --disable_warnings drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; @@ -21,7 +22,8 @@ reset slave; drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; --enable_warnings start slave; -@r/slave-running.result show status like 'Slave_running'; +--require r/slave-running.result +show status like 'Slave_running'; # Set the default connection to 'master' connection master; diff --git a/mysql-test/include/ps_query.inc b/mysql-test/include/ps_query.inc index 9a413bff2f3..a2eac09d121 100644 --- a/mysql-test/include/ps_query.inc +++ b/mysql-test/include/ps_query.inc @@ -52,7 +52,6 @@ execute stmt1; ##### parameter used for keyword like SELECT (must fail) set @arg00='SELECT' ; -# mysqltest gives no output for the next statement, Why ?? --error 1064 @arg00 a from t1 where a=1; --error 1064 diff --git a/mysql-test/include/show_msg.inc b/mysql-test/include/show_msg.inc new file mode 100755 index 00000000000..659dce14686 --- /dev/null +++ b/mysql-test/include/show_msg.inc @@ -0,0 +1,25 @@ +#### include/show_msg.inc +# +# This file writes the value set in @message into the +# a protocol file as part of executing a test sequence +# +# Usage: +# Add the following to any *.test file: +# : +# let $message= ; +# --source include/show_msg.inc +# : +# +# Attention: +# - Please do not write any spaces between $message and the "=", because the +# assignment will not work. +# - Be careful with single quotes. They must be escaped like "''" or "\'". +# +# "include/show_msg80.inc" contains a detailed description and examples. + +--disable_query_log +eval SET @utf8_message = CONVERT('$message' using utf8); +select @utf8_message as "" +union +select repeat(CONVERT('-' using utf8),char_length(@utf8_message)); +--enable_query_log diff --git a/mysql-test/include/show_msg80.inc b/mysql-test/include/show_msg80.inc new file mode 100755 index 00000000000..42fb35edbcc --- /dev/null +++ b/mysql-test/include/show_msg80.inc @@ -0,0 +1,118 @@ +#### include/show_msg80.inc +# +# This file writes the value set in @message into the a protocol file as part +# of executing a test sequence with a dash line that is fixed on 80 characters. +# +# This can be used in the case of long messages, multi line messages that +# exceed 80 or if an 80 char line is desired for short messages. +# +# Usage: +# Add the following to any *.test file: +# : +# let $message= ; +# --source include/show_msg80.inc +# : +# +# Attention: +# - Please do not write any spaces between $message and the "=", because the +# assignment will not work. +# - Be careful with single quotes within the value. They must be escaped like +# "''" or "\'". +# - Do not keep the value between single quotes. +# +# +# Content of "$message" and protocol output depending on the assignment: +# ---------------------------------------------------------------------- +# +# I is assumed, that the value is not kept between double quotes. +# +# first character after "$message=", +# where the content is not (space or tab) +# first character after beginning of the line, +# where the content is not (space or tab) +# last char before ";" +# | beginning or end of line +# +# script: let $message= | +# | | +# |................| +# | ; +# content: " +# ...." +# protocol output: || +# || +# |.....| +# || +# |--- 80 dashes ---| +# +# Attention: +# and set to characters like "-$#" which are also used +# to start comments, options and the names of mysqltest variables +# lead to syntax errors or mangled messages. +# +# +# Examples of messages: +# --------------------- +# +# Variant1 (ease of use): +# Several lines with indentation kept between double quotes +# script: |let $message= +# |" Testcase 3.1 : Ensure that Pi is not an| +# | integer number.| +# | Third line"; +# protocol: |" Testcase 3.1 : Ensure that Pi is not an| +# | integer number.| +# | Third line"| +# |------ 80 dashes ----| +# +# Please mention that +# - the '"' preserves the indentation. +# - it is easy to write the script lines to get a fine indentation, +# if the value starts at the beginning of a new line +# - the '"' is printed +# - there are the least or no problems with characters like "-#$" +# +# +# Variant 2 (grep the messages from the protocol is easy): +# Several lines with indentation + auxiliary character (".") +# at the (non tab or space) beginning of every message line +# script: |let $message= . Testcase 3.1 : Ensure that Pi is not an| +# | . integer number.| +# | . Third line; +# protocol: |. Testcase 3.1 : Ensure that Pi is not an| +# |. integer number.| +# |. Third line| +# |------ 80 dashes ----| +# Please mention that +# - the auxiliary character preserves the indentation. +# - it is easy to write the script lines to get a fine indentation +# - the auxiliary character is printed +# - it is recommended to use "." as auxiliary character +# - auxiliary characters like "-'$#" cause problems +# +# +# +# Bad variant1: Several lines with lost indentation +# script: |let $message= Here is message line 1 +# | message line 2; +# protocol: |Here is message line 1| +# |message line 2| +# |------ 80 dashes ----| +# Please mention, that the leading spaces of the message lines disappeared. +# +# Bad variant2: Several lines leading to a syntax error, because of "-" +# script: |let $message= - This is a message +# | - with a second and +# | - third line; +# protocol: | - third line;; +# |ERROR 42000: You have an error ... near '- third line' +# + several following errors +# +# + +--disable_query_log +eval SET @utf8_message = CONVERT('$message' using utf8); +select @utf8_message as "" +union +select repeat(CONVERT('-' using utf8),80); +--enable_query_log diff --git a/mysql-test/r/check.result b/mysql-test/r/check.result index ecaa13642bd..26dbc9e345c 100644 --- a/mysql-test/r/check.result +++ b/mysql-test/r/check.result @@ -1,6 +1,6 @@ drop table if exists t1; create table t1(n int not null, key(n), key(n), key(n), key(n)); - check table t1 extended; +check table t1 extended; insert into t1 values (200000); Table Op Msg_type Msg_text test.t1 check status OK diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result index 68c86b80e60..b9c1e3e5542 100644 --- a/mysql-test/r/connect.result +++ b/mysql-test/r/connect.result @@ -18,6 +18,10 @@ time_zone_transition_type user show tables; Tables_in_test +connect(localhost,root,z,test2,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'root'@'localhost' (using password: YES) +connect(localhost,root,z,test,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'root'@'localhost' (using password: YES) grant ALL on *.* to test@localhost identified by "gambling"; grant ALL on *.* to test@127.0.0.1 identified by "gambling"; show tables; @@ -39,6 +43,14 @@ time_zone_transition_type user show tables; Tables_in_test +connect(localhost,test,,test2,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'test'@'localhost' (using password: NO) +connect(localhost,test,,"",MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'test'@'localhost' (using password: NO) +connect(localhost,test,zorro,test2,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'test'@'localhost' (using password: YES) +connect(localhost,test,zorro,test,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'test'@'localhost' (using password: YES) update mysql.user set password=old_password("gambling2") where user=_binary"test"; flush privileges; set password=""; @@ -64,6 +76,14 @@ time_zone_transition_type user show tables; Tables_in_test +connect(localhost,test,,test2,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'test'@'localhost' (using password: NO) +connect(localhost,test,,test,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'test'@'localhost' (using password: NO) +connect(localhost,test,zorro,test2,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'test'@'localhost' (using password: YES) +connect(localhost,test,zorro,test,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'test'@'localhost' (using password: YES) delete from mysql.user where user=_binary"test"; flush privileges; create table t1 (id integer not null auto_increment primary key); diff --git a/mysql-test/r/flush.result b/mysql-test/r/flush.result index 384bdc1214b..83b8b769f67 100644 --- a/mysql-test/r/flush.result +++ b/mysql-test/r/flush.result @@ -9,13 +9,13 @@ n flush tables with read lock; drop table t2; ERROR HY000: Table 't2' was locked with a READ lock and can't be updated - drop table t2; +drop table t2; unlock tables; create database mysqltest; create table mysqltest.t1(n int); insert into mysqltest.t1 values (23); flush tables with read lock; - drop database mysqltest; +drop database mysqltest; select * from mysqltest.t1; n 23 diff --git a/mysql-test/r/flush_block_commit.result b/mysql-test/r/flush_block_commit.result index 2e9f1920937..d5b10868358 100644 --- a/mysql-test/r/flush_block_commit.result +++ b/mysql-test/r/flush_block_commit.result @@ -5,7 +5,7 @@ insert into t1 values(1); flush tables with read lock; select * from t1; a - commit; +commit; select * from t1; a unlock tables; @@ -14,8 +14,8 @@ select * from t1 for update; a 1 begin; - select * from t1 for update; - flush tables with read lock; +select * from t1 for update; +flush tables with read lock; commit; a 1 diff --git a/mysql-test/r/func_misc.result b/mysql-test/r/func_misc.result index adf2035173f..246713489b4 100644 --- a/mysql-test/r/func_misc.result +++ b/mysql-test/r/func_misc.result @@ -69,7 +69,7 @@ FROM t1 WHERE conn = 'default'; IS_USED_LOCK('bug16501') = connection_id 1 - SELECT GET_LOCK('bug16501',600); +SELECT GET_LOCK('bug16501',600); SELECT IS_USED_LOCK('bug16501') = CONNECTION_ID(); IS_USED_LOCK('bug16501') = CONNECTION_ID() 1 diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result index c047dc033c6..36d4e51119d 100644 --- a/mysql-test/r/grant2.result +++ b/mysql-test/r/grant2.result @@ -123,12 +123,12 @@ drop database mysqltest_1; set password = password("changed"); ERROR 42000: Access denied for user ''@'localhost' to database 'mysql' lock table mysql.user write; - flush privileges; - grant all on *.* to 'mysqltest_1'@'localhost'; +flush privileges; +grant all on *.* to 'mysqltest_1'@'localhost'; unlock tables; lock table mysql.user write; - set password for 'mysqltest_1'@'localhost' = password(''); - revoke all on *.* from 'mysqltest_1'@'localhost'; +set password for 'mysqltest_1'@'localhost' = password(''); +revoke all on *.* from 'mysqltest_1'@'localhost'; unlock tables; drop user 'mysqltest_1'@'localhost'; insert into mysql.user (user, host) values diff --git a/mysql-test/r/handler.result b/mysql-test/r/handler.result index 9b0c6dbc263..e88d359e0ef 100644 --- a/mysql-test/r/handler.result +++ b/mysql-test/r/handler.result @@ -452,7 +452,7 @@ handler t1 read first; c1 1 send the below to another connection, do not wait for the result - optimize table t1; +optimize table t1; proceed with the normal connection handler t1 read next; c1 diff --git a/mysql-test/r/kill.result b/mysql-test/r/kill.result index eff59b102de..2a88f1ce3a8 100644 --- a/mysql-test/r/kill.result +++ b/mysql-test/r/kill.result @@ -24,7 +24,7 @@ create table t2 (id int unsigned not null); insert into t2 select id from t1; create table t3 (kill_id int); insert into t3 values(connection_id()); - select id from t1 where id in (select distinct id from t2); +select id from t1 where id in (select distinct id from t2); select ((@id := kill_id) - kill_id) from t3; ((@id := kill_id) - kill_id) 0 diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index 9eedbf50064..59b683b1917 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -2,8 +2,8 @@ drop table if exists t1,t2; create table t1(n int); insert into t1 values (1); lock tables t1 write; - update low_priority t1 set n = 4; - select n from t1; +update low_priority t1 set n = 4; +select n from t1; unlock tables; n 4 @@ -11,8 +11,8 @@ drop table t1; create table t1(n int); insert into t1 values (1); lock tables t1 read; - update low_priority t1 set n = 4; - select n from t1; +update low_priority t1 set n = 4; +select n from t1; unlock tables; n 1 @@ -23,7 +23,7 @@ insert into t1 values(1,1); insert into t1 values(2,2); insert into t2 values(1,2); lock table t1 read; - update t1,t2 set c=a where b=d; +update t1,t2 set c=a where b=d; select c from t2; c 2 @@ -32,7 +32,7 @@ drop table t2; create table t1 (a int); create table t2 (a int); lock table t1 write, t2 write; - insert t1 select * from t2; +insert t1 select * from t2; drop table t2; ERROR 42S02: Table 'test.t2' doesn't exist drop table t1; diff --git a/mysql-test/r/mix_innodb_myisam_binlog.result b/mysql-test/r/mix_innodb_myisam_binlog.result index 8cf99e8d623..f8de0336e63 100644 --- a/mysql-test/r/mix_innodb_myisam_binlog.result +++ b/mysql-test/r/mix_innodb_myisam_binlog.result @@ -193,7 +193,7 @@ select (@before:=unix_timestamp())*0; (@before:=unix_timestamp())*0 0 begin; - select * from t1 for update; +select * from t1 for update; insert into t2 values (20); ERROR HY000: Lock wait timeout exceeded; try restarting transaction select (@after:=unix_timestamp())*0; diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result index 091a3c0547d..7276397bdc7 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -16,7 +16,7 @@ otto mysqltest: At line 1: query 'select otto from (select 1 as otto) as t1' succeeded - should have failed with sqlstate 42S22... select friedrich from (select 1 as otto) as t1; ERROR 42S22: Unknown column 'friedrich' in 'field list' -mysqltest: At line 1: query 'select friedrich from (select 1 as otto) as t1' failed with wrong sqlstate 42S22 instead of 00000... +mysqltest: At line 1: query 'select friedrich from (select 1 as otto) as t1' failed with wrong sqlstate 42S22: 'Unknown column 'friedrich' in 'field list'', instead of 00000... select otto from (select 1 as otto) as t1; otto 1 @@ -133,8 +133,7 @@ ERROR 42S02: Table 'test.t1' doesn't exist select 1146 as "after_!errno_masked_error" ; after_!errno_masked_error 1146 -mysqltest: At line 1: query 'select 3 from t1' failed with wrong errno 1146 instead of 1000... -mysqltest: At line 1: query 'select 3 from t1' failed with wrong errno 1146 instead of 1000... +mysqltest: At line 1: query 'select 3 from t1' failed with wrong errno 1146: 'Table 'test.t1' doesn't exist', instead of 1000... garbage ; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'garbage' at line 1 select 1064 as "after_--enable_abort_on_error" ; @@ -142,8 +141,7 @@ after_--enable_abort_on_error 1064 select 3 from t1 ; ERROR 42S02: Table 'test.t1' doesn't exist -mysqltest: At line 1: query 'select 3 from t1' failed with wrong errno 1146 instead of 1064... -mysqltest: At line 1: query 'select 3 from t1' failed: 1146: Table 'test.t1' doesn't exist +mysqltest: At line 1: query 'select 3 from t1' failed with wrong errno 1146: 'Table 'test.t1' doesn't exist', instead of 1064... hello hello ;;;;;;;; @@ -151,7 +149,24 @@ hello mysqltest: At line 1: End of line junk detected: "6" mysqltest: At line 1: End of line junk detected: "6" mysqltest: At line 1: Missing delimiter +mysqltest: At line 1: End of line junk detected: "sleep 7 +# Another comment +" mysqltest: At line 1: Extra delimiter ";" found +mysqltest: At line 1: Extra delimiter ";" found +mysqltest: At line 1: Missing argument(s) to 'error' +mysqltest: At line 1: Missing argument(s) to 'error' +mysqltest: At line 1: The sqlstate definition must start with an uppercase S +mysqltest: At line 1: The error name definition must start with an uppercase E +mysqltest: At line 1: Invalid argument to error: '9eeeee' - the errno may only consist of digits[0-9] +mysqltest: At line 1: Invalid argument to error: '1sssss' - the errno may only consist of digits[0-9] +mysqltest: At line 1: The sqlstate must be exactly 5 chars long +mysqltest: At line 1: The sqlstate may only consist of digits[0-9] and _uppercase_ letters +mysqltest: At line 1: The sqlstate must be exactly 5 chars long +mysqltest: At line 1: Not available in mysqltest for MySQL 4.1.22 +mysqltest: At line 1: Invalid argument to error: '999e9' - the errno may only consist of digits[0-9] +mysqltest: At line 1: Invalid argument to error: '9b' - the errno may only consist of digits[0-9] +mysqltest: At line 1: Too many errorcodes specified MySQL "MySQL" MySQL: The world''s most popular open source database @@ -166,8 +181,8 @@ source database - most popular open - source database - MySQL: The world''s --- most popular open --- source database +-- most popular +-- open source database # MySQL: The --world''s # most popular @@ -193,14 +208,20 @@ source database # source database -- MySQL: The -- world''s most --- popular open --- source database +-- popular +-- open source database # MySQL: The - world''s most -- popular open # source database -'$message' -"$message" +'# MySQL: The +- world''s most +-- popular open +# source database' +"# MySQL: The +- world''s most +-- popular open +# source database" hej hej hej @@ -209,21 +230,32 @@ hej a long variable content a long variable content -a long $where variable content +a long a long variable content variable content +a long \$where variable content +banana = banana +Not a banana: ba\$cat\$cat mysqltest: At line 1: Missing arguments to let mysqltest: At line 1: Missing variable name in let -mysqltest: At line 1: Variable name in hi=hi does not start with '$' mysqltest: At line 1: Missing assignment operator in let mysqltest: At line 1: Missing assignment operator in let -mysqltest: At line 1: Missing arguments to let +mysqltest: At line 1: Missing assignment operator in let +mysqltest: At line 1: Missing variable name in let mysqltest: At line 1: Missing variable name in let -mysqltest: At line 1: Variable name in =hi does not start with '$' mysqltest: At line 1: Missing assignment operator in let -mysqltest: At line 1: Missing file name in source +# Execute: --echo # success: $success +# success: 1 +# Execute: echo # success: $success ; +# success: 1 +# The next two variants work fine and expand the content of $success +# Execute: --echo $success +1 +# Execute: echo $success ; +1 +mysqltest: At line 1: Missing required argument 'filename' to command 'source' mysqltest: At line 1: Could not open file ./non_existingFile -mysqltest: In included file "./var/tmp/recursive.sql": At line 1: Source directives are nesting too deep -mysqltest: In included file "./var/tmp/error.sql": At line 1: query 'garbage ' failed: 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'garbage' at line 1 +mysqltest: In included file "MYSQLTEST_VARDIR/tmp/recursive.sql": At line 1: Source directives are nesting too deep +mysqltest: In included file "MYSQLTEST_VARDIR/tmp/error.sql": At line 1: query 'garbage ' failed: 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'garbage' at line 1 2 = outer loop variable after while here is the sourced script @@ -288,8 +320,8 @@ mysqltest: At line 1: Invalid argument to real_sleep "abc" 101 hej 1 -mysqltest: At line 1: Missing arguments to inc -mysqltest: At line 1: First argument to inc must be a variable (start with $) +mysqltest: At line 1: Missing argument to inc +mysqltest: At line 1: The argument to inc must be a variable (start with $) mysqltest: At line 1: End of line junk detected: "1000" 4 4 @@ -298,27 +330,31 @@ mysqltest: At line 1: End of line junk detected: "1000" 99 hej -1 -mysqltest: At line 1: Missing arguments to dec -mysqltest: At line 1: First argument to dec must be a variable (start with $) +mysqltest: At line 1: Missing argument to dec +mysqltest: At line 1: The argument to dec must be a variable (start with $) mysqltest: At line 1: End of line junk detected: "1000" mysqltest: At line 1: Missing arguments to system, nothing to do! mysqltest: At line 1: Missing arguments to system, nothing to do! mysqltest: At line 1: system command 'false' failed +system command 'NonExistsinfComamdn 2> /dev/null' failed test test2 test3 test4 +Counter is greater than 0, (counter=10) +Counter is not 0, (counter=0) 1 -mysqltest: In included file "./include/mysqltest_while.inc": At line 64: Nesting too deeply +Testing while with not +mysqltest: In included file "MYSQLTEST_VARDIR/tmp/mysqltest_while.inc": At line 64: Nesting too deeply mysqltest: At line 1: missing '(' in while mysqltest: At line 1: missing ')' in while mysqltest: At line 1: Missing '{' after while. Found "dec $i" mysqltest: At line 1: Stray '}' - end of block before beginning mysqltest: At line 1: Stray 'end' command - end of block before beginning -mysqltest: At line 1: query '' failed: 1065: Query was empty +mysqltest: At line 1: query '{' failed: 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '{' at line 1 mysqltest: At line 1: Missing '{' after while. Found "echo hej" mysqltest: At line 3: Missing end of block -mysqltest: At line 1: Missing newline between while and '{' +mysqltest: At line 3: Missing end of block mysqltest: At line 1: missing '(' in if mysqltest: At line 1: Stray 'end' command - end of block before beginning select "b" bs col1, "c" bs col2; @@ -331,6 +367,7 @@ mysqltest: At line 1: Wrong number of arguments to replace_result in 'replace_re mysqltest: At line 1: Wrong number of arguments to replace_result in 'replace_result a;' mysqltest: At line 1: Wrong number of arguments to replace_result in 'replace_result a' mysqltest: At line 1: Wrong number of arguments to replace_result in 'replace_result a ' +OK mysqltest: At line 1: Wrong number of arguments to replace_result in 'replace_result a b c' mysqltest: At line 1: Wrong number of arguments to replace_result in 'replace_result a b c ' select "a" as col1, "c" as col2; @@ -347,6 +384,18 @@ mysqltest: At line 1: Wrong column number to replace_column in 'replace_column 1 mysqltest: At line 1: Invalid integer argument "10!" mysqltest: At line 1: End of line junk detected: "!" mysqltest: At line 1: Invalid integer argument "a" +mysqltest: At line 1: Missing required argument 'connection name' to command 'connect' +mysqltest: At line 1: Missing required argument 'connection name' to command 'connect' +mysqltest: At line 1: Missing required argument 'host' to command 'connect' +mysqltest: At line 1: Missing required argument 'host' to command 'connect' +mysqltest: At line 1: query 'connect con2,localhost,root,,illegal_db' failed: 1049: Unknown database 'illegal_db' +mysqltest: At line 1: Illegal argument for port: 'illegal_port' +mysqltest: At line 1: Illegal option to connect: SMTP +OK +mysqltest: In included file "MYSQLTEST_VARDIR/tmp/mysqltest.sql": At line 7: Connection limit exhausted, you can have max 128 connections +mysqltest: In included file "MYSQLTEST_VARDIR/tmp/mysqltest.sql": At line 3: connection 'test_con1' not found in connection pool +mysqltest: In included file "MYSQLTEST_VARDIR/tmp/mysqltest.sql": At line 2: Connection test_con1 already exists +connect(localhost,root,,test,MASTER_PORT,MASTER_SOCKET); Output from mysqltest-x.inc Output from mysqltest-x.inc Output from mysqltest-x.inc @@ -358,3 +407,98 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp SELECT 1 as a; a 1 +select 1 as `a'b`, 2 as `a"b`; +a'b a"b +1 2 +select 'aaa\\','aa''a',"aa""a"; +aaa\ aa'a aa"a +aaa\ aa'a aa"a + +Here comes a message +-------------------- + +root@localhost +-------------- + +"Here comes a very very long message that + - is longer then 80 characters and + - consists of several lines" +-------------------------------------------------------------------------------- + +. Here comes a very very long message that +. - is longer then 80 characters and +. - consists of several lines +-------------------------------------------------------------------------------- +this will be executed +this will be executed +mysqltest: Result length mismatch +mysqltest: The test didn't produce any output +Failing multi statement query +mysqltest: At line 3: query 'create table t1 (a int primary key); +insert into t1 values (1); +select 'select-me'; +insertz 'error query'' failed: 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'insertz 'error query'' at line 1 +drop table t1; +mysqltest: At line 3: query 'create table t1 (a int primary key); +insert into t1 values (1); +select 'select-me'; +insertz 'error query'' failed: 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'insertz 'error query'' at line 1 +drop table t1; +Multi statement using expected error +create table t1 (a int primary key); +insert into t1 values (1); +select 'select-me'; +insertz error query|||| +select-me +select-me +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'insertz error query' at line 1 +drop table t1; +drop table t1; +sleep; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'sleep' at line 1 +sleep; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'sleep' at line 1 +; +ERROR 42000: Query was empty +select "b" as col1, "c" as col2; +col1 col2 +b c +select "b" as col1, "b" as col2, "c" as col3; +col1 col2 col3 +b b c +seled "b" bs col1, "d" bs col2; +col1 col2 +b d +select "raspberry and strawberry","blackberry","tomato"; +raspberry and strawberry blackberry tomato +raspberry and strawberry blackberry tomato +mysqltest: At line 1: Error parsing replace_regex "a" +mysqltest: At line 1: Error parsing replace_regex "a;" +mysqltest: At line 1: Error parsing replace_regex "a" +mysqltest: At line 1: Error parsing replace_regex "a " +mysqltest: At line 1: Error parsing replace_regex "a b" +mysqltest: At line 1: Error parsing replace_regex "/a b c" +mysqltest: At line 1: Error parsing replace_regex "/a /b c " +create table t1 (a int, b int); +insert into t1 values (1,3); +insert into t1 values (2,4); +select * from t1; +a D +1 1 +1 4 +drop table t1; +mysqltest: At line 1: Missing required argument 'filename' to command 'remove_file' +mysqltest: At line 1: Missing required argument 'filename' to command 'write_file' +mysqltest: At line 1: End of file encountered before 'EOF' delimiter was found +mysqltest: At line 1: End of line junk detected: "write_file filename "; +" +mysqltest: At line 1: Missing required argument 'filename' to command 'file_exists' +mysqltest: At line 1: Missing required argument 'from_file' to command 'copy_file' +mysqltest: At line 1: Missing required argument 'to_file' to command 'copy_file' +hello +hello +hello +mysqltest: At line 1: Max delimiter length(16) exceeded +hello +hello +End of tests diff --git a/mysql-test/r/ps_2myisam.result b/mysql-test/r/ps_2myisam.result index 4b655cfb854..771245c3713 100644 --- a/mysql-test/r/ps_2myisam.result +++ b/mysql-test/r/ps_2myisam.result @@ -85,6 +85,8 @@ c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c 1 1 1 1 1 1 1 1 1 1 1.0000 1.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 1 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext one monday 9 9 9 9 9 9 9 9 9 9 9.0000 9.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 0 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext two tuesday set @arg00='SELECT' ; +@arg00 a from t1 where a=1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@arg00 a from t1 where a=1' at line 1 prepare stmt1 from ' ? a from t1 where a=1 '; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? a from t1 where a=1' at line 1 set @arg00=1 ; diff --git a/mysql-test/r/ps_3innodb.result b/mysql-test/r/ps_3innodb.result index 4d2a62887d6..8435bb348f4 100644 --- a/mysql-test/r/ps_3innodb.result +++ b/mysql-test/r/ps_3innodb.result @@ -85,6 +85,8 @@ c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c 1 1 1 1 1 1 1 1 1 1 1.0000 1.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 1 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext one monday 9 9 9 9 9 9 9 9 9 9 9.0000 9.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 0 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext two tuesday set @arg00='SELECT' ; +@arg00 a from t1 where a=1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@arg00 a from t1 where a=1' at line 1 prepare stmt1 from ' ? a from t1 where a=1 '; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? a from t1 where a=1' at line 1 set @arg00=1 ; diff --git a/mysql-test/r/ps_4heap.result b/mysql-test/r/ps_4heap.result index a4919b664c4..d6876825aa9 100644 --- a/mysql-test/r/ps_4heap.result +++ b/mysql-test/r/ps_4heap.result @@ -86,6 +86,8 @@ c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c 1 1 1 1 1 1 1 1 1 1 1.0000 1.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 1 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext one monday 9 9 9 9 9 9 9 9 9 9 9.0000 9.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 0 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext two tuesday set @arg00='SELECT' ; +@arg00 a from t1 where a=1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@arg00 a from t1 where a=1' at line 1 prepare stmt1 from ' ? a from t1 where a=1 '; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? a from t1 where a=1' at line 1 set @arg00=1 ; diff --git a/mysql-test/r/ps_5merge.result b/mysql-test/r/ps_5merge.result index f98cc1b3cdf..f2d459c6769 100644 --- a/mysql-test/r/ps_5merge.result +++ b/mysql-test/r/ps_5merge.result @@ -128,6 +128,8 @@ c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c 1 1 1 1 1 1 1 1 1 1 1.0000 1.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 1 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext one monday 9 9 9 9 9 9 9 9 9 9 9.0000 9.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 0 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext two tuesday set @arg00='SELECT' ; +@arg00 a from t1 where a=1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@arg00 a from t1 where a=1' at line 1 prepare stmt1 from ' ? a from t1 where a=1 '; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? a from t1 where a=1' at line 1 set @arg00=1 ; @@ -3142,6 +3144,8 @@ c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c 1 1 1 1 1 1 1 1 1 1 1.0000 1.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 1 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext one monday 9 9 9 9 9 9 9 9 9 9 9.0000 9.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 0 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext two tuesday set @arg00='SELECT' ; +@arg00 a from t1 where a=1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@arg00 a from t1 where a=1' at line 1 prepare stmt1 from ' ? a from t1 where a=1 '; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? a from t1 where a=1' at line 1 set @arg00=1 ; diff --git a/mysql-test/r/ps_6bdb.result b/mysql-test/r/ps_6bdb.result index acd7f45de95..3e16923c46f 100644 --- a/mysql-test/r/ps_6bdb.result +++ b/mysql-test/r/ps_6bdb.result @@ -85,6 +85,8 @@ c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c 1 1 1 1 1 1 1 1 1 1 1.0000 1.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 1 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext one monday 9 9 9 9 9 9 9 9 9 9 9.0000 9.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 0 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext two tuesday set @arg00='SELECT' ; +@arg00 a from t1 where a=1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@arg00 a from t1 where a=1' at line 1 prepare stmt1 from ' ? a from t1 where a=1 '; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? a from t1 where a=1' at line 1 set @arg00=1 ; diff --git a/mysql-test/r/ps_7ndb.result b/mysql-test/r/ps_7ndb.result index 27a1ea0925d..baceb1e4a9e 100644 --- a/mysql-test/r/ps_7ndb.result +++ b/mysql-test/r/ps_7ndb.result @@ -85,6 +85,8 @@ c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c 1 1 1 1 1 1 1 1 1 1 1.0000 1.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 1 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext one monday 9 9 9 9 9 9 9 9 9 9 9.0000 9.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 0 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext two tuesday set @arg00='SELECT' ; +@arg00 a from t1 where a=1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@arg00 a from t1 where a=1' at line 1 prepare stmt1 from ' ? a from t1 where a=1 '; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? a from t1 where a=1' at line 1 set @arg00=1 ; diff --git a/mysql-test/r/rename.result b/mysql-test/r/rename.result index 76c0f4422fe..964babb1228 100644 --- a/mysql-test/r/rename.result +++ b/mysql-test/r/rename.result @@ -43,7 +43,7 @@ Note 1051 Unknown table 't4' CREATE TABLE t1 (a int); CREATE TABLE t3 (a int); FLUSH TABLES WITH READ LOCK; - RENAME TABLE t1 TO t2, t3 to t4; +RENAME TABLE t1 TO t2, t3 to t4; show tables; Tables_in_test t1 diff --git a/mysql-test/r/rpl000001.result b/mysql-test/r/rpl000001.result index eef986d8f8c..1bce3b1b235 100644 --- a/mysql-test/r/rpl000001.result +++ b/mysql-test/r/rpl000001.result @@ -59,7 +59,7 @@ create table t2(id int); insert into t2 values(connection_id()); create temporary table t3(n int); insert into t3 select get_lock('crash_lock%20C', 1) from t2; - update t1 set n = n + get_lock('crash_lock%20C', 2); +update t1 set n = n + get_lock('crash_lock%20C', 2); select (@id := id) - id from t2; (@id := id) - id 0 diff --git a/mysql-test/r/rpl000018.result b/mysql-test/r/rpl000018.result deleted file mode 100644 index b71f6492b97..00000000000 --- a/mysql-test/r/rpl000018.result +++ /dev/null @@ -1,14 +0,0 @@ -reset master; -reset slave; -start slave; -show binary logs; -Log_name -master-bin.000001 -master-bin.000002 -drop table if exists t1; -create table t1(n int); -insert into t1 values (3351); -select * from t1; -n -3351 -drop table t1; diff --git a/mysql-test/r/rpl_chain_temp_table.result b/mysql-test/r/rpl_chain_temp_table.result deleted file mode 100644 index 5ece80565c7..00000000000 --- a/mysql-test/r/rpl_chain_temp_table.result +++ /dev/null @@ -1,30 +0,0 @@ -slave stop; -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; -slave start; -reset master; -change master to master_host='127.0.0.1',master_port=9307, master_user='root'; -start slave; -create temporary table t1 (a int); -create temporary table t1 (a int); -show status like 'slave_open_temp_tables'; -Variable_name Value -Slave_open_temp_tables 2 -create temporary table t1 (a int); -create temporary table t1 (a int); -show status like 'slave_open_temp_tables'; -Variable_name Value -Slave_open_temp_tables 4 -stop slave; -insert into t1 values(1); -create table t2 as select * from t1; -start slave; -show status like 'slave_open_temp_tables'; -Variable_name Value -Slave_open_temp_tables 4 -select * from t2; -a -1 -drop table t2; diff --git a/mysql-test/r/rpl_error_ignored_table.result b/mysql-test/r/rpl_error_ignored_table.result index 4a562dbfc70..789ca72cd61 100644 --- a/mysql-test/r/rpl_error_ignored_table.result +++ b/mysql-test/r/rpl_error_ignored_table.result @@ -20,7 +20,7 @@ create table t2 (a int primary key); insert into t2 values(1); create table t3 (id int); insert into t3 values(connection_id()); - update t2 set a = a + 1 + get_lock('crash_lock%20C', 10); +update t2 set a = a + 1 + get_lock('crash_lock%20C', 10); select (@id := id) - id from t3; (@id := id) - id 0 diff --git a/mysql-test/r/rpl_failsafe.result b/mysql-test/r/rpl_failsafe.result deleted file mode 100644 index 956555f9318..00000000000 --- a/mysql-test/r/rpl_failsafe.result +++ /dev/null @@ -1,34 +0,0 @@ -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; -show variables like 'rpl_recovery_rank'; -Variable_name Value -rpl_recovery_rank 1 -show status like 'Rpl_status'; -Variable_name Value -Rpl_status AUTH_MASTER -create table t1(n int); -drop table t1; -show variables like 'rpl_recovery_rank'; -Variable_name Value -rpl_recovery_rank 2 -show status like 'Rpl_status'; -Variable_name Value -Rpl_status ACTIVE_SLAVE -start slave; -show variables like 'rpl_recovery_rank'; -Variable_name Value -rpl_recovery_rank 3 -show status like 'Rpl_status'; -Variable_name Value -Rpl_status ACTIVE_SLAVE -start slave; -show variables like 'rpl_recovery_rank'; -Variable_name Value -rpl_recovery_rank 4 -show status like 'Rpl_status'; -Variable_name Value -Rpl_status ACTIVE_SLAVE diff --git a/mysql-test/r/rpl_heap.result b/mysql-test/r/rpl_heap.result deleted file mode 100644 index 1facbcb7676..00000000000 --- a/mysql-test/r/rpl_heap.result +++ /dev/null @@ -1,29 +0,0 @@ -reset master; -drop table if exists t1; -create table t1 type=HEAP select 10 as a; -insert into t1 values(11); -show binlog events from 79; -Log_name Pos Event_type Server_id Orig_log_pos Info -master-bin.001 79 Query 1 79 use `test`; create table t1 type=HEAP select 10 as a -master-bin.001 154 Query 1 154 use `test`; insert into t1 values(11) -reset slave; -start slave; -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` bigint(2) NOT NULL default '0' -) TYPE=HEAP -select * from t1; -a -10 -11 -select * from t1; -a -select * from t1 limit 10; -a -show binlog events in 'master-bin.002' from 79; -Log_name Pos Event_type Server_id Orig_log_pos Info -master-bin.002 79 Query 1 79 use `test`; DELETE FROM `test`.`t1` -select * from t1; -a -drop table t1; diff --git a/mysql-test/r/rpl_master_pos_wait.result b/mysql-test/r/rpl_master_pos_wait.result index e92d1ffa361..84f2b0bd91e 100644 --- a/mysql-test/r/rpl_master_pos_wait.result +++ b/mysql-test/r/rpl_master_pos_wait.result @@ -12,7 +12,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: Note 1003 select sql_no_cache master_pos_wait(_latin1'master-bin.999999',0,2) AS `master_pos_wait('master-bin.999999',0,2)` - select master_pos_wait('master-bin.999999',0); +select master_pos_wait('master-bin.999999',0); stop slave sql_thread; master_pos_wait('master-bin.999999',0) NULL diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index f5abc4ed42a..84b5a42d173 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -2697,8 +2697,7 @@ select (1,2,3) = (select * from t1); ERROR 21000: Operand should contain 3 column(s) select (select * from t1) = (1,2,3); ERROR 21000: Operand should contain 2 column(s) -drop table t1 -#; +drop table t1; CREATE TABLE `t1` ( `itemid` bigint(20) unsigned NOT NULL auto_increment, `sessionid` bigint(20) unsigned default NULL, diff --git a/mysql-test/r/synchronization.result b/mysql-test/r/synchronization.result index ad9443c86da..8b243fe29b2 100644 --- a/mysql-test/r/synchronization.result +++ b/mysql-test/r/synchronization.result @@ -1,5 +1,5 @@ CREATE TABLE t1 (x1 int); - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -7,7 +7,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -15,7 +15,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -23,7 +23,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -31,7 +31,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -39,7 +39,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -47,7 +47,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -55,7 +55,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -63,7 +63,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -71,7 +71,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -79,7 +79,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -87,7 +87,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -95,7 +95,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -103,7 +103,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -111,7 +111,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -119,7 +119,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -127,7 +127,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -135,7 +135,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -143,7 +143,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -151,7 +151,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table diff --git a/mysql-test/r/type_blob.result b/mysql-test/r/type_blob.result index 6b4b54fbb66..977377c0bc6 100644 --- a/mysql-test/r/type_blob.result +++ b/mysql-test/r/type_blob.result @@ -24,8 +24,7 @@ t3 CREATE TABLE `t3` ( `a` mediumtext, `b` mediumblob ) ENGINE=MyISAM DEFAULT CHARSET=latin1 -drop table t1,t2,t3 -#; +drop table t1,t2,t3; CREATE TABLE t1 (a char(257) default "hello"); ERROR 42000: Column length too big for column 'a' (max = 255); use BLOB or TEXT instead CREATE TABLE t2 (a blob default "hello"); diff --git a/mysql-test/t/connect.test b/mysql-test/t/connect.test index ff15d74e5ac..89e34f0c8e0 100644 --- a/mysql-test/t/connect.test +++ b/mysql-test/t/connect.test @@ -1,7 +1,6 @@ # This test is to check various cases of connections -# with right and wrong password, with and without database -# Unfortunately the check is incomplete as we can't handle errors on connect -# Also we can't connect without database +# with right and wrong password, with and without database +# Unfortunately the check is incomplete as we can't connect without database # This test makes no sense with the embedded server --source include/not_embedded.inc @@ -10,69 +9,73 @@ drop table if exists t1,t2; --enable_warnings + #connect (con1,localhost,root,,""); #show tables; connect (con1,localhost,root,,mysql); show tables; -connect (con1,localhost,root,,test); +connect (con2,localhost,root,,test); show tables; -# Re enable this one day if error handling on connect will take place - -#connect (con1,localhost,root,z,test2); -#--error 1045 -#connect (con1,localhost,root,z,); -#--error 1045 +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,root,z,test2); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,root,z,); grant ALL on *.* to test@localhost identified by "gambling"; grant ALL on *.* to test@127.0.0.1 identified by "gambling"; # Now check this user with different databases - #connect (con1,localhost,test,gambling,""); #show tables; -connect (con1,localhost,test,gambling,mysql); +connect (con3,localhost,test,gambling,mysql); show tables; -connect (con1,localhost,test,gambling,test); +connect (con4,localhost,test,gambling,test); show tables; -# Re enable this one day if error handling on connect will take place - -#connect (con1,localhost,test,,test2); -#--error 1045 -#connect (con1,localhost,test,,""); -#--error 1045 -#connect (con1,localhost,test,zorro,test2); -#--error 1045 -#connect (con1,localhost,test,zorro,); -#--error 1045 +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,test,,test2); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,test,,""); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,test,zorro,test2); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,test,zorro,); # check if old password version also works update mysql.user set password=old_password("gambling2") where user=_binary"test"; flush privileges; -#connect (con1,localhost,test,gambling2,""); -#show tables; -connect (con1,localhost,test,gambling2,mysql); +connect (con10,localhost,test,gambling2,); +connect (con5,localhost,test,gambling2,mysql); +connection con5; set password=""; --error 1105 set password='gambling3'; set password=old_password('gambling3'); show tables; -connect (con1,localhost,test,gambling3,test); +connect (con6,localhost,test,gambling3,test); show tables; -# Re enable this one day if error handling on connect will take place - -#connect (con1,localhost,test,,test2); -#--error 1045 -#connect (con1,localhost,test,,); -#--error 1045 -#connect (con1,localhost,test,zorro,test2); -#--error 1045 -#connect (con1,localhost,test,zorro,); -#--error 1045 +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,test,,test2); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,test,,); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,test,zorro,test2); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,test,zorro,); # remove user 'test' so that other tests which may use 'test' @@ -84,13 +87,13 @@ flush privileges; # # Bug#12517: Clear user variables and replication events before # closing temp tables in thread cleanup. -connect (con2,localhost,root,,test); -connection con2; +connect (con7,localhost,root,,test); +connection con7; create table t1 (id integer not null auto_increment primary key); create temporary table t2(id integer not null auto_increment primary key); set @id := 1; delete from t1 where id like @id; -disconnect con2; +disconnect con7; --sleep 5 connection default; drop table t1; diff --git a/mysql-test/t/init_file.test b/mysql-test/t/init_file.test index de6aca455bd..bbe0c4ff884 100644 --- a/mysql-test/t/init_file.test +++ b/mysql-test/t/init_file.test @@ -4,6 +4,7 @@ # # See mysql-test/std_data/init_file.dat and # mysql-test/t/init_file-master.opt for the actual test -# +# -# End of 4.1 tests +--echo ok +--echo End of 4.1 tests diff --git a/mysql-test/t/mysql_client_test.test b/mysql-test/t/mysql_client_test.test index 1225bf73009..2677e470289 100644 --- a/mysql-test/t/mysql_client_test.test +++ b/mysql-test/t/mysql_client_test.test @@ -8,8 +8,8 @@ # server or run mysql-test-run --debug mysql_client_test and check # var/log/mysql_client_test.trace ---disable_result_log ---exec echo $MYSQL_CLIENT_TEST --getopt-ll-test=25600M ---exec $MYSQL_CLIENT_TEST --getopt-ll-test=25600M +--exec echo "$MYSQL_CLIENT_TEST --getopt-ll-test=25600M" > $MYSQLTEST_VARDIR/log/mysql_client_test.log 2>&1 +--exec $MYSQL_CLIENT_TEST --getopt-ll-test=25600M >> $MYSQLTEST_VARDIR/log/mysql_client_test.log 2>&1 # End of 4.1 tests +echo ok; diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test index a15a143e9f4..5b0df28981f 100644 --- a/mysql-test/t/mysqltest.test +++ b/mysql-test/t/mysqltest.test @@ -1,3 +1,4 @@ +# This test should work in embedded server after mysqltest is fixed -- source include/not_embedded.inc # ============================================================================ @@ -26,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 @@ -49,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' # ---------------------------------------------------------------------------- @@ -67,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; @@ -113,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. @@ -216,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 ; @@ -248,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". # @@ -257,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 ; @@ -316,7 +317,6 @@ select 3 from t1 ; # #select 3 from t1 ; -# End of 4.1 tests --error 1 --exec echo "disable_abort_on_error; enable_abort_on_error; error 1064; select 3 from t1; select 3 from t1;" | $MYSQL_TEST 2>&1 @@ -344,7 +344,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 @@ -359,23 +359,95 @@ select 3 from t1 ; # Missing delimiter # The comment will be "sucked into" the sleep command since # delimiter is missing until after "show status" ---system echo "sleep 4" > var/log/mysqltest.sql ---system echo "# A comment" >> var/log/mysqltest.sql ---system echo "show status;" >> var/log/mysqltest.sql +--system echo "sleep 4" > $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "# A comment" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "show status;" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql --error 1 ---exec $MYSQL_TEST < var/log/mysqltest.sql 2>&1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 + +# +# Missing delimiter until eof +# The comment will be "sucked into" the sleep command since +# delimiter is missing +--system echo "sleep 7" > $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "# Another comment" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--error 1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 # # Extra delimiter # --error 1 --exec echo "--sleep 4;" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "--disable_query_log;" | $MYSQL_TEST 2>&1 # Allow trailing # comment --sleep 1 # Wait for insert delayed to be executed. --sleep 1 # Wait for insert delayed to be executed. +# ---------------------------------------------------------------------------- +# Test error +# ---------------------------------------------------------------------------- + +# Missing argument +--error 1 +--exec echo "error;" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "--error" | $MYSQL_TEST 2>&1 + +# First char must be uppercase 'S' or 'E' or [0-9] +--error 1 +--exec echo "--error s99999" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "--error e99999" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "--error 9eeeee" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "--error 1sssss" | $MYSQL_TEST 2>&1 + +# First char 'S' but too long +--error 1 +--exec echo "--error S999999" | $MYSQL_TEST 2>&1 + +# First char 'S' but lowercase char found +--error 1 +--exec echo "--error S99a99" | $MYSQL_TEST 2>&1 + +# First char 'S' but too short +--error 1 +--exec echo "--error S9999" | $MYSQL_TEST 2>&1 + +# First char 'E' but not found in error array +--error 1 +--exec echo "--error E9999" | $MYSQL_TEST 2>&1 + +# First char [0-9] but contains chars +--error 1 +--exec echo "--error 999e9" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "--error 9b" | $MYSQL_TEST 2>&1 + +# Multiple errorcodes separated by ',' +--error 1,1,1,1 +#--error 9,ER_PARSE_ERROR +#--error ER_PARSE_ERROR +#--error 9,ER_PARSE_ERROR,9,ER_PARSE_ERROR +#--error 9, ER_PARSE_ERROR, 9, ER_PARSE_ERROR +#--error 9,S00000,9,ER_PARSE_ERROR +#--error 9,S00000,9,ER_PARSE_ERROR,ER_PARSE_ERROR,ER_PARSE_ERROR,9,10,11,12 +--error 9,S00000,9 +--error 9,S00000,9,9,10,11,12 +--error 9 ,10 +--error 9 , 10 +--error 9 , 10 +--error 9 , 10 + +# Too many errorcodes specified +--error 1 +--exec echo "--error 1,2,3,4,5,6,7,8,9,10,11" | $MYSQL_TEST 2>&1 + # ---------------------------------------------------------------------------- # Test echo command @@ -399,8 +471,8 @@ echo - MySQL: The world''s - source database; echo - MySQL: The world''s --- most popular open --- source database; +-- most popular +-- open source database; echo # MySQL: The --world''s @@ -419,7 +491,7 @@ echo ; # Illegal use of echo --error 1 ---exec echo "echo $;" | $MYSQL_TEST 2>&1 +--exec echo "echo \$;" | $MYSQL_TEST 2>&1 # ---------------------------------------------------------------------------- @@ -457,8 +529,8 @@ echo $message; let $message= -- MySQL: The -- world''s most --- popular open --- source database; +-- popular +-- open source database; echo $message; let $message= # MySQL: The @@ -495,9 +567,21 @@ echo $where2; let $where3=a long $where variable content; echo $where3; +let $where3=a long \\\$where variable content; +echo $where3; + let $novar1= $novar2; echo $novar1; +let $cat=na; +let $cat=ba$cat$cat; +echo banana = $cat; + +# ba\$cat\$cat should have been sufficient. +# ba\\\$cat\\\$cat -> ba\$cat\$cat -> ba$cat$cat -> banana +# Magnus' upcoming patch will fix the missing second interpretation. +let $cat=ba\\\$cat\\\$cat; +echo Not a banana: $cat; # Test illegal uses of let @@ -506,22 +590,19 @@ echo $novar1; --exec echo "let ;" | $MYSQL_TEST 2>&1 --error 1 ---exec echo "let $=hi;" | $MYSQL_TEST 2>&1 +--exec echo "let \$=hi;" | $MYSQL_TEST 2>&1 --error 1 ---exec echo "let hi=hi;" | $MYSQL_TEST 2>&1 +--exec echo "let \$1 hi;" | $MYSQL_TEST 2>&1 --error 1 ---exec echo "let $1 hi;" | $MYSQL_TEST 2>&1 +--exec echo "let \$m hi;" | $MYSQL_TEST 2>&1 --error 1 ---exec echo "let $m hi;" | $MYSQL_TEST 2>&1 +--exec echo "let \$hi;" | $MYSQL_TEST 2>&1 --error 1 ---exec echo "let $hi;" | $MYSQL_TEST 2>&1 - ---error 1 ---exec echo "let $ hi;" | $MYSQL_TEST 2>&1 +--exec echo "let \$ hi;" | $MYSQL_TEST 2>&1 --error 1 --exec echo "let =hi;" | $MYSQL_TEST 2>&1 @@ -529,6 +610,55 @@ echo $novar1; --error 1 --exec echo "let hi;" | $MYSQL_TEST 2>&1 +# More advanced test for bug#17280 +let $success= 1; +--echo # Execute: --echo # success: \$success +--echo # success: $success +--echo # Execute: echo # success: \$success ; +echo # success: $success ; + +--echo # The next two variants work fine and expand the content of \$success +--echo # Execute: --echo \$success +--echo $success +--echo # Execute: echo \$success ; +echo $success ; + +# ---------------------------------------------------------------------------- +# Test to assign let from query +# let $=``; +# ---------------------------------------------------------------------------- +--disable_parsing +echo var1; +let $var1= `select "hi" as "Col", 1 as "Column1", "hi there" as Col3`; +echo $var1; +echo $var1_Col; +echo $var1_Column1; +echo $var1_Col3; + +echo var2; +let $var2= `select 2 as "Column num 2"`; +echo $var2; +echo $var2_Column num 2; +echo $var2_Column; + +echo var2 again; +let $var2= `select 2 as "Column num 2"`; +echo $var2; +echo $var2_Column num 2; +echo $var2_Column_num_2; +echo $var2_Column; + +echo var3 two columns with same name; +let $var3= `select 1 as "Col", 2 as "Col", 3 as "var3"`; +echo $var3; +echo $var3_Col; +echo $var3_Col; +echo $var3_var3; + +#echo failing query in let; +#--error 1 +#--exec echo "let $var2= `failing query;`" | $MYSQL_TEST 2>&1 +--enable_parsing # ---------------------------------------------------------------------------- # Test source command # ---------------------------------------------------------------------------- @@ -538,29 +668,36 @@ echo $novar1; --error 1 --exec echo "source ;" | $MYSQL_TEST 2>&1 +# Fix win paths +--replace_result \\ / +# Source a nonexisting file --error 1 --exec echo "source non_existingFile;" | $MYSQL_TEST 2>&1 # Too many source ---exec echo "source var/tmp/recursive.sql;" > var/tmp/recursive.sql +--exec echo "source $MYSQLTEST_VARDIR/tmp/recursive.sql;" > $MYSQLTEST_VARDIR/tmp/recursive.sql +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --error 1 ---exec echo "source var/tmp/recursive.sql;" | $MYSQL_TEST 2>&1 +--exec echo "source $MYSQLTEST_VARDIR/tmp/recursive.sql;" | $MYSQL_TEST 2>&1 # Source a file with error ---exec echo "garbage ;" > var/tmp/error.sql +--exec echo "garbage ;" > $MYSQLTEST_VARDIR/tmp/error.sql +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --error 1 ---exec echo "source var/tmp/error.sql;" | $MYSQL_TEST 2>&1 +--exec echo "source $MYSQLTEST_VARDIR/tmp/error.sql;" | $MYSQL_TEST 2>&1 # Test execution of source in a while loop ---exec echo "echo here is the sourced script;" > var/tmp/sourced.sql +--write_file $MYSQLTEST_VARDIR/tmp/sourced.inc +echo here is the sourced script; +EOF --disable_query_log let $outer= 2; # Number of outer loops while ($outer) { eval SELECT '$outer = outer loop variable after while' AS ""; - --source var/tmp/sourced.sql + --source $MYSQLTEST_VARDIR/tmp/sourced.inc eval SELECT '$outer = outer loop variable before dec' AS ""; dec $outer; @@ -581,7 +718,6 @@ while ($outer) # Test execution of source in a while loop ---exec echo "--source var/tmp/sourced.sql" > var/tmp/sourced1.sql --disable_abort_on_error # Sourcing of a file within while loop, sourced file will # source other file @@ -589,11 +725,12 @@ let $num= 9; while ($num) { SELECT 'In loop' AS ""; - --source var/tmp/sourced1.sql + --source $MYSQLTEST_VARDIR/tmp/sourced.inc dec $num; } --enable_abort_on_error --enable_query_log +--remove_file $MYSQLTEST_VARDIR/tmp/sourced.inc # ---------------------------------------------------------------------------- # Test sleep command @@ -683,7 +820,7 @@ system echo "hej" > /dev/null; --exec echo "system false;" | $MYSQL_TEST 2>&1 --disable_abort_on_error -system NonExistsinfComamdn; +system NonExistsinfComamdn 2> /dev/null; --enable_abort_on_error @@ -700,6 +837,30 @@ echo test3stop --delimiter ; echo test4; + +# ---------------------------------------------------------------------------- +# Test if +# ---------------------------------------------------------------------------- + +let $counter=10; +if ($counter) +{ + echo Counter is greater than 0, (counter=10); +} +if (!$counter) +{ + echo Counter is not 0, (counter=10); +} +let $counter=0; +if ($counter) +{ + echo Counter is greater than 0, (counter=0); +} +if (!$counter) +{ + echo Counter is not 0, (counter=0); +} + # ---------------------------------------------------------------------------- # Test while, { and } # ---------------------------------------------------------------------------- @@ -713,11 +874,158 @@ while ($i) # One liner #let $i=1;while ($i){echo $i;dec $i;} - +let $i=0; +while (!$i) +{ + echo Testing while with not; + inc $i; +} # Exceed max nesting level +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest_while.inc +let $1 = 10; +while ($1) +{ +while ($1) +{ +while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + echo $1; + dec $1; +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +EOF +# Fix win path +--replace_result \\ / $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --error 1 ---exec echo "source include/mysqltest_while.inc;" | $MYSQL_TEST 2>&1 +--exec echo "source $MYSQLTEST_VARDIR/tmp/mysqltest_while.inc;" | $MYSQL_TEST 2>&1 +--remove_file $MYSQLTEST_VARDIR/tmp/mysqltest_while.inc --error 1 --exec echo "while \$i;" | $MYSQL_TEST 2>&1 --error 1 @@ -731,20 +1039,20 @@ while ($i) --error 1 --exec echo "{;" | $MYSQL_TEST 2>&1 ---system echo "while (0)" > var/log/mysqltest.sql ---system echo "echo hej;" >> var/log/mysqltest.sql +--system echo "while (0)" > $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "echo hej;" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql --error 1 ---exec $MYSQL_TEST < var/log/mysqltest.sql 2>&1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 ---system echo "while (0)" > var/log/mysqltest.sql ---system echo "{echo hej;" >> var/log/mysqltest.sql +--system echo "while (0)" > $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "{echo hej;" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql --error 1 ---exec $MYSQL_TEST < var/log/mysqltest.sql 2>&1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 ---system echo "while (0){" > var/log/mysqltest.sql ---system echo "echo hej;" >> var/log/mysqltest.sql +--system echo "while (0){" > $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "echo hej;" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql --error 1 ---exec $MYSQL_TEST < var/log/mysqltest.sql 2>&1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 # ---------------------------------------------------------------------------- # Test error messages returned from comments starting with a command @@ -772,7 +1080,7 @@ select "a" as col1, "c" as col2; --exec echo "replace_result a;" | $MYSQL_TEST 2>&1 --error 1 --exec echo "replace_result a ;" | $MYSQL_TEST 2>&1 ---exec echo "replace_result a b;" | $MYSQL_TEST 2>&1 +--exec echo "replace_result a b; echo OK;" | $MYSQL_TEST 2>&1 --error 1 --exec echo "--replace_result a b c" | $MYSQL_TEST 2>&1 --error 1 @@ -809,6 +1117,71 @@ select "a" as col1, "c" as col2; --error 1 --exec echo "save_master_pos; sync_with_master a;" | $MYSQL_TEST 2>&1 +# ---------------------------------------------------------------------------- +# Test connect +# ---------------------------------------------------------------------------- + +--error 1 +--exec echo "connect;" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "connect ();" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "connect (con2);" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "connect (con2,);" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "connect (con2,localhost,root,,illegal_db);" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "connect (con1,localhost,root,,,illegal_port,);" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "connect (con1,localhost,root,,,,,SMTP POP);" | $MYSQL_TEST 2>&1 + +# Repeat connect/disconnect +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql +let $i=100; +while ($i) +{ + connect (test_con1,localhost,root,,); + disconnect test_con1; + dec $i; +} +EOF +--exec echo "source $MYSQLTEST_VARDIR/tmp/mysqltest.sql; echo OK;" | $MYSQL_TEST 2>&1 + +# Repeat connect/disconnect, exceed max number of connections +--system echo "let \$i=200;" > $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "while (\$i)" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "{" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo " connect (test_con1,localhost,root,,); " >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo " disconnect test_con1; " >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo " dec \$i; " >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "}" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--error 1 +--exec echo "source $MYSQLTEST_VARDIR/tmp/mysqltest.sql;" | $MYSQL_TEST 2>&1 + +# Select disconnected connection +--system echo "connect (test_con1,localhost,root,,);" > $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "disconnect test_con1; " >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "connection test_con1;" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--error 1 +--exec echo "source $MYSQLTEST_VARDIR/tmp/mysqltest.sql;" | $MYSQL_TEST 2>&1 + +# Connection name already used +--system echo "connect (test_con1,localhost,root,,);" > $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "connect (test_con1,localhost,root,,);" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--error 1 +--exec echo "source $MYSQLTEST_VARDIR/tmp/mysqltest.sql;" | $MYSQL_TEST 2>&1 + +# connect when "disable_abort_on_error" caused "connection not found" +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--disable_abort_on_error +connect (con1,localhost,root,,); +connection default; +connection con1; +--enable_abort_on_error # ---------------------------------------------------------------------------- # Test mysqltest arguments @@ -818,6 +1191,8 @@ select "a" as col1, "c" as col2; --exec $MYSQL_TEST < $MYSQL_TEST_DIR/include/mysqltest-x.inc --exec $MYSQL_TEST -x $MYSQL_TEST_DIR/include/mysqltest-x.inc --exec $MYSQL_TEST --test_file=$MYSQL_TEST_DIR/include/mysqltest-x.inc +# Fix Win paths +--replace_result \\ / --error 1 --exec $MYSQL_TEST -x non_existing_file.inc 2>&1 @@ -841,3 +1216,275 @@ while ($num) SELECT 1 as a; + +# +# Bug #10251: Identifiers containing quotes not handled correctly +# +select 1 as `a'b`, 2 as `a"b`; + +# Test escaping of quotes +select 'aaa\\','aa''a',"aa""a"; + +# +# Check of include/show_msg.inc and include/show_msg80.inc +# + +# The message contains in most cases a string with the default character set +let $message= Here comes a message; +--source include/show_msg.inc + +# The message could also contain a string with character set utf8 +let $message= `SELECT USER()`; +--source include/show_msg.inc + +# The message contains more then 80 characters on multiple lines +# and is kept between double quotes. +let $message= +"Here comes a very very long message that + - is longer then 80 characters and + - consists of several lines"; +--source include/show_msg80.inc + +# The message contains more then 80 characters on multiple lines +# and uses the auxiliary character "." at the beginning of the message lines. +let $message= . Here comes a very very long message that + . - is longer then 80 characters and + . - consists of several lines; +--source include/show_msg80.inc + +# +# Test --enable_parsing / disable_parsing +# +--disable_query_log +--disable_parsing +# The following will not enable query logging +--enable_query_log +select "this will not be executed"; +--enable_parsing +select "this will be executed"; +--enable_query_log + +# +# Test zero length result file. Should not pass +# +--exec touch $MYSQLTEST_VARDIR/tmp/zero_length_file.result +--exec echo "echo ok;" > $MYSQLTEST_VARDIR/tmp/query.sql +--error 1 +--exec $MYSQL_TEST -x $MYSQLTEST_VARDIR/tmp/query.sql -R $MYSQLTEST_VARDIR/tmp/zero_length_file.result 2>&1 +# +# Test that a test file that does not generate any output fails. +# +--exec echo "let \$i= 1;" > $MYSQLTEST_VARDIR/tmp/query.sql +--error 1 +--exec $MYSQL_TEST -x $MYSQLTEST_VARDIR/tmp/query.sql 2>&1 + +# +# Test that mysqltest fails when there are no queries executed +# but a result file exists +# NOTE! This will never happen as long as it's not allowed to have +# 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 +#--exec $MYSQL_TEST -x $MYSQLTEST_VARDIR/tmp/query.sql -R $MYSQLTEST_VARDIR/tmp/result_file.result 2>&1 + +# +# Bug #11731 mysqltest in multi-statement queries ignores errors in +# non-1st queries +# + +echo Failing multi statement query; +# PS does not support multi statement +--exec echo "--disable_ps_protocol" > $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "delimiter ||||;" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "create table t1 (a int primary key);" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "insert into t1 values (1);" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "select 'select-me';" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "insertz 'error query'||||" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "delimiter ;||||" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql + +--error 1 +--exec $MYSQL_TEST -x $MYSQLTEST_VARDIR/tmp/bug11731.sql 2>&1 +drop table t1; + +--error 1 +--exec $MYSQL_TEST --record -x $MYSQLTEST_VARDIR/tmp/bug11731.sql -R $MYSQLTEST_VARDIR/tmp/bug11731.out 2>&1 +# The .out file should be non existent +--exec test ! -s $MYSQLTEST_VARDIR/tmp/bug11731.out +drop table t1; + + +echo Multi statement using expected error; +# PS does not support multi statement +--exec echo "--disable_ps_protocol" > $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "delimiter ||||;" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "--error 1064" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "create table t1 (a int primary key);" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "insert into t1 values (1);" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "select 'select-me';" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "insertz "error query"||||" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "delimiter ;||||" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql + +# These two should work since the error is expected +--exec $MYSQL_TEST -x $MYSQLTEST_VARDIR/tmp/bug11731.sql 2>&1 +drop table t1; + +--exec $MYSQL_TEST --record -x $MYSQLTEST_VARDIR/tmp/bug11731.sql -R $MYSQLTEST_VARDIR/tmp/bug11731.out 2>&1 +# The .out file should exist +--exec test -s $MYSQLTEST_VARDIR/tmp/bug11731.out +drop table t1; + +# +# Bug#19890 mysqltest: "query" command is broken +# + +# It should be possible to use the command "query" to force mysqltest to +# send the command to the server although it's a builtin mysqltest command. +--error 1064 +query sleep; + +--error 1064 +--query sleep + +# Just an empty query command +--error 1065 +query ; + +# test for replace_regex +--replace_regex /at/b/ +select "at" as col1, "c" as col2; + +--replace_regex /at/b/i +select "at" as col1, "AT" as col2, "c" as col3; + +--replace_regex /a/b/ /ct/d/ +select "a" as col1, "ct" as col2; + +--replace_regex /(strawberry)/raspberry and \1/ /blueberry/blackberry/ /potato/tomato/; +select "strawberry","blueberry","potato"; + +--error 1 +--exec echo "--replace_regex a" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "--replace_regex a;" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "replace_regex a;" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "replace_regex a ;" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "replace_regex a b; echo OK;" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "--replace_regex /a b c" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "replace_regex /a /b c ;" | $MYSQL_TEST 2>&1 + +# REQUIREMENT +# replace_regex should replace substitutions from left to right in output + +create table t1 (a int, b int); +insert into t1 values (1,3); +insert into t1 values (2,4); +--replace_regex /A/C/ /B/D/i /3/2/ /2/1/ +select * from t1; +drop table t1; + +# ---------------------------------------------------------------------------- +# test for remove_file +# ---------------------------------------------------------------------------- + +--error 1 +--exec echo "remove_file ;" | $MYSQL_TEST 2>&1 + +--error 1 +remove_file non_existing_file; + +# ---------------------------------------------------------------------------- +# test for write_file +# ---------------------------------------------------------------------------- +--error 1 +--exec echo "write_file ;" | $MYSQL_TEST 2>&1 + +--error 1 +--exec echo "write_file filename ;" | $MYSQL_TEST 2>&1 + +--error 1 +--exec echo "write_file filename \";" | $MYSQL_TEST 2>&1 + +write_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +Content for test_file1 +EOF +file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +remove_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; + +write_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp END_DELIMITER; +Content for test_file1 contains EOF +END_DELIMITER +file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +remove_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; + +# ---------------------------------------------------------------------------- +# test for file_exist +# ---------------------------------------------------------------------------- +--error 1 +--exec echo "file_exists ;" | $MYSQL_TEST 2>&1 + +--error 0,1 +remove_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +--error 1 +file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +write_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +Content for test_file1 +EOF +file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +remove_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +--error 1 +file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp; + + +# ---------------------------------------------------------------------------- +# test for copy_file +# ---------------------------------------------------------------------------- +--write_file $MYSQLTEST_VARDIR/tmp/file1.tmp +file1 +EOF + +copy_file $MYSQLTEST_VARDIR/tmp/file1.tmp $MYSQLTEST_VARDIR/tmp/file2.tmp; +file_exists $MYSQLTEST_VARDIR/tmp/file2.tmp; +remove_file $MYSQLTEST_VARDIR/tmp/file1.tmp; +remove_file $MYSQLTEST_VARDIR/tmp/file2.tmp; + +--error 1 +--exec echo "copy_file ;" | $MYSQL_TEST 2>&1 + +--error 1 +--exec echo "copy_file from_file;" | $MYSQL_TEST 2>&1 + +# ---------------------------------------------------------------------------- +# test for perl +# ---------------------------------------------------------------------------- +--perl +print "hello\n"; +EOF + +--perl EOF +print "hello\n"; +EOF + +--perl DELIMITER +print "hello\n"; +DELIMITER + +--error 1 +--exec echo "perl TOO_LONG_DELIMITER ;" | $MYSQL_TEST 2>&1 + +perl; +print "hello\n"; +EOF + +perl; + # Print "hello" + print "hello\n"; +EOF + + +--echo End of tests diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 0ca293eb1ba..5be56c59049 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -351,14 +351,14 @@ create table t1 (a int, b int); insert into t1 (a, b) values (1,1), (1,2), (2,1), (2,2); prepare stmt from "explain select * from t1 where t1.a=2 and t1.a=t1.b and t1.b > 1 + ?"; ---replace_column 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - set @v=5; -execute stmt using @v; --replace_column 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - +execute stmt using @v; set @v=0; -execute stmt using @v; --replace_column 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - +execute stmt using @v; set @v=5; +--replace_column 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - execute stmt using @v; drop table t1; deallocate prepare stmt; diff --git a/mysql-test/t/ps_1general.test b/mysql-test/t/ps_1general.test index 6f15ba8f673..ec4964d116a 100644 --- a/mysql-test/t/ps_1general.test +++ b/mysql-test/t/ps_1general.test @@ -305,8 +305,8 @@ prepare stmt4 from ' show table status from test like ''t9%'' '; --replace_result 2147483647 4294967295 # Bug#4288 execute stmt4; ---replace_column 2 # prepare stmt4 from ' show status like ''Threads_running'' '; +--replace_column 2 # execute stmt4; prepare stmt4 from ' show variables like ''sql_mode'' '; execute stmt4; diff --git a/mysql-test/t/ps_grant.test b/mysql-test/t/ps_grant.test index 4cb056db358..afd543caacc 100644 --- a/mysql-test/t/ps_grant.test +++ b/mysql-test/t/ps_grant.test @@ -35,7 +35,7 @@ use mysqltest; --source include/ps_create.inc --source include/ps_renew.inc --enable_query_log -eval use $DB; +use test; grant usage on mysqltest.* to second_user@localhost identified by 'looser' ; grant select on mysqltest.t9 to second_user@localhost diff --git a/mysql-test/t/rpl000018.test b/mysql-test/t/rpl000018.test deleted file mode 100644 index aee052ffd28..00000000000 --- a/mysql-test/t/rpl000018.test +++ /dev/null @@ -1,29 +0,0 @@ -# -# Running test with abort-slave-event-count=1 -# This will force slave to reconnect after every event -# - -require_manager; -connect (master,localhost,root,,test,0,master.sock); -connect (slave,localhost,root,,test,0,slave.sock); -connection master; -reset master; -server_stop master; -server_start master; -connection slave; -reset slave; -start slave; -connection master; -show binary logs; ---disable_warnings -drop table if exists t1; ---enable_warnings -create table t1(n int); -insert into t1 values (3351); -sync_slave_with_master; -select * from t1; -connection master; -drop table t1; -sync_slave_with_master; - -# End of 4.1 tests diff --git a/mysql-test/t/rpl_chain_temp_table.test b/mysql-test/t/rpl_chain_temp_table.test deleted file mode 100644 index 96e228a17a1..00000000000 --- a/mysql-test/t/rpl_chain_temp_table.test +++ /dev/null @@ -1,101 +0,0 @@ - # This test makes some assumptions about values of thread ids, which should be -# true if the servers have been restarted for this test. So we want to -# stop/restart servers. Note that if assumptions are wrong, the test will not -# fail; it will just fail to test the error-prone scenario. -# Using the manager is the only way to have more than one slave server. -# So you must run this test with --manager. - -require_manager; -server_stop master; -server_start master; -server_stop slave; -server_start slave; -# no need for slave_sec (no assumptions on thread ids for this server). - -source include/master-slave.inc; -connect (slave_sec,localhost,root,,test,0,slave.sock-1); -connection master; -save_master_pos; -connection slave; -sync_with_master; -reset master; -save_master_pos; -connection slave_sec; -eval change master to master_host='127.0.0.1',master_port=$SLAVE_MYPORT, master_user='root'; -start slave; -sync_with_master; - -# :P now we have a chain ready-to-test. - -connection master; -create temporary table t1 (a int); -save_master_pos; -connection slave; -sync_with_master; -connection master1; -create temporary table t1 (a int); -save_master_pos; -connection slave; -sync_with_master; -save_master_pos; - -# First test: - -connection slave_sec; -# Before BUG#1686 ("If 2 master threads with same-name temp table, slave makes -# bad binlog") was fixed, sync_with_master failed -sync_with_master; -show status like 'slave_open_temp_tables'; - -# 'master' and 'master1' usually have thread id 2-3 or 3-4. -# 'slave' and 'slave1' usually have thread id 2-3. -connection slave; -create temporary table t1 (a int); -connection slave1; -create temporary table t1 (a int); -# So it's likely that in the binlog of slave we get -# server_id=of_master thread_id=3 create temp... -# server_id=of_slave thread_id=3 create temp... -# which would confuse slave-sec unless slave-sec uses server id to distinguish -# between temp tables (here thread id is obviously not enough to distinguish). - -save_master_pos; - -# Second test: - -connection slave_sec; -# If we did not use the server id to distinguish between temp tables, -# sync_with_master would fail -sync_with_master; -show status like 'slave_open_temp_tables'; - -# Third test (BUG#1240 "slave of slave breaks when STOP SLAVE was issud on -# parent slave and temp tables"). -stop slave; -connection slave; -insert into t1 values(1); -create table t2 as select * from t1; -save_master_pos; -connection slave_sec; -start slave; -sync_with_master; -show status like 'slave_open_temp_tables'; -select * from t2; - -# clean up -connection slave; -drop table t2; -save_master_pos; -connection slave_sec; -sync_with_master; - -# On purpose, we don't delete the temporary tables explicitely. -# So temp tables remain on slave (remember they are not deleted when the slave -# SQL thread terminates). If you run this test with -# --valgrind --valgrind-options=--show-reachable=yes -# you will see if they get cleaned up at slave's shutdown (that is, if the -# memory they use is freed (it should) by mysqld before it terminates). -# If they wouldn't be cleaned up, you would see some "still reachable" blocks in -# Valgrind. - -# End of 4.1 tests diff --git a/mysql-test/t/rpl_failsafe.test b/mysql-test/t/rpl_failsafe.test deleted file mode 100644 index 4336d897fc0..00000000000 --- a/mysql-test/t/rpl_failsafe.test +++ /dev/null @@ -1,24 +0,0 @@ -require_manager; -source include/master-slave.inc; -connect (slave_sec,localhost,root,,test,0,slave.sock-1); -connect (slave_ter,localhost,root,,test,0,slave.sock-2); -connection master; -show variables like 'rpl_recovery_rank'; -show status like 'Rpl_status'; -create table t1(n int); -drop table t1; -sync_slave_with_master; -show variables like 'rpl_recovery_rank'; -show status like 'Rpl_status'; -connection slave_sec; -start slave; -sync_with_master; -show variables like 'rpl_recovery_rank'; -show status like 'Rpl_status'; -connection slave_ter; -start slave; -sync_with_master; -show variables like 'rpl_recovery_rank'; -show status like 'Rpl_status'; - -# End of 4.1 tests diff --git a/mysql-test/t/rpl_flush_tables.test b/mysql-test/t/rpl_flush_tables.test index 378fa479f09..aea8d7a5353 100644 --- a/mysql-test/t/rpl_flush_tables.test +++ b/mysql-test/t/rpl_flush_tables.test @@ -8,6 +8,8 @@ # merge table getting renamed. --source include/not_windows.inc +let $SERVER_VERSION=`select version()`; + create table t1 (a int); insert into t1 values (10); create table t2 (a int); diff --git a/mysql-test/t/rpl_heap.test b/mysql-test/t/rpl_heap.test deleted file mode 100644 index 66dac1d7926..00000000000 --- a/mysql-test/t/rpl_heap.test +++ /dev/null @@ -1,51 +0,0 @@ -# You must run this test with --manager. - -require_manager; - -# Don't know why, but using TCP/IP connections makes this test fail -# with "Lost connection to MySQL server during query" when we -# issue a query after the server restart. -# Maybe this is something awkward in mysqltest or in the manager? -# So we use sockets. -connect (master,localhost,root,,test,0,master.sock); -connect (slave,localhost,root,,test,0,slave.sock); - -connection master; -reset master; -drop table if exists t1; -# we use CREATE SELECT to verify that DELETE does not get into binlog -# before CREATE SELECT -create table t1 type=HEAP select 10 as a; -insert into t1 values(11); -save_master_pos; -show binlog events from 79; -connection slave; -reset slave; -start slave; -sync_with_master; -show create table t1; -select * from t1; # should be one row - -server_stop master; -server_start master; - -connection master; -select * from t1; -# to check that DELETE is not written twice -# (the LIMIT is to not use the query cache) -select * from t1 limit 10; -save_master_pos; -show binlog events in 'master-bin.002' from 79; - -connection slave; -sync_with_master; -select * from t1; # should be empty - -# clean up -connection master; -drop table t1; -save_master_pos; -connection slave; -sync_with_master; - -# End of 4.1 tests diff --git a/mysql-test/t/rpl_trunc_temp.test b/mysql-test/t/rpl_trunc_temp.test index be570a6f80c..b4ea3c318da 100644 --- a/mysql-test/t/rpl_trunc_temp.test +++ b/mysql-test/t/rpl_trunc_temp.test @@ -29,7 +29,4 @@ connection slave; show status like 'Slave_open_temp_tables'; -connection master; - - # End of 4.1 tests diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 10dfb788c10..d34da8eabe2 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -1721,7 +1721,7 @@ select (select a from t1) = (1,2); select (1,2,3) = (select * from t1); -- error 1241 select (select * from t1) = (1,2,3); -drop table t1 +drop table t1; # # Item_int_with_ref check (BUG#10020) diff --git a/mysql-test/t/type_blob.test b/mysql-test/t/type_blob.test index 2ac55da7442..2c3f4e5efce 100644 --- a/mysql-test/t/type_blob.test +++ b/mysql-test/t/type_blob.test @@ -23,7 +23,7 @@ CREATE TABLE t2 (a char(257), b varbinary(70000), c varchar(70000000)); show columns from t2; create table t3 (a long, b long byte); show create TABLE t3; -drop table t1,t2,t3 +drop table t1,t2,t3; # # Check errors with blob From 503c0e1c081ed743d4e19e2311a72781191f554d Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 3 Oct 2006 15:48:41 +0200 Subject: [PATCH 20/68] Add two new result files mysql-test/r/init_file.result: Add new file init_file.result mysql-test/r/mysql_client_test.result: Add new file mysql_client_test.result --- mysql-test/r/init_file.result | 2 ++ mysql-test/r/mysql_client_test.result | 1 + 2 files changed, 3 insertions(+) create mode 100644 mysql-test/r/init_file.result create mode 100644 mysql-test/r/mysql_client_test.result diff --git a/mysql-test/r/init_file.result b/mysql-test/r/init_file.result new file mode 100644 index 00000000000..5404a7b2064 --- /dev/null +++ b/mysql-test/r/init_file.result @@ -0,0 +1,2 @@ +ok +End of 4.1 tests diff --git a/mysql-test/r/mysql_client_test.result b/mysql-test/r/mysql_client_test.result new file mode 100644 index 00000000000..9766475a418 --- /dev/null +++ b/mysql-test/r/mysql_client_test.result @@ -0,0 +1 @@ +ok From d1e63094713f4b21a2c6885b70bae637bd4cf90a Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 3 Oct 2006 15:59:56 +0200 Subject: [PATCH 21/68] Delete files now moved into mysqltest.test BitKeeper/deleted/.del-sourced.inc: Delete: mysql-test/include/sourced.inc BitKeeper/deleted/.del-mysqltest_while.inc: Delete: mysql-test/include/mysqltest_while.inc BitKeeper/deleted/.del-sourced1.inc: Delete: mysql-test/include/sourced1.inc --- mysql-test/include/mysqltest_while.inc | 137 ------------------------- mysql-test/include/sourced.inc | 1 - mysql-test/include/sourced1.inc | 1 - 3 files changed, 139 deletions(-) delete mode 100644 mysql-test/include/mysqltest_while.inc delete mode 100644 mysql-test/include/sourced.inc delete mode 100644 mysql-test/include/sourced1.inc diff --git a/mysql-test/include/mysqltest_while.inc b/mysql-test/include/mysqltest_while.inc deleted file mode 100644 index 90b05ee2695..00000000000 --- a/mysql-test/include/mysqltest_while.inc +++ /dev/null @@ -1,137 +0,0 @@ -let $1 = 10; -while ($1) -{ -while ($1) -{ -while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - while ($1) -{ - echo $1; - dec $1; -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} -} diff --git a/mysql-test/include/sourced.inc b/mysql-test/include/sourced.inc deleted file mode 100644 index be1a270641a..00000000000 --- a/mysql-test/include/sourced.inc +++ /dev/null @@ -1 +0,0 @@ -echo here is the sourced script; diff --git a/mysql-test/include/sourced1.inc b/mysql-test/include/sourced1.inc deleted file mode 100644 index 920561e5de2..00000000000 --- a/mysql-test/include/sourced1.inc +++ /dev/null @@ -1 +0,0 @@ ---source include/sourced.inc From 807b9f94372208931802a5b836fa75aac5b82286 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 3 Oct 2006 16:34:44 +0200 Subject: [PATCH 22/68] Fix typo - Change con_host to ds_connection_name.str client/mysqltest.c: Change con_host to ds_connection_name.str --- client/mysqltest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 623c33d0f4f..d057e7ea4f0 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -3025,7 +3025,7 @@ void do_connect(struct st_command *command) opt_ssl_capath, opt_ssl_cipher); #if MYSQL_VERSION_ID >= 50000 /* Turn on ssl_verify_server_cert only if host is "localhost" */ - opt_ssl_verify_server_cert= !strcmp(con_host, "localhost"); + opt_ssl_verify_server_cert= !strcmp(ds_connection_name.str, "localhost"); mysql_options(&next_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &opt_ssl_verify_server_cert); #endif From 52da19517ca91e219641684626ba58b227818db4 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 3 Oct 2006 16:35:46 +0200 Subject: [PATCH 23/68] Add mysys/my_copy.c as sources for mysqltest --- client/Makefile.am | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/Makefile.am b/client/Makefile.am index d3c320db56c..29624f2017f 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -38,7 +38,9 @@ noinst_HEADERS = sql_string.h completion_hash.h my_readline.h \ mysql_SOURCES = mysql.cc readline.cc sql_string.cc completion_hash.cc mysqladmin_SOURCES = mysqladmin.cc mysql_LDADD = @readline_link@ @TERMCAP_LIB@ $(LDADD) $(CXXLDFLAGS) -mysqltest_SOURCES= mysqltest.c $(top_srcdir)/mysys/my_getsystime.c \ +mysqltest_SOURCES= mysqltest.c \ + $(top_srcdir)/mysys/my_getsystime.c \ + $(top_srcdir)/mysys/my_copy.c \ $(yassl_dummy_link_fix) mysqltest_LDADD = $(top_builddir)/regex/libregex.a $(LDADD) mysqlbinlog_SOURCES = mysqlbinlog.cc $(top_srcdir)/mysys/mf_tempdir.c $(top_srcdir)/mysys/my_new.cc From 14c2d195583136182ecde3b40c6b2f456c3b7b5a Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 3 Oct 2006 23:11:47 +0200 Subject: [PATCH 24/68] Fix problem when both option SSL and COMPRESS specified --- client/mysqltest.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index d057e7ea4f0..f52b4eafe15 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -2991,7 +2991,12 @@ void do_connect(struct st_command *command) con_options= ds_options.str; while (*con_options) { - char* end= con_options; + char* end; + /* Step past any spaces in beginning of option*/ + while (*con_options && my_isspace(charset_info, *con_options)) + con_options++; + /* Find end of this option */ + end= con_options; while (*end && !my_isspace(charset_info, *end)) end++; if (!strncmp(con_options, "SSL", 3)) From 528b7ef9a6e95fb2660edab671ffadc2a7ebaee2 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Oct 2006 01:20:08 +0200 Subject: [PATCH 25/68] Add possibility to send warnings about suspicious commands in .test file to .warnings file Move detection of "parsing enabled" to after command has been read. Cleanup parsing enabled/disabled --- client/mysqltest.c | 225 +++++++++++++++++++++++++++++++++------------ 1 file changed, 167 insertions(+), 58 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index f52b4eafe15..a81854733de 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -391,7 +391,7 @@ struct st_command TYPELIB command_typelib= {array_elements(command_names),"", command_names, 0}; -DYNAMIC_STRING ds_res, ds_progress; +DYNAMIC_STRING ds_res, ds_progress, ds_warning_messages; void die(const char *fmt, ...) /* ATTRIBUTE_FORMAT(printf, 1, 2) */; @@ -399,6 +399,8 @@ void abort_not_supported_test(const char *fmt, ...) /* ATTRIBUTE_FORMAT(printf, 1, 2) */; void verbose_msg(const char *fmt, ...) /* ATTRIBUTE_FORMAT(printf, 1, 2) */; +void warning_msg(const char *fmt, ...) + /* ATTRIBUTE_FORMAT(printf, 1, 2) */; VAR* var_from_env(const char *, const char *); VAR* var_init(VAR* v, const char *name, int name_len, const char *val, @@ -410,6 +412,7 @@ void eval_expr(VAR* v, const char *p, const char** p_end); my_bool match_delimiter(int c, const char *delim, uint length); void dump_result_to_reject_file(const char *record_file, char *buf, int size); void dump_result_to_log_file(const char *record_file, char *buf, int size); +void dump_warning_messages(const char *record_file); void do_eval(DYNAMIC_STRING *query_eval, const char *query, const char *query_end, my_bool pass_through_escape_chars); @@ -697,6 +700,7 @@ void free_used_memory() delete_dynamic(&q_lines); dynstr_free(&ds_res); dynstr_free(&ds_progress); + dynstr_free(&ds_warning_messages); free_all_replace(); my_free(pass,MYF(MY_ALLOW_ZERO_PTR)); free_defaults(default_argv); @@ -741,6 +745,10 @@ void die(const char *fmt, ...) if (result_file && ds_res.length) dump_result_to_log_file(result_file, ds_res.str, ds_res.length); + /* Dump warning messages */ + if (result_file && ds_warning_messages.length) + dump_warning_messages(result_file); + /* Clean up and exit */ free_used_memory(); my_end(MY_CHECK_ERROR); @@ -817,6 +825,31 @@ void verbose_msg(const char *fmt, ...) } +void warning_msg(const char *fmt, ...) +{ + va_list args; + char buff[512]; + size_t len; + DBUG_ENTER("warning_msg"); + + va_start(args, fmt); + dynstr_append(&ds_warning_messages, "mysqltest: "); + if (start_lineno != 0) + { + len= my_snprintf(buff, sizeof(buff), "Warning detected at line %d: ", + start_lineno); + dynstr_append_mem(&ds_warning_messages, + buff, len); + } + len= vsnprintf(buff, sizeof(buff), fmt, args); + dynstr_append_mem(&ds_warning_messages, buff, len); + dynstr_append(&ds_warning_messages, "\n"); + va_end(args); + + DBUG_VOID_RETURN; +} + + /* Compare content of the string ds to content of file fname */ @@ -3495,6 +3528,55 @@ void convert_to_format_v1(char* query) } +/* + Check a command that is about to be sent (or should have been + sent if parsing was enabled) to mysql server for + suspicious things and generate warnings. +*/ + +void scan_command_for_warnings(struct st_command *command) +{ + const char *ptr= command->query; + DBUG_ENTER("scan_command_for_warnings"); + DBUG_PRINT("enter", ("query: %s", command->query)); + + while(*ptr) + { + /* + Look for query's that lines that start with a -- comment + and has a mysqltest command + */ + if (ptr[0] == '\n' && + ptr[1] && ptr[1] == '-' && + ptr[2] && ptr[2] == '-' && + ptr[3]) + { + uint type; + char save; + char *end, *start= (char*)ptr+3; + /* Skip leading spaces */ + while (*start && my_isspace(charset_info, *start)) + start++; + end= start; + /* Find end of command(next space) */ + while (*end && !my_isspace(charset_info, *end)) + end++; + save= *end; + *end= 0; + DBUG_PRINT("info", ("Checking '%s'", start)); + type= find_type(start, &command_typelib, 1+2); + if (type) + warning_msg("Embedded mysqltest command '--%s' detected in " + "query '%s' was this intentional? ", + start, command->query); + *end= save; + } + + *ptr++; + } + DBUG_VOID_RETURN; +} + /* Check for unexpected "junk" after the end of query This is normally caused by missing delimiters @@ -3583,43 +3665,29 @@ int read_command(struct st_command** command_ptr) if (*p == '#') { command->type= Q_COMMENT; - /* This goto is to avoid losing the "expected error" info. */ - goto end; } - if (!parsing_disabled) - { - memcpy(&command->expected_errors, &saved_expected_errors, - sizeof(saved_expected_errors)); - DBUG_PRINT("info", ("There are %d expected errors", - command->expected_errors.count)); - command->abort_on_error= (command->expected_errors.count == 0 && - abort_on_error); - } - - if (p[0] == '-' && p[1] == '-') + else if (p[0] == '-' && p[1] == '-') { command->type= Q_COMMENT_WITH_COMMAND; - p+= 2; /* To calculate first word */ - } - else if (!parsing_disabled) - { - while (*p && my_isspace(charset_info, *p)) - p++ ; + p+= 2; /* Skip past -- */ } -end: + /* Skip leading spaces */ while (*p && my_isspace(charset_info, *p)) p++; if (!(command->query_buf= command->query= my_strdup(p, MYF(MY_WME)))) - die(NullS); + die("Out of memory"); /* Calculate first word and first argument */ for (p= command->query; *p && !my_isspace(charset_info, *p) ; p++) ; command->first_word_len= (uint) (p - command->query); + + /* Skip spaces between command and first argument */ while (*p && my_isspace(charset_info, *p)) p++; command->first_argument= p; + command->end= strend(command->query); command->query_len= (command->end - command->query); parser.read_lines++; @@ -3947,6 +4015,15 @@ void dump_progress(const char *record_file) ds_progress.str, ds_progress.length); } +void dump_warning_messages(const char *record_file) +{ + char warn_file[FN_REFLEN]; + + str_to_file(fn_format(warn_file, record_file, "", ".warnings", + MY_REPLACE_EXT), + ds_warning_messages.str, ds_warning_messages.length); +} + void check_regerr(my_regex_t* r, int err) { char err_buf[1024]; @@ -4880,6 +4957,9 @@ void run_query(MYSQL *mysql, struct st_command *command, int flags) init_dynamic_string(&ds_warnings, NULL, 0, 256); + /* Scan for warning before sendign to server */ + scan_command_for_warnings(command); + /* Evaluate query if this is an eval command */ @@ -5166,28 +5246,19 @@ void get_command_type(struct st_command* command) uint type; DBUG_ENTER("get_command_type"); - if (!parsing_disabled && *command->query == '}') + if (*command->query == '}') { command->type = Q_END_BLOCK; DBUG_VOID_RETURN; } - if (command->type != Q_COMMENT_WITH_COMMAND) - command->type= parsing_disabled ? Q_COMMENT : Q_QUERY; - save=command->query[command->first_word_len]; - command->query[command->first_word_len]=0; - type=find_type(command->query, &command_typelib, 1+2); - command->query[command->first_word_len]=save; + save= command->query[command->first_word_len]; + command->query[command->first_word_len]= 0; + type= find_type(command->query, &command_typelib, 1+2); + command->query[command->first_word_len]= save; if (type > 0) { command->type=(enum enum_commands) type; /* Found command */ - /* - If queries are disabled, only recognize - --enable_parsing and --disable_parsing - */ - if (parsing_disabled && command->type != Q_ENABLE_PARSING && - command->type != Q_DISABLE_PARSING) - command->type= Q_COMMENT; /* Look for case where "query" was explicitly specified to @@ -5199,23 +5270,50 @@ void get_command_type(struct st_command* command) command->query= command->first_argument; } } - else if (command->type == Q_COMMENT_WITH_COMMAND && - command->first_word_len && - strcmp(command->query + command->first_word_len - 1, delimiter) == 0) + else { - /* - Detect comment with command using extra delimiter - Ex --disable_query_log; - ^ Extra delimiter causing the command - to be skipped - */ - save= command->query[command->first_word_len-1]; - command->query[command->first_word_len-1]= 0; - type= find_type(command->query, &command_typelib, 1+2); - command->query[command->first_word_len-1]= save; - if (type > 0) - die("Extra delimiter \";\" found"); + /* No mysqltest command matched */ + + if (command->type != Q_COMMENT_WITH_COMMAND) + { + /* A query that will sent to mysqld */ + command->type= Q_QUERY; + } + else + { + /* -- comment that didn't contain a mysqltest command */ + command->type= Q_COMMENT; + warning_msg("Suspicious command '--%s' detected, was this intentional? "\ + "Use # instead of -- to avoid this warning", + command->query); + + if (command->first_word_len && + strcmp(command->query + command->first_word_len - 1, delimiter) == 0) + { + /* + Detect comment with command using extra delimiter + Ex --disable_query_log; + ^ Extra delimiter causing the command + to be skipped + */ + save= command->query[command->first_word_len-1]; + command->query[command->first_word_len-1]= 0; + if (find_type(command->query, &command_typelib, 1+2) > 0) + die("Extra delimiter \";\" found"); + command->query[command->first_word_len-1]= save; + + } + } } + + /* Set expected error on command */ + memcpy(&command->expected_errors, &saved_expected_errors, + sizeof(saved_expected_errors)); + DBUG_PRINT("info", ("There are %d expected errors", + command->expected_errors.count)); + command->abort_on_error= (command->expected_errors.count == 0 && + abort_on_error); + DBUG_VOID_RETURN; } @@ -5314,6 +5412,7 @@ int main(int argc, char **argv) init_dynamic_string(&ds_res, "", 65536, 65536); init_dynamic_string(&ds_progress, "", 0, 2048); + init_dynamic_string(&ds_warning_messages, "", 0, 2048); parse_args(argc, argv); DBUG_PRINT("info",("result_file: '%s'", result_file ? result_file : "")); @@ -5386,6 +5485,15 @@ int main(int argc, char **argv) int current_line_inc = 1, processed = 0; if (command->type == Q_UNKNOWN || command->type == Q_COMMENT_WITH_COMMAND) get_command_type(command); + + if (parsing_disabled && + command->type != Q_ENABLE_PARSING && + command->type != Q_DISABLE_PARSING) + { + command->type= Q_COMMENT; + scan_command_for_warnings(command); + } + if (cur_block->ok) { command->last_argument= command->first_argument; @@ -5558,7 +5666,6 @@ int main(int argc, char **argv) break; } case Q_COMMENT: /* Ignore row */ - case Q_COMMENT_WITH_COMMAND: command->last_argument= command->end; break; case Q_PING: @@ -5631,10 +5738,11 @@ int main(int argc, char **argv) else check_eol_junk(command->last_argument); - if (command->type != Q_ERROR) + if (command->type != Q_ERROR && + command->type != Q_COMMENT) { /* - As soon as any non "error" command has been executed, + As soon as any non "error" command or comment has been executed, the array with expected errors should be cleared */ memset(&saved_expected_errors, 0, sizeof(saved_expected_errors)); @@ -5650,8 +5758,6 @@ int main(int argc, char **argv) } last_command_executed= command_executed; - - parser.current_line += current_line_inc; if ( opt_mark_progress ) mark_progress(command, parser.current_line); @@ -5708,9 +5814,12 @@ int main(int argc, char **argv) die("No queries executed but result file found!"); } - if ( opt_mark_progress ) + if ( opt_mark_progress && result_file ) dump_progress(result_file); - dynstr_free(&ds_progress); + + /* Dump warning messages */ + if (result_file && ds_warning_messages.length) + dump_warning_messages(result_file); dynstr_free(&ds_res); From 99cfe38ae7d6da2dca8a3b377254d0bfba8b6ac4 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Oct 2006 12:27:05 +0200 Subject: [PATCH 26/68] Don't print version in error message, just too messy to test --- client/mysqltest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index a81854733de..d49693fa324 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -802,7 +802,7 @@ void abort_not_supported_test(const char *fmt, ...) void abort_not_in_this_version() { - die("Not available in mysqltest for MySQL %s", VERSION); + die("Not available in this version of mysqltest"); } From dccc6b10c34cc111e6a587642b4e7eb93bd3b6a1 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Oct 2006 12:47:32 +0200 Subject: [PATCH 27/68] Backport from 5.1 -Add support for detecting version and features from mysqld binary - Autodetect netware - Disable some features not available below 5.0 - Cleanup executable_setup to look for one executable at a time, only llok for the ones that are needed based on the selected testcases and settings mysql-test/lib/mtr_cases.pl: Backport from 5.1 mysql-test/lib/mtr_io.pl: Backport from 5.1 Add new function mtr_appendfile_to_file mysql-test/lib/mtr_misc.pl: Backport from 5.1 mysql-test/lib/mtr_process.pl: Backport from 5.1 mysql-test/lib/mtr_report.pl: Backport from 5.1 mysql-test/mysql-test-run.pl: Add support for detecting version and features from mysqld binary Autodetect netware Disable some features not available below 5.0 Cleanup executable_setup to look for one executable at a time, only llok for the ones that are needed based on the selected testcases and settings mysql-test/r/mysqltest.result: Update result mysql-test/lib/mtr_im.pl: New BitKeeper file ``mysql-test/lib/mtr_im.pl'' mysql-test/lib/mtr_stress.pl: New BitKeeper file ``mysql-test/lib/mtr_stress.pl'' --- mysql-test/lib/mtr_cases.pl | 367 +++- mysql-test/lib/mtr_im.pl | 761 +++++++ mysql-test/lib/mtr_io.pl | 66 +- mysql-test/lib/mtr_misc.pl | 40 +- mysql-test/lib/mtr_process.pl | 709 +++--- mysql-test/lib/mtr_report.pl | 135 +- mysql-test/lib/mtr_stress.pl | 178 ++ mysql-test/mysql-test-run.pl | 3821 ++++++++++++++++++++++++--------- mysql-test/r/mysqltest.result | 2 +- 9 files changed, 4681 insertions(+), 1398 deletions(-) create mode 100644 mysql-test/lib/mtr_im.pl create mode 100644 mysql-test/lib/mtr_stress.pl diff --git a/mysql-test/lib/mtr_cases.pl b/mysql-test/lib/mtr_cases.pl index 650fb79155d..bb92730444c 100644 --- a/mysql-test/lib/mtr_cases.pl +++ b/mysql-test/lib/mtr_cases.pl @@ -5,10 +5,13 @@ # same name. use File::Basename; +use IO::File(); use strict; sub collect_test_cases ($); -sub collect_one_test_case ($$$$$$); +sub collect_one_test_case ($$$$$$$); + +sub mtr_options_from_test_file($$); ############################################################################## # @@ -37,44 +40,114 @@ sub collect_test_cases ($) { opendir(TESTDIR, $testdir) or mtr_error("Can't open dir \"$testdir\": $!"); + # ---------------------------------------------------------------------- + # Disable some tests listed in disabled.def + # ---------------------------------------------------------------------- + my %disabled; + if ( open(DISABLED, "$testdir/disabled.def" ) ) + { + while ( ) + { + chomp; + if ( /^\s*(\S+)\s*:\s*(.*?)\s*$/ ) + { + $disabled{$1}= $2; + } + } + close DISABLED; + } + if ( @::opt_cases ) { foreach my $tname ( @::opt_cases ) { # Run in specified order, no sort - $tname= basename($tname, ".test"); - my $elem= "$tname.test"; - if ( ! -f "$testdir/$elem") + my $elem= undef; + my $component_id= undef; + + # Get rid of directory part (path). Leave the extension since it is used + # to understand type of the test. + + $tname = basename($tname); + + # Check if the extenstion has been specified. + + if ( mtr_match_extension($tname, "test") ) { - mtr_error("Test case $tname ($testdir/$elem) is not found"); + $elem= $tname; + $tname=~ s/\.test$//; + $component_id= 'mysqld'; } - collect_one_test_case($testdir,$resdir,$tname,$elem,$cases,{}); + elsif ( mtr_match_extension($tname, "imtest") ) + { + $elem= $tname; + $tname =~ s/\.imtest$//; + $component_id= 'im'; + } + + # If target component is known, check that the specified test case + # exists. + # + # Otherwise, try to guess the target component. + + if ( $component_id ) + { + if ( ! -f "$testdir/$elem") + { + mtr_error("Test case $tname ($testdir/$elem) is not found"); + } + } + else + { + my $mysqld_test_exists = -f "$testdir/$tname.test"; + my $im_test_exists = -f "$testdir/$tname.imtest"; + + if ( $mysqld_test_exists and $im_test_exists ) + { + mtr_error("Ambiguous test case name ($tname)"); + } + elsif ( ! $mysqld_test_exists and ! $im_test_exists ) + { + mtr_error("Test case $tname is not found"); + } + elsif ( $mysqld_test_exists ) + { + $elem= "$tname.test"; + $component_id= 'mysqld'; + } + elsif ( $im_test_exists ) + { + $elem= "$tname.imtest"; + $component_id= 'im'; + } + } + + collect_one_test_case($testdir,$resdir,$tname,$elem,$cases,\%disabled, + $component_id); } closedir TESTDIR; } else { - # ---------------------------------------------------------------------- - # Disable some tests listed in disabled.def - # ---------------------------------------------------------------------- - my %disabled; - if ( open(DISABLED, "$testdir/disabled.def" ) ) - { - while ( ) - { - chomp; - if ( /^\s*(\S+)\s*:\s*(.*?)\s*$/ ) - { - $disabled{$1}= $2; - } - } - close DISABLED; - } - foreach my $elem ( sort readdir(TESTDIR) ) { - my $tname= mtr_match_extension($elem,"test"); - next if ! defined $tname; + my $component_id= undef; + my $tname= undef; + + if ($tname= mtr_match_extension($elem, 'test')) + { + $component_id = 'mysqld'; + } + elsif ($tname= mtr_match_extension($elem, 'imtest')) + { + $component_id = 'im'; + } + else + { + next; + } + next if $::opt_do_test and ! defined mtr_match_prefix($elem,$::opt_do_test); - collect_one_test_case($testdir,$resdir,$tname,$elem,$cases,\%disabled); + collect_one_test_case($testdir,$resdir,$tname,$elem,$cases,\%disabled, + $component_id); } closedir TESTDIR; } @@ -84,34 +157,38 @@ sub collect_test_cases ($) { if ( $::opt_reorder ) { - @$cases = sort { - if ( ! $a->{'master_restart'} and ! $b->{'master_restart'} ) - { - return $a->{'name'} cmp $b->{'name'}; - } - if ( $a->{'master_restart'} and $b->{'master_restart'} ) - { - my $cmp= mtr_cmp_opts($a->{'master_opt'}, $b->{'master_opt'}); - if ( $cmp == 0 ) - { - return $a->{'name'} cmp $b->{'name'}; - } - else - { - return $cmp; - } - } + my %sort_criteria; + my $tinfo; - if ( $a->{'master_restart'} ) - { - return 1; # Is greater - } - else - { - return -1; # Is less - } - } @$cases; + # Make a mapping of test name to a string that represents how that test + # should be sorted among the other tests. Put the most important criterion + # first, then a sub-criterion, then sub-sub-criterion, et c. + foreach $tinfo (@$cases) + { + my @this_criteria = (); + + # Append the criteria for sorting, in order of importance. + push(@this_criteria, join("!", sort @{$tinfo->{'master_opt'}}) . "~"); # Ending with "~" makes empty sort later than filled + push(@this_criteria, "ndb=" . ($tinfo->{'ndb_test'} ? "1" : "0")); + push(@this_criteria, "restart=" . ($tinfo->{'master_restart'} ? "1" : "0")); + push(@this_criteria, "big_test=" . ($tinfo->{'big_test'} ? "1" : "0")); + push(@this_criteria, join("|", sort keys %{$tinfo})); # Group similar things together. The values may differ substantially. FIXME? + push(@this_criteria, $tinfo->{'name'}); # Finally, order by the name + + $sort_criteria{$tinfo->{"name"}} = join(" ", @this_criteria); + } + + @$cases = sort { $sort_criteria{$a->{"name"}} cmp $sort_criteria{$b->{"name"}}; } @$cases; + +### For debugging the sort-order +# foreach $tinfo (@$cases) +# { +# print $sort_criteria{$tinfo->{"name"}}; +# print " -> \t"; +# print $tinfo->{"name"}; +# print "\n"; +# } } return $cases; @@ -125,13 +202,14 @@ sub collect_test_cases ($) { ############################################################################## -sub collect_one_test_case($$$$$$) { +sub collect_one_test_case($$$$$$$) { my $testdir= shift; my $resdir= shift; my $tname= shift; my $elem= shift; my $cases= shift; my $disabled=shift; + my $component_id= shift; my $path= "$testdir/$elem"; @@ -151,6 +229,7 @@ sub collect_one_test_case($$$$$$) { my $tinfo= {}; $tinfo->{'name'}= $tname; $tinfo->{'result_file'}= "$resdir/$tname.result"; + $tinfo->{'component_id'} = $component_id; push(@$cases, $tinfo); if ( $::opt_skip_test and defined mtr_match_prefix($tname,$::opt_skip_test) ) @@ -171,48 +250,54 @@ sub collect_one_test_case($$$$$$) { if ( $::opt_skip_rpl ) { $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No replication tests(--skip-rpl)"; return; } $tinfo->{'slave_num'}= 1; # Default, use one slave - # FIXME currently we always restart slaves - $tinfo->{'slave_restart'}= 1; - if ( $tname eq 'rpl_failsafe' or $tname eq 'rpl_chain_temp_table' ) { -# $tinfo->{'slave_num'}= 3; # Not 3 ? Check old code, strange + # $tinfo->{'slave_num'}= 3; # Not 3 ? Check old code, strange } } if ( defined mtr_match_prefix($tname,"federated") ) { - $tinfo->{'slave_num'}= 1; # Default, use one slave - - # FIXME currently we always restart slaves - $tinfo->{'slave_restart'}= 1; + # Default, federated uses the first slave as it's federated database + $tinfo->{'slave_num'}= 1; } - # Cluster is needed by test case if testname contains ndb - if ( defined mtr_match_substring($tname,"ndb") ) + if ( $::opt_with_ndbcluster or defined mtr_match_substring($tname,"ndb") ) { + # This is an ndb test or all tests should be run with ndb cluster started $tinfo->{'ndb_test'}= 1; if ( $::opt_skip_ndbcluster ) { - # Skip all ndb tests + # All ndb test's should be skipped $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No ndbcluster test(--skip-ndbcluster)"; return; } - if ( ! $::opt_with_ndbcluster ) + if ( ! $::opt_ndbcluster_supported ) { # Ndb is not supported, skip them $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No ndbcluster support"; return; } } else { + # This is not a ndb test $tinfo->{'ndb_test'}= 0; + if ( $::opt_with_ndbcluster_only ) + { + # Only the ndb test should be run, all other should be skipped + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Only ndbcluster tests(--with-ndbcluster-only)"; + return; + } } # FIXME what about embedded_server + ndbcluster, skip ?! @@ -223,6 +308,7 @@ sub collect_one_test_case($$$$$$) { my $master_sh= "$testdir/$tname-master.sh"; my $slave_sh= "$testdir/$tname-slave.sh"; my $disabled_file= "$testdir/$tname.disabled"; + my $im_opt_file= "$testdir/$tname-im.opt"; $tinfo->{'master_opt'}= []; $tinfo->{'slave_opt'}= []; @@ -303,6 +389,8 @@ sub collect_one_test_case($$$$$$) { if ( $::glob_win32_perl ) { $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No tests with sh scripts on Windows"; + return; } else { @@ -316,6 +404,8 @@ sub collect_one_test_case($$$$$$) { if ( $::glob_win32_perl ) { $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No tests with sh scripts on Windows"; + return; } else { @@ -324,29 +414,166 @@ sub collect_one_test_case($$$$$$) { } } + if ( -f $im_opt_file ) + { + $tinfo->{'im_opts'} = mtr_get_opts_from_file($im_opt_file); + } + else + { + $tinfo->{'im_opts'} = []; + } + # FIXME why this late? + my $marked_as_disabled= 0; if ( $disabled->{$tname} ) { - $tinfo->{'skip'}= 1; - $tinfo->{'disable'}= 1; # Sub type of 'skip' - $tinfo->{'comment'}= $disabled->{$tname} if $disabled->{$tname}; + $marked_as_disabled= 1; + $tinfo->{'comment'}= $disabled->{$tname}; } if ( -f $disabled_file ) { - $tinfo->{'skip'}= 1; - $tinfo->{'disable'}= 1; # Sub type of 'skip' + $marked_as_disabled= 1; $tinfo->{'comment'}= mtr_fromfile($disabled_file); } + # If test was marked as disabled, either opt_enable_disabled is off and then + # we skip this test, or it is on and then we run this test but warn + + if ( $marked_as_disabled ) + { + if ( $::opt_enable_disabled ) + { + $tinfo->{'dont_skip_though_disabled'}= 1; + } + else + { + $tinfo->{'skip'}= 1; + $tinfo->{'disable'}= 1; # Sub type of 'skip' + return; + } + } + + if ( $component_id eq 'im' ) + { + if ( $::glob_use_embedded_server ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No IM with embedded server"; + return; + } + elsif ( $::opt_ps_protocol ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No IM with --ps-protocol"; + return; + } + elsif ( $::opt_skip_im ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No IM tests(--skip-im)"; + return; + } + } + else + { + mtr_options_from_test_file($tinfo,"$testdir/${tname}.test"); + + if ( $tinfo->{'big_test'} and ! $::opt_big_test ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Test need 'big-test' option"; + return; + } + + if ( $tinfo->{'ndb_extra'} and ! $::opt_ndb_extra_test ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Test need 'ndb_extra' option"; + return; + } + + if ( $tinfo->{'require_manager'} ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Test need the _old_ manager(to be removed)"; + return; + } + + if ( defined $tinfo->{'binlog_format'} and + ! ( $tinfo->{'binlog_format'} eq $::used_binlog_format ) ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Not running with binlog format '$tinfo->{'binlog_format'}'"; + return; + } + + if ( $tinfo->{'need_debug'} && ! $::debug_compiled_binaries ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Test need debug binaries"; + return; + } + } + # We can't restart a running server that may be in use if ( $::glob_use_running_server and ( $tinfo->{'master_restart'} or $tinfo->{'slave_restart'} ) ) { $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Can't restart a running server"; + return; } + } +# List of tags in the .test files that if found should set +# the specified value in "tinfo" +our @tags= +( + ["include/have_innodb.inc", "innodb_test", 1], + ["include/have_binlog_format_row.inc", "binlog_format", "row"], + ["include/have_binlog_format_statement.inc", "binlog_format", "stmt"], + ["include/big_test.inc", "big_test", 1], + ["include/have_debug.inc", "need_debug", 1], + ["include/have_ndb_extra.inc", "ndb_extra", 1], + ["require_manager", "require_manager", 1], +); + +sub mtr_options_from_test_file($$) { + my $tinfo= shift; + my $file= shift; + #mtr_verbose("$file"); + my $F= IO::File->new($file) or mtr_error("can't open file \"$file\": $!"); + + while ( my $line= <$F> ) + { + next if ( $line !~ /^--/ ); + + # Match this line against tag in "tags" array + foreach my $tag (@tags) + { + if ( index($line, $tag->[0]) >= 0 ) + { + # Tag matched, assign value to "tinfo" + $tinfo->{"$tag->[1]"}= $tag->[2]; + } + } + + # If test sources another file, open it as well + if ( $line =~ /^\-\-([[:space:]]*)source(.*)$/ ) + { + my $value= $2; + $value =~ s/^\s+//; # Remove leading space + $value =~ s/[[:space:]]+$//; # Remove ending space + + my $sourced_file= "$::glob_mysql_test_dir/$value"; + mtr_options_from_test_file($tinfo, $sourced_file); + } + + } +} + 1; diff --git a/mysql-test/lib/mtr_im.pl b/mysql-test/lib/mtr_im.pl new file mode 100644 index 00000000000..ca17516278e --- /dev/null +++ b/mysql-test/lib/mtr_im.pl @@ -0,0 +1,761 @@ +# -*- cperl -*- + +# This is a library file used by the Perl version of mysql-test-run, +# and is part of the translation of the Bourne shell script with the +# same name. + +use strict; + +# Private IM-related operations. + +sub mtr_im_kill_process ($$$$); +sub mtr_im_load_pids ($); +sub mtr_im_terminate ($); +sub mtr_im_check_alive ($); +sub mtr_im_check_main_alive ($); +sub mtr_im_check_angel_alive ($); +sub mtr_im_check_mysqlds_alive ($); +sub mtr_im_check_mysqld_alive ($); +sub mtr_im_cleanup ($); +sub mtr_im_rm_file ($); +sub mtr_im_errlog ($); +sub mtr_im_kill ($); +sub mtr_im_wait_for_connection ($$$); +sub mtr_im_wait_for_mysqld($$$); + +# Public IM-related operations. + +sub mtr_im_start ($$); +sub mtr_im_stop ($); + +############################################################################## +# +# Private operations. +# +############################################################################## + +sub mtr_im_kill_process ($$$$) { + my $pid_lst= shift; + my $signal= shift; + my $total_retries= shift; + my $timeout= shift; + + my %pids; + + foreach my $pid ( @{$pid_lst} ) + { + $pids{$pid}= 1; + } + + for ( my $cur_attempt= 1; $cur_attempt <= $total_retries; ++$cur_attempt ) + { + foreach my $pid ( keys %pids ) + { + mtr_debug("Sending $signal to $pid..."); + + kill($signal, $pid); + + unless ( kill (0, $pid) ) + { + mtr_debug("Process $pid died."); + delete $pids{$pid}; + } + } + + return if scalar keys %pids == 0; + + mtr_debug("Sleeping $timeout second(s) waiting for processes to die..."); + + sleep($timeout); + } + + mtr_debug("Process(es) " . + join(' ', keys %pids) . + " is still alive after $total_retries " . + "of sending signal $signal."); +} + +########################################################################### + +sub mtr_im_load_pids($) { + my $im= shift; + + mtr_debug("Loading PID files..."); + + # Obtain mysqld-process pids. + + my $instances = $im->{'instances'}; + + for ( my $idx= 0; $idx < 2; ++$idx ) + { + mtr_debug("IM-guarded mysqld[$idx] PID file: '" . + $instances->[$idx]->{'path_pid'} . "'."); + + my $mysqld_pid; + + if ( -r $instances->[$idx]->{'path_pid'} ) + { + $mysqld_pid= mtr_get_pid_from_file($instances->[$idx]->{'path_pid'}); + mtr_debug("IM-guarded mysqld[$idx] PID: $mysqld_pid."); + } + else + { + $mysqld_pid= undef; + mtr_debug("IM-guarded mysqld[$idx]: no PID file."); + } + + $instances->[$idx]->{'pid'}= $mysqld_pid; + } + + # Re-read Instance Manager PIDs from the file, since during tests Instance + # Manager could have been restarted, so its PIDs could have been changed. + + # - IM-main + + mtr_debug("IM-main PID file: '$im->{path_pid}'."); + + if ( -f $im->{'path_pid'} ) + { + $im->{'pid'} = + mtr_get_pid_from_file($im->{'path_pid'}); + + mtr_debug("IM-main PID: $im->{pid}."); + } + else + { + mtr_debug("IM-main: no PID file."); + $im->{'pid'}= undef; + } + + # - IM-angel + + mtr_debug("IM-angel PID file: '$im->{path_angel_pid}'."); + + if ( -f $im->{'path_angel_pid'} ) + { + $im->{'angel_pid'} = + mtr_get_pid_from_file($im->{'path_angel_pid'}); + + mtr_debug("IM-angel PID: $im->{'angel_pid'}."); + } + else + { + mtr_debug("IM-angel: no PID file."); + $im->{'angel_pid'} = undef; + } +} + +########################################################################### + +sub mtr_im_terminate($) { + my $im= shift; + + # Load pids from pid-files. We should do it first of all, because IM deletes + # them on shutdown. + + mtr_im_load_pids($im); + + mtr_debug("Shutting Instance Manager down..."); + + # Ignoring SIGCHLD so that all children could rest in peace. + + start_reap_all(); + + # Send SIGTERM to IM-main. + + if ( defined $im->{'pid'} ) + { + mtr_debug("IM-main pid: $im->{pid}."); + mtr_debug("Stopping IM-main..."); + + mtr_im_kill_process([ $im->{'pid'} ], 'TERM', 10, 1); + } + else + { + mtr_debug("IM-main pid: n/a."); + } + + # If IM-angel was alive, wait for it to die. + + if ( defined $im->{'angel_pid'} ) + { + mtr_debug("IM-angel pid: $im->{'angel_pid'}."); + mtr_debug("Waiting for IM-angel to die..."); + + my $total_attempts= 10; + + for ( my $cur_attempt=1; $cur_attempt <= $total_attempts; ++$cur_attempt ) + { + unless ( kill (0, $im->{'angel_pid'}) ) + { + mtr_debug("IM-angel died."); + last; + } + + sleep(1); + } + } + else + { + mtr_debug("IM-angel pid: n/a."); + } + + stop_reap_all(); + + # Re-load PIDs. + + mtr_im_load_pids($im); +} + +########################################################################### + +sub mtr_im_check_alive($) { + my $im= shift; + + mtr_debug("Checking whether IM-components are alive..."); + + return 1 if mtr_im_check_main_alive($im); + + return 1 if mtr_im_check_angel_alive($im); + + return 1 if mtr_im_check_mysqlds_alive($im); + + return 0; +} + +########################################################################### + +sub mtr_im_check_main_alive($) { + my $im= shift; + + # Check that the process, that we know to be IM's, is dead. + + if ( defined $im->{'pid'} ) + { + if ( kill (0, $im->{'pid'}) ) + { + mtr_debug("IM-main (PID: $im->{pid}) is alive."); + return 1; + } + else + { + mtr_debug("IM-main (PID: $im->{pid}) is dead."); + } + } + else + { + mtr_debug("No PID file for IM-main."); + } + + # Check that IM does not accept client connections. + + if ( mtr_ping_port($im->{'port'}) ) + { + mtr_debug("IM-main (port: $im->{port}) " . + "is accepting connections."); + + mtr_im_errlog("IM-main is accepting connections on port " . + "$im->{port}, but there is no " . + "process information."); + return 1; + } + else + { + mtr_debug("IM-main (port: $im->{port}) " . + "does not accept connections."); + return 0; + } +} + +########################################################################### + +sub mtr_im_check_angel_alive($) { + my $im= shift; + + # Check that the process, that we know to be the Angel, is dead. + + if ( defined $im->{'angel_pid'} ) + { + if ( kill (0, $im->{'angel_pid'}) ) + { + mtr_debug("IM-angel (PID: $im->{angel_pid}) is alive."); + return 1; + } + else + { + mtr_debug("IM-angel (PID: $im->{angel_pid}) is dead."); + return 0; + } + } + else + { + mtr_debug("No PID file for IM-angel."); + return 0; + } +} + +########################################################################### + +sub mtr_im_check_mysqlds_alive($) { + my $im= shift; + + mtr_debug("Checking for IM-guarded mysqld instances..."); + + my $instances = $im->{'instances'}; + + for ( my $idx= 0; $idx < 2; ++$idx ) + { + mtr_debug("Checking mysqld[$idx]..."); + + return 1 + if mtr_im_check_mysqld_alive($instances->[$idx]); + } +} + +########################################################################### + +sub mtr_im_check_mysqld_alive($) { + my $mysqld_instance= shift; + + # Check that the process is dead. + + if ( defined $mysqld_instance->{'pid'} ) + { + if ( kill (0, $mysqld_instance->{'pid'}) ) + { + mtr_debug("Mysqld instance (PID: $mysqld_instance->{pid}) is alive."); + return 1; + } + else + { + mtr_debug("Mysqld instance (PID: $mysqld_instance->{pid}) is dead."); + } + } + else + { + mtr_debug("No PID file for mysqld instance."); + } + + # Check that mysqld does not accept client connections. + + if ( mtr_ping_port($mysqld_instance->{'port'}) ) + { + mtr_debug("Mysqld instance (port: $mysqld_instance->{port}) " . + "is accepting connections."); + + mtr_im_errlog("Mysqld is accepting connections on port " . + "$mysqld_instance->{port}, but there is no " . + "process information."); + return 1; + } + else + { + mtr_debug("Mysqld instance (port: $mysqld_instance->{port}) " . + "does not accept connections."); + return 0; + } +} + +########################################################################### + +sub mtr_im_cleanup($) { + my $im= shift; + + mtr_im_rm_file($im->{'path_pid'}); + mtr_im_rm_file($im->{'path_sock'}); + + mtr_im_rm_file($im->{'path_angel_pid'}); + + for ( my $idx= 0; $idx < 2; ++$idx ) + { + mtr_im_rm_file($im->{'instances'}->[$idx]->{'path_pid'}); + mtr_im_rm_file($im->{'instances'}->[$idx]->{'path_sock'}); + } +} + +########################################################################### + +sub mtr_im_rm_file($) +{ + my $file_path= shift; + + if ( -f $file_path ) + { + mtr_debug("Removing '$file_path'..."); + + unless ( unlink($file_path) ) + { + mtr_warning("Can not remove '$file_path'.") + } + } + else + { + mtr_debug("File '$file_path' does not exist already."); + } +} + +########################################################################### + +sub mtr_im_errlog($) { + my $msg= shift; + + # Complain in error log so that a warning will be shown. + # + # TODO: unless BUG#20761 is fixed, we will print the warning to stdout, so + # that it can be seen on console and does not produce pushbuild error. + + # my $errlog= "$opt_vardir/log/mysql-test-run.pl.err"; + # + # open (ERRLOG, ">>$errlog") || + # mtr_error("Can not open error log ($errlog)"); + # + # my $ts= localtime(); + # print ERRLOG + # "Warning: [$ts] $msg\n"; + # + # close ERRLOG; + + my $ts= localtime(); + print "Warning: [$ts] $msg\n"; +} + +########################################################################### + +sub mtr_im_kill($) { + my $im= shift; + + # Re-load PIDs. That can be useful because some processes could have been + # restarted. + + mtr_im_load_pids($im); + + # Ignoring SIGCHLD so that all children could rest in peace. + + start_reap_all(); + + # Kill IM-angel first of all. + + if ( defined $im->{'angel_pid'} ) + { + mtr_debug("Killing IM-angel (PID: $im->{angel_pid})..."); + mtr_im_kill_process([ $im->{'angel_pid'} ], 'KILL', 10, 1) + } + else + { + mtr_debug("IM-angel is dead."); + } + + # Re-load PIDs again. + + mtr_im_load_pids($im); + + # Kill IM-main. + + if ( defined $im->{'pid'} ) + { + mtr_debug("Killing IM-main (PID: $im->pid})..."); + mtr_im_kill_process([ $im->{'pid'} ], 'KILL', 10, 1); + } + else + { + mtr_debug("IM-main is dead."); + } + + # Re-load PIDs again. + + mtr_im_load_pids($im); + + # Kill guarded mysqld instances. + + my @mysqld_pids; + + mtr_debug("Collecting PIDs of mysqld instances to kill..."); + + for ( my $idx= 0; $idx < 2; ++$idx ) + { + my $pid= $im->{'instances'}->[$idx]->{'pid'}; + + unless ( defined $pid ) + { + next; + } + + mtr_debug(" - IM-guarded mysqld[$idx] PID: $pid."); + + push (@mysqld_pids, $pid); + } + + if ( scalar @mysqld_pids > 0 ) + { + mtr_debug("Killing IM-guarded mysqld instances..."); + mtr_im_kill_process(\@mysqld_pids, 'KILL', 10, 1); + } + + # That's all. + + stop_reap_all(); +} + +############################################################################## + +sub mtr_im_wait_for_connection($$$) { + my $im= shift; + my $total_attempts= shift; + my $connect_timeout= shift; + + mtr_debug("Waiting for IM on port $im->{port} " . + "to start accepting connections..."); + + for ( my $cur_attempt= 1; $cur_attempt <= $total_attempts; ++$cur_attempt ) + { + mtr_debug("Trying to connect to IM ($cur_attempt of $total_attempts)..."); + + if ( mtr_ping_port($im->{'port'}) ) + { + mtr_debug("IM is accepting connections " . + "on port $im->{port}."); + return 1; + } + + mtr_debug("Sleeping $connect_timeout..."); + sleep($connect_timeout); + } + + mtr_debug("IM does not accept connections " . + "on port $im->{port} after " . + ($total_attempts * $connect_timeout) . " seconds."); + + return 0; +} + +############################################################################## + +sub mtr_im_wait_for_mysqld($$$) { + my $mysqld= shift; + my $total_attempts= shift; + my $connect_timeout= shift; + + mtr_debug("Waiting for IM-guarded mysqld on port $mysqld->{port} " . + "to start accepting connections..."); + + for ( my $cur_attempt= 1; $cur_attempt <= $total_attempts; ++$cur_attempt ) + { + mtr_debug("Trying to connect to mysqld " . + "($cur_attempt of $total_attempts)..."); + + if ( mtr_ping_port($mysqld->{'port'}) ) + { + mtr_debug("Mysqld is accepting connections " . + "on port $mysqld->{port}."); + return 1; + } + + mtr_debug("Sleeping $connect_timeout..."); + sleep($connect_timeout); + } + + mtr_debug("Mysqld does not accept connections " . + "on port $mysqld->{port} after " . + ($total_attempts * $connect_timeout) . " seconds."); + + return 0; +} + +############################################################################## +# +# Public operations. +# +############################################################################## + +sub mtr_im_start($$) { + my $im = shift; + my $opts = shift; + + mtr_debug("Starting Instance Manager..."); + + my $args; + mtr_init_args(\$args); + mtr_add_arg($args, "--defaults-file=%s", $im->{'defaults_file'}); + + foreach my $opt ( @{$opts} ) + { + mtr_add_arg($args, $opt); + } + + $im->{'pid'} = + mtr_spawn( + $::exe_im, # path to the executable + $args, # cmd-line args + '', # stdin + $im->{'path_log'}, # stdout + $im->{'path_err'}, # stderr + '', # pid file path (not used) + { append_log_file => 1 } # append log files + ); + + unless ( $im->{'pid'} ) + { + mtr_error('Could not start Instance Manager.') + } + + # Instance Manager can be run in daemon mode. In this case, it creates + # several processes and the parent process, created by mtr_spawn(), exits just + # after start. So, we have to obtain Instance Manager PID from the PID file. + + mtr_debug("Waiting for IM to create PID file (" . + "path: '$im->{path_pid}'; " . + "timeout: $im->{start_timeout})..."); + + unless ( sleep_until_file_created($im->{'path_pid'}, + $im->{'start_timeout'}, + -1) ) # real PID is still unknown + { + mtr_debug("IM has not created PID file in $im->{start_timeout} secs."); + mtr_debug("Aborting test suite..."); + + mtr_kill_leftovers(); + + mtr_report("IM has not created PID file in $im->{start_timeout} secs."); + return 0; + } + + $im->{'pid'}= mtr_get_pid_from_file($im->{'path_pid'}); + + mtr_debug("Instance Manager started. PID: $im->{pid}."); + + # Wait until we can connect to IM. + + my $IM_CONNECT_TIMEOUT= 30; + + unless ( mtr_im_wait_for_connection($im, + $IM_CONNECT_TIMEOUT, 1) ) + { + mtr_debug("Can not connect to Instance Manager " . + "in $IM_CONNECT_TIMEOUT seconds after start."); + mtr_debug("Aborting test suite..."); + + mtr_kill_leftovers(); + + mtr_report("Can not connect to Instance Manager " . + "in $IM_CONNECT_TIMEOUT seconds after start."); + return 0; + } + + # Wait for IM to start guarded instances: + # - wait for PID files; + + mtr_debug("Waiting for guarded mysqlds instances to create PID files..."); + + for ( my $idx= 0; $idx < 2; ++$idx ) + { + my $mysqld= $im->{'instances'}->[$idx]; + + if ( exists $mysqld->{'nonguarded'} ) + { + next; + } + + mtr_debug("Waiting for mysqld[$idx] to create PID file (" . + "path: '$mysqld->{path_pid}'; " . + "timeout: $mysqld->{start_timeout})..."); + + unless ( sleep_until_file_created($mysqld->{'path_pid'}, + $mysqld->{'start_timeout'}, + -1) ) # real PID is still unknown + { + mtr_debug("mysqld[$idx] has not created PID file in " . + "$mysqld->{start_timeout} secs."); + mtr_debug("Aborting test suite..."); + + mtr_kill_leftovers(); + + mtr_report("mysqld[$idx] has not created PID file in " . + "$mysqld->{start_timeout} secs."); + return 0; + } + + mtr_debug("PID file for mysqld[$idx] ($mysqld->{path_pid} created."); + } + + # Wait until we can connect to guarded mysqld-instances + # (in other words -- wait for IM to start guarded instances). + + mtr_debug("Waiting for guarded mysqlds to start accepting connections..."); + + for ( my $idx= 0; $idx < 2; ++$idx ) + { + my $mysqld= $im->{'instances'}->[$idx]; + + if ( exists $mysqld->{'nonguarded'} ) + { + next; + } + + mtr_debug("Waiting for mysqld[$idx] to accept connection..."); + + unless ( mtr_im_wait_for_mysqld($mysqld, 30, 1) ) + { + mtr_debug("Can not connect to mysqld[$idx] " . + "in $IM_CONNECT_TIMEOUT seconds after start."); + mtr_debug("Aborting test suite..."); + + mtr_kill_leftovers(); + + mtr_report("Can not connect to mysqld[$idx] " . + "in $IM_CONNECT_TIMEOUT seconds after start."); + return 0; + } + + mtr_debug("mysqld[$idx] started."); + } + + mtr_debug("Instance Manager and its components are up and running."); + + return 1; +} + +############################################################################## + +sub mtr_im_stop($) { + my $im= shift; + + mtr_debug("Stopping Instance Manager..."); + + # Try graceful shutdown. + + mtr_im_terminate($im); + + # Check that all processes died. + + unless ( mtr_im_check_alive($im) ) + { + mtr_debug("Instance Manager has been stopped successfully."); + mtr_im_cleanup($im); + return 1; + } + + # Instance Manager don't want to die. We should kill it. + + mtr_im_errlog("Instance Manager did not shutdown gracefully."); + + mtr_im_kill($im); + + # Check again that all IM-related processes have been killed. + + my $im_is_alive= mtr_im_check_alive($im); + + mtr_im_cleanup($im); + + if ( $im_is_alive ) + { + mtr_debug("Can not kill Instance Manager or its children."); + return 0; + } + + mtr_debug("Instance Manager has been killed successfully."); + return 1; +} + +########################################################################### + +1; diff --git a/mysql-test/lib/mtr_io.pl b/mysql-test/lib/mtr_io.pl index b3da6d97664..8a7fc56269c 100644 --- a/mysql-test/lib/mtr_io.pl +++ b/mysql-test/lib/mtr_io.pl @@ -11,6 +11,8 @@ sub mtr_get_opts_from_file ($); sub mtr_fromfile ($); sub mtr_tofile ($@); sub mtr_tonewfile($@); +sub mtr_lastlinefromfile($); +sub mtr_appendfile_to_file ($$); ############################################################################## # @@ -19,13 +21,39 @@ sub mtr_tonewfile($@); ############################################################################## sub mtr_get_pid_from_file ($) { - my $file= shift; + my $pid_file_path= shift; + my $TOTAL_ATTEMPTS= 30; + my $timeout= 1; - open(FILE,"<",$file) or mtr_error("can't open file \"$file\": $!"); - my $pid= ; - chomp($pid); - close FILE; - return $pid; + # We should read from the file until we get correct pid. As it is + # stated in BUG#21884, pid file can be empty at some moment. So, we should + # read it until we get valid data. + + for (my $cur_attempt= 1; $cur_attempt <= $TOTAL_ATTEMPTS; ++$cur_attempt) + { + mtr_debug("Reading pid file '$pid_file_path' " . + "($cur_attempt of $TOTAL_ATTEMPTS)..."); + + open(FILE, '<', $pid_file_path) + or mtr_error("can't open file \"$pid_file_path\": $!"); + + my $pid= ; + + chomp($pid) if defined $pid; + + close FILE; + + return $pid if defined $pid && $pid ne ''; + + mtr_debug("Pid file '$pid_file_path' is empty. " . + "Sleeping $timeout second(s)..."); + + sleep(1); + } + + mtr_error("Pid file '$pid_file_path' is corrupted. " . + "Can not retrieve PID in " . + ($timeout * $TOTAL_ATTEMPTS) . " seconds."); } sub mtr_get_opts_from_file ($) { @@ -113,6 +141,20 @@ sub mtr_fromfile ($) { return $text; } +sub mtr_lastlinefromfile ($) { + my $file= shift; + my $text; + + open(FILE,"<",$file) or mtr_error("can't open file \"$file\": $!"); + while (my $line= ) + { + $text= $line; + } + close FILE; + return $text; +} + + sub mtr_tofile ($@) { my $file= shift; @@ -129,5 +171,17 @@ sub mtr_tonewfile ($@) { close FILE; } +sub mtr_appendfile_to_file ($$) { + my $from_file= shift; + my $to_file= shift; + + open(TOFILE,">>",$to_file) or mtr_error("can't open file \"$to_file\": $!"); + open(FROMFILE,">>",$from_file) + or mtr_error("can't open file \"$from_file\": $!"); + print TOFILE while (); + close FROMFILE; + close TOFILE; +} + 1; diff --git a/mysql-test/lib/mtr_misc.pl b/mysql-test/lib/mtr_misc.pl index 08c99e90906..dd9d24ebc8e 100644 --- a/mysql-test/lib/mtr_misc.pl +++ b/mysql-test/lib/mtr_misc.pl @@ -9,9 +9,10 @@ use strict; sub mtr_full_hostname (); sub mtr_short_hostname (); sub mtr_init_args ($); -sub mtr_add_arg ($$); +sub mtr_add_arg ($$@); sub mtr_path_exists(@); sub mtr_script_exists(@); +sub mtr_file_exists(@); sub mtr_exe_exists(@); sub mtr_copy_dir($$); sub mtr_same_opts($$); @@ -54,7 +55,7 @@ sub mtr_init_args ($) { $$args = []; # Empty list } -sub mtr_add_arg ($$) { +sub mtr_add_arg ($$@) { my $args= shift; my $format= shift; my @fargs = @_; @@ -101,6 +102,14 @@ sub mtr_script_exists (@) { } } +sub mtr_file_exists (@) { + foreach my $path ( @_ ) + { + return $path if -e $path; + } + return ""; +} + sub mtr_exe_exists (@) { my @path= @_; map {$_.= ".exe"} @path if $::glob_win32; @@ -125,18 +134,29 @@ sub mtr_exe_exists (@) { } } + sub mtr_copy_dir($$) { - my $srcdir= shift; - my $dstdir= shift; + my $from_dir= shift; + my $to_dir= shift; + +# mtr_verbose("Copying from $from_dir to $to_dir"); + + mkpath("$to_dir"); + opendir(DIR, "$from_dir") + or mtr_error("Can't find $from_dir$!"); + for(readdir(DIR)) { + next if "$_" eq "." or "$_" eq ".."; + if ( -d "$from_dir/$_" ) + { + mtr_copy_dir("$from_dir/$_", "$to_dir/$_"); + next; + } + copy("$from_dir/$_", "$to_dir/$_"); + } + closedir(DIR); - # Create destination directory - mkpath($dstdir); - find(\&mtr_copy_one_file, $dstdir); } -sub mtr_copy_one_file { - print $File::Find::name, "\n"; -} sub mtr_same_opts ($$) { my $l1= shift; diff --git a/mysql-test/lib/mtr_process.pl b/mysql-test/lib/mtr_process.pl index 662b70a4fee..5e21248790e 100644 --- a/mysql-test/lib/mtr_process.pl +++ b/mysql-test/lib/mtr_process.pl @@ -4,7 +4,6 @@ # and is part of the translation of the Bourne shell script with the # same name. -#use Carp qw(cluck); use Socket; use Errno; use strict; @@ -14,12 +13,17 @@ use POSIX 'WNOHANG'; sub mtr_run ($$$$$$;$); sub mtr_spawn ($$$$$$;$); -sub mtr_stop_mysqld_servers ($); +sub mtr_check_stop_servers ($); sub mtr_kill_leftovers (); +sub mtr_wait_blocking ($); sub mtr_record_dead_children (); +sub mtr_ndbmgm_start($$); +sub mtr_mysqladmin_start($$$); sub mtr_exit ($); sub sleep_until_file_created ($$$); sub mtr_kill_processes ($); +sub mtr_ping_with_timeout($); +sub mtr_ping_port ($); # static in C sub spawn_impl ($$$$$$$$); @@ -31,7 +35,6 @@ sub spawn_impl ($$$$$$$$); ############################################################################## # This function try to mimic the C version used in "netware/mysql_test_run.c" -# FIXME learn it to handle append mode as well, a "new" flag or a "append" sub mtr_run ($$$$$$;$) { my $path= shift; @@ -112,6 +115,9 @@ sub spawn_impl ($$$$$$$$) { print STDERR "#### ", "-" x 78, "\n"; } + mtr_error("Can't spawn with empty \"path\"") unless defined $path; + + FORK: { my $pid= fork(); @@ -144,17 +150,6 @@ sub spawn_impl ($$$$$$$$) { $SIG{INT}= 'DEFAULT'; # Parent do some stuff, we don't - if ( $::glob_cygwin_shell and $mode eq 'test' ) - { - # Programs started from mysqltest under Cygwin, are to - # execute them within Cygwin. Else simple things in test - # files like - # --system "echo 1 > file" - # will fail. - # FIXME not working :-( -# $ENV{'COMSPEC'}= "$::glob_cygwin_shell -c"; - } - my $log_file_open_mode = '>'; if ($spawn_opts and $spawn_opts->{'append_log_file'}) @@ -164,7 +159,15 @@ sub spawn_impl ($$$$$$$$) { if ( $output ) { - if ( ! open(STDOUT,$log_file_open_mode,$output) ) + if ( $::glob_win32_perl ) + { + # Don't redirect stdout on ActiveState perl since this is + # just another thread in the same process. + # Should be fixed so that the thread that is created with fork + # executes the exe in another process and wait's for it to return. + # In the meanwhile, we get all the output from mysqld's to screen + } + elsif ( ! open(STDOUT,$log_file_open_mode,$output) ) { mtr_child_error("can't redirect STDOUT to \"$output\": $!"); } @@ -216,8 +219,7 @@ sub spawn_parent_impl { { # Simple run of command, we wait for it to return my $ret_pid= waitpid($pid,0); - - if ( $ret_pid <= 0 ) + if ( $ret_pid != $pid ) { mtr_error("$path ($pid) got lost somehow"); } @@ -245,7 +247,6 @@ sub spawn_parent_impl { # Someone terminated, don't know who. Collect # status info first before $? is lost, # but not $exit_value, this is flagged from - # my $timer_name= mtr_timer_timeout($::glob_timers, $ret_pid); if ( $timer_name ) @@ -272,45 +273,22 @@ sub spawn_parent_impl { last; } - # If one of the mysqld processes died, we want to - # mark this, and kill the mysqltest process. + # One of the child processes died, unless this was expected + # mysqltest should be killed and test aborted - foreach my $idx (0..1) - { - if ( $::master->[$idx]->{'pid'} eq $ret_pid ) - { - mtr_debug("child $ret_pid was master[$idx], " . - "exit during mysqltest run"); - $::master->[$idx]->{'pid'}= 0; - last; - } - } - - foreach my $idx (0..2) - { - if ( $::slave->[$idx]->{'pid'} eq $ret_pid ) - { - mtr_debug("child $ret_pid was slave[$idx], " . - "exit during mysqltest run"); - $::slave->[$idx]->{'pid'}= 0; - last; - } - } - - mtr_debug("waitpid() catched exit of unknown child $ret_pid, " . - "exit during mysqltest run"); + check_expected_crash_and_restart($ret_pid); } if ( $ret_pid != $pid ) { # We terminated the waiting because a "mysqld" process died. # Kill the mysqltest process. - + mtr_verbose("Kill mysqltest because another process died"); kill(9,$pid); $ret_pid= waitpid($pid,0); - if ( $ret_pid == -1 ) + if ( $ret_pid != $pid ) { mtr_error("$path ($pid) got lost somehow"); } @@ -351,39 +329,101 @@ sub mtr_process_exit_status { # ############################################################################## -# We just "ping" on the ports, and if we can't do a socket connect -# we assume the server is dead. So we don't *really* know a server -# is dead, we just hope that it after letting the listen port go, -# it is dead enough for us to start a new server. +# Kill all processes(mysqld, ndbd, ndb_mgmd and im) that would conflict with +# this run +# Make sure to remove the PID file, if any. +# kill IM manager first, else it will restart the servers sub mtr_kill_leftovers () { - # First, kill all masters and slaves that would conflict with - # this run. Make sure to remove the PID file, if any. + mtr_report("Killing Possible Leftover Processes"); + mtr_debug("mtr_kill_leftovers(): started."); - my @args; + mkpath("$::opt_vardir/log"); # Needed for mysqladmin log - for ( my $idx; $idx < 2; $idx++ ) + # Stop or kill Instance Manager and all its children. If we failed to do + # that, we can only abort -- there is nothing left to do. + +# mtr_error("Failed to stop Instance Manager.") +# unless mtr_im_stop($::instance_manager); + + # Start shutdown of masters and slaves. Don't touch IM-managed mysqld + # instances -- they should be stopped by mtr_im_stop(). + + mtr_debug("Shutting down mysqld-instances..."); + + my @kill_pids; + my %admin_pids; + + foreach my $srv (@{$::master}, @{$::slave}) { - push(@args,{ - pid => 0, # We don't know the PID - pidfile => $::master->[$idx]->{'path_mypid'}, - sockfile => $::master->[$idx]->{'path_mysock'}, - port => $::master->[$idx]->{'path_myport'}, - }); + mtr_debug(" - mysqld " . + "(pid: $srv->{pid}; " . + "pid file: '$srv->{path_pid}'; " . + "socket: '$srv->{path_sock}'; ". + "port: $srv->{port})"); + + my $pid= mtr_mysqladmin_start($srv, "shutdown", 70); + + # Save the pid of the mysqladmin process + $admin_pids{$pid}= 1; + + push(@kill_pids,{ + pid => $srv->{'pid'}, + pidfile => $srv->{'path_pid'}, + sockfile => $srv->{'path_sock'}, + port => $srv->{'port'}, + }); + $srv->{'pid'}= 0; # Assume we are done with it } - for ( my $idx; $idx < 3; $idx++ ) + if ( ! $::opt_skip_ndbcluster ) { - push(@args,{ - pid => 0, # We don't know the PID - pidfile => $::slave->[$idx]->{'path_mypid'}, - sockfile => $::slave->[$idx]->{'path_mysock'}, - port => $::slave->[$idx]->{'path_myport'}, - }); + # Start shutdown of clusters. + mtr_debug("Shutting down cluster..."); + + foreach my $cluster (@{$::clusters}) + { + mtr_debug(" - cluster " . + "(pid: $cluster->{pid}; " . + "pid file: '$cluster->{path_pid})"); + + my $pid= mtr_ndbmgm_start($cluster, "shutdown"); + + # Save the pid of the ndb_mgm process + $admin_pids{$pid}= 1; + + push(@kill_pids,{ + pid => $cluster->{'pid'}, + pidfile => $cluster->{'path_pid'} + }); + + $cluster->{'pid'}= 0; # Assume we are done with it + + foreach my $ndbd (@{$cluster->{'ndbds'}}) + { + mtr_debug(" - ndbd " . + "(pid: $ndbd->{pid}; " . + "pid file: '$ndbd->{path_pid})"); + + push(@kill_pids,{ + pid => $ndbd->{'pid'}, + pidfile => $ndbd->{'path_pid'}, + }); + $ndbd->{'pid'}= 0; # Assume we are done with it + } + } } - mtr_mysqladmin_shutdown(\@args, 20); + # Wait for all the admin processes to complete + mtr_wait_blocking(\%admin_pids); + + # If we trusted "mysqladmin --shutdown_timeout= ..." we could just + # terminate now, but we don't (FIXME should be debugged). + # So we try again to ping and at least wait the same amount of time + # mysqladmin would for all to die. + + mtr_ping_with_timeout(\@kill_pids); # We now have tried to terminate nice. We have waited for the listen # port to be free, but can't really tell if the mysqld process died @@ -401,6 +441,8 @@ sub mtr_kill_leftovers () { # FIXME $path_run_dir or something my $rundir= "$::opt_vardir/run"; + mtr_debug("Processing PID files in directory '$rundir'..."); + if ( -d $rundir ) { opendir(RUNDIR, $rundir) @@ -414,26 +456,32 @@ sub mtr_kill_leftovers () { if ( -f $pidfile ) { + mtr_debug("Processing PID file: '$pidfile'..."); + my $pid= mtr_get_pid_from_file($pidfile); - # Race, could have been removed between I tested with -f - # and the unlink() below, so I better check again with -f - - if ( ! unlink($pidfile) and -f $pidfile ) - { - mtr_error("can't remove $pidfile"); - } + mtr_debug("Got pid: $pid from file '$pidfile'"); if ( $::glob_cygwin_perl or kill(0, $pid) ) { + mtr_debug("There is process with pid $pid -- scheduling for kill."); push(@pids, $pid); # We know (cygwin guess) it exists } + else + { + mtr_debug("There is no process with pid $pid -- skipping."); + } } } closedir(RUNDIR); if ( @pids ) { + mtr_debug("Killing the following processes with PID files: " . + join(' ', @pids) . "..."); + + start_reap_all(); + if ( $::glob_cygwin_perl ) { # We have no (easy) way of knowing the Cygwin controlling @@ -447,8 +495,9 @@ sub mtr_kill_leftovers () { my $retries= 10; # 10 seconds do { + mtr_debug("Sending SIGKILL to pids: " . join(' ', @pids)); kill(9, @pids); - mtr_debug("Sleep 1 second waiting for processes to die"); + mtr_report("Sleep 1 second waiting for processes to die"); sleep(1) # Wait one second } while ( $retries-- and kill(0, @pids) ); @@ -457,56 +506,74 @@ sub mtr_kill_leftovers () { mtr_warning("can't kill process(es) " . join(" ", @pids)); } } + + stop_reap_all(); } } + else + { + mtr_debug("Directory for PID files ($rundir) does not exist."); + } - # We may have failed everything, bug we now check again if we have + # We may have failed everything, but we now check again if we have # the listen ports free to use, and if they are free, just go for it. - foreach my $srv ( @args ) + mtr_debug("Checking known mysqld servers..."); + + foreach my $srv ( @kill_pids ) { - if ( mtr_ping_mysqld_server($srv->{'port'}, $srv->{'sockfile'}) ) + if ( defined $srv->{'port'} and mtr_ping_port($srv->{'port'}) ) { - mtr_warning("can't kill old mysqld holding port $srv->{'port'}"); + mtr_warning("can't kill old process holding port $srv->{'port'}"); } } + + mtr_debug("mtr_kill_leftovers(): finished."); } -############################################################################## -# -# Shut down mysqld servers we have started from this run of this script -# -############################################################################## - -# To speed things we kill servers in parallel. The argument is a list -# of 'ports', 'pids', 'pidfiles' and 'socketfiles'. +# Check that all processes in list are killed +# The argument is a list of 'ports', 'pids', 'pidfiles' and 'socketfiles' +# for which shutdown has been started. Make sure they all get killed +# in one way or the other. +# # FIXME On Cygwin, and maybe some other platforms, $srv->{'pid'} and -# $srv->{'pidfile'} will not be the same PID. We need to try to kill +# the pid in $srv->{'pidfile'} will not be the same PID. We need to try to kill # both I think. -sub mtr_stop_mysqld_servers ($) { +sub mtr_check_stop_servers ($) { my $spec= shift; - # ---------------------------------------------------------------------- - # First try nice normal shutdown using 'mysqladmin' - # ---------------------------------------------------------------------- + # Return if no processes are defined + return if ! @$spec; - # Shutdown time must be high as slave may be in reconnect - mtr_mysqladmin_shutdown($spec, 70); + #mtr_report("mtr_check_stop_servers"); + + mtr_ping_with_timeout(\@$spec); # ---------------------------------------------------------------------- # We loop with waitpid() nonblocking to see how many of the ones we - # are to kill, actually got killed by mtr_mysqladmin_shutdown(). - # Note that we don't rely on this, the mysqld server might have stop + # are to kill, actually got killed by mysqladmin or ndb_mgm + # + # Note that we don't rely on this, the mysqld server might have stopped # listening to the port, but still be alive. But it is a start. # ---------------------------------------------------------------------- foreach my $srv ( @$spec ) { - if ( $srv->{'pid'} and (waitpid($srv->{'pid'},&WNOHANG) == $srv->{'pid'}) ) + my $ret_pid; + if ( $srv->{'pid'} ) { - $srv->{'pid'}= 0; + $ret_pid= waitpid($srv->{'pid'},&WNOHANG); + if ($ret_pid == $srv->{'pid'}) + { + mtr_verbose("Caught exit of process $ret_pid"); + $srv->{'pid'}= 0; + } + else + { + # mtr_warning("caught exit of unknown child $ret_pid"); + } } } @@ -540,13 +607,12 @@ sub mtr_stop_mysqld_servers ($) { } # ---------------------------------------------------------------------- - # If the processes where started from this script, and we had no PIDS + # If all the processes in list already have been killed, # then we don't have to do anything. # ---------------------------------------------------------------------- if ( ! keys %mysqld_pids ) { - # cluck "This is how we got here!"; return; } @@ -595,141 +661,290 @@ sub mtr_stop_mysqld_servers ($) { foreach my $file ($srv->{'pidfile'}, $srv->{'sockfile'}) { # Know it is dead so should be no race, careful anyway - if ( -f $file and ! unlink($file) and -f $file ) + if ( defined $file and -f $file and ! unlink($file) and -f $file ) { $errors++; mtr_warning("couldn't delete $file"); } } + $srv->{'pid'}= 0; } } } if ( $errors ) { - # We are in trouble, just die.... - mtr_error("we could not kill or clean up all processes"); + # There where errors killing processes + # do one last attempt to ping the servers + # and if they can't be pinged, assume they are dead + if ( ! mtr_ping_with_timeout( \@$spec ) ) + { + mtr_error("we could not kill or clean up all processes"); + } + else + { + mtr_verbose("All ports were free, continuing"); + } } } # FIXME We just assume they are all dead, for Cygwin we are not # really sure - + +} + +# Wait for all the process in the list to terminate +sub mtr_wait_blocking($) { + my $admin_pids= shift; + + + # Return if no processes defined + return if ! %$admin_pids; + + mtr_verbose("mtr_wait_blocking"); + + # Wait for all the started processes to exit + # As mysqladmin is such a simple program, we trust it to terminate itself. + # I.e. we wait blocking, and wait for them all before we go on. + foreach my $pid (keys %{$admin_pids}) + { + my $ret_pid= waitpid($pid,0); + + } +} + +# Start "mysqladmin shutdown" for a specific mysqld +sub mtr_mysqladmin_start($$$) { + my $srv= shift; + my $command= shift; + my $adm_shutdown_tmo= shift; + + my $args; + mtr_init_args(\$args); + + mtr_add_arg($args, "--no-defaults"); + mtr_add_arg($args, "--user=%s", $::opt_user); + mtr_add_arg($args, "--password="); + mtr_add_arg($args, "--silent"); + if ( -e $srv->{'path_sock'} ) + { + mtr_add_arg($args, "--socket=%s", $srv->{'path_sock'}); + } + if ( $srv->{'port'} ) + { + mtr_add_arg($args, "--port=%s", $srv->{'port'}); + } + if ( $srv->{'port'} and ! -e $srv->{'path_sock'} ) + { + mtr_add_arg($args, "--protocol=tcp"); # Needed if no --socket + } + mtr_add_arg($args, "--connect_timeout=5"); + + # Shutdown time must be high as slave may be in reconnect + mtr_add_arg($args, "--shutdown_timeout=$adm_shutdown_tmo"); + mtr_add_arg($args, "$command"); + my $path_mysqladmin_log= "$::opt_vardir/log/mysqladmin.log"; + my $pid= mtr_spawn($::exe_mysqladmin, $args, + "", $path_mysqladmin_log, $path_mysqladmin_log, "", + { append_log_file => 1 }); + mtr_verbose("mtr_mysqladmin_start, pid: $pid"); + return $pid; + +} + +# Start "ndb_mgm shutdown" for a specific cluster, it will +# shutdown all data nodes and leave the ndb_mgmd running +sub mtr_ndbmgm_start($$) { + my $cluster= shift; + my $command= shift; + + my $args; + + mtr_init_args(\$args); + + mtr_add_arg($args, "--no-defaults"); + mtr_add_arg($args, "--core"); + mtr_add_arg($args, "--try-reconnect=1"); + mtr_add_arg($args, "--ndb_connectstring=%s", $cluster->{'connect_string'}); + mtr_add_arg($args, "-e"); + mtr_add_arg($args, "$command"); + + my $pid= mtr_spawn($::exe_ndb_mgm, $args, + "", "/dev/null", "/dev/null", "", + {}); + mtr_verbose("mtr_ndbmgm_start, pid: $pid"); + return $pid; + } -############################################################################## -# -# Shut down mysqld servers using "mysqladmin ... shutdown". -# To speed this up, we start them in parallel and use waitpid() to -# catch their termination. Note that this doesn't say the servers -# are terminated, just that 'mysqladmin' is terminated. -# -# Note that mysqladmin will ask the server about what PID file it uses, -# and mysqladmin will wait for it to be removed before it terminates -# (unless passes timeout). -# -# This function will take at most about 20 seconds, and we still are not -# sure we killed them all. If none is responding to ping, we return 1, -# else we return 0. -# -############################################################################## - -sub mtr_mysqladmin_shutdown { +# Ping all servers in list, exit when none of them answers +# or when timeout has passed +sub mtr_ping_with_timeout($) { my $spec= shift; - my $adm_shutdown_tmo= shift; - - my %mysql_admin_pids; - my @to_kill_specs; - - foreach my $srv ( @$spec ) - { - if ( mtr_ping_mysqld_server($srv->{'port'}, $srv->{'sockfile'}) ) - { - push(@to_kill_specs, $srv); - } - } - - - foreach my $srv ( @to_kill_specs ) - { - # FIXME wrong log..... - # FIXME, stderr..... - # Shutdown time must be high as slave may be in reconnect - my $args; - - mtr_init_args(\$args); - - mtr_add_arg($args, "--no-defaults"); - mtr_add_arg($args, "--user=%s", $::opt_user); - mtr_add_arg($args, "--password="); - if ( -e $srv->{'sockfile'} ) - { - mtr_add_arg($args, "--socket=%s", $srv->{'sockfile'}); - } - if ( $srv->{'port'} ) - { - mtr_add_arg($args, "--port=%s", $srv->{'port'}); - } - if ( $srv->{'port'} and ! -e $srv->{'sockfile'} ) - { - mtr_add_arg($args, "--protocol=tcp"); # Needed if no --socket - } - mtr_add_arg($args, "--connect_timeout=5"); - mtr_add_arg($args, "--shutdown_timeout=$adm_shutdown_tmo"); - mtr_add_arg($args, "shutdown"); - # We don't wait for termination of mysqladmin - my $pid= mtr_spawn($::exe_mysqladmin, $args, - "", $::path_manager_log, $::path_manager_log, "", - { append_log_file => 1 }); - $mysql_admin_pids{$pid}= 1; - } - - # As mysqladmin is such a simple program, we trust it to terminate. - # I.e. we wait blocking, and wait wait for them all before we go on. - while (keys %mysql_admin_pids) - { - foreach my $pid (keys %mysql_admin_pids) - { - if ( waitpid($pid,0) > 0 ) - { - delete $mysql_admin_pids{$pid}; - } - } - } - - # If we trusted "mysqladmin --shutdown_timeout= ..." we could just - # terminate now, but we don't (FIXME should be debugged). - # So we try again to ping and at least wait the same amount of time - # mysqladmin would for all to die. - - my $timeout= 20; # 20 seconds max + my $timeout= 200; # 20 seconds max my $res= 1; # If we just fall through, we are done # in the sense that the servers don't # listen to their ports any longer + + mtr_debug("Waiting for mysqld servers to stop..."); + TIME: while ( $timeout-- ) { - foreach my $srv ( @to_kill_specs ) + foreach my $srv ( @$spec ) { $res= 1; # We are optimistic - if ( mtr_ping_mysqld_server($srv->{'port'}, $srv->{'sockfile'}) ) + if ( $srv->{'pid'} and defined $srv->{'port'} ) { - mtr_debug("Sleep 1 second waiting for processes to stop using port"); - sleep(1); # One second - $res= 0; - next TIME; + if ( mtr_ping_port($srv->{'port'}) ) + { + mtr_verbose("waiting for process $srv->{'pid'} to stop ". + "using port $srv->{'port'}"); + + # Millisceond sleep emulated with select + select(undef, undef, undef, (0.1)); + $res= 0; + next TIME; + } + else + { + # Process was not using port + } } } last; # If we got here, we are done } - $timeout or mtr_debug("At least one server is still listening to its port"); - - sleep(5) if $::glob_win32; # FIXME next startup fails if no sleep + if ($res) + { + mtr_debug("mtr_ping_with_timeout(): All mysqld instances are down."); + } + else + { + mtr_report("mtr_ping_with_timeout(): At least one server is alive."); + } return $res; } + +# +# Loop through our list of processes and look for and entry +# with the provided pid +# Set the pid of that process to 0 if found +# +sub mark_process_dead($) +{ + my $ret_pid= shift; + + foreach my $mysqld (@{$::master}, @{$::slave}) + { + if ( $mysqld->{'pid'} eq $ret_pid ) + { + mtr_verbose("$mysqld->{'type'} $mysqld->{'idx'} exited, pid: $ret_pid"); + $mysqld->{'pid'}= 0; + return; + } + } + + foreach my $cluster (@{$::clusters}) + { + if ( $cluster->{'pid'} eq $ret_pid ) + { + mtr_verbose("$cluster->{'name'} cluster ndb_mgmd exited, pid: $ret_pid"); + $cluster->{'pid'}= 0; + return; + } + + foreach my $ndbd (@{$cluster->{'ndbds'}}) + { + if ( $ndbd->{'pid'} eq $ret_pid ) + { + mtr_verbose("$cluster->{'name'} cluster ndbd exited, pid: $ret_pid"); + $ndbd->{'pid'}= 0; + return; + } + } + } + mtr_warning("mark_process_dead couldn't find an entry for pid: $ret_pid"); + +} + +# +# Loop through our list of processes and look for and entry +# with the provided pid, if found check for the file indicating +# expected crash and restart it. +# +sub check_expected_crash_and_restart($) +{ + my $ret_pid= shift; + + foreach my $mysqld (@{$::master}, @{$::slave}) + { + if ( $mysqld->{'pid'} eq $ret_pid ) + { + mtr_verbose("$mysqld->{'type'} $mysqld->{'idx'} exited, pid: $ret_pid"); + $mysqld->{'pid'}= 0; + + # Check if crash expected and restart if it was + my $expect_file= "$::opt_vardir/tmp/" . "$mysqld->{'type'}" . + "$mysqld->{'idx'}" . ".expect"; + if ( -f $expect_file ) + { + mtr_verbose("Crash was expected, file $expect_file exists"); + mysqld_start($mysqld, $mysqld->{'start_opts'}, + $mysqld->{'start_slave_master_info'}); + unlink($expect_file); + } + + return; + } + } + + foreach my $cluster (@{$::clusters}) + { + if ( $cluster->{'pid'} eq $ret_pid ) + { + mtr_verbose("$cluster->{'name'} cluster ndb_mgmd exited, pid: $ret_pid"); + $cluster->{'pid'}= 0; + + # Check if crash expected and restart if it was + my $expect_file= "$::opt_vardir/tmp/ndb_mgmd_" . "$cluster->{'type'}" . + ".expect"; + if ( -f $expect_file ) + { + mtr_verbose("Crash was expected, file $expect_file exists"); + ndbmgmd_start($cluster); + unlink($expect_file); + } + return; + } + + foreach my $ndbd (@{$cluster->{'ndbds'}}) + { + if ( $ndbd->{'pid'} eq $ret_pid ) + { + mtr_verbose("$cluster->{'name'} cluster ndbd exited, pid: $ret_pid"); + $ndbd->{'pid'}= 0; + + # Check if crash expected and restart if it was + my $expect_file= "$::opt_vardir/tmp/ndbd_" . "$cluster->{'type'}" . + "$ndbd->{'idx'}" . ".expect"; + if ( -f $expect_file ) + { + mtr_verbose("Crash was expected, file $expect_file exists"); + ndbd_start($cluster, $ndbd->{'idx'}, + $ndbd->{'start_extra_args'}); + unlink($expect_file); + } + return; + } + } + } + mtr_warning("check_expected_crash_and_restart couldn't find an entry for pid: $ret_pid"); + +} + ############################################################################## # # The operating system will keep information about dead children, @@ -740,32 +955,18 @@ sub mtr_mysqladmin_shutdown { sub mtr_record_dead_children () { + my $process_died= 0; my $ret_pid; - # FIXME the man page says to wait for -1 to terminate, - # but on OS X we get '0' all the time... - while ( ($ret_pid= waitpid(-1,&WNOHANG)) > 0 ) + # Wait without blockinng to see if any processes had died + # -1 or 0 means there are no more procesess to wait for + while ( ($ret_pid= waitpid(-1,&WNOHANG)) != 0 and $ret_pid != -1) { - mtr_debug("waitpid() catched exit of child $ret_pid"); - foreach my $idx (0..1) - { - if ( $::master->[$idx]->{'pid'} eq $ret_pid ) - { - mtr_debug("child $ret_pid was master[$idx]"); - $::master->[$idx]->{'pid'}= 0; - } - } - - foreach my $idx (0..2) - { - if ( $::slave->[$idx]->{'pid'} eq $ret_pid ) - { - mtr_debug("child $ret_pid was slave[$idx]"); - $::slave->[$idx]->{'pid'}= 0; - last; - } - } + mtr_warning("mtr_record_dead_children: $ret_pid"); + mark_process_dead($ret_pid); + $process_died= 1; } + return $process_died; } sub start_reap_all { @@ -777,16 +978,24 @@ sub start_reap_all { # here. If a process terminated before setting $SIG{CHLD} (but after # any attempt to waitpid() it), it will still be a zombie. So we # have to handle any such process here. - while(waitpid(-1, &WNOHANG) > 0) { }; + my $pid; + while(($pid= waitpid(-1, &WNOHANG)) != 0 and $pid != -1) + { + mtr_warning("start_reap_all pid: $pid"); + mark_process_dead($pid); + }; } sub stop_reap_all { $SIG{CHLD}= 'DEFAULT'; } -sub mtr_ping_mysqld_server () { + +sub mtr_ping_port ($) { my $port= shift; + mtr_verbose("mtr_ping_port: $port"); + my $remote= "localhost"; my $iaddr= inet_aton($remote); if ( ! $iaddr ) @@ -799,13 +1008,18 @@ sub mtr_ping_mysqld_server () { { mtr_error("can't create socket: $!"); } + + mtr_debug("Pinging server (port: $port)..."); + if ( connect(SOCK, $paddr) ) { close(SOCK); # FIXME check error? + mtr_verbose("USED"); return 1; } else { + mtr_verbose("FREE"); return 0; } } @@ -822,30 +1036,36 @@ sub sleep_until_file_created ($$$) { my $pidfile= shift; my $timeout= shift; my $pid= shift; + my $sleeptime= 100; # Milliseconds + my $loops= ($timeout * 1000) / $sleeptime; - for ( my $loop= 1; $loop <= $timeout; $loop++ ) + for ( my $loop= 1; $loop <= $loops; $loop++ ) { if ( -r $pidfile ) { return $pid; } - # Check if it died after the fork() was successful - if ( waitpid($pid,&WNOHANG) == $pid ) + # Check if it died after the fork() was successful + if ( $pid != 0 && waitpid($pid,&WNOHANG) == $pid ) { + mtr_warning("Process $pid died"); return 0; } - mtr_debug("Sleep 1 second waiting for creation of $pidfile"); + mtr_debug("Sleep $sleeptime milliseconds waiting for $pidfile"); - if ( $loop % 60 == 0 ) + # Print extra message every 60 seconds + my $seconds= ($loop * $sleeptime) / 1000; + if ( $seconds > 1 and int($seconds) % 60 == 0 ) { - my $left= $timeout - $loop; - mtr_warning("Waited $loop seconds for $pidfile to be created, " . + my $left= $timeout - $seconds; + mtr_warning("Waited $seconds seconds for $pidfile to be created, " . "still waiting for $left seconds..."); } - sleep(1); + # Millisceond sleep emulated with select + select(undef, undef, undef, ($sleeptime/1000)); } return 0; @@ -855,18 +1075,18 @@ sub sleep_until_file_created ($$$) { sub mtr_kill_processes ($) { my $pids = shift; - foreach my $sig (15,9) + mtr_verbose("mtr_kill_processes " . join(" ", @$pids)); + + foreach my $pid (@$pids) { - my $retries= 20; # FIXME 20 seconds, this is silly! - kill($sig, @{$pids}); - while ( $retries-- and kill(0, @{$pids}) ) + foreach my $sig (15, 9) { - mtr_debug("Sleep 1 second waiting for processes to die"); - sleep(1) # Wait one second + last if mtr_im_kill_process([ $pid ], $sig, 10, 1); } } } + ############################################################################## # # When we exit, we kill off all children @@ -876,7 +1096,7 @@ sub mtr_kill_processes ($) { # FIXME something is wrong, we sometimes terminate with "Hangup" written # to tty, and no STDERR output telling us why. -# FIXME for some readon, setting HUP to 'IGNORE' will cause exit() to +# FIXME for some reason, setting HUP to 'IGNORE' will cause exit() to # write out "Hangup", and maybe loose some output. We insert a sleep... sub mtr_exit ($) { @@ -884,9 +1104,18 @@ sub mtr_exit ($) { # cluck("Called mtr_exit()"); mtr_timer_stop_all($::glob_timers); local $SIG{HUP} = 'IGNORE'; - kill('HUP', -$$); - sleep 2; + # ToDo: Signalling -$$ will only work if we are the process group + # leader (in fact on QNX it will signal our session group leader, + # which might be Do-compile or Pushbuild, causing tests to be + # aborted). So we only do it if we are the group leader. We might + # set ourselves as the group leader at startup (with + # POSIX::setpgrp(0,0)), but then care must be needed to always do + # proper child process cleanup. + kill('HUP', -$$) if !$::glob_win32_perl and $$ == getpgrp(); + exit($code); } +########################################################################### + 1; diff --git a/mysql-test/lib/mtr_report.pl b/mysql-test/lib/mtr_report.pl index 515988ee5c7..6e3796133f2 100644 --- a/mysql-test/lib/mtr_report.pl +++ b/mysql-test/lib/mtr_report.pl @@ -10,6 +10,7 @@ sub mtr_report_test_name($); sub mtr_report_test_passed($); sub mtr_report_test_failed($); sub mtr_report_test_skipped($); +sub mtr_report_test_not_skipped_though_disabled($); sub mtr_show_failed_diff ($); sub mtr_report_stats ($); @@ -21,6 +22,7 @@ sub mtr_warning (@); sub mtr_error (@); sub mtr_child_error (@); sub mtr_debug (@); +sub mtr_verbose (@); ############################################################################## @@ -36,6 +38,7 @@ sub mtr_show_failed_diff ($) { my $reject_file= "r/$tname.reject"; my $result_file= "r/$tname.result"; + my $log_file= "r/$tname.log"; my $eval_file= "r/$tname.eval"; if ( $::opt_suite ne "main" ) @@ -43,10 +46,11 @@ sub mtr_show_failed_diff ($) { $reject_file= "$::glob_mysql_test_dir/suite/$::opt_suite/$reject_file"; $result_file= "$::glob_mysql_test_dir/suite/$::opt_suite/$result_file"; $eval_file= "$::glob_mysql_test_dir/suite/$::opt_suite/$eval_file"; + $log_file= "$::glob_mysql_test_dir/suite/$::opt_suite/$log_file"; } if ( -f $eval_file ) - { + { $result_file= $eval_file; } elsif ( $::opt_result_ext and @@ -70,6 +74,12 @@ sub mtr_show_failed_diff ($) { print "http://www.mysql.com/doc/en/Reporting_mysqltest_bugs.html\n"; print "to find the reason to this problem and how to report this.\n\n"; } + + if ( -f $log_file ) + { + print "Result from queries before failure can be found in $log_file\n"; + # FIXME Maybe a tail -f -n 10 $log_file here + } } sub mtr_report_test_name ($) { @@ -88,7 +98,24 @@ sub mtr_report_test_skipped ($) { } else { - print "[ skipped ]\n"; + print "[ skipped ] $tinfo->{'comment'}\n"; + } +} + +sub mtr_report_tests_not_skipped_though_disabled ($) { + my $tests= shift; + + if ( $::opt_enable_disabled ) + { + my @disabled_tests= grep {$_->{'dont_skip_though_disabled'}} @$tests; + if ( @disabled_tests ) + { + print "\nTest(s) which will be run though they are marked as disabled:\n"; + foreach my $tinfo ( sort {$a->{'name'} cmp $b->{'name'}} @disabled_tests ) + { + printf " %-20s : %s\n", $tinfo->{'name'}, $tinfo->{'comment'}; + } + } } } @@ -99,7 +126,7 @@ sub mtr_report_test_passed ($) { if ( $::opt_timer and -f "$::opt_vardir/log/timer" ) { $timer= mtr_fromfile("$::opt_vardir/log/timer"); - $::glob_tot_real_time += $timer; + $::glob_tot_real_time += ($timer/1000); $timer= sprintf "%12s", $timer; } $tinfo->{'result'}= 'MTR_RES_PASSED'; @@ -114,6 +141,11 @@ sub mtr_report_test_failed ($) { { print "[ fail ] timeout\n"; } + elsif ( $tinfo->{'ndb_test'} and $::cluster->[0]->{'installed_ok'} eq "NO") + { + print "[ fail ] ndbcluster start failure\n"; + return; + } else { print "[ fail ]\n"; @@ -144,6 +176,8 @@ sub mtr_report_stats ($) { my $tot_passed= 0; my $tot_failed= 0; my $tot_tests= 0; + my $tot_restarts= 0; + my $found_problems= 0; # Some warnings are errors... foreach my $tinfo (@$tests) { @@ -161,6 +195,10 @@ sub mtr_report_stats ($) { $tot_tests++; $tot_failed++; } + if ( $tinfo->{'restarted'} ) + { + $tot_restarts++; + } } # ---------------------------------------------------------------------- @@ -183,41 +221,69 @@ sub mtr_report_stats ($) { "the documentation at\n", "http://www.mysql.com/doc/en/MySQL_test_suite.html\n"; } + print + "The servers were restarted $tot_restarts times\n"; + + if ( $::opt_timer ) + { + print + "Spent $::glob_tot_real_time seconds actually executing testcases\n" + } # ---------------------------------------------------------------------- + # If a debug run, there might be interesting information inside + # the "var/log/*.err" files. We save this info in "var/log/warnings" # ---------------------------------------------------------------------- if ( ! $::glob_use_running_server ) { + # Save and report if there was any fatal warnings/errors in err logs - # Report if there was any fatal warnings/errors in the log files - # - unlink("$::opt_vardir/log/warnings"); - unlink("$::opt_vardir/log/warnings.tmp"); - # Remove some non fatal warnings from the log files + my $warnlog= "$::opt_vardir/log/warnings"; -# FIXME what is going on ????? ;-) -# sed -e 's!Warning: Table:.* on delete!!g' -e 's!Warning: Setting lower_case_table_names=2!!g' -e 's!Warning: One can only use the --user.*root!!g' \ -# var/log/*.err \ -# | sed -e 's!Warning: Table:.* on rename!!g' \ -# > var/log/warnings.tmp; -# -# found_error=0; -# # Find errors -# for i in "^Warning:" "^Error:" "^==.* at 0x" -# do -# if ( $GREP "$i" var/log/warnings.tmp >> var/log/warnings ) -# { -# found_error=1 -# } -# done -# unlink("$::opt_vardir/log/warnings.tmp"); -# if ( $found_error= "1" ) -# { -# print "WARNING: Got errors/warnings while running tests. Please examine\n" -# print "$::opt_vardir/log/warnings for details.\n" -# } -# } + unless ( open(WARN, ">$warnlog") ) + { + mtr_warning("can't write to the file \"$warnlog\": $!"); + } + else + { + # We report different types of problems in order + foreach my $pattern ( "^Warning:", "^Error:", "^==.* at 0x", + "InnoDB: Warning", "missing DBUG_RETURN", + "mysqld: Warning", + "Attempting backtrace", "Assertion .* failed" ) + { + foreach my $errlog ( sort glob("$::opt_vardir/log/*.err") ) + { + unless ( open(ERR, $errlog) ) + { + mtr_warning("can't read $errlog"); + next; + } + while ( ) + { + # Skip some non fatal warnings from the log files + if ( /Warning:\s+Table:.* on (delete|rename)/ or + /Warning:\s+Setting lower_case_table_names=2/ or + /Warning:\s+One can only use the --user.*root/ or + /InnoDB: Warning: we did not need to do crash recovery/) + { + next; # Skip these lines + } + if ( /$pattern/ ) + { + $found_problems= 1; + print WARN $_; + } + } + } + } + if ( $found_problems ) + { + mtr_warning("Got errors/warnings while running tests, please examine", + "\"$warnlog\" for details."); + } + } } print "\n"; @@ -235,6 +301,9 @@ sub mtr_report_stats ($) { } } print "\n"; + } + if ( $tot_failed != 0 || $found_problems) + { mtr_error("there where failing test cases"); } } @@ -298,5 +367,11 @@ sub mtr_debug (@) { print STDERR "####: ",join(" ", @_),"\n"; } } +sub mtr_verbose (@) { + if ( $::opt_verbose ) + { + print STDERR "> ",join(" ", @_),"\n"; + } +} 1; diff --git a/mysql-test/lib/mtr_stress.pl b/mysql-test/lib/mtr_stress.pl new file mode 100644 index 00000000000..a7d4b68b69d --- /dev/null +++ b/mysql-test/lib/mtr_stress.pl @@ -0,0 +1,178 @@ +# -*- cperl -*- + +# This is a library file used by the Perl version of mysql-test-run, +# and is part of the translation of the Bourne shell script with the +# same name. + +use strict; +use File::Spec; + +# These are not to be prefixed with "mtr_" + +sub run_stress_test (); + +############################################################################## +# +# Run tests in the stress mode +# +############################################################################## + +sub run_stress_test () +{ + + my $args; + my $stress_suitedir; + + mtr_report("Starting stress testing\n"); + + if ( ! $::glob_use_embedded_server ) + { + if ( ! mysqld_start($::master->[0],[],[]) ) + { + mtr_error("Can't start the mysqld server"); + } + } + + my $stress_basedir=File::Spec->catdir($::opt_vardir, "stress"); + + #Clean up stress dir + if ( -d $stress_basedir ) + { + rmtree($stress_basedir); + } + mkpath($stress_basedir); + + if ($::opt_stress_suite ne 'main' && $::opt_stress_suite ne 'default' ) + { + $stress_suitedir=File::Spec->catdir($::glob_mysql_test_dir, "suite", + $::opt_stress_suite); + } + else + { + $stress_suitedir=$::glob_mysql_test_dir; + } + + if ( -d $stress_suitedir ) + { + #$stress_suite_t_dir=File::Spec->catdir($stress_suitedir, "t"); + #$stress_suite_r_dir=File::Spec->catdir($stress_suitedir, "r"); + #FIXME: check dirs above for existence to ensure that test suite + # contains tests and results dirs + } + else + { + mtr_error("Specified test suite $::opt_stress_suite doesn't exist"); + } + + if ( @::opt_cases ) + { + $::opt_stress_test_file=File::Spec->catfile($stress_basedir, "stress_tests.txt"); + open(STRESS_FILE, ">$::opt_stress_test_file"); + print STRESS_FILE join("\n",@::opt_cases),"\n"; + close(STRESS_FILE); + } + elsif ( $::opt_stress_test_file ) + { + $::opt_stress_test_file=File::Spec->catfile($stress_suitedir, + $::opt_stress_test_file); + if ( ! -f $::opt_stress_test_file ) + { + mtr_error("Specified file $::opt_stress_test_file with list of tests does not exist\n", + "Please ensure that file exists and has proper permissions"); + } + } + else + { + $::opt_stress_test_file=File::Spec->catfile($stress_suitedir, + "stress_tests.txt"); + if ( ! -f $::opt_stress_test_file ) + { + mtr_error("Default file $::opt_stress_test_file with list of tests does not exist\n", + "Please use --stress-test-file option to specify custom one or you can\n", + "just specify name of test for testing as last argument in command line"); + + } + } + + if ( $::opt_stress_init_file ) + { + $::opt_stress_init_file=File::Spec->catfile($stress_suitedir, + $::opt_stress_init_file); + if ( ! -f $::opt_stress_init_file ) + { + mtr_error("Specified file $::opt_stress_init_file with list of tests does not exist\n", + "Please ensure that file exists and has proper permissions"); + } + } + else + { + $::opt_stress_init_file=File::Spec->catfile($stress_suitedir, + "stress_init.txt"); + if ( ! -f $::opt_stress_init_file ) + { + $::opt_stress_init_file=''; + } + } + + if ( $::opt_stress_mode ne 'random' && $::opt_stress_mode ne 'seq' ) + { + mtr_error("You specified wrong mode $::opt_stress_mode for stress test\n", + "Correct values are 'random' or 'seq'"); + } + + mtr_init_args(\$args); + + mtr_add_arg($args, "--server-socket=%s", $::master->[0]->{'path_mysock'}); + mtr_add_arg($args, "--server-user=%s", $::opt_user); + mtr_add_arg($args, "--server-database=%s", "test"); + mtr_add_arg($args, "--stress-suite-basedir=%s", $::glob_mysql_test_dir); + mtr_add_arg($args, "--suite=%s", $::opt_stress_suite); + mtr_add_arg($args, "--stress-tests-file=%s", $::opt_stress_test_file); + mtr_add_arg($args, "--stress-basedir=%s", $stress_basedir); + mtr_add_arg($args, "--server-logs-dir=%s", $stress_basedir); + mtr_add_arg($args, "--stress-mode=%s", $::opt_stress_mode); + mtr_add_arg($args, "--mysqltest=%s", $::exe_mysqltest); + mtr_add_arg($args, "--threads=%s", $::opt_stress_threads); + mtr_add_arg($args, "--verbose"); + mtr_add_arg($args, "--cleanup"); + mtr_add_arg($args, "--log-error-details"); + mtr_add_arg($args, "--abort-on-error"); + + if ( $::opt_stress_init_file ) + { + mtr_add_arg($args, "--stress-init-file=%", $::opt_stress_init_file); + } + + if ( !$::opt_stress_loop_count && !$::opt_stress_test_count && + !$::opt_stress_test_duration ) + { + #Limit stress testing with 20 loops in case when any limit parameter + #was specified + $::opt_stress_test_count=20; + } + + if ( $::opt_stress_loop_count ) + { + mtr_add_arg($args, "--loop-count=%s", $::opt_stress_loop_count); + } + + if ( $::opt_stress_test_count ) + { + mtr_add_arg($args, "--test-count=%s", $::opt_stress_test_count); + } + + if ( $::opt_stress_test_duration ) + { + mtr_add_arg($args, "--test-duration=%s", $::opt_stress_test_duration); + } + + #Run stress test + mtr_run("$::glob_mysql_test_dir/mysql-stress-test.pl", $args, "", "", "", ""); + + if ( ! $::glob_use_embedded_server ) + { + stop_masters(); + } +} + +1; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index d1e049ff883..39c116a3fcb 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -66,10 +66,6 @@ # "http://www.plover.com/~mjd/perl/Trace/" and run this script like # "perl -d:Trace mysql-test-run.pl" # -# FIXME Save a PID file from this code as well, to record the process -# id we think it has. In Cygwin, a fork creates one Cygwin process, -# and then the real Win32 process. Cygwin Perl can only kill Cygwin -# processes. And "mysqld --bootstrap ..." doesn't save a PID file. $Devel::Trace::TRACE= 0; # Don't trace boring init stuff @@ -80,14 +76,19 @@ use File::Copy; use Cwd; use Getopt::Long; use Sys::Hostname; -#use Carp; use IO::Socket; use IO::Socket::INET; use Data::Dumper; use strict; #use diagnostics; +our $glob_win32_perl= ($^O eq "MSWin32"); # ActiveState Win32 Perl +our $glob_cygwin_perl= ($^O eq "cygwin"); # Cygwin Perl +our $glob_win32= ($glob_win32_perl or $glob_cygwin_perl); +our $glob_netware= ($^O eq "NetWare"); # NetWare + require "lib/mtr_cases.pl"; +require "lib/mtr_im.pl"; require "lib/mtr_process.pl"; require "lib/mtr_timer.pl"; require "lib/mtr_io.pl"; @@ -97,6 +98,7 @@ require "lib/mtr_report.pl"; require "lib/mtr_diff.pl"; require "lib/mtr_match.pl"; require "lib/mtr_misc.pl"; +require "lib/mtr_stress.pl"; $Devel::Trace::TRACE= 1; @@ -128,11 +130,7 @@ our @mysqld_src_dirs= # structs. We let each struct be a separate hash. # Misc global variables - -our $glob_win32= 0; # OS and native Win32 executables -our $glob_win32_perl= 0; # ActiveState Win32 Perl -our $glob_cygwin_perl= 0; # Cygwin Perl -our $glob_cygwin_shell= undef; +our $mysql_version_id; our $glob_mysql_test_dir= undef; our $glob_mysql_bench_dir= undef; our $glob_hostname= undef; @@ -140,32 +138,30 @@ our $glob_scriptname= undef; our $glob_timers= undef; our $glob_use_running_server= 0; our $glob_use_running_ndbcluster= 0; +our $glob_use_running_ndbcluster_slave= 0; our $glob_use_embedded_server= 0; our @glob_test_mode; our $glob_basedir; -# The total result - our $path_charsetsdir; our $path_client_bindir; our $path_language; our $path_timefile; -our $path_manager_log; # Used by mysqldadmin -our $path_slave_load_tmpdir; # What is this?! +our $path_snapshot; our $path_mysqltest_log; +our $path_current_test_log; our $path_my_basedir; + our $opt_vardir; # A path but set directly on cmd line +our $path_vardir_trace; # unix formatted opt_vardir for trace files our $opt_tmpdir; # A path but set directly on cmd line our $opt_usage; our $opt_suite; -our $opt_netware; - our $opt_script_debug= 0; # Script debugging, enable with --script-debug - -# Options FIXME not all.... +our $opt_verbose= 0; # Verbose output, enable with --verbose our $exe_master_mysqld; our $exe_mysql; @@ -173,13 +169,21 @@ our $exe_mysqladmin; our $exe_mysqlbinlog; our $exe_mysql_client_test; our $exe_mysqld; -our $exe_mysqldump; # Called from test case -our $exe_mysqlimport; # Called from test case -our $exe_mysqlshow; # Called from test case +our $exe_mysqlcheck; +our $exe_mysqldump; +our $exe_mysqlslap; +our $exe_mysqlimport; +our $exe_mysqlshow; our $exe_mysql_fix_system_tables; our $exe_mysqltest; +our $exe_ndbd; +our $exe_ndb_mgmd; our $exe_slave_mysqld; +our $exe_im; our $exe_my_print_defaults; +our $exe_perror; +our $lib_udf_example; +our $exe_libtool; our $opt_bench= 0; our $opt_small_bench= 0; @@ -187,10 +191,15 @@ our $opt_big_test= 0; # Send --big-test to mysqltest our @opt_extra_mysqld_opt; -our $opt_comment; our $opt_compress; -our $opt_current_test; -our $opt_ddd; +our $opt_ssl; +our $opt_skip_ssl; +our $opt_ssl_supported; +our $opt_ps_protocol; +our $opt_sp_protocol; +our $opt_cursor_protocol; +our $opt_view_protocol; + our $opt_debug; our $opt_do_test; our @opt_cases; # The test cases names in argv @@ -198,53 +207,60 @@ our $opt_embedded_server; our $opt_extern; our $opt_fast; our $opt_force; -our $opt_reorder; +our $opt_reorder= 0; +our $opt_enable_disabled; our $opt_gcov; our $opt_gcov_err; our $opt_gcov_msg; +our $glob_debugger= 0; our $opt_gdb; our $opt_client_gdb; +our $opt_ddd; +our $opt_client_ddd; our $opt_manual_gdb; +our $opt_manual_ddd; +our $opt_manual_debug; +our $opt_debugger; +our $opt_client_debugger; our $opt_gprof; our $opt_gprof_dir; our $opt_gprof_master; our $opt_gprof_slave; -our $opt_local; -our $opt_local_master; - -our $master; # Will be struct in C +our $master; our $slave; +our $clusters; + +our $instance_manager; our $opt_ndbcluster_port; our $opt_ndbconnectstring; - -our $opt_no_manager; # Does nothing now, we never use manager -our $opt_manager_port; # Does nothing now, we never use manager - -our $opt_old_master; +our $opt_ndbcluster_port_slave; +our $opt_ndbconnectstring_slave; our $opt_record; +our $opt_check_testcases; our $opt_result_ext; our $opt_skip; our $opt_skip_rpl; -our $opt_skip_im; # --skip-im on command line will just be ignored +our $max_slave_num= 0; +our $use_innodb; our $opt_skip_test; +our $opt_skip_im; our $opt_sleep; -our $opt_ps_protocol; our $opt_sleep_time_after_restart= 1; our $opt_sleep_time_for_delete= 10; our $opt_testcase_timeout; our $opt_suite_timeout; my $default_testcase_timeout= 15; # 15 min max -my $default_suite_timeout= 120; # 2 hours max +my $default_suite_timeout= 180; # 3 hours max our $opt_socket; @@ -256,18 +272,28 @@ our $opt_start_from; our $opt_strace_client; -our $opt_timer; +our $opt_timer= 1; our $opt_user; our $opt_user_test; -our $opt_valgrind; -our $opt_valgrind_mysqld; -our $opt_valgrind_mysqltest; -our $opt_valgrind_all; +our $opt_valgrind= 0; +our $opt_valgrind_mysqld= 0; +our $opt_valgrind_mysqltest= 0; +our $default_valgrind_options= "--show-reachable=yes"; our $opt_valgrind_options; +our $opt_valgrind_path; +our $opt_callgrind; -our $opt_verbose; +our $opt_stress= ""; +our $opt_stress_suite= "main"; +our $opt_stress_mode= "random"; +our $opt_stress_threads= 5; +our $opt_stress_test_count= 0; +our $opt_stress_loop_count= 0; +our $opt_stress_test_duration= 0; +our $opt_stress_init_file= ""; +our $opt_stress_test_file= ""; our $opt_wait_for_master; our $opt_wait_for_slave; @@ -277,17 +303,30 @@ our $opt_warnings; our $opt_udiff; -our $opt_skip_ndbcluster; -our $opt_with_ndbcluster; -our $opt_with_ndbcluster_only= 0; # dummy, ignored - -our $opt_with_openssl; +our $opt_skip_ndbcluster= 0; +our $opt_skip_ndbcluster_slave= 0; +our $opt_with_ndbcluster= 0; +our $opt_with_ndbcluster_only= 0; +our $opt_ndbcluster_supported= 0; +our $opt_ndb_extra_test= 0; +our $opt_skip_master_binlog= 0; +our $opt_skip_slave_binlog= 0; our $exe_ndb_mgm; +our $exe_ndb_waiter; our $path_ndb_tools_dir; -our $path_ndb_backup_dir; -our $file_ndb_testrun_log; -our $flag_ndb_status_ok= 1; +our $path_ndb_examples_dir; +our $exe_ndb_example; +our $path_ndb_testrun_log; + +our @data_dir_lst; + +our $used_binlog_format; +our $debug_compiled_binaries; +our $glob_tot_real_time= 0; + +# Default values read from mysqld +our $default_mysqld_port; ###################################################################### # @@ -298,27 +337,36 @@ our $flag_ndb_status_ok= 1; sub main (); sub initial_setup (); sub command_line_setup (); +sub snapshot_setup (); sub executable_setup (); sub environment_setup (); sub kill_running_server (); -sub kill_and_cleanup (); -sub ndbcluster_support (); -sub ndbcluster_install (); -sub ndbcluster_start (); -sub ndbcluster_stop (); +sub cleanup_stale_files (); +sub check_ssl_support ($); +sub check_running_as_root(); +sub check_ndbcluster_support ($); +sub rm_ndbcluster_tables ($); +sub ndbcluster_start_install ($); +sub ndbcluster_start ($$); +sub ndbcluster_wait_started ($$); +sub mysqld_wait_started($); sub run_benchmarks ($); -sub run_tests (); +sub initialize_servers (); sub mysql_install_db (); sub install_db ($$); +sub copy_install_db ($$); sub run_testcase ($); +sub run_testcase_stop_servers ($$$); +sub run_testcase_start_servers ($); +sub run_testcase_check_skip_test($); sub report_failure_and_restart ($); sub do_before_start_master ($$); sub do_before_start_slave ($$); -sub mysqld_start ($$$$); +sub ndbd_start ($$$); +sub ndb_mgmd_start ($); +sub mysqld_start ($$$); sub mysqld_arguments ($$$$$); -sub stop_masters_slaves (); -sub stop_masters (); -sub stop_slaves (); +sub stop_all_servers (); sub run_mysqltest ($); sub usage ($); @@ -335,12 +383,6 @@ sub main () { initial_setup(); command_line_setup(); executable_setup(); - - if (! $opt_skip_ndbcluster and ! $opt_with_ndbcluster) - { - $opt_with_ndbcluster= ndbcluster_support(); - } - environment_setup(); signal_setup(); @@ -354,44 +396,44 @@ sub main () { gprof_prepare(); } - if ( ! $glob_use_running_server ) - { - if ( $opt_start_dirty ) - { - kill_running_server(); - } - else - { - kill_and_cleanup(); - mysql_install_db(); - -# mysql_loadstd(); FIXME copying from "std_data" .frm and -# .MGR but there are none?! - } - } - - if ( $opt_start_dirty ) - { - if ( ndbcluster_start() ) - { - mtr_error("Can't start ndbcluster"); - } - if ( mysqld_start('master',0,[],[]) ) - { - mtr_report("Servers started, exiting"); - } - else - { - mtr_error("Can't start the mysqld server"); - } - } - elsif ( $opt_bench ) + if ( $opt_bench ) { + initialize_servers(); run_benchmarks(shift); # Shift what? Extra arguments?! } + elsif ( $opt_stress ) + { + initialize_servers(); + run_stress_test() + } else { - run_tests(); + # Figure out which tests we are going to run + my $tests= collect_test_cases($opt_suite); + + # Turn off NDB and other similar options if no tests use it + my ($need_ndbcluster,$need_im); + foreach my $test (@$tests) + { + $need_ndbcluster||= $test->{ndb_test}; + $need_im||= $test->{component_id} eq 'im'; + + # Count max number of slaves used by a test case + if ( $test->{slave_num} > $max_slave_num) + { + $max_slave_num= $test->{slave_num}; + mtr_error("Too many slaves") if $max_slave_num > 3; + } + $use_innodb||= $test->{'innodb_test'}; + } + $opt_skip_ndbcluster= $opt_skip_ndbcluster_slave= 1 + unless $need_ndbcluster; + $opt_skip_im= 1 unless $need_im; + + snapshot_setup(); + initialize_servers(); + + run_suite($opt_suite, $tests); } mtr_exit(0); @@ -410,13 +452,8 @@ sub initial_setup () { $glob_scriptname= basename($0); - $glob_win32_perl= ($^O eq "MSWin32"); - $glob_cygwin_perl= ($^O eq "cygwin"); - $glob_win32= ($glob_win32_perl or $glob_cygwin_perl); - # We require that we are in the "mysql-test" directory # to run mysql-test-run - if (! -f $glob_scriptname) { mtr_error("Can't find the location for the mysql-test-run script\n" . @@ -437,16 +474,13 @@ sub initial_setup () { { # Windows programs like 'mysqld' needs Windows paths $glob_mysql_test_dir= `cygpath -m "$glob_mysql_test_dir"`; - my $shell= $ENV{'SHELL'} || "/bin/bash"; - $glob_cygwin_shell= `cygpath -w "$shell"`; # The Windows path c:\... chomp($glob_mysql_test_dir); - chomp($glob_cygwin_shell); } $glob_basedir= dirname($glob_mysql_test_dir); - $glob_mysql_bench_dir= "$glob_basedir/mysql-bench"; # FIXME make configurable - # needs to be same length to test logging (FIXME what???) - $path_slave_load_tmpdir= "../../var/tmp"; + # Expect mysql-bench to be located adjacent to the source tree, by default + $glob_mysql_bench_dir= "$glob_basedir/../mysql-bench" + unless defined $glob_mysql_bench_dir; $path_my_basedir= $opt_source_dist ? $glob_mysql_test_dir : $glob_basedir; @@ -467,9 +501,15 @@ sub command_line_setup () { # These are defaults for things that are set on the command line $opt_suite= "main"; # Special default suite - my $opt_master_myport= 9306; - my $opt_slave_myport= 9308; - $opt_ndbcluster_port= 9350; + my $opt_comment; + + my $opt_master_myport= 9306; + my $opt_slave_myport= 9308; + $opt_ndbcluster_port= 9310; + $opt_ndbcluster_port_slave= 9311; + my $im_port= 9312; + my $im_mysqld1_port= 9313; + my $im_mysqld2_port= 9314; # # To make it easier for different devs to work on the same host, @@ -488,9 +528,13 @@ sub command_line_setup () { if ( $ENV{'MTR_BUILD_THREAD'} ) { # Up to two masters, up to three slaves - $opt_master_myport= $ENV{'MTR_BUILD_THREAD'} * 10 + 10000; # and 1 - $opt_slave_myport= $opt_master_myport + 2; # and 3 4 - $opt_ndbcluster_port= $opt_master_myport + 5; + $opt_master_myport= $ENV{'MTR_BUILD_THREAD'} * 10 + 10000; # and 1 + $opt_slave_myport= $opt_master_myport + 2; # and 3 4 + $opt_ndbcluster_port= $opt_master_myport + 5; + $opt_ndbcluster_port_slave= $opt_master_myport + 6; + $im_port= $opt_master_myport + 7; + $im_mysqld1_port= $opt_master_myport + 8; + $im_mysqld2_port= $opt_master_myport + 9; } if ( $opt_master_myport < 5001 or $opt_master_myport + 10 >= 32767 ) @@ -500,6 +544,11 @@ sub command_line_setup () { "($opt_master_myport - $opt_master_myport + 10)"); } + # This is needed for test log evaluation in "gen-build-status-page" + # in all cases where the calling tool does not log the commands + # directly before it executes them, like "make test-force-pl" in RPM builds. + print "Logging: $0 ", join(" ", @ARGV), "\n"; + # Read the command line # Note: Keep list, and the order, in sync with usage at end of this file @@ -508,42 +557,63 @@ sub command_line_setup () { # Control what engine/variation to run 'embedded-server' => \$opt_embedded_server, 'ps-protocol' => \$opt_ps_protocol, + 'sp-protocol' => \$opt_sp_protocol, + 'view-protocol' => \$opt_view_protocol, + 'cursor-protocol' => \$opt_cursor_protocol, + 'ssl|with-openssl' => \$opt_ssl, + 'skip-ssl' => \$opt_skip_ssl, + 'compress' => \$opt_compress, 'bench' => \$opt_bench, 'small-bench' => \$opt_small_bench, - 'no-manager' => \$opt_no_manager, # Currently not used # Control what test suites or cases to run 'force' => \$opt_force, 'with-ndbcluster' => \$opt_with_ndbcluster, - 'skip-ndbcluster|skip-ndb' => \$opt_skip_ndbcluster, 'with-ndbcluster-only' => \$opt_with_ndbcluster_only, + 'skip-ndbcluster|skip-ndb' => \$opt_skip_ndbcluster, + 'skip-ndbcluster-slave|skip-ndb-slave' + => \$opt_skip_ndbcluster_slave, + 'ndb-extra-test' => \$opt_ndb_extra_test, + 'skip-master-binlog' => \$opt_skip_master_binlog, + 'skip-slave-binlog' => \$opt_skip_slave_binlog, 'do-test=s' => \$opt_do_test, + 'start-from=s' => \$opt_start_from, 'suite=s' => \$opt_suite, 'skip-rpl' => \$opt_skip_rpl, - 'skip-im' => \$opt_skip_im, + 'skip-im' => \$opt_skip_im, 'skip-test=s' => \$opt_skip_test, + 'big-test' => \$opt_big_test, # Specify ports 'master_port=i' => \$opt_master_myport, 'slave_port=i' => \$opt_slave_myport, - 'ndbcluster_port=i' => \$opt_ndbcluster_port, - 'manager-port=i' => \$opt_manager_port, # Currently not used + 'ndbcluster-port|ndbcluster_port=i' => \$opt_ndbcluster_port, + 'ndbcluster-port-slave=i' => \$opt_ndbcluster_port_slave, + 'im-port=i' => \$im_port, # Instance Manager port. + 'im-mysqld1-port=i' => \$im_mysqld1_port, # Port of mysqld, controlled by IM + 'im-mysqld2-port=i' => \$im_mysqld2_port, # Port of mysqld, controlled by IM # Test case authoring 'record' => \$opt_record, + 'check-testcases' => \$opt_check_testcases, - # ??? + # Extra options used when starting mysqld 'mysqld=s' => \@opt_extra_mysqld_opt, # Run test on running server 'extern' => \$opt_extern, - 'ndbconnectstring=s' => \$opt_ndbconnectstring, + 'ndb-connectstring=s' => \$opt_ndbconnectstring, + 'ndb-connectstring-slave=s' => \$opt_ndbconnectstring_slave, # Debugging 'gdb' => \$opt_gdb, - 'manual-gdb' => \$opt_manual_gdb, 'client-gdb' => \$opt_client_gdb, + 'manual-gdb' => \$opt_manual_gdb, + 'manual-debug' => \$opt_manual_debug, 'ddd' => \$opt_ddd, + 'client-ddd' => \$opt_client_ddd, + 'debugger=s' => \$opt_debugger, + 'client-debugger=s' => \$opt_client_debugger, 'strace-client' => \$opt_strace_client, 'master-binary=s' => \$exe_master_mysqld, 'slave-binary=s' => \$exe_slave_mysqld, @@ -551,48 +621,54 @@ sub command_line_setup () { # Coverage, profiling etc 'gcov' => \$opt_gcov, 'gprof' => \$opt_gprof, - 'valgrind:s' => \$opt_valgrind, - 'valgrind-mysqltest:s' => \$opt_valgrind_mysqltest, - 'valgrind-all:s' => \$opt_valgrind_all, + 'valgrind|valgrind-all' => \$opt_valgrind, + 'valgrind-mysqltest' => \$opt_valgrind_mysqltest, + 'valgrind-mysqld' => \$opt_valgrind_mysqld, 'valgrind-options=s' => \$opt_valgrind_options, + 'valgrind-path=s' => \$opt_valgrind_path, + 'callgrind' => \$opt_callgrind, + + # Stress testing + 'stress' => \$opt_stress, + 'stress-suite=s' => \$opt_stress_suite, + 'stress-threads=i' => \$opt_stress_threads, + 'stress-test-file=s' => \$opt_stress_test_file, + 'stress-init-file=s' => \$opt_stress_init_file, + 'stress-mode=s' => \$opt_stress_mode, + 'stress-loop-count=i' => \$opt_stress_loop_count, + 'stress-test-count=i' => \$opt_stress_test_count, + 'stress-test-duration=i' => \$opt_stress_test_duration, + + # Directories + 'tmpdir=s' => \$opt_tmpdir, + 'vardir=s' => \$opt_vardir, + 'benchdir=s' => \$glob_mysql_bench_dir, # Misc - 'big-test' => \$opt_big_test, 'comment=s' => \$opt_comment, - 'compress' => \$opt_compress, 'debug' => \$opt_debug, 'fast' => \$opt_fast, - 'local' => \$opt_local, - 'local-master' => \$opt_local_master, - 'netware' => \$opt_netware, - 'old-master' => \$opt_old_master, 'reorder' => \$opt_reorder, + 'enable-disabled' => \$opt_enable_disabled, 'script-debug' => \$opt_script_debug, + 'verbose' => \$opt_verbose, 'sleep=i' => \$opt_sleep, 'socket=s' => \$opt_socket, 'start-dirty' => \$opt_start_dirty, 'start-and-exit' => \$opt_start_and_exit, - 'start-from=s' => \$opt_start_from, - 'timer' => \$opt_timer, - 'tmpdir=s' => \$opt_tmpdir, + 'timer!' => \$opt_timer, 'unified-diff|udiff' => \$opt_udiff, 'user-test=s' => \$opt_user_test, 'user=s' => \$opt_user, - 'vardir=s' => \$opt_vardir, - 'verbose' => \$opt_verbose, 'wait-timeout=i' => \$opt_wait_timeout, 'testcase-timeout=i' => \$opt_testcase_timeout, 'suite-timeout=i' => \$opt_suite_timeout, 'warnings|log-warnings' => \$opt_warnings, - 'with-openssl' => \$opt_with_openssl, 'help|h' => \$opt_usage, ) or usage("Can't read options"); - if ( $opt_usage ) - { - usage(""); - } + usage("") if $opt_usage; if ( $opt_comment ) { @@ -602,11 +678,6 @@ sub command_line_setup () { print '#' x 78, "\n\n"; } - if ( $opt_with_ndbcluster_only ) - { - print "# Option '--with-ndbcluster-only' is ignored in this release.\n"; - } - foreach my $arg ( @ARGV ) { if ( $arg =~ /^--skip-/ ) @@ -629,6 +700,21 @@ sub command_line_setup () { } } + # -------------------------------------------------------------------------- + # Find out type of logging that are being used + # -------------------------------------------------------------------------- + + # NOTE if the default binlog format is changed, this has to be changed + $used_binlog_format= "stmt"; + foreach my $arg ( @opt_extra_mysqld_opt ) + { + if ( defined mtr_match_substring($arg,"binlog-format=row")) + { + $used_binlog_format= "row"; + } + } + mtr_report("Using binlog format '$used_binlog_format'"); + # -------------------------------------------------------------------------- # Set the "var/" directory, as it is the base for everything else # -------------------------------------------------------------------------- @@ -637,6 +723,15 @@ sub command_line_setup () { { $opt_vardir= "$glob_mysql_test_dir/var"; } + elsif ( $mysql_version_id < 50000 ) + { + # --vardir was specified + mtr_error("--vardir option not supported until MySQL 5.0"); + } + + $path_vardir_trace= $opt_vardir; + # Chop off any "c:", DBUG likes a unix path ex: c:/src/... => /src/... + $path_vardir_trace=~ s/^\w://; # We make the path absolute, as the server will do a chdir() before usage unless ( $opt_vardir =~ m,^/, or @@ -652,27 +747,11 @@ sub command_line_setup () { $opt_tmpdir= "$opt_vardir/tmp" unless $opt_tmpdir; $opt_tmpdir =~ s,/+$,,; # Remove ending slash if any - # FIXME maybe not needed? - $path_manager_log= "$opt_vardir/log/manager.log" - unless $path_manager_log; - $opt_current_test= "$opt_vardir/log/current_test" - unless $opt_current_test; # -------------------------------------------------------------------------- # Do sanity checks of command line arguments # -------------------------------------------------------------------------- - if ( $opt_extern and $opt_local ) - { - mtr_error("Can't use --extern and --local at the same time"); - } - - if ( ! $opt_socket ) - { # FIXME set default before reading options? -# $opt_socket= '@MYSQL_UNIX_ADDR@'; - $opt_socket= "/tmp/mysql.sock"; # FIXME - } - # -------------------------------------------------------------------------- # Look at the command line options and set script flags # -------------------------------------------------------------------------- @@ -687,7 +766,8 @@ sub command_line_setup () { $glob_use_embedded_server= 1; push(@glob_test_mode, "embedded"); $opt_skip_rpl= 1; # We never run replication with embedded - $opt_skip_ndbcluster= 1; + $opt_skip_ndbcluster= 1; # Turn off use of NDB cluster + $opt_skip_ssl= 1; # Turn off use of SSL if ( $opt_extern ) { @@ -700,11 +780,40 @@ sub command_line_setup () { push(@glob_test_mode, "ps-protocol"); } - # FIXME don't understand what this is -# if ( $opt_local_master ) -# { -# $opt_master_myport= 3306; -# } + if ( $opt_with_ndbcluster and $opt_skip_ndbcluster) + { + mtr_error("Can't specify both --with-ndbcluster and --skip-ndbcluster"); + } + + if ( $opt_ndbconnectstring ) + { + $glob_use_running_ndbcluster= 1; + mtr_error("Can't specify --ndb-connectstring and --skip-ndbcluster") + if $opt_skip_ndbcluster; + mtr_error("Can't specify --ndb-connectstring and --ndbcluster-port") + if $opt_ndbcluster_port; + } + else + { + # Set default connect string + $opt_ndbconnectstring= "host=localhost:$opt_ndbcluster_port"; + } + + if ( $opt_ndbconnectstring_slave ) + { + $glob_use_running_ndbcluster_slave= 1; + mtr_error("Can't specify ndb-connectstring_slave and " . + "--skip-ndbcluster-slave") + if $opt_skip_ndbcluster; + mtr_error("Can't specify --ndb-connectstring-slave and " . + "--ndbcluster-port-slave") + if $opt_ndbcluster_port_slave; + } + else + { + # Set default connect string + $opt_ndbconnectstring_slave= "host=localhost:$opt_ndbcluster_port_slave"; + } if ( $opt_small_bench ) { @@ -721,83 +830,83 @@ sub command_line_setup () { mtr_error("Coverage test needs the source - please use source dist"); } - if ( $opt_gdb ) + # Check debug related options + if ( $opt_gdb || $opt_client_gdb || $opt_ddd || $opt_client_ddd || + $opt_manual_gdb || $opt_manual_ddd || $opt_manual_debug || + $opt_debugger || $opt_client_debugger ) { + # Indicate that we are using debugger + $glob_debugger= 1; + # Increase timeouts $opt_wait_timeout= 300; if ( $opt_extern ) { - mtr_error("Can't use --extern with --gdb"); + mtr_error("Can't use --extern when using debugger"); } } - if ( $opt_manual_gdb ) + # Check IM arguments + if ( $glob_win32 ) { - $opt_gdb= 1; - if ( $opt_extern ) - { - mtr_error("Can't use --extern with --manual-gdb"); - } + mtr_report("Disable Instance manager - not supported on Windows"); + $opt_skip_im= 1; + } + # Check valgrind arguments + if ( $opt_valgrind or $opt_valgrind_path or defined $opt_valgrind_options) + { + mtr_report("Turning on valgrind for all executables"); + $opt_valgrind= 1; + $opt_valgrind_mysqld= 1; + $opt_valgrind_mysqltest= 1; + } + elsif ( $opt_valgrind_mysqld ) + { + mtr_report("Turning on valgrind for mysqld(s) only"); + $opt_valgrind= 1; + } + elsif ( $opt_valgrind_mysqltest ) + { + mtr_report("Turning on valgrind for mysqltest only"); + $opt_valgrind= 1; } - if ( $opt_ddd ) + if ( $opt_callgrind ) { - if ( $opt_extern ) - { - mtr_error("Can't use --extern with --ddd"); - } + mtr_report("Turning on valgrind with callgrind for mysqld(s)"); + $opt_valgrind= 1; + $opt_valgrind_mysqld= 1; + + # Set special valgrind options unless options passed on command line + $opt_valgrind_options="--trace-children=yes" + unless defined $opt_valgrind_options; } - if ( $opt_ndbconnectstring ) + if ( $opt_valgrind ) { - $glob_use_running_ndbcluster= 1; - $opt_with_ndbcluster= 1; - } - else - { - $opt_ndbconnectstring= "host=localhost:$opt_ndbcluster_port"; - } + # Set valgrind_options to default unless already defined + $opt_valgrind_options=$default_valgrind_options + unless defined $opt_valgrind_options; - if ( $opt_skip_ndbcluster ) - { - $opt_with_ndbcluster= 0; - } - - # The ":s" in the argument spec, means we have three different cases - # - # undefined option not set - # "" option set with no argument - # "somestring" option is name/path of valgrind executable - - # Take executable path from any of them, if any - $opt_valgrind_mysqld= $opt_valgrind; - $opt_valgrind= $opt_valgrind_mysqltest if $opt_valgrind_mysqltest; - $opt_valgrind= $opt_valgrind_all if $opt_valgrind_all; - - # If valgrind flag not defined, define if other valgrind flags are - unless ( defined $opt_valgrind ) - { - $opt_valgrind= "" - if defined $opt_valgrind_mysqltest or defined $opt_valgrind_all; + mtr_report("Running valgrind with options \"$opt_valgrind_options\""); } if ( ! $opt_testcase_timeout ) { $opt_testcase_timeout= $default_testcase_timeout; - $opt_testcase_timeout*= 10 if defined $opt_valgrind; + $opt_testcase_timeout*= 10 if $opt_valgrind; } if ( ! $opt_suite_timeout ) { $opt_suite_timeout= $default_suite_timeout; - $opt_suite_timeout*= 4 if defined $opt_valgrind; + $opt_suite_timeout*= 6 if $opt_valgrind; } - if ( defined $opt_valgrind ) + # Increase times to wait for executables to start if using valgrind + if ( $opt_valgrind ) { $opt_sleep_time_after_restart= 10; $opt_sleep_time_for_delete= 60; - # >=2.1.2 requires the --tool option, some versions write to stdout, some to stderr - # valgrind --help 2>&1 | grep "\-\-tool" > /dev/null && VALGRIND="$VALGRIND --tool=memcheck" } if ( ! $opt_user ) @@ -812,74 +921,210 @@ sub command_line_setup () { } } + # On QNX, /tmp/dir/master.sock and /tmp/dir//master.sock seem to be + # considered different, so avoid the extra slash (/) in the socket + # paths. + my $sockdir = $opt_tmpdir; + $sockdir =~ s|/+$||; + # Put this into a hash, will be a C struct $master->[0]= { + pid => 0, + type => "master", + idx => 0, path_myddir => "$opt_vardir/master-data", path_myerr => "$opt_vardir/log/master.err", path_mylog => "$opt_vardir/log/master.log", - path_mypid => "$opt_vardir/run/master.pid", - path_mysock => "$opt_tmpdir/master.sock", - path_myport => $opt_master_myport, + path_pid => "$opt_vardir/run/master.pid", + path_sock => "$sockdir/master.sock", + port => $opt_master_myport, start_timeout => 400, # enough time create innodb tables - - ndbcluster => 1, # ndbcluster not started + cluster => 0, # index in clusters list + start_opts => [], }; $master->[1]= { + pid => 0, + type => "master", + idx => 1, path_myddir => "$opt_vardir/master1-data", path_myerr => "$opt_vardir/log/master1.err", path_mylog => "$opt_vardir/log/master1.log", - path_mypid => "$opt_vardir/run/master1.pid", - path_mysock => "$opt_tmpdir/master1.sock", - path_myport => $opt_master_myport + 1, + path_pid => "$opt_vardir/run/master1.pid", + path_sock => "$sockdir/master1.sock", + port => $opt_master_myport + 1, start_timeout => 400, # enough time create innodb tables + cluster => 0, # index in clusters list + start_opts => [], }; $slave->[0]= { + pid => 0, + type => "slave", + idx => 0, path_myddir => "$opt_vardir/slave-data", path_myerr => "$opt_vardir/log/slave.err", path_mylog => "$opt_vardir/log/slave.log", - path_mypid => "$opt_vardir/run/slave.pid", - path_mysock => "$opt_tmpdir/slave.sock", - path_myport => $opt_slave_myport, + path_pid => "$opt_vardir/run/slave.pid", + path_sock => "$sockdir/slave.sock", + port => $opt_slave_myport, start_timeout => 400, + + cluster => 1, # index in clusters list + start_opts => [], }; $slave->[1]= { + pid => 0, + type => "slave", + idx => 1, path_myddir => "$opt_vardir/slave1-data", path_myerr => "$opt_vardir/log/slave1.err", path_mylog => "$opt_vardir/log/slave1.log", - path_mypid => "$opt_vardir/run/slave1.pid", - path_mysock => "$opt_tmpdir/slave1.sock", - path_myport => $opt_slave_myport + 1, + path_pid => "$opt_vardir/run/slave1.pid", + path_sock => "$sockdir/slave1.sock", + port => $opt_slave_myport + 1, start_timeout => 300, + cluster => -1, # index in clusters list + start_opts => [], }; $slave->[2]= { + pid => 0, + type => "slave", + idx => 2, path_myddir => "$opt_vardir/slave2-data", path_myerr => "$opt_vardir/log/slave2.err", path_mylog => "$opt_vardir/log/slave2.log", - path_mypid => "$opt_vardir/run/slave2.pid", - path_mysock => "$opt_tmpdir/slave2.sock", - path_myport => $opt_slave_myport + 2, + path_pid => "$opt_vardir/run/slave2.pid", + path_sock => "$sockdir/slave2.sock", + port => $opt_slave_myport + 2, start_timeout => 300, + cluster => -1, # index in clusters list + start_opts => [], }; + $instance_manager= + { + path_err => "$opt_vardir/log/im.err", + path_log => "$opt_vardir/log/im.log", + path_pid => "$opt_vardir/run/im.pid", + path_angel_pid => "$opt_vardir/run/im.angel.pid", + path_sock => "$sockdir/im.sock", + port => $im_port, + start_timeout => $master->[0]->{'start_timeout'}, + admin_login => 'im_admin', + admin_password => 'im_admin_secret', + admin_sha1 => '*598D51AD2DFF7792045D6DF3DDF9AA1AF737B295', + password_file => "$opt_vardir/im.passwd", + defaults_file => "$opt_vardir/im.cnf", + }; + + $instance_manager->{'instances'}->[0]= + { + server_id => 1, + port => $im_mysqld1_port, + path_datadir => "$opt_vardir/im_mysqld_1.data", + path_sock => "$sockdir/mysqld_1.sock", + path_pid => "$opt_vardir/run/mysqld_1.pid", + start_timeout => 400, # enough time create innodb tables + old_log_format => 1 + }; + + $instance_manager->{'instances'}->[1]= + { + server_id => 2, + port => $im_mysqld2_port, + path_datadir => "$opt_vardir/im_mysqld_2.data", + path_sock => "$sockdir/mysqld_2.sock", + path_pid => "$opt_vardir/run/mysqld_2.pid", + nonguarded => 1, + start_timeout => 400, # enough time create innodb tables + old_log_format => 1 + }; + + my $data_dir= "$opt_vardir/ndbcluster-$opt_ndbcluster_port"; + $clusters->[0]= + { + name => "Master", + nodes => 2, + port => "$opt_ndbcluster_port", + data_dir => "$data_dir", + connect_string => "$opt_ndbconnectstring", + path_pid => "$data_dir/ndb_3.pid", # Nodes + 1 + pid => 0, # pid of ndb_mgmd + installed_ok => 0, + }; + + $data_dir= "$opt_vardir/ndbcluster-$opt_ndbcluster_port_slave"; + $clusters->[1]= + { + name => "Slave", + nodes => 1, + port => "$opt_ndbcluster_port_slave", + data_dir => "$data_dir", + connect_string => "$opt_ndbconnectstring_slave", + path_pid => "$data_dir/ndb_2.pid", # Nodes + 1 + pid => 0, # pid of ndb_mgmd + installed_ok => 0, + }; + + # Init pids of ndbd's + foreach my $cluster ( @{$clusters} ) + { + for ( my $idx= 0; $idx < $cluster->{'nodes'}; $idx++ ) + { + my $nodeid= $idx+1; + $cluster->{'ndbds'}->[$idx]= + { + pid => 0, + nodeid => $nodeid, + path_pid => "$cluster->{'data_dir'}/ndb_${nodeid}.pid", + path_fs => "$cluster->{'data_dir'}/ndb_${nodeid}_fs", + }; + } + } + if ( $opt_extern ) { $glob_use_running_server= 1; $opt_skip_rpl= 1; # We don't run rpl test cases - $master->[0]->{'path_mysock'}= $opt_socket; + $master->[0]->{'path_sock'}= $opt_socket; } $path_timefile= "$opt_vardir/log/mysqltest-time"; $path_mysqltest_log= "$opt_vardir/log/mysqltest.log"; + $path_current_test_log= "$opt_vardir/log/current_test"; + $path_ndb_testrun_log= "$opt_vardir/log/ndb_testrun.log"; + + $path_snapshot= "$opt_tmpdir/snapshot_$opt_master_myport/"; +} + +sub snapshot_setup () { + + # Make a list of all data_dirs + @data_dir_lst = ( + $master->[0]->{'path_myddir'}, + $master->[1]->{'path_myddir'}); + + for (my $idx= 0; $idx < $max_slave_num; $idx++) + { + push(@data_dir_lst, $slave->[$idx]->{'path_myddir'}); + } + + unless ($opt_skip_im) + { + foreach my $instance (@{$instance_manager->{'instances'}}) + { + push(@data_dir_lst, $instance->{'path_datadir'}); + } + } } @@ -889,136 +1134,222 @@ sub command_line_setup () { # ############################################################################## + +sub check_mysqld_features () { + # + # Execute "mysqld --no-defaults --help --verbose", that will + # print out version and a list of all features and settings + # + my $found_variable_list_start= 0; + my $spec_file= "$opt_vardir/tmp/mysqld.spec"; + if ( mtr_run($exe_mysqld, + ["--no-defaults", + "--verbose", + "--help"], + "", "$spec_file", "$spec_file", "") != 0 ) + { + mtr_error("Failed to get version and list of features from %s", + $exe_mysqld); + } + + my %mysqld_variables; + my $F= IO::File->new($spec_file) or + mtr_error("can't open file \"$spec_file\": $!"); + + while ( my $line= <$F> ) + { + # First look for version + if ( !$mysql_version_id ) + { + # Look for version + if ( $line =~ /^$exe_mysqld\s\sVer\s([0-9]*)\.([0-9]*)\.([0-9]*)/ ) + { + print "Major: $1 Minor: $2 Build: $3\n"; + $mysql_version_id= $1*10000 + $2*100 + $3; + print "mysql_version_id: $mysql_version_id\n"; + } + } + else + { + if (!$found_variable_list_start) + { + # Look for start of variables list + if ( $line =~ /-*\s-*/ ) + { + $found_variable_list_start= 1; + } + } else { + # Put variables into hash + if ( $line =~ /^(\w*)[ \t]*(\w*)$/ ) + { + $mysqld_variables{$1}= $2; + } + } + } + } + mtr_error("Could not find version of MySQL") unless $mysql_version_id; + + check_ndbcluster_support(\%mysqld_variables); + check_ssl_support(\%mysqld_variables); + check_debug_support(\%mysqld_variables); + + if ( $mysql_version_id < 50000 ) + { + # Instance manager is not supported until 5.0 + $opt_skip_im= 1; + } + + # Set default values from mysqld_variables + $opt_socket= %mysqld_variables->{'socket'}; + $default_mysqld_port = %mysqld_variables->{'port'}; +} + + sub executable_setup () { - if ( $opt_source_dist ) + # + # Check if libtool is available in this distribution/clone + # we need it when valgrinding or debugging non installed binary + # Otherwise valgrind will valgrind the libtool wrapper or bash + # and gdb will not find the real executable to debug + # + if ( -x "../libtool") { - if ( $glob_win32 ) + $exe_libtool= "../libtool"; + if ($opt_valgrind or $glob_debugger) { - $path_client_bindir= mtr_path_exists("$glob_basedir/client_release", - "$glob_basedir/bin"); - $exe_mysqld= mtr_exe_exists ("$path_client_bindir/mysqld-nt", - "$path_client_bindir/mysqld", - "$path_client_bindir/mysqld-debug", - "$path_client_bindir/mysqld-max", - "$path_client_bindir/mysqld-max-nt"); - $path_language= mtr_path_exists("$glob_basedir/share/english/"); - $path_charsetsdir= mtr_path_exists("$glob_basedir/share/charsets"); - $exe_my_print_defaults= - mtr_exe_exists("$path_client_bindir/my_print_defaults"); + mtr_report("Using \"$exe_libtool\" when running valgrind or debugger"); } - else - { - $path_client_bindir= mtr_path_exists("$glob_basedir/client"); - $exe_mysqld= mtr_exe_exists ("$glob_basedir/sql/mysqld"); - $path_language= mtr_path_exists("$glob_basedir/sql/share/english/"); - $path_charsetsdir= mtr_path_exists("$glob_basedir/sql/share/charsets"); - $exe_my_print_defaults= - mtr_exe_exists("$glob_basedir/extra/my_print_defaults"); - } - - if ( $glob_use_embedded_server ) - { - my $path_examples= "$glob_basedir/libmysqld/examples"; - $exe_mysqltest= mtr_exe_exists("$path_examples/mysqltest_embedded"); - $exe_mysql_client_test= - mtr_exe_exists("$path_examples/mysql_client_test_embedded", - "/usr/bin/false"); - } - else - { - if ( $opt_valgrind_mysqltest ) - { - # client/mysqltest might be a libtool .sh script, so look for real exe - # to avoid valgrinding bash ;) - $exe_mysqltest= - mtr_exe_exists("$path_client_bindir/.libs/lt-mysqltest", - "$path_client_bindir/.libs/mysqltest", - "$path_client_bindir/mysqltest"); - } - else - { - $exe_mysqltest= mtr_exe_exists("$path_client_bindir/mysqltest"); - } - $exe_mysql_client_test= - mtr_exe_exists("$glob_basedir/tests/mysql_client_test", - "/usr/bin/false"); - } - $exe_mysqldump= mtr_exe_exists("$path_client_bindir/mysqldump"); - $exe_mysqlimport= mtr_exe_exists("$path_client_bindir/mysqlimport"); - $exe_mysqlshow= mtr_exe_exists("$path_client_bindir/mysqlshow"); - $exe_mysqlbinlog= mtr_exe_exists("$path_client_bindir/mysqlbinlog"); - $exe_mysqladmin= mtr_exe_exists("$path_client_bindir/mysqladmin"); - $exe_mysql= mtr_exe_exists("$path_client_bindir/mysql"); - $exe_mysql_fix_system_tables= - mtr_script_exists("$glob_basedir/scripts/mysql_fix_privilege_tables"); - $path_ndb_tools_dir= mtr_path_exists("$glob_basedir/ndb/tools"); - $exe_ndb_mgm= "$glob_basedir/ndb/src/mgmclient/ndb_mgm"; } - else - { - $path_client_bindir= mtr_path_exists("$glob_basedir/bin"); - $exe_mysqldump= mtr_exe_exists("$path_client_bindir/mysqldump"); - $exe_mysqlimport= mtr_exe_exists("$path_client_bindir/mysqlimport"); - $exe_mysqlshow= mtr_exe_exists("$path_client_bindir/mysqlshow"); - $exe_mysqlbinlog= mtr_exe_exists("$path_client_bindir/mysqlbinlog"); - $exe_mysqladmin= mtr_exe_exists("$path_client_bindir/mysqladmin"); - $exe_mysql= mtr_exe_exists("$path_client_bindir/mysql"); - $exe_mysql_fix_system_tables= - mtr_script_exists("$path_client_bindir/mysql_fix_privilege_tables", - "$glob_basedir/scripts/mysql_fix_privilege_tables"); - $exe_my_print_defaults= - mtr_exe_exists("$path_client_bindir/my_print_defaults"); - $path_language= mtr_path_exists("$glob_basedir/share/mysql/english/", - "$glob_basedir/share/english/"); - $path_charsetsdir= mtr_path_exists("$glob_basedir/share/mysql/charsets", - "$glob_basedir/share/charsets"); + # Look for the path where to find the client binaries + $path_client_bindir= mtr_path_exists("$glob_basedir/client", + "$glob_basedir/client_release", + "$glob_basedir/client_debug", + "$glob_basedir/client/release", + "$glob_basedir/client/debug", + "$glob_basedir/bin"); - if ( $glob_win32 ) - { - $exe_mysqld= mtr_exe_exists ("$glob_basedir/bin/mysqld-nt", - "$glob_basedir/bin/mysqld", - "$glob_basedir/bin/mysqld-debug",); - } - else - { - $exe_mysqld= mtr_exe_exists ("$glob_basedir/libexec/mysqld", - "$glob_basedir/bin/mysqld"); - } - - if ( $glob_use_embedded_server ) - { - $exe_mysqltest= mtr_exe_exists("$path_client_bindir/mysqltest_embedded"); - $exe_mysql_client_test= - mtr_exe_exists("$glob_basedir/tests/mysql_client_test_embedded", - "$path_client_bindir/mysql_client_test_embedded", - "/usr/bin/false"); - } - else - { - $exe_mysqltest= mtr_exe_exists("$path_client_bindir/mysqltest"); - $exe_mysql_client_test= - mtr_exe_exists("$path_client_bindir/mysql_client_test", - "/usr/bin/false"); # FIXME temporary - } - - $path_ndb_tools_dir= "$glob_basedir/bin"; - $exe_ndb_mgm= "$glob_basedir/bin/ndb_mgm"; - } + # Look for the mysqld executable + $exe_mysqld= mtr_exe_exists ("$glob_basedir/sql/mysqld", + "$path_client_bindir/mysqld-max-nt", + "$path_client_bindir/mysqld-max", + "$path_client_bindir/mysqld-nt", + "$path_client_bindir/mysqld", + "$path_client_bindir/mysqld-debug", + "$path_client_bindir/mysqld-max", + "$glob_basedir/libexec/mysqld", + "$glob_basedir/sql/release/mysqld", + "$glob_basedir/sql/debug/mysqld"); $exe_master_mysqld= $exe_master_mysqld || $exe_mysqld; $exe_slave_mysqld= $exe_slave_mysqld || $exe_mysqld; - $path_ndb_backup_dir= - "$opt_vardir/ndbcluster-$opt_ndbcluster_port"; - $file_ndb_testrun_log= "$opt_vardir/log/ndb_testrun.log"; + # Use the mysqld found above to find out what features are available + check_mysqld_features(); + + # Look for language files and charsetsdir, use same share + my $path_share= mtr_path_exists("$glob_basedir/share", + "$glob_basedir/sql/share", + "$glob_basedir/share/mysql", + "$glob_basedir/share"); + + $path_language= mtr_path_exists("$path_share/english"); + $path_charsetsdir= mtr_path_exists("$path_share/charsets"); + + # Look for my_print_defaults + $exe_my_print_defaults= + mtr_exe_exists("$path_client_bindir/my_print_defaults", + "$glob_basedir/extra/my_print_defaults", + "$glob_basedir/extra/release/my_print_defaults", + "$glob_basedir/extra/debug/my_print_defaults"); + + # Look for perror + $exe_perror= mtr_exe_exists("$glob_basedir/extra/perror", + "$path_client_bindir/perror", + "$glob_basedir/extra/release/perror", + "$glob_basedir/extra/debug/perror"); + + + if ( ! $opt_skip_im ) + { + # Look for instance manager binary - mysqlmanager + $exe_im= + mtr_exe_exists( + "$glob_basedir/server-tools/instance-manager/mysqlmanager", + "$glob_basedir/libexec/mysqlmanager"); + } + + # Look for the client binaries + $exe_mysqlcheck= mtr_exe_exists("$path_client_bindir/mysqlcheck"); + $exe_mysqldump= mtr_exe_exists("$path_client_bindir/mysqldump"); + $exe_mysqlimport= mtr_exe_exists("$path_client_bindir/mysqlimport"); + $exe_mysqlshow= mtr_exe_exists("$path_client_bindir/mysqlshow"); + $exe_mysqlbinlog= mtr_exe_exists("$path_client_bindir/mysqlbinlog"); + $exe_mysqladmin= mtr_exe_exists("$path_client_bindir/mysqladmin"); + $exe_mysql= mtr_exe_exists("$path_client_bindir/mysql"); + + # Look for mysql_fix_system_table script + $exe_mysql_fix_system_tables= + mtr_script_exists("$glob_basedir/scripts/mysql_fix_privilege_tables", + "$path_client_bindir/mysql_fix_privilege_tables"); + + if ( $opt_with_ndbcluster) + { + # Look for ndb tols and binaries + $path_ndb_tools_dir= mtr_path_exists("$glob_basedir/ndb/tools", + "$glob_basedir/bin"); + $exe_ndb_mgm= mtr_exe_exists("$glob_basedir/ndb/src/mgmclient/ndb_mgm", + "$glob_basedir/bin/ndb_mgm"); + } + + # Look for the udf_example library + $lib_udf_example= + mtr_file_exists("$glob_basedir/sql/.libs/udf_example.so"); + + + # Look for mysqltest executable + if ( $glob_use_embedded_server ) + { + $exe_mysqltest= + mtr_exe_exists("$glob_basedir/libmysqld/examples/mysqltest_embedded", + "$path_client_bindir/mysqltest_embedded"); + } + else + { + $exe_mysqltest= mtr_exe_exists("$path_client_bindir/mysqltest"); + + + } + + # Look for mysql_client_test executable + if ( $glob_use_embedded_server ) + { + $exe_mysql_client_test= + mtr_exe_exists("$glob_basedir/libmysqld/examples/mysql_client_test_embedded", + "$glob_basedir/tests/mysqltest_embedded"); + } + else + { + $exe_mysql_client_test= + mtr_exe_exists("$glob_basedir/tests/mysql_client_test"); + } +} + + +sub generate_cmdline_mysqldump ($) { + my($mysqld) = @_; + return + "$exe_mysqldump --no-defaults -uroot " . + "--port=$mysqld->{'port'} " . + "--socket=$mysqld->{'path_sock'} --password="; } ############################################################################## # -# Set environment to be used by childs of this process +# Set environment to be used by childs of this process for +# things that are constant duting the whole lifetime of mysql-test-run.pl # ############################################################################## @@ -1026,65 +1357,320 @@ sub executable_setup () { sub environment_setup () { - # -------------------------------------------------------------------------- - # We might not use a standard installation directory, like /usr/lib. - # Set LD_LIBRARY_PATH to make sure we find our installed libraries. - # -------------------------------------------------------------------------- + umask(022); - unless ( $opt_source_dist ) + my @ld_library_paths; + + # -------------------------------------------------------------------------- + # Setup LD_LIBRARY_PATH so the libraries from this distro/clone + # are used in favor of the system installed ones + # -------------------------------------------------------------------------- + if ( $opt_source_dist ) { - $ENV{'LD_LIBRARY_PATH'}= - "$glob_basedir/lib" . - ($ENV{'LD_LIBRARY_PATH'} ? ":$ENV{'LD_LIBRARY_PATH'}" : ""); - $ENV{'DYLD_LIBRARY_PATH'}= - "$glob_basedir/lib" . - ($ENV{'DYLD_LIBRARY_PATH'} ? ":$ENV{'DYLD_LIBRARY_PATH'}" : ""); + push(@ld_library_paths, "$glob_basedir/libmysql/.libs/", + "$glob_basedir/libmysql_r/.libs/"); } + else + { + push(@ld_library_paths, "$glob_basedir/lib"); + } + + # -------------------------------------------------------------------------- + # Add the path where libndbclient can be found + # -------------------------------------------------------------------------- + if ( $opt_ndbcluster_supported ) + { + push(@ld_library_paths, "$glob_basedir/storage/ndb/src/.libs"); + } + + # -------------------------------------------------------------------------- + # Add the path where mysqld will find udf_example.so + # -------------------------------------------------------------------------- + if ( $lib_udf_example ) + { + push(@ld_library_paths, dirname($lib_udf_example)); + } + + # -------------------------------------------------------------------------- + # Valgrind need to be run with debug libraries otherwise it's almost + # impossible to add correct supressions, that means if "/usr/lib/debug" + # is available, it should be added to + # LD_LIBRARY_PATH + # -------------------------------------------------------------------------- + my $debug_libraries_path= "/usr/lib/debug"; + if ( $opt_valgrind and -d $debug_libraries_path ) + { + push(@ld_library_paths, $debug_libraries_path); + } + + $ENV{'LD_LIBRARY_PATH'}= join(":", @ld_library_paths, + split(':', $ENV{'LD_LIBRARY_PATH'})); + mtr_debug("LD_LIBRARY_PATH: $ENV{'LD_LIBRARY_PATH'}"); + + $ENV{'DYLD_LIBRARY_PATH'}= join(":", @ld_library_paths, + split(':', $ENV{'DYLD_LIBRARY_PATH'})); + mtr_debug("DYLD_LIBRARY_PATH: $ENV{'DYLD_LIBRARY_PATH'}"); + # -------------------------------------------------------------------------- # Also command lines in .opt files may contain env vars # -------------------------------------------------------------------------- + $ENV{'CHARSETSDIR'}= $path_charsetsdir; $ENV{'UMASK'}= "0660"; # The octal *string* $ENV{'UMASK_DIR'}= "0770"; # The octal *string* $ENV{'LC_COLLATE'}= "C"; $ENV{'USE_RUNNING_SERVER'}= $glob_use_running_server; $ENV{'MYSQL_TEST_DIR'}= $glob_mysql_test_dir; - $ENV{'MYSQL_TEST_WINDIR'}= $glob_mysql_test_dir; $ENV{'MYSQLTEST_VARDIR'}= $opt_vardir; - $ENV{'MASTER_WINMYSOCK'}= $master->[0]->{'path_mysock'}; - $ENV{'MASTER_MYSOCK'}= $master->[0]->{'path_mysock'}; - $ENV{'MASTER_MYSOCK1'}= $master->[1]->{'path_mysock'}; - $ENV{'MASTER_MYPORT'}= $master->[0]->{'path_myport'}; - $ENV{'MASTER_MYPORT1'}= $master->[1]->{'path_myport'}; - $ENV{'SLAVE_MYPORT'}= $slave->[0]->{'path_myport'}; - $ENV{'SLAVE_MYPORT1'}= $slave->[1]->{'path_myport'}; - $ENV{'SLAVE_MYPORT2'}= $slave->[2]->{'path_myport'}; -# $ENV{'MYSQL_TCP_PORT'}= '@MYSQL_TCP_PORT@'; # FIXME - $ENV{'MYSQL_TCP_PORT'}= 3306; - - $ENV{'NDBCLUSTER_PORT'}= $opt_ndbcluster_port; - - if ( $glob_cygwin_perl ) - { - foreach my $key ('MYSQL_TEST_WINDIR','MASTER_MYSOCK') - { - $ENV{$key}= `cygpath -w $ENV{$key}`; - $ENV{$key} =~ s,\\,\\\\,g; - chomp($ENV{$key}); - } - } + $ENV{'MYSQL_TMP_DIR'}= $opt_tmpdir; + $ENV{'MASTER_MYSOCK'}= $master->[0]->{'path_sock'}; + $ENV{'MASTER_MYSOCK1'}= $master->[1]->{'path_sock'}; + $ENV{'MASTER_MYPORT'}= $master->[0]->{'port'}; + $ENV{'MASTER_MYPORT1'}= $master->[1]->{'port'}; + $ENV{'SLAVE_MYSOCK'}= $slave->[0]->{'path_sock'}; + $ENV{'SLAVE_MYPORT'}= $slave->[0]->{'port'}; + $ENV{'SLAVE_MYPORT1'}= $slave->[1]->{'port'}; + $ENV{'SLAVE_MYPORT2'}= $slave->[2]->{'port'}; + $ENV{'MYSQL_TCP_PORT'}= $default_mysqld_port; $ENV{MTR_BUILD_THREAD}= 0 unless $ENV{MTR_BUILD_THREAD}; # Set if not set + # ---------------------------------------------------- + # Setup env for NDB + # ---------------------------------------------------- + $ENV{'NDB_MGM'}= $exe_ndb_mgm; + + $ENV{'NDBCLUSTER_PORT'}= $opt_ndbcluster_port; + $ENV{'NDBCLUSTER_PORT_SLAVE'}= $opt_ndbcluster_port_slave; + + $ENV{'NDB_EXTRA_TEST'}= $opt_ndb_extra_test; + + $ENV{'NDB_BACKUP_DIR'}= $clusters->[0]->{'data_dir'}; + $ENV{'NDB_DATA_DIR'}= $clusters->[0]->{'data_dir'}; + $ENV{'NDB_TOOLS_DIR'}= $path_ndb_tools_dir; + $ENV{'NDB_TOOLS_OUTPUT'}= $path_ndb_testrun_log; + $ENV{'NDB_CONNECTSTRING'}= $opt_ndbconnectstring; + + $ENV{'NDB_EXAMPLES_DIR'}= $path_ndb_examples_dir; + $ENV{'MY_NDB_EXAMPLES_BINARY'}= $exe_ndb_example; + $ENV{'NDB_EXAMPLES_OUTPUT'}= $path_ndb_testrun_log; + + # ---------------------------------------------------- + # Setup env for IM + # ---------------------------------------------------- + $ENV{'IM_EXE'}= $exe_im; + $ENV{'IM_PATH_PID'}= $instance_manager->{path_pid}; + $ENV{'IM_PATH_ANGEL_PID'}= $instance_manager->{path_angel_pid}; + $ENV{'IM_PORT'}= $instance_manager->{port}; + $ENV{'IM_DEFAULTS_PATH'}= $instance_manager->{defaults_file}; + $ENV{'IM_PASSWORD_PATH'}= $instance_manager->{password_file}; + + $ENV{'IM_MYSQLD1_SOCK'}= $instance_manager->{instances}->[0]->{path_sock}; + $ENV{'IM_MYSQLD1_PORT'}= $instance_manager->{instances}->[0]->{port}; + $ENV{'IM_MYSQLD1_PATH_PID'}=$instance_manager->{instances}->[0]->{path_pid}; + $ENV{'IM_MYSQLD2_SOCK'}= $instance_manager->{instances}->[1]->{path_sock}; + $ENV{'IM_MYSQLD2_PORT'}= $instance_manager->{instances}->[1]->{port}; + $ENV{'IM_MYSQLD2_PATH_PID'}=$instance_manager->{instances}->[1]->{path_pid}; + + # ---------------------------------------------------- + # Setup env so childs can execute mysqlcheck + # ---------------------------------------------------- + my $cmdline_mysqlcheck= + "$exe_mysqlcheck --no-defaults -uroot " . + "--port=$master->[0]->{'port'} " . + "--socket=$master->[0]->{'path_sock'} --password="; + + if ( $opt_debug ) + { + $cmdline_mysqlcheck .= + " --debug=d:t:A,$path_vardir_trace/log/mysqlcheck.trace"; + } + $ENV{'MYSQL_CHECK'}= $cmdline_mysqlcheck; + + # ---------------------------------------------------- + # Setup env to childs can execute myqldump + # ---------------------------------------------------- + my $cmdline_mysqldump= generate_cmdline_mysqldump($master->[0]); + my $cmdline_mysqldumpslave= generate_cmdline_mysqldump($slave->[0]); + + if ( $opt_debug ) + { + $cmdline_mysqldump .= + " --debug=d:t:A,$path_vardir_trace/log/mysqldump-master.trace"; + $cmdline_mysqldumpslave .= + " --debug=d:t:A,$path_vardir_trace/log/mysqldump-slave.trace"; + } + $ENV{'MYSQL_DUMP'}= $cmdline_mysqldump; + $ENV{'MYSQL_DUMP_SLAVE'}= $cmdline_mysqldumpslave; + + + # ---------------------------------------------------- + # Setup env so childs can execute mysqlslap + # ---------------------------------------------------- + unless ( $glob_win32 ) + { + my $cmdline_mysqlslap= + "$exe_mysqlslap -uroot " . + "--port=$master->[0]->{'port'} " . + "--socket=$master->[0]->{'path_sock'} --password= " . + "--lock-directory=$opt_tmpdir"; + + if ( $opt_debug ) + { + $cmdline_mysqlslap .= + " --debug=d:t:A,$path_vardir_trace/log/mysqlslap.trace"; + } + $ENV{'MYSQL_SLAP'}= $cmdline_mysqlslap; + } + + # ---------------------------------------------------- + # Setup env so childs can execute mysqlimport + # ---------------------------------------------------- + my $cmdline_mysqlimport= + "$exe_mysqlimport -uroot " . + "--port=$master->[0]->{'port'} " . + "--socket=$master->[0]->{'path_sock'} --password="; + + if ( $opt_debug ) + { + $cmdline_mysqlimport .= + " --debug=d:t:A,$path_vardir_trace/log/mysqlimport.trace"; + } + $ENV{'MYSQL_IMPORT'}= $cmdline_mysqlimport; + + + # ---------------------------------------------------- + # Setup env so childs can execute mysqlshow + # ---------------------------------------------------- + my $cmdline_mysqlshow= + "$exe_mysqlshow -uroot " . + "--port=$master->[0]->{'port'} " . + "--socket=$master->[0]->{'path_sock'} --password="; + + if ( $opt_debug ) + { + $cmdline_mysqlshow .= + " --debug=d:t:A,$path_vardir_trace/log/mysqlshow.trace"; + } + $ENV{'MYSQL_SHOW'}= $cmdline_mysqlshow; + + # ---------------------------------------------------- + # Setup env so childs can execute mysqlbinlog + # ---------------------------------------------------- + my $cmdline_mysqlbinlog= + "$exe_mysqlbinlog" . + " --no-defaults --local-load=$opt_tmpdir"; + if ( $mysql_version_id >= 50000 ) + { + $cmdline_mysqlbinlog .=" --character-sets-dir=$path_charsetsdir"; + } + + if ( $opt_debug ) + { + $cmdline_mysqlbinlog .= + " --debug=d:t:A,$path_vardir_trace/log/mysqlbinlog.trace"; + } + $ENV{'MYSQL_BINLOG'}= $cmdline_mysqlbinlog; + + # ---------------------------------------------------- + # Setup env so childs can execute mysql + # ---------------------------------------------------- + my $cmdline_mysql= + "$exe_mysql --no-defaults --host=localhost --user=root --password= " . + "--port=$master->[0]->{'port'} " . + "--socket=$master->[0]->{'path_sock'} ". + "--character-sets-dir=$path_charsetsdir"; + + $ENV{'MYSQL'}= $cmdline_mysql; + + # ---------------------------------------------------- + # Setup env so childs can execute mysql_client_test + # ---------------------------------------------------- + my $cmdline_mysql_client_test= + "$exe_mysql_client_test --no-defaults --testcase --user=root --silent " . + "--port=$master->[0]->{'port'} " . + "--socket=$master->[0]->{'path_sock'}"; + if ( $mysql_version_id >= 50000 ) + { + $cmdline_mysql_client_test .=" --vardir=$opt_vardir"; + } + + if ( $opt_debug ) + { + $cmdline_mysql_client_test .= + " --debug=d:t:A,$path_vardir_trace/log/mysql_client_test.trace"; + } + + if ( $glob_use_embedded_server ) + { + $cmdline_mysql_client_test.= + " -A --language=$path_language" . + " -A --datadir=$slave->[0]->{'path_myddir'}" . + " -A --character-sets-dir=$path_charsetsdir"; + } + $ENV{'MYSQL_CLIENT_TEST'}= $cmdline_mysql_client_test; + + + # ---------------------------------------------------- + # Setup env so childs can execute mysql_fix_system_tables + # ---------------------------------------------------- + my $cmdline_mysql_fix_system_tables= + "$exe_mysql_fix_system_tables --no-defaults --host=localhost --user=root --password= " . + "--basedir=$glob_basedir --bindir=$path_client_bindir --verbose " . + "--port=$master->[0]->{'port'} " . + "--socket=$master->[0]->{'path_sock'}"; + + $ENV{'MYSQL_FIX_SYSTEM_TABLES'}= $cmdline_mysql_fix_system_tables; + + # ---------------------------------------------------- + # Setup env so childs can execute my_print_defaults + # ---------------------------------------------------- + $ENV{'MYSQL_MY_PRINT_DEFAULTS'}= $exe_my_print_defaults; + + + # ---------------------------------------------------- + # Setup env so childs can execute perror + # ---------------------------------------------------- + $ENV{'MY_PERROR'}= $exe_perror; + + # ---------------------------------------------------- + # Add the path where mysqld will find udf_example.so + # ---------------------------------------------------- + $ENV{'UDF_EXAMPLE_LIB'}= + ($lib_udf_example ? basename($lib_udf_example) : ""); + + $ENV{'LD_LIBRARY_PATH'}= + ($lib_udf_example ? dirname($lib_udf_example) : "") . + ($ENV{'LD_LIBRARY_PATH'} ? ":$ENV{'LD_LIBRARY_PATH'}" : ""); + + + # ---------------------------------------------------- # We are nice and report a bit about our settings - print "Using MTR_BUILD_THREAD = $ENV{MTR_BUILD_THREAD}\n"; - print "Using MASTER_MYPORT = $ENV{MASTER_MYPORT}\n"; - print "Using MASTER_MYPORT1 = $ENV{MASTER_MYPORT1}\n"; - print "Using SLAVE_MYPORT = $ENV{SLAVE_MYPORT}\n"; - print "Using SLAVE_MYPORT1 = $ENV{SLAVE_MYPORT1}\n"; - print "Using SLAVE_MYPORT2 = $ENV{SLAVE_MYPORT2}\n"; - print "Using NDBCLUSTER_PORT = $ENV{NDBCLUSTER_PORT}\n"; + # ---------------------------------------------------- + if (!$opt_extern) + { + print "Using MTR_BUILD_THREAD = $ENV{MTR_BUILD_THREAD}\n"; + print "Using MASTER_MYPORT = $ENV{MASTER_MYPORT}\n"; + print "Using MASTER_MYPORT1 = $ENV{MASTER_MYPORT1}\n"; + print "Using SLAVE_MYPORT = $ENV{SLAVE_MYPORT}\n"; + print "Using SLAVE_MYPORT1 = $ENV{SLAVE_MYPORT1}\n"; + print "Using SLAVE_MYPORT2 = $ENV{SLAVE_MYPORT2}\n"; + if ( ! $opt_skip_ndbcluster ) + { + print "Using NDBCLUSTER_PORT = $ENV{NDBCLUSTER_PORT}\n"; + if ( ! $opt_skip_ndbcluster_slave ) + { + print "Using NDBCLUSTER_PORT_SLAVE = $ENV{NDBCLUSTER_PORT_SLAVE}\n"; + } + } + if ( ! $opt_skip_im ) + { + print "Using IM_PORT = $ENV{IM_PORT}\n"; + print "Using IM_MYSQLD1_PORT = $ENV{IM_MYSQLD1_PORT}\n"; + print "Using IM_MYSQLD2_PORT = $ENV{IM_MYSQLD2_PORT}\n"; + } + } } @@ -1099,10 +1685,11 @@ sub signal_setup () { $SIG{INT}= \&handle_int_signal; } + sub handle_int_signal () { $SIG{INT}= 'DEFAULT'; # If we get a ^C again, we die... mtr_warning("got INT signal, cleaning up....."); - stop_masters_slaves(); + stop_all_servers(); mtr_error("We die from ^C signal from user"); } @@ -1118,31 +1705,24 @@ sub kill_running_server () { if ( $opt_fast or $glob_use_embedded_server ) { # FIXME is embedded server really using PID files?! - unlink($master->[0]->{'path_mypid'}); - unlink($master->[1]->{'path_mypid'}); - unlink($slave->[0]->{'path_mypid'}); - unlink($slave->[1]->{'path_mypid'}); - unlink($slave->[2]->{'path_mypid'}); + unlink($master->[0]->{'path_pid'}); + unlink($master->[1]->{'path_pid'}); + unlink($slave->[0]->{'path_pid'}); + unlink($slave->[1]->{'path_pid'}); + unlink($slave->[2]->{'path_pid'}); } else { # Ensure that no old mysqld test servers are running # This is different from terminating processes we have - # started from ths run of the script, this is terminating + # started from this run of the script, this is terminating # leftovers from previous runs. - mtr_report("Killing Possible Leftover Processes"); - mkpath("$opt_vardir/log"); # Needed for mysqladmin log mtr_kill_leftovers(); - - ndbcluster_stop(); - $master->[0]->{'ndbcluster'}= 1; - } + } } -sub kill_and_cleanup () { - - kill_running_server (); +sub cleanup_stale_files () { mtr_report("Removing Stale Files"); @@ -1186,16 +1766,7 @@ sub kill_and_cleanup () { mkpath("$opt_vardir/tmp"); mkpath($opt_tmpdir) if $opt_tmpdir ne "$opt_vardir/tmp"; - # FIXME do we really need to create these all, or are they - # created for us when tables are created? - - my @data_dir_lst = ( - $master->[0]->{'path_myddir'}, - $master->[1]->{'path_myddir'}, - $slave->[0]->{'path_myddir'}, - $slave->[1]->{'path_myddir'}, - $slave->[2]->{'path_myddir'}); - + # Remove old and create new data dirs foreach my $data_dir (@data_dir_lst) { rmtree("$data_dir"); @@ -1214,104 +1785,353 @@ sub kill_and_cleanup () { mkpath("$opt_vardir/std_data_ln"); opendir(DIR, "$glob_mysql_test_dir/std_data") or mtr_error("Can't find the std_data directory: $!"); - for my $elem ( readdir(DIR) ) { - next if -d "$glob_mysql_test_dir/std_data/$elem"; - copy("$glob_mysql_test_dir/std_data/$elem", "$opt_vardir/std_data_ln/$elem"); + for(readdir(DIR)) { + next if -d "$glob_mysql_test_dir/std_data/$_"; + copy("$glob_mysql_test_dir/std_data/$_", "$opt_vardir/std_data_ln/$_"); } closedir(DIR); } } +sub check_running_as_root () { + # Check if running as root + # i.e a file can be read regardless what mode we set it to + my $test_file= "$opt_vardir/test_running_as_root.txt"; + mtr_tofile($test_file, "MySQL"); + chmod(oct("0000"), $test_file); + + my $result=""; + if (open(FILE,"<",$test_file)) + { + $result= join('', ); + close FILE; + } + + chmod(oct("0755"), $test_file); + unlink($test_file); + + $ENV{'MYSQL_TEST_ROOT'}= "NO"; + if ($result eq "MySQL") + { + mtr_warning("running this script as _root_ will cause some " . + "tests to be skipped"); + $ENV{'MYSQL_TEST_ROOT'}= "YES"; + } +} + + +sub check_ssl_support ($) { + my $mysqld_variables= shift; + + if ($opt_skip_ssl || $opt_extern) + { + mtr_report("Skipping SSL"); + $opt_ssl_supported= 0; + $opt_ssl= 0; + return; + } + + if ( $mysqld_variables->{'ssl'} eq "FALSE" ) + { + if ( $opt_ssl) + { + mtr_error("Couldn't find support for SSL"); + return; + } + mtr_report("Skipping SSL, mysqld not compiled with SSL"); + $opt_ssl_supported= 0; + $opt_ssl= 0; + return; + } + mtr_report("Setting mysqld to support SSL connections"); + $opt_ssl_supported= 1; +} + + +sub check_debug_support ($) { + my $mysqld_variables= shift; + + if ( $mysqld_variables->{'debug'} ) + { + #mtr_report("Binaries are not debug compiled"); + $debug_compiled_binaries= 0; + + if ( $opt_debug ) + { + mtr_error("Can't use --debug, binaries does not support it"); + } + return; + } + mtr_report("Binaries are debug compiled"); + $debug_compiled_binaries= 1; +} + ############################################################################## # # Start the ndb cluster # ############################################################################## -sub ndbcluster_support () { +sub check_ndbcluster_support ($) { + my $mysqld_variables= shift; - # check ndbcluster support by testing using a switch - # that is only available in that case - if ( mtr_run($exe_mysqld, - ["--no-defaults", - "--ndb-use-exact-count", - "--help"], - "", "/dev/null", "/dev/null", "") != 0 ) - { - mtr_report("No ndbcluster support"); - return 0; - } - mtr_report("Has ndbcluster support"); - return 1; -} - -# FIXME why is there a different start below?! - -sub ndbcluster_install () { - - if ( ! $opt_with_ndbcluster or $glob_use_running_ndbcluster ) - { - return 0; - } - mtr_report("Install ndbcluster"); - my $ndbcluster_opts= $opt_bench ? "" : "--small"; - my $ndbcluster_port_base= $opt_ndbcluster_port + 2; - if ( mtr_run("$glob_mysql_test_dir/ndb/ndbcluster", - ["--port=$opt_ndbcluster_port", - "--port-base=$ndbcluster_port_base", - "--data-dir=$opt_vardir", - $ndbcluster_opts, - "--initial"], - "", "", "", "") ) - { - mtr_error("Error ndbcluster_install"); - return 1; - } - - ndbcluster_stop(); - $master->[0]->{'ndbcluster'}= 1; - - return 0; -} - -sub ndbcluster_start () { - - if ( ! $opt_with_ndbcluster or $glob_use_running_ndbcluster ) - { - return 0; - } - # FIXME, we want to _append_ output to file $file_ndb_testrun_log instead of /dev/null - if ( mtr_run("$glob_mysql_test_dir/ndb/ndbcluster", - ["--port=$opt_ndbcluster_port", - "--data-dir=$opt_vardir"], - "", "/dev/null", "", "") ) - { - mtr_error("Error ndbcluster_start"); - return 1; - } - - return 0; -} - -sub ndbcluster_stop () { - - if ( ! $opt_with_ndbcluster or $glob_use_running_ndbcluster ) + if ($opt_skip_ndbcluster) { + mtr_report("Skipping ndbcluster"); + $opt_skip_ndbcluster_slave= 1; return; } - my $ndbcluster_port_base= $opt_ndbcluster_port + 2; - # FIXME, we want to _append_ output to file $file_ndb_testrun_log instead of /dev/null - mtr_run("$glob_mysql_test_dir/ndb/ndbcluster", - ["--port=$opt_ndbcluster_port", - "--data-dir=$opt_vardir", - "--stop"], - "", "/dev/null", "", ""); + if ( $mysqld_variables->{'ndbcluster'} eq "FALSE") + { + mtr_report("Skipping ndbcluster, mysqld not compiled with ndbcluster"); + $opt_skip_ndbcluster= 1; + $opt_skip_ndbcluster_slave= 1; + return; + } + $opt_ndbcluster_supported= 1; + mtr_report("Using ndbcluster when necessary, mysqld supports it"); return; } +sub ndbcluster_start_install ($) { + my $cluster= shift; + + if ( $opt_skip_ndbcluster or $glob_use_running_ndbcluster ) + { + return 0; + } + + mtr_report("Installing $cluster->{'name'} Cluster"); + + mkdir($cluster->{'data_dir'}); + + # Create a config file from template + my $ndb_no_ord=512; + my $ndb_no_attr=2048; + my $ndb_con_op=105000; + my $ndb_dmem="80M"; + my $ndb_imem="24M"; + my $ndb_pbmem="32M"; + my $nodes= $cluster->{'nodes'}; + my $ndb_host= "localhost"; + my $ndb_diskless= 0; + + if (!$opt_bench) + { + # Use a smaller configuration + $ndb_no_ord=32; + $ndb_con_op=5000; + $ndb_dmem="20M"; + $ndb_imem="1M"; + $ndb_pbmem="4M"; + } + + my $config_file_template= "ndb/ndb_config_${nodes}_node.ini"; + my $config_file= "$cluster->{'data_dir'}/config.ini"; + + open(IN, $config_file_template) + or mtr_error("Can't open $config_file_template: $!"); + open(OUT, ">", $config_file) + or mtr_error("Can't write to $config_file: $!"); + while () + { + chomp; + + s/CHOOSE_MaxNoOfAttributes/$ndb_no_attr/; + s/CHOOSE_MaxNoOfOrderedIndexes/$ndb_no_ord/; + s/CHOOSE_MaxNoOfConcurrentOperations/$ndb_con_op/; + s/CHOOSE_DataMemory/$ndb_dmem/; + s/CHOOSE_IndexMemory/$ndb_imem/; + s/CHOOSE_Diskless/$ndb_diskless/; + s/CHOOSE_HOSTNAME_.*/$ndb_host/; + s/CHOOSE_FILESYSTEM/$cluster->{'data_dir'}/; + s/CHOOSE_PORT_MGM/$cluster->{'port'}/; + s/CHOOSE_DiskPageBufferMemory/$ndb_pbmem/; + + print OUT "$_ \n"; + } + close OUT; + close IN; + + + # Start cluster with "--initial" + + ndbcluster_start($cluster, "--initial"); + + return 0; +} + + +sub ndbcluster_wait_started($$){ + my $cluster= shift; + my $ndb_waiter_extra_opt= shift; + my $path_waiter_log= "$cluster->{'data_dir'}/ndb_waiter.log"; + my $args; + + mtr_init_args(\$args); + + mtr_add_arg($args, "--no-defaults"); + mtr_add_arg($args, "--core"); + mtr_add_arg($args, "--ndb-connectstring=%s", $cluster->{'connect_string'}); + mtr_add_arg($args, "--timeout=60"); + + if ($ndb_waiter_extra_opt) + { + mtr_add_arg($args, "$ndb_waiter_extra_opt"); + } + + # Start the ndb_waiter which will connect to the ndb_mgmd + # and poll it for state of the ndbd's, will return when + # all nodes in the cluster is started + my $res= mtr_run($exe_ndb_waiter, $args, + "", $path_waiter_log, $path_waiter_log, ""); + mtr_verbose("ndbcluster_wait_started, returns: $res") if $res; + return $res; +} + + + +sub mysqld_wait_started($){ + my $mysqld= shift; + + my $res= sleep_until_file_created($mysqld->{'path_pid'}, + $mysqld->{'start_timeout'}, + $mysqld->{'pid'}); + return $res == 0; +} + + +sub ndb_mgmd_wait_started($) { + my ($cluster)= @_; + + my $retries= 100; + while (ndbcluster_wait_started($cluster, "--no-contact") and + $retries) + { + # Millisceond sleep emulated with select + select(undef, undef, undef, (0.1)); + + $retries--; + } + + return $retries == 0; + +} + +sub ndb_mgmd_start ($) { + my $cluster= shift; + + my $args; # Arg vector + my $pid= -1; + + mtr_init_args(\$args); + mtr_add_arg($args, "--no-defaults"); + mtr_add_arg($args, "--core"); + mtr_add_arg($args, "--nodaemon"); + mtr_add_arg($args, "--config-file=%s", "$cluster->{'data_dir'}/config.ini"); + + + my $path_ndb_mgmd_log= "$cluster->{'data_dir'}/\l$cluster->{'name'}_ndb_mgmd.log"; + $pid= mtr_spawn($exe_ndb_mgmd, $args, "", + $path_ndb_mgmd_log, + $path_ndb_mgmd_log, + "", + { append_log_file => 1 }); + + # FIXME Should not be needed + # Unfortunately the cluster nodes will fail to start + # if ndb_mgmd has not started properly + if (ndb_mgmd_wait_started($cluster)) + { + mtr_error("Failed to wait for start of ndb_mgmd"); + } + + # Remember pid of ndb_mgmd + $cluster->{'pid'}= $pid; + + mtr_verbose("ndb_mgmd_start, pid: $pid"); + + return $pid; +} + + +sub ndbd_start ($$$) { + my $cluster= shift; + my $idx= shift; + my $extra_args= shift; + + my $args; # Arg vector + my $pid= -1; + + mtr_init_args(\$args); + mtr_add_arg($args, "--no-defaults"); + mtr_add_arg($args, "--core"); + mtr_add_arg($args, "--ndb-connectstring=%s", "$cluster->{'connect_string'}"); + mtr_add_arg($args, "--character-sets-dir=%s", "$path_charsetsdir"); + mtr_add_arg($args, "--nodaemon"); + mtr_add_arg($args, "$extra_args"); + + my $nodeid= $cluster->{'ndbds'}->[$idx]->{'nodeid'}; + my $path_ndbd_log= "$cluster->{'data_dir'}/ndb_${nodeid}.log"; + $pid= mtr_spawn($exe_ndbd, $args, "", + $path_ndbd_log, + $path_ndbd_log, + "", + { append_log_file => 1 }); + + # Add pid to list of pids for this cluster + $cluster->{'ndbds'}->[$idx]->{'pid'}= $pid; + + # Rememeber options used when starting + $cluster->{'ndbds'}->[$idx]->{'start_extra_args'}= $extra_args; + $cluster->{'ndbds'}->[$idx]->{'idx'}= $idx; + + mtr_verbose("ndbd_start, pid: $pid"); + + return $pid; +} + + +sub ndbcluster_start ($$) { + my $cluster= shift; + my $extra_args= shift; + + mtr_verbose("ndbcluster_start '$cluster->{'name'}'"); + + if ( $glob_use_running_ndbcluster ) + { + return 0; + } + + if ( $cluster->{'pid'} ) + { + mtr_error("Cluster '$cluster->{'name'}' already started"); + } + + ndb_mgmd_start($cluster); + + for ( my $idx= 0; $idx < $cluster->{'nodes'}; $idx++ ) + { + ndbd_start($cluster, $idx, $extra_args); + } + + return 0; +} + + +sub rm_ndbcluster_tables ($) { + my $dir= shift; + foreach my $bin ( glob("$dir/cluster/apply_status*"), + glob("$dir/cluster/schema*") ) + { + unlink($bin); + } +} + + ############################################################################## # # Run the benchmark suite @@ -1323,9 +2143,9 @@ sub run_benchmarks ($) { my $args; - if ( ! $glob_use_embedded_server and ! $opt_local_master ) + if ( ! $glob_use_embedded_server ) { - $master->[0]->{'pid'}= mysqld_start('master',0,[],[]); + mysqld_start($master->[0],[],[]); if ( ! $master->[0]->{'pid'} ) { mtr_error("Can't start the mysqld server"); @@ -1334,7 +2154,7 @@ sub run_benchmarks ($) { mtr_init_args(\$args); - mtr_add_arg($args, "--socket=%s", $master->[0]->{'path_mysock'}); + mtr_add_arg($args, "--socket=%s", $master->[0]->{'path_sock'}); mtr_add_arg($args, "--user=%s", $opt_user); if ( $opt_small_bench ) @@ -1348,10 +2168,8 @@ sub run_benchmarks ($) { mtr_add_arg($args, "--create-options=TYPE=ndb"); } - my $benchdir= "$glob_basedir/sql-bench"; - chdir($benchdir); # FIXME check error - - # FIXME write shorter.... + chdir($glob_mysql_bench_dir) + or mtr_error("Couldn't chdir to '$glob_mysql_bench_dir': $!"); if ( ! $benchmark ) { @@ -1384,29 +2202,26 @@ sub run_benchmarks ($) { # ############################################################################## -# FIXME how to specify several suites to run? Comma separated list? - -sub run_tests () { - run_suite($opt_suite); -} - sub run_suite () { - my $suite= shift; + my ($suite, $tests)= @_; mtr_print_thick_line(); - mtr_report("Finding Tests in the '$suite' suite"); - mtr_timer_start($glob_timers,"suite", 60 * $opt_suite_timeout); - my $tests= collect_test_cases($suite); - mtr_report("Starting Tests in the '$suite' suite"); + mtr_report_tests_not_skipped_though_disabled($tests); + mtr_print_header(); foreach my $tinfo ( @$tests ) { + if (run_testcase_check_skip_test($tinfo)) + { + next; + } + mtr_timer_start($glob_timers,"testcase", 60 * $opt_testcase_timeout); run_testcase($tinfo); mtr_timer_stop($glob_timers,"testcase"); @@ -1414,10 +2229,11 @@ sub run_suite () { mtr_print_line(); - if ( ! $opt_gdb and ! $glob_use_running_server and - ! $opt_ddd and ! $glob_use_embedded_server ) + if ( ! $glob_debugger and + ! $glob_use_running_server and + ! $glob_use_embedded_server ) { - stop_masters_slaves(); + stop_all_servers(); } if ( $opt_gcov ) @@ -1431,6 +2247,8 @@ sub run_suite () { mtr_report_stats($tests); +# Look for testname.warning files if --do-test= + mtr_timer_stop($glob_timers,"suite"); } @@ -1441,26 +2259,99 @@ sub run_suite () { # ############################################################################## +sub initialize_servers () { + if ( ! $glob_use_running_server ) + { + kill_running_server(); + + unless ( $opt_start_dirty ) + { + cleanup_stale_files(); + mysql_install_db(); + if ( $opt_force ) + { + save_installed_db(); + } + } + check_running_as_root(); + } +} + sub mysql_install_db () { - # FIXME not exactly true I think, needs improvements install_db('master', $master->[0]->{'path_myddir'}); - install_db('master', $master->[1]->{'path_myddir'}); - install_db('slave', $slave->[0]->{'path_myddir'}); - install_db('slave', $slave->[1]->{'path_myddir'}); - install_db('slave', $slave->[2]->{'path_myddir'}); - if ( ndbcluster_install() ) + # FIXME check if testcase really is using second master + copy_install_db('master', $master->[1]->{'path_myddir'}); + + # Install the number of slave databses needed + for (my $idx= 0; $idx < $max_slave_num; $idx++) { - # failed to install, disable usage but flag that its no ok - $opt_with_ndbcluster= 0; - $flag_ndb_status_ok= 0; + copy_install_db("slave".($idx+1), $slave->[$idx]->{'path_myddir'}); } + if ( ! $opt_skip_im ) + { + im_prepare_env($instance_manager); + } + + my $cluster_started_ok= 1; # Assume it can be started + + if (ndbcluster_start_install($clusters->[0]) || + $max_slave_num && ndbcluster_start_install($clusters->[1])) + { + mtr_warning("Failed to start install of cluster"); + $cluster_started_ok= 0; + } + + foreach my $cluster (@{$clusters}) + { + + next if !$cluster->{'pid'}; + + $cluster->{'installed_ok'}= 1; # Assume install suceeds + + if (ndbcluster_wait_started($cluster, "")) + { + # failed to install, disable usage and flag that its no ok + mtr_report("ndbcluster_install of $cluster->{'name'} failed"); + $cluster->{"installed_ok"}= 0; + + $cluster_started_ok= 0; + } + } + + if ( ! $cluster_started_ok ) + { + if ( $opt_force) + { + # Continue without cluster + } + else + { + mtr_error("To continue, re-run with '--force'."); + } + } + + # Stop clusters... + stop_all_servers(); + return 0; } +sub copy_install_db ($$) { + my $type= shift; + my $data_dir= shift; + + mtr_report("Installing \u$type Database"); + + # Just copy the installed db from first master + mtr_copy_dir($master->[0]->{'path_myddir'}, $data_dir); + +} + + sub install_db ($$) { my $type= shift; my $data_dir= shift; @@ -1469,7 +2360,7 @@ sub install_db ($$) { my $init_db_sql_tmp= "/tmp/init_db.sql$$"; my $args; - mtr_report("Installing \u$type Databases"); + mtr_report("Installing \u$type Database"); open(IN, $init_db_sql) or mtr_error("Can't open $init_db_sql: $!"); @@ -1505,16 +2396,29 @@ sub install_db ($$) { mtr_add_arg($args, "--datadir=%s", $data_dir); mtr_add_arg($args, "--skip-innodb"); mtr_add_arg($args, "--skip-ndbcluster"); - mtr_add_arg($args, "--skip-bdb"); + mtr_add_arg($args, "--tmpdir=."); - if ( ! $opt_netware ) + if ( $opt_debug ) + { + mtr_add_arg($args, "--debug=d:t:i:A,%s/log/bootstrap_%s.trace", + $path_vardir_trace, $type); + } + + if ( ! $glob_netware ) { mtr_add_arg($args, "--language=%s", $path_language); mtr_add_arg($args, "--character-sets-dir=%s", $path_charsetsdir); } + # Log bootstrap command + my $path_bootstrap_log= "$opt_vardir/log/bootstrap.log"; + mtr_tofile($path_bootstrap_log, + "$exe_mysqld " . join(" ", @$args) . "\n"); + if ( mtr_run($exe_mysqld, $args, $init_db_sql_tmp, - $path_manager_log, $path_manager_log, "") != 0 ) + $path_bootstrap_log, $path_bootstrap_log, + "", { append_log_file => 1 }) != 0 ) + { unlink($init_db_sql_tmp); mtr_error("Error executing mysqld --bootstrap\n" . @@ -1524,6 +2428,196 @@ sub install_db ($$) { } +sub im_prepare_env($) { + my $instance_manager = shift; + + im_create_passwd_file($instance_manager); + im_prepare_data_dir($instance_manager); +} + + +sub im_create_passwd_file($) { + my $instance_manager = shift; + + my $pwd_file_path = $instance_manager->{'password_file'}; + + mtr_report("Creating IM password file ($pwd_file_path)"); + + open(OUT, ">", $pwd_file_path) + or mtr_error("Can't write to $pwd_file_path: $!"); + + print OUT $instance_manager->{'admin_login'}, ":", + $instance_manager->{'admin_sha1'}, "\n"; + + close(OUT); +} + + +sub im_create_defaults_file($) { + my $instance_manager = shift; + + my $defaults_file = $instance_manager->{'defaults_file'}; + + open(OUT, ">", $defaults_file) + or mtr_error("Can't write to $defaults_file: $!"); + + print OUT <{path_pid} +angel-pid-file = $instance_manager->{path_angel_pid} +socket = $instance_manager->{path_sock} +port = $instance_manager->{port} +password-file = $instance_manager->{password_file} +default-mysqld-path = $exe_mysqld + +EOF +; + + foreach my $instance (@{$instance_manager->{'instances'}}) + { + my $server_id = $instance->{'server_id'}; + + print OUT <{path_sock} +pid-file = $instance->{path_pid} +port = $instance->{port} +datadir = $instance->{path_datadir} +log = $instance->{path_datadir}/mysqld$server_id.log +log-error = $instance->{path_datadir}/mysqld$server_id.err.log +log-slow-queries = $instance->{path_datadir}/mysqld$server_id.slow.log +language = $path_language +character-sets-dir = $path_charsetsdir +basedir = $path_my_basedir +server_id = $server_id +skip-stack-trace +skip-innodb +skip-ndbcluster +EOF +; + + print OUT "nonguarded\n" if $instance->{'nonguarded'}; + print OUT "log-output=FILE\n" if $instance->{'old_log_format'}; + print OUT "\n"; + } + + close(OUT); +} + + +sub im_prepare_data_dir($) { + my $instance_manager = shift; + + foreach my $instance (@{$instance_manager->{'instances'}}) + { + copy_install_db( + 'im_mysqld_' . $instance->{'server_id'}, + $instance->{'path_datadir'}); + } +} + + + +# +# Restore snapshot of the installed slave databases +# if the snapshot exists +# +sub restore_slave_databases ($) { + my ($num_slaves)= @_; + + if ( -d $path_snapshot) + { + for (my $idx= 0; $idx < $num_slaves; $idx++) + { + my $data_dir= $slave->[$idx]->{'path_myddir'}; + my $name= basename($data_dir); + rmtree($data_dir); + mtr_copy_dir("$path_snapshot/$name", $data_dir); + } + } +} + + +sub run_testcase_check_skip_test($) +{ + my ($tinfo)= @_; + + # ---------------------------------------------------------------------- + # If marked to skip, just print out and return. + # Note that a test case not marked as 'skip' can still be + # skipped later, because of the test case itself in cooperation + # with the mysqltest program tells us so. + # ---------------------------------------------------------------------- + + if ( $tinfo->{'skip'} ) + { + mtr_report_test_name($tinfo); + mtr_report_test_skipped($tinfo); + return 1; + } + + # If test needs cluster, check that master installed ok + if ( $tinfo->{'ndb_test'} and !$clusters->[0]->{'installed_ok'} ) + { + mtr_report_test_name($tinfo); + mtr_report_test_failed($tinfo); + return 1; + } + + # If test needs slave cluster, check that it installed ok + if ( $tinfo->{'ndb_test'} and $tinfo->{'slave_num'} and + !$clusters->[1]->{'installed_ok'} ) + { + mtr_report_test_name($tinfo); + mtr_report_test_failed($tinfo); + return 1; + } + + return 0; +} + + +sub do_before_run_mysqltest($) +{ + my $tinfo= shift; + my $tname= $tinfo->{'name'}; + + # Remove old reject file + if ( $opt_suite eq "main" ) + { + unlink("r/$tname.reject"); + } + else + { + unlink("suite/$opt_suite/r/$tname.reject"); + } + + mtr_tonewfile($path_current_test_log,"$tname\n"); # Always tell where we are + + # output current test to ndbcluster log file to enable diagnostics + mtr_tofile($path_ndb_testrun_log,"CURRENT TEST $tname\n"); + + mtr_tofile($master->[0]->{'path_myerr'},"CURRENT_TEST: $tname\n"); + if ( $master->[1]->{'pid'} ) + { + mtr_tofile($master->[1]->{'path_myerr'},"CURRENT_TEST: $tname\n"); + } +} + +sub do_after_run_mysqltest($) +{ + my $tinfo= shift; + my $tname= $tinfo->{'name'}; + + # Save info from this testcase run to mysqltest.log + mtr_appendfile_to_file($path_timefile, $path_mysqltest_log) + if -f $path_timefile; + mtr_tofile($path_mysqltest_log,"CURRENT TEST $tname\n"); +} + + ############################################################################## # # Run a single test case @@ -1543,231 +2637,34 @@ sub install_db ($$) { sub run_testcase ($) { my $tinfo= shift; - my $tname= $tinfo->{'name'}; + my $master_restart= run_testcase_need_master_restart($tinfo); + my $slave_restart= run_testcase_need_slave_restart($tinfo); - mtr_tonewfile($opt_current_test,"$tname\n"); # Always tell where we are - - # output current test to ndbcluster log file to enable diagnostics - mtr_tofile($file_ndb_testrun_log,"CURRENT TEST $tname\n"); - - # ---------------------------------------------------------------------- - # If marked to skip, just print out and return. - # Note that a test case not marked as 'skip' can still be - # skipped later, because of the test case itself in cooperation - # with the mysqltest program tells us so. - # ---------------------------------------------------------------------- - - if ( $tinfo->{'skip'} ) + if ($master_restart or $slave_restart) { - mtr_report_test_name($tinfo); - mtr_report_test_skipped($tinfo); - return; + run_testcase_stop_servers($tinfo, $master_restart, $slave_restart); + } + my $died= mtr_record_dead_children(); + if ($died or $master_restart or $slave_restart) + { + run_testcase_start_servers($tinfo); } # ---------------------------------------------------------------------- - # If not using a running servers we may need to stop and restart. - # We restart in the case we have initiation scripts, server options - # etc to run. But we also restart again after the test first restart - # and test is run, to get back to normal server settings. - # - # To make the code a bit more clean, we actually only stop servers - # here, and mark this to be done. Then a generic "start" part will - # start up the needed servers again. + # If --start-and-exit or --start-dirty given, stop here to let user manually + # run tests # ---------------------------------------------------------------------- - - if ( ! $glob_use_running_server and ! $glob_use_embedded_server ) - { - # We try to find out if we are to restart the server - my $do_restart= 0; # Assumes we don't have to - - if ( $tinfo->{'master_sh'} ) - { - $do_restart= 1; # Always restart if script to run - } - elsif ( $tinfo->{'ndb_test'} and $master->[0]->{'ndbcluster'} == 1 ) - { - $do_restart= 1; # Restart with cluster - # print "Restarting because cluster need to be enabled\n"; - } - elsif ($tinfo->{'ndb_test'} == 0 and $master->[0]->{'ndbcluster'} == 0) - { - $do_restart= 1; # Restart without cluster - # print "Restarting because cluster need to be disabled\n"; - } - elsif ( $master->[0]->{'running_master_is_special'} and - $master->[0]->{'running_master_is_special'}->{'timezone'} eq - $tinfo->{'timezone'} and - mtr_same_opts($master->[0]->{'running_master_is_special'}->{'master_opt'}, - $tinfo->{'master_opt'}) ) - { - # If running master was started with special settings, but - # the current test requuires the same ones, we *don't* restart. - $do_restart= 0; - } - elsif ( $tinfo->{'master_restart'} or - $master->[0]->{'running_master_is_special'} ) - { - $do_restart= 1; - } - - if ( $do_restart ) - { - stop_masters(); - delete $master->[0]->{'running_master_is_special'}; # Forget history - } - - # ---------------------------------------------------------------------- - # Always terminate all slaves, if any. Else we may have useless - # reconnection attempts and error messages in case the slave and - # master servers restart. - # ---------------------------------------------------------------------- - - stop_slaves(); - } - - # ---------------------------------------------------------------------- - # Prepare to start masters. Even if we use embedded, we want to run - # the preparation. - # ---------------------------------------------------------------------- - - $ENV{'TZ'}= $tinfo->{'timezone'}; - - mtr_report_test_name($tinfo); - - mtr_tofile($master->[0]->{'path_myerr'},"CURRENT_TEST: $tname\n"); - -# FIXME test cases that depend on each other, prevent this from -# being at this location. -# do_before_start_master($tname,$tinfo->{'master_sh'}); - - # ---------------------------------------------------------------------- - # If any mysqld servers running died, we have to know - # ---------------------------------------------------------------------- - - mtr_record_dead_children(); - - # ---------------------------------------------------------------------- - # Start masters - # ---------------------------------------------------------------------- - - if ( ! $glob_use_running_server and ! $glob_use_embedded_server ) - { - # FIXME give the args to the embedded server?! - # FIXME what does $opt_local_master mean?! - # FIXME split up start and check that started so that can do - # starts in parallel, masters and slaves at the same time. - - if ( ! $opt_local_master ) - { - if ( $master->[0]->{'ndbcluster'} && $tinfo->{'ndb_test'}) - { - $master->[0]->{'ndbcluster'}= ndbcluster_start(); - if ( $master->[0]->{'ndbcluster'} ) - { - report_failure_and_restart($tinfo); - return; - } - } - if ( ! $master->[0]->{'pid'} ) - { - # FIXME not correct location for do_before_start_master() - do_before_start_master($tname,$tinfo->{'master_sh'}); - - # Save skip_ndbcluster - my $save_opt_skip_ndbcluster= $opt_skip_ndbcluster; - if (!$tinfo->{'ndb_test'}) - { - # Modify skip_ndbcluster so cluster is skipped for this - # and subsequent testcases(until we find one that does not cluster) - $opt_skip_ndbcluster= 1; - } - - $master->[0]->{'pid'}= - mysqld_start('master',0,$tinfo->{'master_opt'},[]); - - # Restore skip_ndbcluster - $opt_skip_ndbcluster= $save_opt_skip_ndbcluster; - - if ( ! $master->[0]->{'pid'} ) - { - report_failure_and_restart($tinfo); - return; - } - } - if ( $opt_with_ndbcluster and ! $master->[1]->{'pid'} ) - { - # Test needs cluster, start an extra mysqld connected to cluster - $master->[1]->{'pid'}= - mysqld_start('master',1,$tinfo->{'master_opt'},[]); - if ( ! $master->[1]->{'pid'} ) - { - report_failure_and_restart($tinfo); - return; - } - } - - if ( $tinfo->{'master_restart'} ) - { - # Save this test case information, so next can examine it - $master->[0]->{'running_master_is_special'}= $tinfo; - } - } - - # ---------------------------------------------------------------------- - # Start slaves - if needed - # ---------------------------------------------------------------------- - - if ( $tinfo->{'slave_num'} ) - { - mtr_tofile($slave->[0]->{'path_myerr'},"CURRENT_TEST: $tname\n"); - - do_before_start_slave($tname,$tinfo->{'slave_sh'}); - - for ( my $idx= 0; $idx < $tinfo->{'slave_num'}; $idx++ ) - { - if ( ! $slave->[$idx]->{'pid'} ) - { - $slave->[$idx]->{'pid'}= - mysqld_start('slave',$idx, - $tinfo->{'slave_opt'}, $tinfo->{'slave_mi'}); - if ( ! $slave->[$idx]->{'pid'} ) - { - report_failure_and_restart($tinfo); - return; - } - } - } - } - } - - # ---------------------------------------------------------------------- - # If --start-and-exit given, stop here to let user manually run tests - # ---------------------------------------------------------------------- - - if ( $opt_start_and_exit ) + if ( $opt_start_and_exit or $opt_start_dirty ) { mtr_report("\nServers started, exiting"); exit(0); } - # ---------------------------------------------------------------------- - # Run the test case - # ---------------------------------------------------------------------- - { - # remove the old reject file - if ( $opt_suite eq "main" ) - { - unlink("r/$tname.reject"); - } - else - { - unlink("suite/$opt_suite/r/$tname.reject"); - } - unlink($path_timefile); + do_before_run_mysqltest($tinfo); my $res= run_mysqltest($tinfo); - + mtr_report_test_name($tinfo); if ( $res == 0 ) { mtr_report_test_passed($tinfo); @@ -1775,6 +2672,12 @@ sub run_testcase ($) { elsif ( $res == 62 ) { # Testcase itself tell us to skip this one + + # Try to get reason from mysqltest.log + my $last_line= mtr_lastlinefromfile($path_timefile) if -f $path_timefile; + my $reason= mtr_match_prefix($last_line, "reason: "); + $tinfo->{'comment'}= + defined $reason ? $reason : "Detected by testcase(reason unknown) "; mtr_report_test_skipped($tinfo); } elsif ( $res == 63 ) @@ -1791,38 +2694,128 @@ sub run_testcase ($) { "mysqltest returned unexpected code $res, " . "it has probably crashed"); } + report_failure_and_restart($tinfo); } + + do_after_run_mysqltest($tinfo); + } + + # ---------------------------------------------------------------------- + # Stop Instance Manager if we are processing an IM-test case. + # ---------------------------------------------------------------------- + + if ( ! $glob_use_running_server and $tinfo->{'component_id'} eq 'im' ) + { + unless ( mtr_im_stop($instance_manager, $tinfo->{'name'}) ) + { + mtr_error("Failed to stop Instance Manager.") + } } } +# +# Save a snapshot of the installed test db(s) +# I.e take a snapshot of the var/ dir +# +sub save_installed_db () { + + mtr_report("Saving snapshot of installed databases"); + rmtree($path_snapshot); + + foreach my $data_dir (@data_dir_lst) + { + my $name= basename($data_dir); + mtr_copy_dir("$data_dir", "$path_snapshot/$name"); + } +} + + +# +# Save any interesting files in the data_dir +# before the data dir is removed. +# +sub save_files_before_restore($$) { + my $test_name= shift; + my $data_dir= shift; + my $save_name= "$opt_vardir/log/$test_name"; + + # Look for core files + foreach my $core_file ( glob("$data_dir/core*") ) + { + my $core_name= basename($core_file); + mtr_report("Saving $core_name"); + mkdir($save_name) if ! -d $save_name; + rename("$core_file", "$save_name/$core_name"); + } +} + + +# +# Restore snapshot of the installed test db(s) +# if the snapshot exists +# +sub restore_installed_db ($) { + my $test_name= shift; + + if ( -d $path_snapshot) + { + mtr_report("Restoring snapshot of databases"); + + foreach my $data_dir (@data_dir_lst) + { + my $name= basename($data_dir); + save_files_before_restore($test_name, $data_dir); + rmtree("$data_dir"); + mtr_copy_dir("$path_snapshot/$name", "$data_dir"); + } + + # Remove the ndb_*_fs dirs for all ndbd nodes + # forcing a clean start of ndb + foreach my $cluster (@{$clusters}) + { + foreach my $ndbd (@{$cluster->{'ndbds'}}) + { + rmtree("$ndbd->{'path_fs'}" ); + } + } + } + else + { + # No snapshot existed + mtr_error("No snapshot existed"); + } +} + sub report_failure_and_restart ($) { my $tinfo= shift; mtr_report_test_failed($tinfo); mtr_show_failed_diff($tinfo->{'name'}); print "\n"; - if ( ! $opt_force ) + if ( $opt_force ) { - my $test_mode= join(" ", @::glob_test_mode) || "default"; - print "Aborting: $tinfo->{'name'} failed in $test_mode mode. "; - print "To continue, re-run with '--force'.\n"; - if ( ! $opt_gdb and ! $glob_use_running_server and - ! $opt_ddd and ! $glob_use_embedded_server ) - { - stop_masters_slaves(); - } - mtr_exit(1); + # Stop all servers that are known to be running + stop_all_servers(); + + # Restore the snapshot of the installed test db + restore_installed_db($tinfo->{'name'}); + print "Resuming Tests\n\n"; + return; } - # FIXME always terminate on failure?! - if ( ! $opt_gdb and ! $glob_use_running_server and - ! $opt_ddd and ! $glob_use_embedded_server ) + my $test_mode= join(" ", @::glob_test_mode) || "default"; + print "Aborting: $tinfo->{'name'} failed in $test_mode mode. "; + print "To continue, re-run with '--force'.\n"; + if ( ! $glob_debugger and + ! $glob_use_running_server and + ! $glob_use_embedded_server ) { - stop_masters_slaves(); + stop_all_servers(); } - print "Resuming Tests\n\n"; + mtr_exit(1); + } @@ -1832,9 +2825,9 @@ sub report_failure_and_restart ($) { # ############################################################################## + # The embedded server needs the cleanup so we do some of the start work # but stop before actually running mysqld or anything. - sub do_before_start_master ($$) { my $tname= shift; my $init_script= shift; @@ -1867,13 +2860,14 @@ sub do_before_start_master ($$) { if ( $ret != 0 ) { # FIXME rewrite those scripts to return 0 if successful -# mtr_warning("$init_script exited with code $ret"); + # mtr_warning("$init_script exited with code $ret"); } } # for gcov FIXME needed? If so we need more absolute paths -# chdir($glob_basedir); + # chdir($glob_basedir); } + sub do_before_start_slave ($$) { my $tname= shift; my $init_script= shift; @@ -1901,7 +2895,7 @@ sub do_before_start_slave ($$) { if ( $ret != 0 ) { # FIXME rewrite those scripts to return 0 if successful -# mtr_warning("$init_script exited with code $ret"); + # mtr_warning("$init_script exited with code $ret"); } } @@ -1911,9 +2905,10 @@ sub do_before_start_slave ($$) { } } + sub mysqld_arguments ($$$$$) { my $args= shift; - my $type= shift; # master/slave/bootstrap + my $type= shift; my $idx= shift; my $extra_opt= shift; my $slave_master_info= shift; @@ -1921,7 +2916,7 @@ sub mysqld_arguments ($$$$$) { my $sidx= ""; # Index as string, 0 is empty string if ( $idx > 0 ) { - $sidx= sprintf("%d", $idx); # sprintf not needed in Perl for this + $sidx= "$idx"; } my $prefix= ""; # If mysqltest server arg @@ -1937,15 +2932,19 @@ sub mysqld_arguments ($$$$$) { mtr_add_arg($args, "%s--console", $prefix); mtr_add_arg($args, "%s--basedir=%s", $prefix, $path_my_basedir); mtr_add_arg($args, "%s--character-sets-dir=%s", $prefix, $path_charsetsdir); - mtr_add_arg($args, "%s--core", $prefix); + + if ( $mysql_version_id >= 50000 ) + { + mtr_add_arg($args, "%s--log-bin-trust-function-creators", $prefix); + } + mtr_add_arg($args, "%s--default-character-set=latin1", $prefix); mtr_add_arg($args, "%s--language=%s", $prefix, $path_language); mtr_add_arg($args, "%s--tmpdir=$opt_tmpdir", $prefix); - if ( defined $opt_valgrind_mysqld ) + if ( $opt_valgrind_mysqld ) { mtr_add_arg($args, "%s--skip-safemalloc", $prefix); - mtr_add_arg($args, "%s--skip-bdb", $prefix); } my $pidfile; @@ -1954,29 +2953,41 @@ sub mysqld_arguments ($$$$$) { { my $id= $idx > 0 ? $idx + 101 : 1; - mtr_add_arg($args, "%s--log-bin=%s/log/master-bin%s", $prefix, - $opt_vardir, $sidx); + if (! $opt_skip_master_binlog) + { + mtr_add_arg($args, "%s--log-bin=%s/log/master-bin%s", $prefix, + $opt_vardir, $sidx); + } mtr_add_arg($args, "%s--pid-file=%s", $prefix, - $master->[$idx]->{'path_mypid'}); + $master->[$idx]->{'path_pid'}); mtr_add_arg($args, "%s--port=%d", $prefix, - $master->[$idx]->{'path_myport'}); + $master->[$idx]->{'port'}); mtr_add_arg($args, "%s--server-id=%d", $prefix, $id); mtr_add_arg($args, "%s--socket=%s", $prefix, - $master->[$idx]->{'path_mysock'}); - mtr_add_arg($args, "%s--innodb_data_file_path=ibdata1:50M", $prefix); + $master->[$idx]->{'path_sock'}); + mtr_add_arg($args, "%s--innodb_data_file_path=ibdata1:10M:autoextend", $prefix); mtr_add_arg($args, "%s--local-infile", $prefix); mtr_add_arg($args, "%s--datadir=%s", $prefix, $master->[$idx]->{'path_myddir'}); - if ( $idx > 0 ) + if ( $idx > 0 or !$use_innodb) { mtr_add_arg($args, "%s--skip-innodb", $prefix); } - if ( $opt_skip_ndbcluster ) + my $cluster= $clusters->[$master->[$idx]->{'cluster'}]; + if ( $opt_skip_ndbcluster || + !$cluster->{'pid'}) { mtr_add_arg($args, "%s--skip-ndbcluster", $prefix); } + else + { + mtr_add_arg($args, "%s--ndbcluster", $prefix); + mtr_add_arg($args, "%s--ndb-connectstring=%s", $prefix, + $cluster->{'connect_string'}); + mtr_add_arg($args, "%s--ndb-extra-logging", $prefix); + } } if ( $type eq 'slave' ) @@ -1986,25 +2997,26 @@ sub mysqld_arguments ($$$$$) { mtr_add_arg($args, "%s--datadir=%s", $prefix, $slave->[$idx]->{'path_myddir'}); - # FIXME slave get this option twice?! - mtr_add_arg($args, "%s--exit-info=256", $prefix); mtr_add_arg($args, "%s--init-rpl-role=slave", $prefix); - mtr_add_arg($args, "%s--log-bin=%s/log/slave%s-bin", $prefix, - $opt_vardir, $sidx); # FIXME use own dir for binlogs - mtr_add_arg($args, "%s--log-slave-updates", $prefix); - # FIXME option duplicated for slave + if (! $opt_skip_slave_binlog) + { + mtr_add_arg($args, "%s--log-bin=%s/log/slave%s-bin", $prefix, + $opt_vardir, $sidx); # FIXME use own dir for binlogs + mtr_add_arg($args, "%s--log-slave-updates", $prefix); + } + mtr_add_arg($args, "%s--log=%s", $prefix, $slave->[$idx]->{'path_mylog'}); mtr_add_arg($args, "%s--master-retry-count=10", $prefix); mtr_add_arg($args, "%s--pid-file=%s", $prefix, - $slave->[$idx]->{'path_mypid'}); + $slave->[$idx]->{'path_pid'}); mtr_add_arg($args, "%s--port=%d", $prefix, - $slave->[$idx]->{'path_myport'}); + $slave->[$idx]->{'port'}); mtr_add_arg($args, "%s--relay-log=%s/log/slave%s-relay-bin", $prefix, $opt_vardir, $sidx); mtr_add_arg($args, "%s--report-host=127.0.0.1", $prefix); mtr_add_arg($args, "%s--report-port=%d", $prefix, - $slave->[$idx]->{'path_myport'}); + $slave->[$idx]->{'port'}); mtr_add_arg($args, "%s--report-user=root", $prefix); mtr_add_arg($args, "%s--skip-innodb", $prefix); mtr_add_arg($args, "%s--skip-ndbcluster", $prefix); @@ -2014,9 +3026,9 @@ sub mysqld_arguments ($$$$$) { # on the server. The path need to have constant length otherwise # test results will vary, thus a relative path is used. mtr_add_arg($args, "%s--slave-load-tmpdir=%s", $prefix, - $path_slave_load_tmpdir); + "../tmp"); mtr_add_arg($args, "%s--socket=%s", $prefix, - $slave->[$idx]->{'path_mysock'}); + $slave->[$idx]->{'path_sock'}); mtr_add_arg($args, "%s--set-variable=slave_net_timeout=10", $prefix); if ( @$slave_master_info ) @@ -2033,10 +3045,24 @@ sub mysqld_arguments ($$$$$) { mtr_add_arg($args, "%s--master-host=127.0.0.1", $prefix); mtr_add_arg($args, "%s--master-password=", $prefix); mtr_add_arg($args, "%s--master-port=%d", $prefix, - $master->[0]->{'path_myport'}); # First master + $master->[0]->{'port'}); # First master mtr_add_arg($args, "%s--server-id=%d", $prefix, $slave_server_id); mtr_add_arg($args, "%s--rpl-recovery-rank=%d", $prefix, $slave_rpl_rank); } + + if ( $opt_skip_ndbcluster_slave || + $slave->[$idx]->{'cluster'} == -1 || + !$clusters->[$slave->[$idx]->{'cluster'}]->{'pid'} ) + { + mtr_add_arg($args, "%s--skip-ndbcluster", $prefix); + } + else + { + mtr_add_arg($args, "%s--ndbcluster", $prefix); + mtr_add_arg($args, "%s--ndb-connectstring=%s", $prefix, + $clusters->[$slave->[$idx]->{'cluster'}]->{'connect_string'}); + mtr_add_arg($args, "%s--ndb-extra-logging", $prefix); + } } # end slave if ( $opt_debug ) @@ -2044,28 +3070,21 @@ sub mysqld_arguments ($$$$$) { if ( $type eq 'master' ) { mtr_add_arg($args, "%s--debug=d:t:i:A,%s/log/master%s.trace", - $prefix, $opt_vardir, $sidx); + $prefix, $path_vardir_trace, $sidx); } if ( $type eq 'slave' ) { mtr_add_arg($args, "%s--debug=d:t:i:A,%s/log/slave%s.trace", - $prefix, $opt_vardir, $sidx); + $prefix, $path_vardir_trace, $sidx); } } - if ( $opt_with_ndbcluster && !$opt_skip_ndbcluster && $type eq 'master') - { - mtr_add_arg($args, "%s--ndbcluster", $prefix); - mtr_add_arg($args, "%s--ndb-connectstring=%s", $prefix, - $opt_ndbconnectstring); - } - # FIXME always set nowdays??? SMALL_SERVER mtr_add_arg($args, "%s--key_buffer_size=1M", $prefix); mtr_add_arg($args, "%s--sort_buffer=256K", $prefix); mtr_add_arg($args, "%s--max_heap_table_size=1M", $prefix); - if ( $opt_with_openssl ) + if ( $opt_ssl_supported ) { mtr_add_arg($args, "%s--ssl-ca=%s/std_data/cacert.pem", $prefix, $glob_mysql_test_dir); @@ -2080,7 +3099,8 @@ sub mysqld_arguments ($$$$$) { mtr_add_arg($args, "%s--log-warnings", $prefix); } - if ( $opt_gdb or $opt_client_gdb or $opt_manual_gdb or $opt_ddd) + # Indicate to "mysqld" it will be debugged in debugger + if ( $glob_debugger ) { mtr_add_arg($args, "%s--gdb", $prefix); } @@ -2093,27 +3113,22 @@ sub mysqld_arguments ($$$$$) { mtr_add_arg($args, "%s--user=root", $prefix); } - if ( $type eq 'master' ) - { - - if ( ! $opt_old_master ) - { - mtr_add_arg($args, "%s--rpl-recovery-rank=1", $prefix); - mtr_add_arg($args, "%s--init-rpl-role=master", $prefix); - } - - # FIXME strange,..... - # FIXME MYSQL_MYPORT is not set anythere?! - if ( $opt_local_master ) - { - mtr_add_arg($args, "%s--host=127.0.0.1", $prefix); - mtr_add_arg($args, "%s--port=%s", $prefix, $ENV{'MYSQL_MYPORT'}); - } - } - + my $found_skip_core= 0; foreach my $arg ( @opt_extra_mysqld_opt, @$extra_opt ) { - mtr_add_arg($args, "%s%s", $prefix, $arg); + # Allow --skip-core-file to be set in master.opt file + if ($arg eq "--skip-core-file") + { + $found_skip_core= 1; + } + else + { + mtr_add_arg($args, "%s%s", $prefix, $arg); + } + } + if ( !$found_skip_core ) + { + mtr_add_arg($args, "%s%s", $prefix, "--core-file"); } if ( $opt_bench ) @@ -2123,7 +3138,6 @@ sub mysqld_arguments ($$$$$) { } elsif ( $type eq 'master' ) { - mtr_add_arg($args, "%s--exit-info=256", $prefix); mtr_add_arg($args, "%s--open-files-limit=1024", $prefix); mtr_add_arg($args, "%s--log=%s", $prefix, $master->[0]->{'path_mylog'}); } @@ -2131,14 +3145,6 @@ sub mysqld_arguments ($$$$$) { return $args; } -# FIXME -# if ( $type eq 'master' and $glob_use_embedded_server ) -# { -# # Add a -A to each argument to pass it to embedded server -# my @mysqltest_opt= map {("-A",$_)} @args; -# $opt_extra_mysqltest_opt= \@mysqltest_opt; -# return; -# } ############################################################################## # @@ -2146,15 +3152,18 @@ sub mysqld_arguments ($$$$$) { # ############################################################################## -sub mysqld_start ($$$$) { - my $type= shift; # master/slave/bootstrap - my $idx= shift; +sub mysqld_start ($$$) { + my $mysqld= shift; my $extra_opt= shift; my $slave_master_info= shift; my $args; # Arg vector my $exe; - my $pid; + my $pid= -1; + my $wait_for_pid_file= 1; + + my $type= $mysqld->{'type'}; + my $idx= $mysqld->{'idx'}; if ( $type eq 'master' ) { @@ -2166,214 +3175,682 @@ sub mysqld_start ($$$$) { } else { - $exe= $exe_mysqld; + mtr_error("Unknown 'type' \"$type\" passed to mysqld_start"); } mtr_init_args(\$args); - if ( defined $opt_valgrind_mysqld ) + if ( $opt_valgrind_mysqld ) { valgrind_arguments($args, \$exe); } mysqld_arguments($args,$type,$idx,$extra_opt,$slave_master_info); - if ( $type eq 'master' ) + if ( $opt_gdb || $opt_manual_gdb) { - if ( $pid= mtr_spawn($exe, $args, "", - $master->[$idx]->{'path_myerr'}, - $master->[$idx]->{'path_myerr'}, - "", - { append_log_file => 1 }) ) + gdb_arguments(\$args, \$exe, "$type"."_$idx"); + } + elsif ( $opt_ddd || $opt_manual_ddd ) + { + ddd_arguments(\$args, \$exe, "$type"."_$idx"); + } + elsif ( $opt_debugger ) + { + debugger_arguments(\$args, \$exe, "$type"."_$idx"); + } + elsif ( $opt_manual_debug ) + { + print "\nStart $type in your debugger\n" . + "dir: $glob_mysql_test_dir\n" . + "exe: $exe\n" . + "args: " . join(" ", @$args) . "\n\n" . + "Waiting ....\n"; + + # Indicate the exe should not be started + $exe= undef; + } + else + { + # Default to not wait until pid file has been created + $wait_for_pid_file= 0; + } + + if ($exe_libtool and $opt_valgrind) + { + # Add "libtool --mode-execute" + # if running in valgrind(to avoid valgrinding bash) + unshift(@$args, "--mode=execute", $exe); + $exe= $exe_libtool; + } + + + if ( defined $exe ) + { + $pid= mtr_spawn($exe, $args, "", + $mysqld->{'path_myerr'}, + $mysqld->{'path_myerr'}, + "", + { append_log_file => 1 }); + } + + + if ( $wait_for_pid_file && !sleep_until_file_created($mysqld->{'path_pid'}, + $mysqld->{'start_timeout'}, + $pid)) + { + + mtr_error("Failed to start mysqld $mysqld->{'type'}"); + } + + + # Remember pid of the started process + $mysqld->{'pid'}= $pid; + + # Remember options used when starting + $mysqld->{'start_opts'}= $extra_opt; + $mysqld->{'start_slave_master_info'}= $slave_master_info; + + mtr_verbose("mysqld pid: $pid"); + return $pid; +} + + +sub stop_all_servers () { + + print "Stopping All Servers\n"; + + if ( ! $opt_skip_im ) + { + print "Shutting-down Instance Manager\n"; + unless (mtr_im_stop($instance_manager, "stop_all_servers")) { - return sleep_until_file_created($master->[$idx]->{'path_mypid'}, - $master->[$idx]->{'start_timeout'}, $pid); + mtr_error("Failed to stop Instance Manager.") } } - if ( $type eq 'slave' ) + my %admin_pids; # hash of admin processes that requests shutdown + my @kill_pids; # list of processes to shutdown/kill + my $pid; + + # Start shutdown of all started masters + foreach my $mysqld (@{$master}, @{$slave}) { - if ( $pid= mtr_spawn($exe, $args, "", - $slave->[$idx]->{'path_myerr'}, - $slave->[$idx]->{'path_myerr'}, - "", - { append_log_file => 1 }) ) + if ( $mysqld->{'pid'} ) { - return sleep_until_file_created($slave->[$idx]->{'path_mypid'}, - $master->[$idx]->{'start_timeout'}, $pid); + $pid= mtr_mysqladmin_start($mysqld, "shutdown", 70); + $admin_pids{$pid}= 1; + + push(@kill_pids,{ + pid => $mysqld->{'pid'}, + pidfile => $mysqld->{'path_pid'}, + sockfile => $mysqld->{'path_sock'}, + port => $mysqld->{'port'}, + }); + + $mysqld->{'pid'}= 0; # Assume we are done with it } } - return 0; -} - -sub stop_masters_slaves () { - - print "Ending Tests\n"; - print "Shutting-down MySQL daemon\n\n"; - stop_masters(); - print "Master(s) shutdown finished\n"; - stop_slaves(); - print "Slave(s) shutdown finished\n"; -} - -sub stop_masters () { - - my @args; - - for ( my $idx; $idx < 2; $idx++ ) + # Start shutdown of clusters + foreach my $cluster (@{$clusters}) { - # FIXME if we hit ^C before fully started, this test will prevent - # the mysqld process from being killed - if ( $master->[$idx]->{'pid'} ) + if ( $cluster->{'pid'} ) { - push(@args,{ - pid => $master->[$idx]->{'pid'}, - pidfile => $master->[$idx]->{'path_mypid'}, - sockfile => $master->[$idx]->{'path_mysock'}, - port => $master->[$idx]->{'path_myport'}, - }); - $master->[$idx]->{'pid'}= 0; # Assume we are done with it + $pid= mtr_ndbmgm_start($cluster, "shutdown"); + $admin_pids{$pid}= 1; + + push(@kill_pids,{ + pid => $cluster->{'pid'}, + pidfile => $cluster->{'path_pid'} + }); + + $cluster->{'pid'}= 0; # Assume we are done with it + + foreach my $ndbd (@{$cluster->{'ndbds'}}) + { + if ( $ndbd->{'pid'} ) + { + push(@kill_pids,{ + pid => $ndbd->{'pid'}, + pidfile => $ndbd->{'path_pid'}, + }); + $ndbd->{'pid'}= 0; + } + } } } - if ( ! $master->[0]->{'ndbcluster'} ) - { - ndbcluster_stop(); - $master->[0]->{'ndbcluster'}= 1; - } + # Wait blocking until all shutdown processes has completed + mtr_wait_blocking(\%admin_pids); - mtr_stop_mysqld_servers(\@args); + # Make sure that process has shutdown else try to kill them + mtr_check_stop_servers(\@kill_pids); + + foreach my $mysqld (@{$master}, @{$slave}) + { + rm_ndbcluster_tables($mysqld->{'path_myddir'}); + } } -sub stop_slaves () { - my $force= shift; - my @args; +sub run_testcase_need_master_restart($) +{ + my ($tinfo)= @_; - for ( my $idx; $idx < 3; $idx++ ) + # We try to find out if we are to restart the master(s) + my $do_restart= 0; # Assumes we don't have to + + if ( $tinfo->{'master_sh'} ) { - if ( $slave->[$idx]->{'pid'} ) + $do_restart= 1; # Always restart if script to run + mtr_verbose("Restart because: Always restart if script to run"); + } + elsif ( ! $opt_skip_ndbcluster and + $tinfo->{'ndb_test'} == 0 and + $clusters->[0]->{'pid'} != 0 ) + { + $do_restart= 1; # Restart without cluster + mtr_verbose("Restart because: Test does not need cluster"); + } + elsif ( ! $opt_skip_ndbcluster and + $tinfo->{'ndb_test'} == 1 and + $clusters->[0]->{'pid'} == 0 ) + { + $do_restart= 1; # Restart with cluster + mtr_verbose("Restart because: Test need cluster"); + } + elsif ( $master->[0]->{'running_master_is_special'} and + $master->[0]->{'running_master_is_special'}->{'timezone'} eq + $tinfo->{'timezone'} and + mtr_same_opts($master->[0]->{'running_master_is_special'}->{'master_opt'}, + $tinfo->{'master_opt'}) ) + { + # If running master was started with special settings, but + # the current test requires the same ones, we *don't* restart. + $do_restart= 0; + mtr_verbose("Skip restart: options are equal " . + join(" ", @{$tinfo->{'master_opt'}})); + } + elsif ( $tinfo->{'master_restart'} ) + { + $do_restart= 1; + mtr_verbose("Restart because: master_restart"); + } + elsif ( $master->[0]->{'running_master_is_special'} ) + { + $do_restart= 1; + mtr_verbose("Restart because: running_master_is_special"); + } + # Check that running master was started with same options + # as the current test requires + elsif (! mtr_same_opts($master->[0]->{'start_opts'}, + $tinfo->{'master_opt'}) ) + { + $do_restart= 1; + mtr_verbose("Restart because: running with different options '" . + join(" ", @{$tinfo->{'master_opt'}}) . "' != '" . + join(" ", @{$master->[0]->{'start_opts'}}) . "'" ); + } + + return $do_restart; +} + +sub run_testcase_need_slave_restart($) +{ + my ($tinfo)= @_; + + # We try to find out if we are to restart the slaves + my $do_slave_restart= 0; # Assumes we don't have to + + # FIXME only restart slave when necessary + $do_slave_restart= 1; + +# if ( ! $slave->[0]->{'pid'} ) +# { +# # mtr_verbose("Slave not started, no need to check slave restart"); +# } +# elsif ( $do_restart ) +# { +# $do_slave_restart= 1; # Always restart if master restart +# mtr_verbose("Restart slave because: Master restart"); +# } +# elsif ( $tinfo->{'slave_sh'} ) +# { +# $do_slave_restart= 1; # Always restart if script to run +# mtr_verbose("Restart slave because: Always restart if script to run"); +# } +# elsif ( ! $opt_skip_ndbcluster_slave and +# $tinfo->{'ndb_test'} == 0 and +# $clusters->[1]->{'pid'} != 0 ) +# { +# $do_slave_restart= 1; # Restart without slave cluster +# mtr_verbose("Restart slave because: Test does not need slave cluster"); +# } +# elsif ( ! $opt_with_ndbcluster_slave and +# $tinfo->{'ndb_test'} == 1 and +# $clusters->[1]->{'pid'} == 0 ) +# { +# $do_slave_restart= 1; # Restart with slave cluster +# mtr_verbose("Restart slave because: Test need slave cluster"); +# } +# elsif ( $tinfo->{'slave_restart'} ) +# { +# $do_slave_restart= 1; +# mtr_verbose("Restart slave because: slave_restart"); +# } +# elsif ( $slave->[0]->{'running_slave_is_special'} ) +# { +# $do_slave_restart= 1; +# mtr_verbose("Restart slave because: running_slave_is_special"); +# } +# # Check that running slave was started with same options +# # as the current test requires +# elsif (! mtr_same_opts($slave->[0]->{'start_opts'}, +# $tinfo->{'slave_opt'}) ) +# { +# $do_slave_restart= 1; +# mtr_verbose("Restart slave because: running with different options '" . +# join(" ", @{$tinfo->{'slave_opt'}}) . "' != '" . +# join(" ", @{$slave->[0]->{'start_opts'}}) . "'" ); +# } + + return $do_slave_restart; + +} + +# ---------------------------------------------------------------------- +# If not using a running servers we may need to stop and restart. +# We restart in the case we have initiation scripts, server options +# etc to run. But we also restart again after the test first restart +# and test is run, to get back to normal server settings. +# +# To make the code a bit more clean, we actually only stop servers +# here, and mark this to be done. Then a generic "start" part will +# start up the needed servers again. +# ---------------------------------------------------------------------- + +sub run_testcase_stop_servers($$$) { + my ($tinfo, $do_restart, $do_slave_restart)= @_; + + if ( $glob_use_running_server || $glob_use_embedded_server ) + { + return; + } + + my $pid; + my %admin_pids; # hash of admin processes that requests shutdown + my @kill_pids; # list of processes to shutdown/kill + + # Remember if we restarted for this test case + $tinfo->{'restarted'}= $do_restart; + + if ( $do_restart ) + { + delete $master->[0]->{'running_master_is_special'}; # Forget history + + # Start shutdown of all started masters + foreach my $mysqld (@{$master}) { - push(@args,{ - pid => $slave->[$idx]->{'pid'}, - pidfile => $slave->[$idx]->{'path_mypid'}, - sockfile => $slave->[$idx]->{'path_mysock'}, - port => $slave->[$idx]->{'path_myport'}, - }); - $slave->[$idx]->{'pid'}= 0; # Assume we are done with it + if ( $mysqld->{'pid'} ) + { + $pid= mtr_mysqladmin_start($mysqld, "shutdown", 70); + + $admin_pids{$pid}= 1; + + push(@kill_pids,{ + pid => $mysqld->{'pid'}, + pidfile => $mysqld->{'path_pid'}, + sockfile => $mysqld->{'path_sock'}, + port => $mysqld->{'port'}, + }); + + $mysqld->{'pid'}= 0; # Assume we are done with it + } + } + + # Start shutdown of master cluster + my $cluster= $clusters->[0]; + if ( $cluster->{'pid'} ) + { + $pid= mtr_ndbmgm_start($cluster, "shutdown"); + $admin_pids{$pid}= 1; + + push(@kill_pids,{ + pid => $cluster->{'pid'}, + pidfile => $cluster->{'path_pid'} + }); + + $cluster->{'pid'}= 0; # Assume we are done with it + + foreach my $ndbd (@{$cluster->{'ndbds'}}) + { + push(@kill_pids,{ + pid => $ndbd->{'pid'}, + pidfile => $ndbd->{'path_pid'}, + }); + $ndbd->{'pid'}= 0; # Assume we are done with it + } } } - mtr_stop_mysqld_servers(\@args); + if ( $do_restart || $do_slave_restart ) + { + + delete $slave->[0]->{'running_slave_is_special'}; # Forget history + + # Start shutdown of all started slaves + foreach my $mysqld (@{$slave}) + { + if ( $mysqld->{'pid'} ) + { + $pid= mtr_mysqladmin_start($mysqld, "shutdown", 70); + + $admin_pids{$pid}= 1; + + push(@kill_pids,{ + pid => $mysqld->{'pid'}, + pidfile => $mysqld->{'path_pid'}, + sockfile => $mysqld->{'path_sock'}, + port => $mysqld->{'port'}, + }); + + + $mysqld->{'pid'}= 0; # Assume we are done with it + } + } + + # Start shutdown of slave cluster + my $cluster= $clusters->[1]; + if ( $cluster->{'pid'} ) + { + $pid= mtr_ndbmgm_start($cluster, "shutdown"); + + $admin_pids{$pid}= 1; + + push(@kill_pids,{ + pid => $cluster->{'pid'}, + pidfile => $cluster->{'path_pid'} + }); + + $cluster->{'pid'}= 0; # Assume we are done with it + + foreach my $ndbd (@{$cluster->{'ndbds'}} ) + { + push(@kill_pids,{ + pid => $ndbd->{'pid'}, + pidfile => $ndbd->{'path_pid'}, + }); + $ndbd->{'pid'}= 0; # Assume we are done with it + } + } + } + + # ---------------------------------------------------------------------- + # Shutdown has now been started and lists for the shutdown processes + # and the processes to be killed has been created + # ---------------------------------------------------------------------- + + # Wait blocking until all shutdown processes has completed + mtr_wait_blocking(\%admin_pids); + + + # Make sure that process has shutdown else try to kill them + mtr_check_stop_servers(\@kill_pids); + + foreach my $mysqld (@{$master}, @{$slave}) + { + if ( ! $mysqld->{'pid'} ) + { + # Remove ndbcluster tables if server is stopped + rm_ndbcluster_tables($mysqld->{'path_myddir'}); + } + } } +sub run_testcase_start_servers($) { + my $tinfo= shift; + + my $tname= $tinfo->{'name'}; + + if ( $glob_use_running_server or $glob_use_embedded_server ) + { + return; + } + + # ------------------------------------------------------- + # Init variables that can change between server starts + # ------------------------------------------------------- + $ENV{'TZ'}= $tinfo->{'timezone'}; + + if ( $tinfo->{'component_id'} eq 'mysqld' ) + { + if ( ! $opt_skip_ndbcluster and + !$clusters->[0]->{'pid'} and + $tinfo->{'ndb_test'} ) + { + # Test need cluster, cluster is not started, start it + ndbcluster_start($clusters->[0], ""); + } + + if ( !$master->[0]->{'pid'} ) + { + # Master mysqld is not started + do_before_start_master($tname,$tinfo->{'master_sh'}); + + mysqld_start($master->[0],$tinfo->{'master_opt'},[]); + + } + + if ( $clusters->[0]->{'pid'} and ! $master->[1]->{'pid'} ) + { + # Test needs cluster, start an extra mysqld connected to cluster + + # First wait for first mysql server to have created ndb system tables ok + # FIXME This is a workaround so that only one mysqld creates the tables + if ( ! sleep_until_file_created( + "$master->[0]->{'path_myddir'}/cluster/apply_status.ndb", + $master->[0]->{'start_timeout'}, + $master->[0]->{'pid'})) + { + mtr_report("Failed to create 'cluster/apply_status' table"); + report_failure_and_restart($tinfo); + return; + } + mtr_tofile($master->[1]->{'path_myerr'},"CURRENT_TEST: $tname\n"); + + mysqld_start($master->[1],$tinfo->{'master_opt'},[]); + } + + if ( $tinfo->{'master_restart'} ) + { + # Save this test case information, so next can examine it + $master->[0]->{'running_master_is_special'}= $tinfo; + } + } + elsif ( ! $opt_skip_im and $tinfo->{'component_id'} eq 'im' ) + { + # We have to create defaults file every time, in order to ensure that it + # will be the same for each test. The problem is that test can change the + # file (by SET/UNSET commands), so w/o recreating the file, execution of + # one test can affect the other. + + im_create_defaults_file($instance_manager); + + unless ( mtr_im_start($instance_manager, $tinfo->{im_opts}) ) + { + report_failure_and_restart($tinfo); + mtr_report("Failed to start Instance Manager. " . + "The test '$tname' is marked as failed."); + return; + } + } + + # ---------------------------------------------------------------------- + # Start slaves - if needed + # ---------------------------------------------------------------------- + if ( $tinfo->{'slave_num'} ) + { + mtr_tofile($slave->[0]->{'path_myerr'},"CURRENT_TEST: $tname\n"); + + restore_slave_databases($tinfo->{'slave_num'}); + + do_before_start_slave($tname,$tinfo->{'slave_sh'}); + + if ( ! $opt_skip_ndbcluster_slave and + !$clusters->[1]->{'pid'} and + $tinfo->{'ndb_test'} ) + { + # Test need slave cluster, cluster is not started, start it + ndbcluster_start($clusters->[1], ""); + } + + for ( my $idx= 0; $idx < $tinfo->{'slave_num'}; $idx++ ) + { + if ( ! $slave->[$idx]->{'pid'} ) + { + mysqld_start($slave->[$idx],$tinfo->{'slave_opt'}, + $tinfo->{'slave_mi'}); + + } + } + + if ( $tinfo->{'slave_restart'} ) + { + # Save this test case information, so next can examine it + $slave->[0]->{'running_slave_is_special'}= $tinfo; + } + + } + + # Wait for clusters to start + foreach my $cluster (@{$clusters}) + { + + next if !$cluster->{'pid'}; + + if (ndbcluster_wait_started($cluster, "")) + { + # failed to start + mtr_report("Start of $cluster->{'name'} cluster failed, "); + } + } + + # Wait for mysqld's to start + foreach my $mysqld (@{$master},@{$slave}) + { + + next if !$mysqld->{'pid'}; + + if (mysqld_wait_started($mysqld)) + { + mtr_warning("Failed to start $mysqld->{'type'} mysqld $mysqld->{'idx'}"); + } + } +} + +# +# Run include/check-testcase.test +# Before a testcase, run in record mode, save result file to var +# After testcase, run and compare with the recorded file, they should be equal! +# +sub run_check_testcase ($$) { + + my $mode= shift; + my $mysqld= shift; + + my $name= "check-" . $mysqld->{'type'} . $mysqld->{'idx'}; + + my $args; + mtr_init_args(\$args); + + mtr_add_arg($args, "--no-defaults"); + mtr_add_arg($args, "--silent"); + mtr_add_arg($args, "-v"); + mtr_add_arg($args, "--skip-safemalloc"); + mtr_add_arg($args, "--tmpdir=%s", $opt_tmpdir); + + mtr_add_arg($args, "--socket=%s", $mysqld->{'path_sock'}); + mtr_add_arg($args, "--port=%d", $mysqld->{'port'}); + mtr_add_arg($args, "--database=test"); + mtr_add_arg($args, "--user=%s", $opt_user); + mtr_add_arg($args, "--password="); + + mtr_add_arg($args, "-R"); + mtr_add_arg($args, "$opt_vardir/tmp/$name.result"); + + if ( $mode eq "before" ) + { + mtr_add_arg($args, "--record"); + } + + my $res = mtr_run_test($exe_mysqltest,$args, + "include/check-testcase.test", "", "", ""); + + if ( $res == 1 and $mode = "after") + { + mtr_run("diff",["-u", + "$opt_vardir/tmp/$name.result", + "$opt_vardir/tmp/$name.reject"], + "", "", "", ""); + } + elsif ( $res ) + { + mtr_error("Could not execute 'check-testcase' $mode testcase"); + } +} + + sub run_mysqltest ($) { - my $tinfo= shift; - - my $cmdline_mysqldump= "$exe_mysqldump --no-defaults -uroot " . - "--port=$master->[0]->{'path_myport'} " . - "--socket=$master->[0]->{'path_mysock'} --password="; - if ( $opt_debug ) - { - $cmdline_mysqldump .= - " --debug=d:t:A,$opt_vardir/log/mysqldump.trace"; - } - my $cmdline_mysqlimport= "$exe_mysqlimport -uroot " . - "--port=$master->[0]->{'path_myport'} " . - "--socket=$master->[0]->{'path_mysock'} --password="; - if ( $opt_debug ) - { - $cmdline_mysqlimport .= - " --debug=d:t:A,$opt_vardir/log/mysqlimport.trace"; - } - - my $cmdline_mysqlshow= "$exe_mysqlshow -uroot " . - "--port=$master->[0]->{'path_myport'} " . - "--socket=$master->[0]->{'path_mysock'} --password="; - if ( $opt_debug ) - { - $cmdline_mysqlshow .= - " --debug=d:t:A,$opt_vardir/log/mysqlshow.trace"; - } - - my $cmdline_mysqlbinlog= - "$exe_mysqlbinlog --no-defaults --local-load=$opt_tmpdir"; - - if ( $opt_debug ) - { - $cmdline_mysqlbinlog .= - " --debug=d:t:A,$opt_vardir/log/mysqlbinlog.trace"; - } - - my $cmdline_mysql= - "$exe_mysql --host=localhost --user=root --password= " . - "--port=$master->[0]->{'path_myport'} " . - "--socket=$master->[0]->{'path_mysock'}"; - - my $cmdline_mysql_client_test= - "$exe_mysql_client_test --no-defaults --testcase --user=root --silent " . - "--port=$master->[0]->{'path_myport'} " . - "--socket=$master->[0]->{'path_mysock'}"; - - if ( $glob_use_embedded_server ) - { - $cmdline_mysql_client_test.= - " -A --language=$path_language" . - " -A --datadir=$slave->[0]->{'path_myddir'}" . - " -A --character-sets-dir=$path_charsetsdir"; - } - - my $cmdline_mysql_fix_system_tables= - "$exe_mysql_fix_system_tables --no-defaults --host=localhost --user=root --password= " . - "--basedir=$glob_basedir --bindir=$path_client_bindir --verbose " . - "--port=$master->[0]->{'path_myport'} " . - "--socket=$master->[0]->{'path_mysock'}"; - - - - # FIXME really needing a PATH??? - # $ENV{'PATH'}= "/bin:/usr/bin:/usr/local/bin:/usr/bsd:/usr/X11R6/bin:/usr/openwin/bin:/usr/bin/X11:$ENV{'PATH'}"; - - $ENV{'MYSQL'}= $cmdline_mysql; - $ENV{'MYSQL_DUMP'}= $cmdline_mysqldump; - $ENV{'MYSQL_IMPORT'}= $cmdline_mysqlimport; - $ENV{'MYSQL_SHOW'}= $cmdline_mysqlshow; - $ENV{'MYSQL_BINLOG'}= $cmdline_mysqlbinlog; - $ENV{'MYSQL_FIX_SYSTEM_TABLES'}= $cmdline_mysql_fix_system_tables; - $ENV{'MYSQL_CLIENT_TEST'}= $cmdline_mysql_client_test; - $ENV{'CHARSETSDIR'}= $path_charsetsdir; - $ENV{'MYSQL_MY_PRINT_DEFAULTS'}= $exe_my_print_defaults; - - $ENV{'NDB_STATUS_OK'}= $flag_ndb_status_ok; - $ENV{'NDB_MGM'}= $exe_ndb_mgm; - $ENV{'NDB_BACKUP_DIR'}= $path_ndb_backup_dir; - $ENV{'NDB_TOOLS_DIR'}= $path_ndb_tools_dir; - $ENV{'NDB_TOOLS_OUTPUT'}= $file_ndb_testrun_log; - $ENV{'NDB_CONNECTSTRING'}= $opt_ndbconnectstring; - + my ($tinfo)= @_; my $exe= $exe_mysqltest; my $args; mtr_init_args(\$args); mtr_add_arg($args, "--no-defaults"); - mtr_add_arg($args, "--socket=%s", $master->[0]->{'path_mysock'}); - mtr_add_arg($args, "--database=test"); - mtr_add_arg($args, "--user=%s", $opt_user); - mtr_add_arg($args, "--password="); mtr_add_arg($args, "--silent"); mtr_add_arg($args, "-v"); mtr_add_arg($args, "--skip-safemalloc"); mtr_add_arg($args, "--tmpdir=%s", $opt_tmpdir); - mtr_add_arg($args, "--port=%d", $master->[0]->{'path_myport'}); + + if ($tinfo->{'component_id'} eq 'im') + { + mtr_add_arg($args, "--socket=%s", $instance_manager->{'path_sock'}); + mtr_add_arg($args, "--port=%d", $instance_manager->{'port'}); + mtr_add_arg($args, "--user=%s", $instance_manager->{'admin_login'}); + mtr_add_arg($args, "--password=%s", $instance_manager->{'admin_password'}); + } + else # component_id == mysqld + { + mtr_add_arg($args, "--socket=%s", $master->[0]->{'path_sock'}); + mtr_add_arg($args, "--port=%d", $master->[0]->{'port'}); + mtr_add_arg($args, "--database=test"); + mtr_add_arg($args, "--user=%s", $opt_user); + mtr_add_arg($args, "--password="); + } if ( $opt_ps_protocol ) { mtr_add_arg($args, "--ps-protocol"); } + if ( $opt_sp_protocol ) + { + mtr_add_arg($args, "--sp-protocol"); + } + + if ( $opt_view_protocol ) + { + mtr_add_arg($args, "--view-protocol"); + } + + if ( $opt_cursor_protocol ) + { + mtr_add_arg($args, "--cursor-protocol"); + } + if ( $opt_strace_client ) { $exe= "strace"; # FIXME there are ktrace, .... @@ -2392,6 +3869,11 @@ sub run_mysqltest ($) { mtr_add_arg($args, "--big-test"); } + if ( $opt_valgrind ) + { + mtr_add_arg($args, "--valgrind"); + } + if ( $opt_compress ) { mtr_add_arg($args, "--compress"); @@ -2404,17 +3886,30 @@ sub run_mysqltest ($) { if ( $opt_debug ) { - mtr_add_arg($args, "--debug=d:t:A,%s/log/mysqltest.trace", $opt_vardir); + mtr_add_arg($args, "--debug=d:t:A,%s/log/mysqltest.trace", + $path_vardir_trace); } - if ( $opt_with_openssl ) + if ( $opt_ssl_supported ) { mtr_add_arg($args, "--ssl-ca=%s/std_data/cacert.pem", - $glob_mysql_test_dir); + $glob_mysql_test_dir); mtr_add_arg($args, "--ssl-cert=%s/std_data/client-cert.pem", - $glob_mysql_test_dir); + $glob_mysql_test_dir); mtr_add_arg($args, "--ssl-key=%s/std_data/client-key.pem", - $glob_mysql_test_dir); + $glob_mysql_test_dir); + } + + if ( $opt_ssl ) + { + # Turn on SSL for _all_ test cases if option --ssl was used + mtr_add_arg($args, "--ssl", + $glob_mysql_test_dir); + } + elsif ( $opt_ssl_supported ) + { + mtr_add_arg($args, "--skip-ssl", + $glob_mysql_test_dir); } # ---------------------------------------------------------------------- @@ -2435,7 +3930,7 @@ sub run_mysqltest ($) { # Add arguments that should not go into the MYSQL_TEST env var # ---------------------------------------------------------------------- - if ( defined $opt_valgrind_mysqltest ) + if ( $opt_valgrind_mysqltest ) { # Prefix the Valgrind options to the argument list. # We do this here, since we do not want to Valgrind the nested invocations @@ -2446,7 +3941,10 @@ sub run_mysqltest ($) { mtr_add_arg($args, "%s", $_) for @args_saved; } - mtr_add_arg($args, "-R"); + mtr_add_arg($args, "--test-file"); + mtr_add_arg($args, $tinfo->{'path'}); + + mtr_add_arg($args, "--result-file"); mtr_add_arg($args, $tinfo->{'result_file'}); if ( $opt_record ) @@ -2454,36 +3952,248 @@ sub run_mysqltest ($) { mtr_add_arg($args, "--record"); } - return mtr_run_test($exe,$args,$tinfo->{'path'},"",$path_timefile,""); + if ( $opt_client_gdb ) + { + gdb_arguments(\$args, \$exe, "client"); + } + elsif ( $opt_client_ddd ) + { + ddd_arguments(\$args, \$exe, "client"); + } + elsif ( $opt_client_debugger ) + { + debugger_arguments(\$args, \$exe, "client"); + } + + if ($exe_libtool and $opt_valgrind) + { + # Add "libtool --mode-execute" before the test to execute + # if running in valgrind(to avoid valgrinding bash) + unshift(@$args, "--mode=execute", $exe); + $exe= $exe_libtool; + } + + if ( $opt_check_testcases ) + { + foreach my $mysqld (@{$master}, @{$slave}) + { + if ($mysqld->{'pid'}) + { + run_check_testcase("before", $mysqld); + } + } + } + + my $res = mtr_run_test($exe,$args,"","",$path_timefile,""); + + if ( $opt_check_testcases ) + { + foreach my $mysqld (@{$master}, @{$slave}) + { + if ($mysqld->{'pid'}) + { + run_check_testcase("after", $mysqld); + } + } + } + + return $res; + } +# +# Modify the exe and args so that program is run in gdb in xterm +# +sub gdb_arguments { + my $args= shift; + my $exe= shift; + my $type= shift; + + # Write $args to gdb init file + my $str= join(" ", @$$args); + my $gdb_init_file= "$opt_tmpdir/gdbinit.$type"; + + # Remove the old gdbinit file + unlink($gdb_init_file); + + if ( $type eq "client" ) + { + # write init file for client + mtr_tofile($gdb_init_file, + "set args $str\n" . + "break main\n"); + } + else + { + # write init file for mysqld + mtr_tofile($gdb_init_file, + "set args $str\n" . + "break mysql_parse\n" . + "commands 1\n" . + "disable 1\n" . + "end\n" . + "run"); + } + + if ( $opt_manual_gdb ) + { + print "\nTo start gdb for $type, type in another window:\n"; + print "cd $glob_mysql_test_dir;\n"; + print "gdb -x $gdb_init_file $$exe\n"; + + # Indicate the exe should not be started + $$exe= undef; + return; + } + + $$args= []; + mtr_add_arg($$args, "-title"); + mtr_add_arg($$args, "$type"); + mtr_add_arg($$args, "-e"); + + if ( $exe_libtool ) + { + mtr_add_arg($$args, $exe_libtool); + mtr_add_arg($$args, "--mode=execute"); + } + + mtr_add_arg($$args, "gdb"); + mtr_add_arg($$args, "-x"); + mtr_add_arg($$args, "$gdb_init_file"); + mtr_add_arg($$args, "$$exe"); + + $$exe= "xterm"; +} + + +# +# Modify the exe and args so that program is run in ddd +# +sub ddd_arguments { + my $args= shift; + my $exe= shift; + my $type= shift; + + # Write $args to ddd init file + my $str= join(" ", @$$args); + my $gdb_init_file= "$opt_tmpdir/gdbinit.$type"; + + # Remove the old gdbinit file + unlink($gdb_init_file); + + if ( $type eq "client" ) + { + # write init file for client + mtr_tofile($gdb_init_file, + "set args $str\n" . + "break main\n"); + } + else + { + # write init file for mysqld + mtr_tofile($gdb_init_file, + "file $$exe\n" . + "set args $str\n" . + "break mysql_parse\n" . + "commands 1\n" . + "disable 1\n" . + "end"); + } + + if ( $opt_manual_ddd ) + { + print "\nTo start ddd for $type, type in another window:\n"; + print "cd $glob_mysql_test_dir;\n"; + print "ddd -x $gdb_init_file $$exe\n"; + + # Indicate the exe should not be started + $$exe= undef; + return; + } + + my $save_exe= $$exe; + $$args= []; + if ( $exe_libtool ) + { + $$exe= $exe_libtool; + mtr_add_arg($$args, "--mode=execute"); + mtr_add_arg($$args, "ddd"); + } + else + { + $$exe= "ddd"; + } + mtr_add_arg($$args, "--command=$gdb_init_file"); + mtr_add_arg($$args, "$save_exe"); +} + + +# +# Modify the exe and args so that program is run in the selected debugger +# +sub debugger_arguments { + my $args= shift; + my $exe= shift; + my $debugger= $opt_debugger || $opt_client_debugger; + + # FIXME Need to change the below "eq"'s to + # "case unsensitive string contains" + if ( $debugger eq "vcexpress" or $debugger eq "vc") + { + # vc[express] /debugexe exe arg1 .. argn + + # Add /debugexe and name of the exe before args + unshift(@$$args, "/debugexe"); + unshift(@$$args, "$$exe"); + + } + elsif ( $debugger eq "windbg" ) + { + # windbg exe arg1 .. argn + + # Add name of the exe before args + unshift(@$$args, "$$exe"); + + } + else + { + mtr_error("Unknown argument \"$debugger\" passed to --debugger"); + } + + # Set exe to debuggername + $$exe= $debugger; +} + + +# +# Modify the exe and args so that program is run in valgrind +# sub valgrind_arguments { my $args= shift; my $exe= shift; - mtr_add_arg($args, "--tool=memcheck"); # From >= 2.1.2 needs this option - mtr_add_arg($args, "--alignment=8"); - mtr_add_arg($args, "--leak-check=yes"); - mtr_add_arg($args, "--num-callers=16"); - mtr_add_arg($args, "--suppressions=%s/valgrind.supp", $glob_mysql_test_dir) - if -f "$glob_mysql_test_dir/valgrind.supp"; - - if ( defined $opt_valgrind_all ) + if ( $opt_callgrind) { - mtr_add_arg($args, "-v"); - mtr_add_arg($args, "--show-reachable=yes"); + mtr_add_arg($args, "--tool=callgrind"); + mtr_add_arg($args, "--base=$opt_vardir/log"); + } + else + { + mtr_add_arg($args, "--tool=memcheck"); # From >= 2.1.2 needs this option + mtr_add_arg($args, "--alignment=8"); + mtr_add_arg($args, "--leak-check=yes"); + mtr_add_arg($args, "--num-callers=16"); + mtr_add_arg($args, "--suppressions=%s/valgrind.supp", $glob_mysql_test_dir) + if -f "$glob_mysql_test_dir/valgrind.supp"; } - if ( $opt_valgrind_options ) - { - # FIXME split earlier and put into @glob_valgrind_* - mtr_add_arg($args, '%s', $_) for (split(' ', $opt_valgrind_options)); - } + # Add valgrind options, can be overriden by user + mtr_add_arg($args, '%s', $_) for (split(' ', $opt_valgrind_options)); mtr_add_arg($args, $$exe); - $$exe= $opt_valgrind || "valgrind"; + $$exe= $opt_valgrind_path || "valgrind"; } @@ -2503,38 +4213,60 @@ sub usage ($) { print STDERR < Date: Wed, 4 Oct 2006 13:09:37 +0200 Subject: [PATCH 28/68] Update tests and result files after running with new mysqltest that better detects problems with test files mysql-test/r/csv.result: Update after add of missing semicolon mysql-test/r/drop.result: Update result file, no space before commands that has been "sent" mysql-test/r/flush.result: Update result file, no space before commands that has been "sent" mysql-test/r/flush_block_commit.result: Update result file, no space before commands that has been "sent" mysql-test/r/flush_read_lock_kill.result: Update result file, no space before commands that has been "sent" mysql-test/r/grant2.result: Update result file, no space before commands that has been "sent" mysql-test/r/handler.result: Update result file, no space before commands that has been "sent" mysql-test/r/innodb_notembedded.result: Update result file, no space before commands that has been "sent" mysql-test/r/kill.result: Update result file, no space before commands that has been "sent" mysql-test/r/lock_multi.result: Update result file, no space before commands that has been "sent" mysql-test/r/multi_update.result: Update result file, no space before commands that has been "sent" mysql-test/r/mysqltest.result: Update result mysql-test/r/query_cache.result: Update after add of missing semicolon mysql-test/r/query_cache_notembedded.result: Update result file, no space before commands that has been "sent" mysql-test/r/sp-threads.result: Update result file, no space before commands that has been "sent" mysql-test/r/sp_notembedded.result: Update after add of missing semicolon mysql-test/r/type_blob.result: Remove extra drop table mysql-test/t/csv.test: Add missing semicolon mysql-test/t/query_cache.test: Add missing semicolon mysql-test/t/sp-error.test: Remove "tab" from end of error declaration mysql-test/t/sp.test: Wrong delimiter, used ; instead of | mysql-test/t/sp_notembedded.test: Wrong delimiter, used ; instead of | mysql-test/t/view_grant.test: An incomplete error name specification was used. --- mysql-test/r/csv.result | 3 +-- mysql-test/r/drop.result | 4 ++-- mysql-test/r/flush.result | 4 ++-- mysql-test/r/flush_block_commit.result | 2 +- mysql-test/r/flush_read_lock_kill.result | 2 +- mysql-test/r/grant2.result | 8 ++++---- mysql-test/r/handler.result | 4 ++-- mysql-test/r/innodb_notembedded.result | 2 +- mysql-test/r/kill.result | 4 ++-- mysql-test/r/lock_multi.result | 12 ++++++------ mysql-test/r/multi_update.result | 4 ++-- mysql-test/r/mysqltest.result | 2 +- mysql-test/r/query_cache.result | 3 +-- mysql-test/r/query_cache_notembedded.result | 2 +- mysql-test/r/sp-threads.result | 4 ++-- mysql-test/r/sp_notembedded.result | 3 +-- mysql-test/r/type_blob.result | 1 - mysql-test/t/csv.test | 2 +- mysql-test/t/query_cache.test | 2 +- mysql-test/t/sp-error.test | 2 +- mysql-test/t/sp.test | 10 +++++----- mysql-test/t/sp_notembedded.test | 12 ++++++------ mysql-test/t/view_grant.test | 2 +- 23 files changed, 45 insertions(+), 49 deletions(-) diff --git a/mysql-test/r/csv.result b/mysql-test/r/csv.result index f3e91a663b8..8ec79e9d7a9 100644 --- a/mysql-test/r/csv.result +++ b/mysql-test/r/csv.result @@ -4913,8 +4913,7 @@ bonfire Colombo nondecreasing DROP TABLE t1; -ALTER TABLE t2 RENAME t1 -#; +ALTER TABLE t2 RENAME t1; DROP TABLE t1; CREATE TABLE t1 ( Period smallint(4) unsigned zerofill DEFAULT '0000' NOT NULL, diff --git a/mysql-test/r/drop.result b/mysql-test/r/drop.result index d122dabc4ec..ff11905aa34 100644 --- a/mysql-test/r/drop.result +++ b/mysql-test/r/drop.result @@ -77,9 +77,9 @@ drop table if exists t1; create table t1 (i int); lock tables t1 read; create database mysqltest; - drop table t1; +drop table t1; show open tables; - drop database mysqltest; +drop database mysqltest; select 1; 1 1 diff --git a/mysql-test/r/flush.result b/mysql-test/r/flush.result index 23c7619c31c..7eb7fd16edb 100644 --- a/mysql-test/r/flush.result +++ b/mysql-test/r/flush.result @@ -51,7 +51,7 @@ drop table t1, t2, t3; create table t1 (c1 int); create table t2 (c1 int); lock table t1 write; - flush tables with read lock; - insert into t2 values(1); +flush tables with read lock; +insert into t2 values(1); unlock tables; drop table t1, t2; diff --git a/mysql-test/r/flush_block_commit.result b/mysql-test/r/flush_block_commit.result index 7dbbd4678df..9e9085d2fc8 100644 --- a/mysql-test/r/flush_block_commit.result +++ b/mysql-test/r/flush_block_commit.result @@ -45,7 +45,7 @@ flush tables with read lock; show master status; File Position Binlog_Do_DB Binlog_Ignore_DB master-bin.000001 98 - commit; +commit; show master status; File Position Binlog_Do_DB Binlog_Ignore_DB master-bin.000001 98 diff --git a/mysql-test/r/flush_read_lock_kill.result b/mysql-test/r/flush_read_lock_kill.result index dfdcf51457a..6703b6bd533 100644 --- a/mysql-test/r/flush_read_lock_kill.result +++ b/mysql-test/r/flush_read_lock_kill.result @@ -1,7 +1,7 @@ drop table if exists t1; create table t1 (kill_id int); insert into t1 values(connection_id()); - flush tables with read lock; +flush tables with read lock; select ((@id := kill_id) - kill_id) from t1; ((@id := kill_id) - kill_id) 0 diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result index 2700426257b..ff9b7bc6f1f 100644 --- a/mysql-test/r/grant2.result +++ b/mysql-test/r/grant2.result @@ -336,12 +336,12 @@ drop database mysqltest_1; set password = password("changed"); ERROR 42000: Access denied for user ''@'localhost' to database 'mysql' lock table mysql.user write; - flush privileges; - grant all on *.* to 'mysqltest_1'@'localhost'; +flush privileges; +grant all on *.* to 'mysqltest_1'@'localhost'; unlock tables; lock table mysql.user write; - set password for 'mysqltest_1'@'localhost' = password(''); - revoke all on *.* from 'mysqltest_1'@'localhost'; +set password for 'mysqltest_1'@'localhost' = password(''); +revoke all on *.* from 'mysqltest_1'@'localhost'; unlock tables; drop user 'mysqltest_1'@'localhost'; create database TESTDB; diff --git a/mysql-test/r/handler.result b/mysql-test/r/handler.result index 104025e83eb..85cf47b5806 100644 --- a/mysql-test/r/handler.result +++ b/mysql-test/r/handler.result @@ -452,7 +452,7 @@ handler t1 read first; c1 1 send the below to another connection, do not wait for the result - optimize table t1; +optimize table t1; proceed with the normal connection handler t1 read next; c1 @@ -469,7 +469,7 @@ flush tables with read lock; drop table t1; ERROR HY000: Can't execute the query because you have a conflicting read lock send the below to another connection, do not wait for the result - drop table t1; +drop table t1; proceed with the normal connection select * from t1; c1 diff --git a/mysql-test/r/innodb_notembedded.result b/mysql-test/r/innodb_notembedded.result index 9aac20e515d..cc13a429dfc 100644 --- a/mysql-test/r/innodb_notembedded.result +++ b/mysql-test/r/innodb_notembedded.result @@ -10,7 +10,7 @@ start transaction; select f1(); f1() 100 - update t1 set col2=0 where col1=1; +update t1 set col2=0 where col1=1; select * from t1; col1 col2 1 100 diff --git a/mysql-test/r/kill.result b/mysql-test/r/kill.result index b78c677fb02..cf5486d1091 100644 --- a/mysql-test/r/kill.result +++ b/mysql-test/r/kill.result @@ -22,7 +22,7 @@ create table t2 (id int unsigned not null); insert into t2 select id from t1; create table t3 (kill_id int); insert into t3 values(connection_id()); - select id from t1 where id in (select distinct id from t2); +select id from t1 where id in (select distinct id from t2); select ((@id := kill_id) - kill_id) from t3; ((@id := kill_id) - kill_id) 0 @@ -32,7 +32,7 @@ drop table t1, t2, t3; select get_lock("a", 10); get_lock("a", 10) 1 - select get_lock("a", 10); +select get_lock("a", 10); get_lock("a", 10) NULL select 1; diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index c9d5eafd115..af49e1a27dc 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -45,7 +45,7 @@ unlock tables; drop table t1; CREATE DATABASE mysqltest_1; FLUSH TABLES WITH READ LOCK; - DROP DATABASE mysqltest_1; +DROP DATABASE mysqltest_1; DROP DATABASE mysqltest_1; ERROR HY000: Can't execute the query because you have a conflicting read lock UNLOCK TABLES; @@ -55,7 +55,7 @@ use mysql; LOCK TABLES columns_priv WRITE, db WRITE, host WRITE, user WRITE; FLUSH TABLES; use mysql; - SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1; +SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1; OPTIMIZE TABLES columns_priv, db, host, user; Table Op Msg_type Msg_text mysql.columns_priv optimize status OK @@ -69,14 +69,14 @@ use test; use test; CREATE TABLE t1 (c1 int); LOCK TABLE t1 WRITE; - FLUSH TABLES WITH READ LOCK; +FLUSH TABLES WITH READ LOCK; CREATE TABLE t2 (c1 int); UNLOCK TABLES; UNLOCK TABLES; DROP TABLE t1, t2; CREATE TABLE t1 (c1 int); LOCK TABLE t1 WRITE; - FLUSH TABLES WITH READ LOCK; +FLUSH TABLES WITH READ LOCK; CREATE TABLE t2 AS SELECT * FROM t1; ERROR HY000: Table 't2' was not locked with LOCK TABLES UNLOCK TABLES; @@ -84,7 +84,7 @@ UNLOCK TABLES; DROP TABLE t1; create table t1 (f1 int(12) unsigned not null auto_increment, primary key(f1)) engine=innodb; lock tables t1 write; - alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; // - alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; // +alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; // +alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; // unlock tables; drop table t1; diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result index b4a7aa5cb76..78872f27c47 100644 --- a/mysql-test/r/multi_update.result +++ b/mysql-test/r/multi_update.result @@ -509,8 +509,8 @@ create table t2 (a int); insert into t2 values (10), (20), (30); create view v1 as select a as b, a/10 as a from t2; lock table t1 write; - alter table t1 add column c int default 100 after a; - update t1, v1 set t1.b=t1.a+t1.b+v1.b where t1.a=v1.a; +alter table t1 add column c int default 100 after a; +update t1, v1 set t1.b=t1.a+t1.b+v1.b where t1.a=v1.a; unlock tables; select * from t1; a c b diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result index 7276397bdc7..628ba7f9c88 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -163,7 +163,7 @@ mysqltest: At line 1: Invalid argument to error: '1sssss' - the errno may only c mysqltest: At line 1: The sqlstate must be exactly 5 chars long mysqltest: At line 1: The sqlstate may only consist of digits[0-9] and _uppercase_ letters mysqltest: At line 1: The sqlstate must be exactly 5 chars long -mysqltest: At line 1: Not available in mysqltest for MySQL 4.1.22 +mysqltest: At line 1: Unknown SQL error name 'E9999' mysqltest: At line 1: Invalid argument to error: '999e9' - the errno may only consist of digits[0-9] mysqltest: At line 1: Invalid argument to error: '9b' - the errno may only consist of digits[0-9] mysqltest: At line 1: Too many errorcodes specified diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result index a735b52a26f..ebf0148c7fd 100644 --- a/mysql-test/r/query_cache.result +++ b/mysql-test/r/query_cache.result @@ -932,8 +932,7 @@ abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzab zyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcba flush query cache; drop table t1, t2; -set GLOBAL query_cache_size=1355776 -#; +set GLOBAL query_cache_size=1355776; flush status; CREATE TABLE t1 ( `date` datetime NOT NULL default '0000-00-00 00:00:00', diff --git a/mysql-test/r/query_cache_notembedded.result b/mysql-test/r/query_cache_notembedded.result index 4226738725a..8e5df012cfb 100644 --- a/mysql-test/r/query_cache_notembedded.result +++ b/mysql-test/r/query_cache_notembedded.result @@ -326,7 +326,7 @@ insert into t1 values(3); set i_var = sleep(3); return 0; end;| - select f1(); +select f1(); select sleep(4); sleep(4) 0 diff --git a/mysql-test/r/sp-threads.result b/mysql-test/r/sp-threads.result index c516d7a643f..953830ecc87 100644 --- a/mysql-test/r/sp-threads.result +++ b/mysql-test/r/sp-threads.result @@ -31,7 +31,7 @@ create procedure bug9486() update t1, t2 set val= 1 where id1=id2; call bug9486(); lock tables t2 write; - call bug9486(); +call bug9486(); show processlist; Id User Host db Command Time State Info # root localhost test Sleep # NULL @@ -77,7 +77,7 @@ select * from t1; end| use test; lock table t1 write; - call p2(); +call p2(); use test; drop procedure p1; create procedure p1() select * from t1; diff --git a/mysql-test/r/sp_notembedded.result b/mysql-test/r/sp_notembedded.result index bcde32572c2..60e427e17a5 100644 --- a/mysql-test/r/sp_notembedded.result +++ b/mysql-test/r/sp_notembedded.result @@ -85,8 +85,7 @@ flush status| flush query cache| delete from t1| drop procedure bug3583| -drop table t1; -#| +drop table t1| drop procedure if exists bug6807| create procedure bug6807() begin diff --git a/mysql-test/r/type_blob.result b/mysql-test/r/type_blob.result index 4eb5b129a83..73b67a2241e 100644 --- a/mysql-test/r/type_blob.result +++ b/mysql-test/r/type_blob.result @@ -26,7 +26,6 @@ t3 CREATE TABLE `t3` ( `a` mediumtext, `b` mediumblob ) ENGINE=MyISAM DEFAULT CHARSET=latin1 -drop table t1,t2,t3; show create TABLE t4; Table Create Table t4 CREATE TABLE `t4` ( diff --git a/mysql-test/t/csv.test b/mysql-test/t/csv.test index 8bd48b7da2c..65173cbf355 100644 --- a/mysql-test/t/csv.test +++ b/mysql-test/t/csv.test @@ -1295,7 +1295,7 @@ SELECT fld3 FROM t2; # DROP TABLE t1; -ALTER TABLE t2 RENAME t1 +ALTER TABLE t2 RENAME t1; # # Drop and recreate diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test index d416f34ce45..d86f1a464b1 100644 --- a/mysql-test/t/query_cache.test +++ b/mysql-test/t/query_cache.test @@ -695,7 +695,7 @@ select a from t1; flush query cache; drop table t1, t2; -set GLOBAL query_cache_size=1355776 +set GLOBAL query_cache_size=1355776; # diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index abb36f040d2..a229c7b7665 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -319,7 +319,7 @@ begin declare x int; end| ---error 1332 +--error 1332 create procedure p() begin declare c condition for 1064; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 4b0f463a9e3..1d20fd0ad02 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -2944,11 +2944,11 @@ begin show warnings; end| --disable_parsing -show binlog events; -show storage engines; -show master status; -show slave hosts; -show slave status; +show binlog events| +show storage engines| +show master status| +show slave hosts| +show slave status| --enable_parsing call bug4902()| diff --git a/mysql-test/t/sp_notembedded.test b/mysql-test/t/sp_notembedded.test index b087f699f86..4e18e69d3d2 100644 --- a/mysql-test/t/sp_notembedded.test +++ b/mysql-test/t/sp_notembedded.test @@ -19,11 +19,11 @@ begin show grants for 'root'@'localhost'; end| --disable_parsing -show binlog events; -show storage engines; -show master status; -show slave hosts; -show slave status; +show binlog events| +show storage engines| +show master status| +show slave hosts| +show slave status| --enable_parsing call bug4902()| @@ -108,7 +108,7 @@ flush status| flush query cache| delete from t1| drop procedure bug3583| -drop table t1; +drop table t1| # # BUG#6807: Stored procedure crash if CREATE PROCEDURE ... KILL QUERY diff --git a/mysql-test/t/view_grant.test b/mysql-test/t/view_grant.test index c75e5422de7..8bc34cfe148 100644 --- a/mysql-test/t/view_grant.test +++ b/mysql-test/t/view_grant.test @@ -26,7 +26,7 @@ grant create view,select on test.* to mysqltest_1@localhost; connect (user1,localhost,mysqltest_1,,test); connection user1; --- error ER_SPECIFIC_ACCESS_DENIED +-- error ER_SPECIFIC_ACCESS_DENIED_ERROR create definer=root@localhost view v1 as select * from mysqltest.t1; create view v1 as select * from mysqltest.t1; # try to modify view without DROP privilege on it From e5d14529bc1a6a542130859714d6ae505197266c Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Oct 2006 16:28:23 +0200 Subject: [PATCH 29/68] Fixup the regex's parsing the mysqld's version and variables Add paths for ndb binaries Some more backport issues mysql-test/include/have_ndb.inc: Remove the check for NDB_STATUS_OK, handled by mysql-test-run.pl --- mysql-test/include/have_ndb.inc | 1 - mysql-test/mysql-test-run.pl | 104 ++++++++++++++++++++++++-------- 2 files changed, 78 insertions(+), 27 deletions(-) diff --git a/mysql-test/include/have_ndb.inc b/mysql-test/include/have_ndb.inc index 9b85197abe8..84e60657876 100644 --- a/mysql-test/include/have_ndb.inc +++ b/mysql-test/include/have_ndb.inc @@ -1,4 +1,3 @@ ---exec test x$NDB_STATUS_OK = x1 -- require r/have_ndb.require disable_query_log; show variables like "have_ndbcluster"; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 39c116a3fcb..3ad083dbd0c 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1141,7 +1141,7 @@ sub check_mysqld_features () { # print out version and a list of all features and settings # my $found_variable_list_start= 0; - my $spec_file= "$opt_vardir/tmp/mysqld.spec"; + my $spec_file= "$opt_vardir/mysqld.spec"; if ( mtr_run($exe_mysqld, ["--no-defaults", "--verbose", @@ -1174,20 +1174,31 @@ sub check_mysqld_features () { if (!$found_variable_list_start) { # Look for start of variables list - if ( $line =~ /-*\s-*/ ) + if ( $line =~ /[\-]+\s[\-]+/ ) { $found_variable_list_start= 1; } - } else { + } + else + { # Put variables into hash - if ( $line =~ /^(\w*)[ \t]*(\w*)$/ ) + if ( $line =~ /^([\S]+)[ \t]+(.*)$/ ) { + # print "$1=$2\n"; $mysqld_variables{$1}= $2; } + else + { + # The variable list is ended with a blank line, so when a line + # doesn't match the above regex, break the loop + last; + } } } } + unlink($spec_file); mtr_error("Could not find version of MySQL") unless $mysql_version_id; + mtr_error("Could not find variabes list") unless $found_variable_list_start; check_ndbcluster_support(\%mysqld_variables); check_ssl_support(\%mysqld_variables); @@ -1197,6 +1208,14 @@ sub check_mysqld_features () { { # Instance manager is not supported until 5.0 $opt_skip_im= 1; + + } + + if ( $mysql_version_id < 50100 ) + { + # Slave cluster is not supported until 5.1 + $opt_skip_ndbcluster_slave= 1; + } # Set default values from mysqld_variables @@ -1294,13 +1313,26 @@ sub executable_setup () { mtr_script_exists("$glob_basedir/scripts/mysql_fix_privilege_tables", "$path_client_bindir/mysql_fix_privilege_tables"); - if ( $opt_with_ndbcluster) + if ( ! $opt_skip_ndbcluster) { # Look for ndb tols and binaries - $path_ndb_tools_dir= mtr_path_exists("$glob_basedir/ndb/tools", + my $ndb_path= mtr_path_exists("$glob_basedir/ndb", + "$glob_basedir/storage/ndb"); + + $path_ndb_tools_dir= mtr_path_exists("$ndb_path/tools", "$glob_basedir/bin"); - $exe_ndb_mgm= mtr_exe_exists("$glob_basedir/ndb/src/mgmclient/ndb_mgm", - "$glob_basedir/bin/ndb_mgm"); + $exe_ndb_mgm= + mtr_exe_exists("$ndb_path/src/mgmclient/ndb_mgm", + "$glob_basedir/bin/ndb_mgm"); + $exe_ndb_mgmd= + mtr_exe_exists("$ndb_path/src/mgmsrv/ndb_mgmd", + "$glob_basedir/bin/ndb_mgmd"); + $exe_ndb_waiter= + mtr_exe_exists("$ndb_path/tools/ndb_waiter", + "$glob_basedir/bin/ndb_waiter"); + $exe_ndbd= + mtr_exe_exists("$ndb_path/src/kernel/ndbd", + "$glob_basedir/bin/ndbd"); } # Look for the udf_example library @@ -1616,7 +1648,8 @@ sub environment_setup () { # Setup env so childs can execute mysql_fix_system_tables # ---------------------------------------------------- my $cmdline_mysql_fix_system_tables= - "$exe_mysql_fix_system_tables --no-defaults --host=localhost --user=root --password= " . + "$exe_mysql_fix_system_tables --no-defaults --host=localhost " . + "--user=root --password= " . "--basedir=$glob_basedir --bindir=$path_client_bindir --verbose " . "--port=$master->[0]->{'port'} " . "--socket=$master->[0]->{'path_sock'}"; @@ -1832,7 +1865,7 @@ sub check_ssl_support ($) { return; } - if ( $mysqld_variables->{'ssl'} eq "FALSE" ) + if ( ! $mysqld_variables->{'ssl'} ) { if ( $opt_ssl) { @@ -1883,7 +1916,7 @@ sub check_ndbcluster_support ($) { return; } - if ( $mysqld_variables->{'ndbcluster'} eq "FALSE") + if ( ! $mysqld_variables->{'ndb-connectstring'} ) { mtr_report("Skipping ndbcluster, mysqld not compiled with ndbcluster"); $opt_skip_ndbcluster= 1; @@ -1949,6 +1982,11 @@ sub ndbcluster_start_install ($) { s/CHOOSE_HOSTNAME_.*/$ndb_host/; s/CHOOSE_FILESYSTEM/$cluster->{'data_dir'}/; s/CHOOSE_PORT_MGM/$cluster->{'port'}/; + if ( $mysql_version_id < 50000 ) + { + my $base_port= $cluster->{'port'} + 1; + s/CHOOSE_PORT_TRANSPORTER/$base_port/; + } s/CHOOSE_DiskPageBufferMemory/$ndb_pbmem/; print OUT "$_ \n"; @@ -2070,7 +2108,10 @@ sub ndbd_start ($$$) { mtr_add_arg($args, "--no-defaults"); mtr_add_arg($args, "--core"); mtr_add_arg($args, "--ndb-connectstring=%s", "$cluster->{'connect_string'}"); - mtr_add_arg($args, "--character-sets-dir=%s", "$path_charsetsdir"); + if ( $mysql_version_id >= 50000) + { + mtr_add_arg($args, "--character-sets-dir=%s", "$path_charsetsdir"); + } mtr_add_arg($args, "--nodaemon"); mtr_add_arg($args, "$extra_args"); @@ -2247,8 +2288,6 @@ sub run_suite () { mtr_report_stats($tests); -# Look for testname.warning files if --do-test= - mtr_timer_stop($glob_timers,"suite"); } @@ -2298,7 +2337,8 @@ sub mysql_install_db () { my $cluster_started_ok= 1; # Assume it can be started if (ndbcluster_start_install($clusters->[0]) || - $max_slave_num && ndbcluster_start_install($clusters->[1])) + ($max_slave_num && !$opt_skip_ndbcluster_slave && + ndbcluster_start_install($clusters->[1]))) { mtr_warning("Failed to start install of cluster"); $cluster_started_ok= 0; @@ -2986,7 +3026,10 @@ sub mysqld_arguments ($$$$$) { mtr_add_arg($args, "%s--ndbcluster", $prefix); mtr_add_arg($args, "%s--ndb-connectstring=%s", $prefix, $cluster->{'connect_string'}); - mtr_add_arg($args, "%s--ndb-extra-logging", $prefix); + if ( $mysql_version_id >= 50000 ) + { + mtr_add_arg($args, "%s--ndb-extra-logging", $prefix); + } } } @@ -3061,7 +3104,10 @@ sub mysqld_arguments ($$$$$) { mtr_add_arg($args, "%s--ndbcluster", $prefix); mtr_add_arg($args, "%s--ndb-connectstring=%s", $prefix, $clusters->[$slave->[$idx]->{'cluster'}]->{'connect_string'}); - mtr_add_arg($args, "%s--ndb-extra-logging", $prefix); + if ( $mysql_version_id >= 50000 ) + { + mtr_add_arg($args, "%s--ndb-extra-logging", $prefix); + } } } # end slave @@ -3646,16 +3692,21 @@ sub run_testcase_start_servers($) { { # Test needs cluster, start an extra mysqld connected to cluster - # First wait for first mysql server to have created ndb system tables ok - # FIXME This is a workaround so that only one mysqld creates the tables - if ( ! sleep_until_file_created( - "$master->[0]->{'path_myddir'}/cluster/apply_status.ndb", - $master->[0]->{'start_timeout'}, - $master->[0]->{'pid'})) + if ( $mysql_version_id >= 50100 ) { - mtr_report("Failed to create 'cluster/apply_status' table"); - report_failure_and_restart($tinfo); - return; + # First wait for first mysql server to have created ndb system + # tables ok FIXME This is a workaround so that only one mysqld + # create the tables + if ( ! sleep_until_file_created( + "$master->[0]->{'path_myddir'}/cluster/apply_status.ndb", + $master->[0]->{'start_timeout'}, + $master->[0]->{'pid'})) + { + mtr_report_test_name($tinfo); + mtr_report("Failed to create 'cluster/apply_status' table"); + report_failure_and_restart($tinfo); + return; + } } mtr_tofile($master->[1]->{'path_myerr'},"CURRENT_TEST: $tname\n"); @@ -3679,6 +3730,7 @@ sub run_testcase_start_servers($) { unless ( mtr_im_start($instance_manager, $tinfo->{im_opts}) ) { + mtr_report_test_name($tinfo); report_failure_and_restart($tinfo); mtr_report("Failed to start Instance Manager. " . "The test '$tname' is marked as failed."); From 091de56e601023fa373921d62f6670700b4d6ef8 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Oct 2006 18:48:22 +0200 Subject: [PATCH 30/68] --ndb-extra-logging option to mysqld is not available until 5.1 --- mysql-test/mysql-test-run.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 3ad083dbd0c..91e2920ff19 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3026,7 +3026,7 @@ sub mysqld_arguments ($$$$$) { mtr_add_arg($args, "%s--ndbcluster", $prefix); mtr_add_arg($args, "%s--ndb-connectstring=%s", $prefix, $cluster->{'connect_string'}); - if ( $mysql_version_id >= 50000 ) + if ( $mysql_version_id >= 50100 ) { mtr_add_arg($args, "%s--ndb-extra-logging", $prefix); } @@ -3104,7 +3104,7 @@ sub mysqld_arguments ($$$$$) { mtr_add_arg($args, "%s--ndbcluster", $prefix); mtr_add_arg($args, "%s--ndb-connectstring=%s", $prefix, $clusters->[$slave->[$idx]->{'cluster'}]->{'connect_string'}); - if ( $mysql_version_id >= 50000 ) + if ( $mysql_version_id >= 50100 ) { mtr_add_arg($args, "%s--ndb-extra-logging", $prefix); } From 3f45e43c06705846f54264c1b28ab9c9b6e702ff Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Oct 2006 19:21:22 +0200 Subject: [PATCH 31/68] Create the mysqld.spec file with a unique name in mysql-test dir, $opt_var_dir haven't been created yet --- mysql-test/mysql-test-run.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 91e2920ff19..1305d9d59e6 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1141,7 +1141,7 @@ sub check_mysqld_features () { # print out version and a list of all features and settings # my $found_variable_list_start= 0; - my $spec_file= "$opt_vardir/mysqld.spec"; + my $spec_file= "$glob_mysql_test_dir/mysqld.spec.$$"; if ( mtr_run($exe_mysqld, ["--no-defaults", "--verbose", From fe8d349ad99089cf349dae6cfc230af71cc05a7f Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Oct 2006 22:50:34 +0200 Subject: [PATCH 32/68] Set slave_load_path to ../../var/tmp in version prior to 5.0 to be compatible with mysql-test-run.sh --- mysql-test/mysql-test-run.pl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 1305d9d59e6..108a97f03b4 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3068,8 +3068,13 @@ sub mysqld_arguments ($$$$$) { # Directory where slaves find the dumps generated by "load data" # on the server. The path need to have constant length otherwise # test results will vary, thus a relative path is used. + my $slave_load_path= "../tmp"; + if ( $mysql_version_id < 50000 ) + { + $slave_load_path= "../../var/tmp"; + } mtr_add_arg($args, "%s--slave-load-tmpdir=%s", $prefix, - "../tmp"); + $slave_load_path); mtr_add_arg($args, "%s--socket=%s", $prefix, $slave->[$idx]->{'path_sock'}); mtr_add_arg($args, "%s--set-variable=slave_net_timeout=10", $prefix); From ca25d07ee5714182c4a54ad638ac89b472a01a55 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Oct 2006 22:52:44 +0200 Subject: [PATCH 33/68] Use replace_regex to mask the output of file_id= in "show binlog events" that depends on previous tests What is this? replace_regex in 4.1! :) mysql-test/r/rpl_charset.result: Use replace_regex to mask the output of file_id= in "show binlog events" that depends on previous tests mysql-test/t/rpl_charset.test: Use replace_regex to mask the output of file_id= in "show binlog events" that depends on previous tests --- mysql-test/r/rpl_charset.result | 4 ++-- mysql-test/t/rpl_charset.test | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/rpl_charset.result b/mysql-test/r/rpl_charset.result index 292cfb19175..0ebd97d0b6a 100644 --- a/mysql-test/r/rpl_charset.result +++ b/mysql-test/r/rpl_charset.result @@ -147,10 +147,10 @@ master-bin.000001 3577 Intvar 1 3577 INSERT_ID=4 master-bin.000001 3605 Query 1 3605 use `mysqltest2`; insert into t1 (b) values(LEAST("Müller","Muffler")) master-bin.000001 3698 Query 1 3698 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64 master-bin.000001 3839 Intvar 1 3839 INSERT_ID=74 -master-bin.000001 3867 Create_file 1 3867 db=mysqltest2;table=t1;file_id=1;block_len=581 +master-bin.000001 3867 Create_file 1 3867 db=mysqltest2;table=t1;file_id=x;block_len=581 master-bin.000001 4540 Query 1 4540 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64 master-bin.000001 4681 Intvar 1 4681 INSERT_ID=5 -master-bin.000001 4709 Exec_load 1 4709 ;file_id=1 +master-bin.000001 4709 Exec_load 1 4709 ;file_id=x master-bin.000001 4732 Query 1 4732 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64 master-bin.000001 4873 Query 1 4873 use `mysqltest2`; truncate table t1 master-bin.000001 4931 Query 1 4931 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64 diff --git a/mysql-test/t/rpl_charset.test b/mysql-test/t/rpl_charset.test index cf079048fc3..a6f4c2ba9be 100644 --- a/mysql-test/t/rpl_charset.test +++ b/mysql-test/t/rpl_charset.test @@ -106,6 +106,8 @@ select * from mysqltest2.t1 order by a; connection master; drop database mysqltest2; drop database mysqltest3; +# file_id: xx can vary depending on previous tests +--replace_regex /file_id=[0-9]/file_id=x/ show binlog events from 79; sync_slave_with_master; From d7b6fdcd6cd17826145773fd1ab352354f5192c5 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Oct 2006 23:05:12 +0200 Subject: [PATCH 34/68] Clean up de_before_start_master/slave --- mysql-test/mysql-test-run.pl | 57 +++++++++++++----------------------- 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 108a97f03b4..1d3c0ff1dd4 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -360,8 +360,8 @@ sub run_testcase_stop_servers ($$$); sub run_testcase_start_servers ($); sub run_testcase_check_skip_test($); sub report_failure_and_restart ($); -sub do_before_start_master ($$); -sub do_before_start_slave ($$); +sub do_before_start_master ($); +sub do_before_start_slave ($); sub ndbd_start ($$$); sub ndb_mgmd_start ($); sub mysqld_start ($$$); @@ -2866,24 +2866,17 @@ sub report_failure_and_restart ($) { ############################################################################## -# The embedded server needs the cleanup so we do some of the start work -# but stop before actually running mysqld or anything. -sub do_before_start_master ($$) { - my $tname= shift; - my $init_script= shift; +sub do_before_start_master ($) { + my ($tinfo)= @_; + + my $tname= $tinfo->{'name'}; + my $init_script= $tinfo->{'master_sh'}; # FIXME what about second master..... - # Remove stale binary logs except for 2 tests which need them FIXME here???? - if ( $tname ne "rpl_crash_binlog_ib_1b" and - $tname ne "rpl_crash_binlog_ib_2b" and - $tname ne "rpl_crash_binlog_ib_3b") + foreach my $bin ( glob("$opt_vardir/log/master*-bin*") ) { - # FIXME we really want separate dir for binlogs - foreach my $bin ( glob("$opt_vardir/log/master*-bin*") ) - { - unlink($bin); - } + unlink($bin); } # FIXME only remove the ones that are tied to this master @@ -2903,31 +2896,23 @@ sub do_before_start_master ($$) { # mtr_warning("$init_script exited with code $ret"); } } - # for gcov FIXME needed? If so we need more absolute paths - # chdir($glob_basedir); } -sub do_before_start_slave ($$) { - my $tname= shift; - my $init_script= shift; +sub do_before_start_slave ($) { + my ($tinfo)= @_; - # Remove stale binary logs and old master.info files - # except for too tests which need them - if ( $tname ne "rpl_crash_binlog_ib_1b" and - $tname ne "rpl_crash_binlog_ib_2b" and - $tname ne "rpl_crash_binlog_ib_3b" ) + my $tname= $tinfo->{'name'}; + my $init_script= $tinfo->{'master_sh'}; + + foreach my $bin ( glob("$opt_vardir/log/slave*-bin*") ) { - # FIXME we really want separate dir for binlogs - foreach my $bin ( glob("$opt_vardir/log/slave*-bin*") ) - { - unlink($bin); - } - # FIXME really master?! - unlink("$slave->[0]->{'path_myddir'}/master.info"); - unlink("$slave->[0]->{'path_myddir'}/relay-log.info"); + unlink($bin); } + unlink("$slave->[0]->{'path_myddir'}/master.info"); + unlink("$slave->[0]->{'path_myddir'}/relay-log.info"); + # Run slave initialization shell script if one exists if ( $init_script ) { @@ -3687,7 +3672,7 @@ sub run_testcase_start_servers($) { if ( !$master->[0]->{'pid'} ) { # Master mysqld is not started - do_before_start_master($tname,$tinfo->{'master_sh'}); + do_before_start_master($tinfo); mysqld_start($master->[0],$tinfo->{'master_opt'},[]); @@ -3752,7 +3737,7 @@ sub run_testcase_start_servers($) { restore_slave_databases($tinfo->{'slave_num'}); - do_before_start_slave($tname,$tinfo->{'slave_sh'}); + do_before_start_slave($tinfo); if ( ! $opt_skip_ndbcluster_slave and !$clusters->[1]->{'pid'} and From 4e75b447491fbe01df33a03dec54ca761bffba95 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Oct 2006 23:07:49 +0200 Subject: [PATCH 35/68] Use switch --old-log-format to mysqld started by im only in 5.1 and up --- mysql-test/mysql-test-run.pl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 1d3c0ff1dd4..a2a840208da 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -2539,7 +2539,10 @@ EOF ; print OUT "nonguarded\n" if $instance->{'nonguarded'}; - print OUT "log-output=FILE\n" if $instance->{'old_log_format'}; + if ( $mysql_version_id >= 50100 ) + { + print OUT "log-output=FILE\n" if $instance->{'old_log_format'}; + } print OUT "\n"; } From 693465a66ae47eaea24f39ec1f9e015b3b899736 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Oct 2006 23:15:04 +0200 Subject: [PATCH 36/68] Look for mysqlslap from 5.1, it's also available on windows --- mysql-test/mysql-test-run.pl | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index a2a840208da..b5fdf01ada4 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1307,6 +1307,10 @@ sub executable_setup () { $exe_mysqlbinlog= mtr_exe_exists("$path_client_bindir/mysqlbinlog"); $exe_mysqladmin= mtr_exe_exists("$path_client_bindir/mysqladmin"); $exe_mysql= mtr_exe_exists("$path_client_bindir/mysql"); + if ( $mysql_version_id >= 50100 ) + { + $exe_mysqlslap= mtr_exe_exists("$path_client_bindir/mysqlslap"); + } # Look for mysql_fix_system_table script $exe_mysql_fix_system_tables= @@ -1540,21 +1544,18 @@ sub environment_setup () { # ---------------------------------------------------- # Setup env so childs can execute mysqlslap # ---------------------------------------------------- - unless ( $glob_win32 ) - { - my $cmdline_mysqlslap= - "$exe_mysqlslap -uroot " . - "--port=$master->[0]->{'port'} " . - "--socket=$master->[0]->{'path_sock'} --password= " . - "--lock-directory=$opt_tmpdir"; + my $cmdline_mysqlslap= + "$exe_mysqlslap -uroot " . + "--port=$master->[0]->{'port'} " . + "--socket=$master->[0]->{'path_sock'} --password= " . + "--lock-directory=$opt_tmpdir"; - if ( $opt_debug ) - { - $cmdline_mysqlslap .= - " --debug=d:t:A,$path_vardir_trace/log/mysqlslap.trace"; - } - $ENV{'MYSQL_SLAP'}= $cmdline_mysqlslap; + if ( $opt_debug ) + { + $cmdline_mysqlslap .= + " --debug=d:t:A,$path_vardir_trace/log/mysqlslap.trace"; } + $ENV{'MYSQL_SLAP'}= $cmdline_mysqlslap; # ---------------------------------------------------- # Setup env so childs can execute mysqlimport From 395a16482d16e7b54d7b2f55fe8dc286186a51c0 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Oct 2006 23:19:06 +0200 Subject: [PATCH 37/68] Only setup cmdline_myslslap if exe_mysqlslap defined --- mysql-test/mysql-test-run.pl | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index b5fdf01ada4..7f2b61efb4c 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1544,18 +1544,21 @@ sub environment_setup () { # ---------------------------------------------------- # Setup env so childs can execute mysqlslap # ---------------------------------------------------- - my $cmdline_mysqlslap= - "$exe_mysqlslap -uroot " . - "--port=$master->[0]->{'port'} " . - "--socket=$master->[0]->{'path_sock'} --password= " . - "--lock-directory=$opt_tmpdir"; - - if ( $opt_debug ) + if ( $exe_mysqlslap ) { - $cmdline_mysqlslap .= - " --debug=d:t:A,$path_vardir_trace/log/mysqlslap.trace"; + my $cmdline_mysqlslap= + "$exe_mysqlslap -uroot " . + "--port=$master->[0]->{'port'} " . + "--socket=$master->[0]->{'path_sock'} --password= " . + "--lock-directory=$opt_tmpdir"; + + if ( $opt_debug ) + { + $cmdline_mysqlslap .= + " --debug=d:t:A,$path_vardir_trace/log/mysqlslap.trace"; + } + $ENV{'MYSQL_SLAP'}= $cmdline_mysqlslap; } - $ENV{'MYSQL_SLAP'}= $cmdline_mysqlslap; # ---------------------------------------------------- # Setup env so childs can execute mysqlimport From 93c0cddbd4bb2767b9c93f2d90a419d61b37a81e Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Oct 2006 23:34:05 +0200 Subject: [PATCH 38/68] Update "get_errcode_from_name" - add missing DBUG_RETURN - Change type of st_error->code to uint, that should be more than enough to hold the error numbers between 1000 and around 2000 --- client/mysqltest.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index d49693fa324..f4fa95afeb1 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -2429,7 +2429,7 @@ void do_set_charset(struct st_command *command) typedef struct { const char *name; - long code; + uint code; } st_error; static st_error global_error_names[] = @@ -2443,8 +2443,8 @@ static st_error global_error_names[] = uint get_errcode_from_name(char *error_name, char *error_end) { -#ifdef HAVE_MYSQLD_ERNAME DBUG_ENTER("get_errcode_from_name"); +#ifdef HAVE_MYSQLD_ERNAME /* SQL error as string */ st_error *e= global_error_names; @@ -2461,8 +2461,7 @@ uint get_errcode_from_name(char *error_name, char *error_end) if (!strncmp(error_name, e->name, (int) (error_end - error_name)) && (uint) strlen(e->name) == (uint) (error_end - error_name)) { - return (uint) e->code; - break; + DBUG_RETURN(e->code); } } if (!e->name) @@ -2472,7 +2471,7 @@ uint get_errcode_from_name(char *error_name, char *error_end) LINT_INIT(error_end); abort_not_in_this_version(); #endif - return 0; + DBUG_RETURN(0);; } From 70c1311fb9431cddd087dbb046746ff133f64699 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Oct 2006 23:48:24 +0200 Subject: [PATCH 39/68] Update test results to new mysqltest Remove extra space for "send" commands mysql-test/extra/rpl_tests/rpl_truncate_helper.test: Get $SERVER_VERSION from the mysqld mysql-test/r/flush_table.result: Remove extra space for "send" commands mysql-test/r/handler_innodb.result: Remove extra space for "send" commands mysql-test/r/lock_multi.result: Remove extra space for "send" commands mysql-test/r/log_tables.result: Remove extra space for "send" commands --- .../extra/rpl_tests/rpl_truncate_helper.test | 1 + mysql-test/r/flush_table.result | 6 ++--- mysql-test/r/handler_innodb.result | 4 ++-- mysql-test/r/lock_multi.result | 24 +++++++++---------- mysql-test/r/log_tables.result | 4 ++-- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/mysql-test/extra/rpl_tests/rpl_truncate_helper.test b/mysql-test/extra/rpl_tests/rpl_truncate_helper.test index 1e485baca36..7f1506c4010 100644 --- a/mysql-test/extra/rpl_tests/rpl_truncate_helper.test +++ b/mysql-test/extra/rpl_tests/rpl_truncate_helper.test @@ -36,6 +36,7 @@ SELECT * FROM t1; --echo **** On Master **** connection master; DROP TABLE t1; +let $SERVER_VERSION=`select version()`; --replace_result $SERVER_VERSION SERVER_VERSION --replace_regex /\/\* xid=[0-9]+ \*\//\/* xid= *\// /table_id: [0-9]+/table_id: #/ SHOW BINLOG EVENTS; diff --git a/mysql-test/r/flush_table.result b/mysql-test/r/flush_table.result index b29c83eb574..8821bade6b4 100644 --- a/mysql-test/r/flush_table.result +++ b/mysql-test/r/flush_table.result @@ -9,7 +9,7 @@ test.t1 check status OK unlock tables; lock table t1 read; lock table t1 read; - flush table t1; +flush table t1; select * from t1; a 1 @@ -19,7 +19,7 @@ a 1 unlock tables; lock table t1 write; - lock table t1 read; +lock table t1 read; flush table t1; select * from t1; a @@ -27,7 +27,7 @@ a unlock tables; unlock tables; lock table t1 read; - lock table t1 write; +lock table t1 write; flush table t1; select * from t1; a diff --git a/mysql-test/r/handler_innodb.result b/mysql-test/r/handler_innodb.result index 89569d918ca..1bd50612a3f 100644 --- a/mysql-test/r/handler_innodb.result +++ b/mysql-test/r/handler_innodb.result @@ -476,7 +476,7 @@ handler t1 read first; c1 1 send the below to another connection, do not wait for the result - optimize table t1; +optimize table t1; proceed with the normal connection handler t1 read next; c1 @@ -502,7 +502,7 @@ flush tables with read lock; drop table t1; ERROR HY000: Can't execute the query because you have a conflicting read lock send the below to another connection, do not wait for the result - drop table t1; +drop table t1; proceed with the normal connection select * from t1; c1 diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index 8ff02d898a3..07b8bf78efd 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -2,8 +2,8 @@ drop table if exists t1,t2; create table t1(n int); insert into t1 values (1); lock tables t1 write; - update low_priority t1 set n = 4; - select n from t1; +update low_priority t1 set n = 4; +select n from t1; unlock tables; n 4 @@ -11,8 +11,8 @@ drop table t1; create table t1(n int); insert into t1 values (1); lock tables t1 read; - update low_priority t1 set n = 4; - select n from t1; +update low_priority t1 set n = 4; +select n from t1; unlock tables; n 1 @@ -23,7 +23,7 @@ insert into t1 values(1,1); insert into t1 values(2,2); insert into t2 values(1,2); lock table t1 read; - update t1,t2 set c=a where b=d; +update t1,t2 set c=a where b=d; select c from t2; c 2 @@ -32,7 +32,7 @@ drop table t2; create table t1 (a int); create table t2 (a int); lock table t1 write, t2 write; - insert t1 select * from t2; +insert t1 select * from t2; drop table t2; ERROR 42S02: Table 'test.t2' doesn't exist drop table t1; @@ -54,7 +54,7 @@ use mysql; LOCK TABLES columns_priv WRITE, db WRITE, host WRITE, user WRITE; FLUSH TABLES; use mysql; - SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1; +SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1; OPTIMIZE TABLES columns_priv, db, host, user; Table Op Msg_type Msg_text mysql.columns_priv optimize status OK @@ -68,14 +68,14 @@ use test; use test; CREATE TABLE t1 (c1 int); LOCK TABLE t1 WRITE; - FLUSH TABLES WITH READ LOCK; +FLUSH TABLES WITH READ LOCK; CREATE TABLE t2 (c1 int); UNLOCK TABLES; UNLOCK TABLES; DROP TABLE t1, t2; CREATE TABLE t1 (c1 int); LOCK TABLE t1 WRITE; - FLUSH TABLES WITH READ LOCK; +FLUSH TABLES WITH READ LOCK; CREATE TABLE t2 AS SELECT * FROM t1; ERROR HY000: Table 't2' was not locked with LOCK TABLES UNLOCK TABLES; @@ -83,7 +83,7 @@ UNLOCK TABLES; DROP TABLE t1; CREATE DATABASE mysqltest_1; FLUSH TABLES WITH READ LOCK; - DROP DATABASE mysqltest_1; +DROP DATABASE mysqltest_1; DROP DATABASE mysqltest_1; ERROR HY000: Can't execute the query because you have a conflicting read lock UNLOCK TABLES; @@ -91,7 +91,7 @@ DROP DATABASE mysqltest_1; ERROR HY000: Can't drop database 'mysqltest_1'; database doesn't exist create table t1 (f1 int(12) unsigned not null auto_increment, primary key(f1)) engine=innodb; lock tables t1 write; - alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; // - alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; // +alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; // +alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; alter table t1 auto_increment=0; // unlock tables; drop table t1; diff --git a/mysql-test/r/log_tables.result b/mysql-test/r/log_tables.result index 638c05dd712..53b2fd9f4d8 100644 --- a/mysql-test/r/log_tables.result +++ b/mysql-test/r/log_tables.result @@ -39,14 +39,14 @@ ERROR HY000: You can't use usual read lock with log tables. Try READ LOCAL inste lock tables mysql.slow_log READ LOCAL, mysql.general_log READ LOCAL; unlock tables; lock tables mysql.general_log READ LOCAL; - flush logs; +flush logs; unlock tables; select "Mark that we woke up from flush logs in the test" as "test passed"; test passed Mark that we woke up from flush logs in the test lock tables mysql.general_log READ LOCAL; - truncate mysql.general_log; +truncate mysql.general_log; unlock tables; select "Mark that we woke up from TRUNCATE in the test" as "test passed"; From bd1f1d9841805d1cb05ea5ebeec5d73520e6c140 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Oct 2006 23:57:31 +0200 Subject: [PATCH 40/68] Add skip-bdb to im defaults file in 5.0 --- mysql-test/mysql-test-run.pl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 7f2b61efb4c..dac1a4b553e 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -2541,7 +2541,10 @@ skip-innodb skip-ndbcluster EOF ; - + if ( $mysql_version_id < 50100 ) + { + print OUT "skip-bdb\n"; + } print OUT "nonguarded\n" if $instance->{'nonguarded'}; if ( $mysql_version_id >= 50100 ) { From 49859bd66dcb819456464a170a0ebba0c6cededb Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 00:00:34 +0200 Subject: [PATCH 41/68] Update result file mysql-test/r/im_utils.result: Change order of ouput, skip-bdb is now after skip-ndbcluster --- mysql-test/r/im_utils.result | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/im_utils.result b/mysql-test/r/im_utils.result index f671089d31d..626f24b4635 100644 --- a/mysql-test/r/im_utils.result +++ b/mysql-test/r/im_utils.result @@ -22,8 +22,8 @@ basedir VALUE server_id VALUE skip-stack-trace VALUE skip-innodb VALUE -skip-bdb VALUE skip-ndbcluster VALUE +skip-bdb VALUE SHOW INSTANCE OPTIONS mysqld2; option_name value instance_name VALUE @@ -42,8 +42,8 @@ basedir VALUE server_id VALUE skip-stack-trace VALUE skip-innodb VALUE -skip-bdb VALUE skip-ndbcluster VALUE +skip-bdb VALUE START INSTANCE mysqld2; Success: the process has been started. STOP INSTANCE mysqld2; From 08ef106201b42fea50117325756833a19491bd24 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 00:12:54 +0200 Subject: [PATCH 42/68] Set the small configuration a little bit larger for 4.1 --- mysql-test/mysql-test-run.pl | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index dac1a4b553e..f40077e53a1 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1959,11 +1959,22 @@ sub ndbcluster_start_install ($) { if (!$opt_bench) { # Use a smaller configuration - $ndb_no_ord=32; - $ndb_con_op=5000; - $ndb_dmem="20M"; - $ndb_imem="1M"; - $ndb_pbmem="4M"; + if ( $mysql_version_id < 50000 ) + { + # 4.1 is using a "larger" --small configuration + $ndb_no_ord=128; + $ndb_con_op=10000; + $ndb_dmem="40M"; + $ndb_imem="12M"; + } + else + { + $ndb_no_ord=32; + $ndb_con_op=5000; + $ndb_dmem="20M"; + $ndb_imem="1M"; + $ndb_pbmem="4M"; + } } my $config_file_template= "ndb/ndb_config_${nodes}_node.ini"; From 95e329152f9d168aab504752dbd0c34517febb0b Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 00:19:24 +0200 Subject: [PATCH 43/68] Fix detection of debug binaries. If mysqld ouputs "debug xx" in the variable list it's a debug binary --- mysql-test/mysql-test-run.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index f40077e53a1..1ff0b57687b 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1889,7 +1889,7 @@ sub check_ssl_support ($) { sub check_debug_support ($) { my $mysqld_variables= shift; - if ( $mysqld_variables->{'debug'} ) + if ( ! $mysqld_variables->{'debug'} ) { #mtr_report("Binaries are not debug compiled"); $debug_compiled_binaries= 0; From ec061718af55d026496ade752dada3ff703c9453 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 00:36:03 +0200 Subject: [PATCH 44/68] Update result file Get server version from mysqld mysql-test/r/lock_multi.result: Update result format to latest version of mysqltest, remove leading scpace from query that has been "send" mysql-test/t/rpl_truncate_7ndb.test: get server version from mysqld --- mysql-test/r/lock_multi.result | 2 +- mysql-test/t/rpl_truncate_7ndb.test | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index 07b8bf78efd..2445b3e0c69 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -39,7 +39,7 @@ drop table t1; create table t1 (a int); create table t2 (a int); lock table t1 write, t2 write, t1 as t1_2 write, t2 as t2_2 write; - insert t1 select * from t2; +insert t1 select * from t2; drop table t2; ERROR 42S02: Table 'test.t2' doesn't exist drop table t1; diff --git a/mysql-test/t/rpl_truncate_7ndb.test b/mysql-test/t/rpl_truncate_7ndb.test index b0d4bdfc763..1d69eee5dd0 100644 --- a/mysql-test/t/rpl_truncate_7ndb.test +++ b/mysql-test/t/rpl_truncate_7ndb.test @@ -39,6 +39,7 @@ SELECT * FROM t1 ORDER BY a,b; --echo **** On Master **** connection master; DROP TABLE t1; +let SERVER_VERSION=`select version()`; --replace_regex /\/\* xid=[0-9]+ \*\//\/* xid= *\// /table_id: [0-9]+/table_id: #/ --replace_result $SERVER_VERSION SERVER_VERSION SHOW BINLOG EVENTS; From 4c8931294b600b2388d970e2324c4fc63ccea4b8 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 12:25:24 +0200 Subject: [PATCH 45/68] Improve "check_eol_junk" to detect junk although there are multi line comments in the way. I.e take advantage of the fact that a # comment is always terminated by a new line Add tests for the above client/mysqltest.c: Improve "check_eol_junk" to detect junk although there are multi line comments in the way. I.e take advantage of the fact that a # comment is always terminated by a new line mysql-test/r/mysqltest.result: Update resut file mysql-test/t/mysqltest.test: Add test for improved check_eol_junk --- client/mysqltest.c | 44 +++++++++++++++++---- mysql-test/r/mysqltest.result | 17 ++++++++ mysql-test/t/mysqltest.test | 74 ++++++++++++++++++++++++++++++++--- 3 files changed, 121 insertions(+), 14 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index f4fa95afeb1..87d8484dbf8 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -3578,17 +3578,15 @@ void scan_command_for_warnings(struct st_command *command) /* Check for unexpected "junk" after the end of query - This is normally caused by missing delimiters + This is normally caused by missing delimiters or when + switching between different delimiters */ -void check_eol_junk(const char *eol) +void check_eol_junk_line(const char *line) { - const char *p= eol; - DBUG_ENTER("check_eol_junk"); - DBUG_PRINT("enter", ("eol: %s", eol)); - /* Remove all spacing chars except new line */ - while (*p && my_isspace(charset_info, *p) && (*p != '\n')) - p++; + const char *p= line; + DBUG_ENTER("check_eol_junk_line"); + DBUG_PRINT("enter", ("line: %s", line)); /* Check for extra delimiter */ if (*p && !strncmp(p, delimiter, delimiter_length)) @@ -3604,6 +3602,36 @@ void check_eol_junk(const char *eol) DBUG_VOID_RETURN; } +void check_eol_junk(const char *eol) +{ + const char *p= eol; + DBUG_ENTER("check_eol_junk"); + DBUG_PRINT("enter", ("eol: %s", eol)); + + /* Skip past all spacing chars and comments */ + while (*p && (my_isspace(charset_info, *p) || *p == '#' || *p == '\n')) + { + /* Skip past comments started with # and ended with newline */ + if (*p && *p == '#') + { + p++; + while (*p && *p != '\n') + p++; + } + + /* Check this line */ + if (*p && *p == '\n') + check_eol_junk_line(p); + + if (*p) + p++; + } + + check_eol_junk_line(p); + + DBUG_VOID_RETURN; +} + /* diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result index 4c243a64484..ed7267fb71d 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -152,6 +152,23 @@ mysqltest: At line 1: Missing delimiter mysqltest: At line 1: End of line junk detected: "sleep 7 # Another comment " +mysqltest: At line 1: Missing delimiter +mysqltest: At line 1: Missing delimiter +mysqltest: At line 1: End of line junk detected: "disconnect default + +# +# comment +# comment2 + +# comment 3 +--disable_query_log +" +mysqltest: At line 1: End of line junk detected: "disconnect default # comment +# comment part2 + +# comment 3 +--disable_query_log +" mysqltest: At line 1: Extra delimiter ";" found mysqltest: At line 1: Extra delimiter ";" found mysqltest: At line 1: Missing argument(s) to 'error' diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test index 5b0df28981f..d98375ca746 100644 --- a/mysql-test/t/mysqltest.test +++ b/mysql-test/t/mysqltest.test @@ -359,18 +359,80 @@ select 3 from t1 ; # Missing delimiter # The comment will be "sucked into" the sleep command since # delimiter is missing until after "show status" ---system echo "sleep 4" > $MYSQLTEST_VARDIR/tmp/mysqltest.sql ---system echo "# A comment" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql ---system echo "show status;" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql +sleep 4 +# A comment +show status; +EOF --error 1 --exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 # # Missing delimiter until eof # The comment will be "sucked into" the sleep command since -# delimiter is missing ---system echo "sleep 7" > $MYSQLTEST_VARDIR/tmp/mysqltest.sql ---system echo "# Another comment" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +# delimiter is missing +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql +sleep 7 +# Another comment +EOF +--error 1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 + +# +# Missing delimiter until "disable_query_log" +# +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql +disconnect default + +# +# comment +# comment 3 +disable_query_log; +EOF +--error 1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 + +# +# Missing delimiter until "disable_query_log" +# +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql +disconnect default + +# +# comment + +# comment 3 +disable_query_log; +EOF +--error 1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 + +# +# Missing delimiter until eof +# +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql +disconnect default + +# +# comment +# comment2 + +# comment 3 +--disable_query_log +EOF +--error 1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 + +# +# Missing delimiter until eof +# +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql +disconnect default # comment +# comment part2 + +# comment 3 +--disable_query_log +EOF --error 1 --exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 From c3e8fb57a068cf192b273d94ff6ff0f267a08ce0 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 12:28:23 +0200 Subject: [PATCH 46/68] Set the environment variable NDB_STATUS_OK to indicate that cluster is installed ok. This is actually not needed for this script as it will skip ndb tests if cluster install failed. But provided for compatibility mysql-test/include/have_ndb.inc: Revert --- mysql-test/include/have_ndb.inc | 1 + mysql-test/mysql-test-run.pl | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/mysql-test/include/have_ndb.inc b/mysql-test/include/have_ndb.inc index 84e60657876..9b85197abe8 100644 --- a/mysql-test/include/have_ndb.inc +++ b/mysql-test/include/have_ndb.inc @@ -1,3 +1,4 @@ +--exec test x$NDB_STATUS_OK = x1 -- require r/have_ndb.require disable_query_log; show variables like "have_ndbcluster"; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 1ff0b57687b..d74a4e6c3be 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -2665,6 +2665,19 @@ sub do_before_run_mysqltest($) { mtr_tofile($master->[1]->{'path_myerr'},"CURRENT_TEST: $tname\n"); } + + if ( $mysql_version_id < 50000 ) + { + # Set envirnoment variable NDB_STATUS_OK to 1 + # if script decided to run mysqltest cluster _is_ installed ok + $ENV{'NDB_STATUS_OK'} = "1"; + } + elsif ( $mysql_version_id < 50100 ) + { + # Set envirnoment variable NDB_STATUS_OK to YES + # if script decided to run mysqltest cluster _is_ installed ok + $ENV{'NDB_STATUS_OK'} = "YES"; + } } sub do_after_run_mysqltest($) From a4b13f10f55b64e4b61d9ea19c950c98f33f6cfb Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 12:41:26 +0200 Subject: [PATCH 47/68] Add missing semicolon --- mysql-test/t/wait_timeout.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/t/wait_timeout.test b/mysql-test/t/wait_timeout.test index 195d1a5d3f2..67ed8fedfeb 100644 --- a/mysql-test/t/wait_timeout.test +++ b/mysql-test/t/wait_timeout.test @@ -52,7 +52,7 @@ select 2; select 3; # Disconnect so that we will not be confused by a future abort from this # connection. -disconnect default +disconnect default; # # Do the same test as above on a TCP connection From ddf0fa97b54f0cfd167af933c9169a1a5acfcabc Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 13:09:06 +0200 Subject: [PATCH 48/68] Print warning message if test was failed becuase of cluster install failure Improve check of wheter test case need slave cluster --- mysql-test/mysql-test-run.pl | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index d74a4e6c3be..f680ed909b2 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -2619,21 +2619,24 @@ sub run_testcase_check_skip_test($) return 1; } - # If test needs cluster, check that master installed ok - if ( $tinfo->{'ndb_test'} and !$clusters->[0]->{'installed_ok'} ) + if ($tinfo->{'ndb_test'}) { - mtr_report_test_name($tinfo); - mtr_report_test_failed($tinfo); - return 1; - } + foreach my $cluster (@{$clusters}) + { + last if ($opt_skip_ndbcluster_slave and + $cluster->{'name'} eq 'Slave'); - # If test needs slave cluster, check that it installed ok - if ( $tinfo->{'ndb_test'} and $tinfo->{'slave_num'} and - !$clusters->[1]->{'installed_ok'} ) - { - mtr_report_test_name($tinfo); - mtr_report_test_failed($tinfo); - return 1; + # If test needs this cluster, check it was installed ok + if ( !$cluster->{'installed_ok'} ) + { + mtr_tofile($path_timefile, + "Test marked as failed because $cluster->{'name'} " . + "was not installed ok!"); + mtr_report_test_name($tinfo); + mtr_report_test_failed($tinfo); + return 1; + } + } } return 0; From 9674059f685dc3421f3ce68d41b19d7572f61196 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 13:38:08 +0200 Subject: [PATCH 49/68] Add regex to detect the empty line that end the variable list. Some variable names in 5.1 is soo long that there is no space inbetween name and value. Outputing a warning about it, as that should probably be fixed. At least one space should be put between the name and value. --- mysql-test/mysql-test-run.pl | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index f680ed909b2..5ae5684d14c 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1189,9 +1189,19 @@ sub check_mysqld_features () { } else { - # The variable list is ended with a blank line, so when a line - # doesn't match the above regex, break the loop - last; + # The variable list is ended with a blank line + if ( $line =~ /^[\s]*$/ ) + { + last; + } + else + { + # Send out a warning, we should fix the variables that has no + # space between variable name and it's value + # or should it be fixed width column parsing? It does not + # look like that in function my_print_variables in my_getopt.c + mtr_warning("Could not parse variable list line : $line"); + } } } } From 2317c0cf59414a682babe1d434d1fd32cfc37f82 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 13:45:50 +0200 Subject: [PATCH 50/68] 5.0 is also using the somewhat larger config for cluster --- mysql-test/mysql-test-run.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 5ae5684d14c..04daeb11b02 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1969,9 +1969,9 @@ sub ndbcluster_start_install ($) { if (!$opt_bench) { # Use a smaller configuration - if ( $mysql_version_id < 50000 ) + if ( $mysql_version_id < 50100 ) { - # 4.1 is using a "larger" --small configuration + # 4.1 and 5.0 is using a "larger" --small configuration $ndb_no_ord=128; $ndb_con_op=10000; $ndb_dmem="40M"; From 068641aa53ccbb0aa3654ba2171ae6785d46a4ad Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 14:37:46 +0200 Subject: [PATCH 51/68] Add new option --mem to mysql-test-run.pl. It will automatically setup a symlink from var/ to a tmpfs area and thereby speed up the execution of the testsuite significantly mysql-test/mysql-test-run.pl: Add new option --mem to mysql-test-run.pl. It will automatically setup a symlink from var/ to a tmpfs area and thereby speed up the execution of the testsuite significantly --- mysql-test/mysql-test-run.pl | 71 ++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 04daeb11b02..20123a68159 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -209,6 +209,7 @@ our $opt_fast; our $opt_force; our $opt_reorder= 0; our $opt_enable_disabled; +our $opt_mem; our $opt_gcov; our $opt_gcov_err; @@ -643,6 +644,7 @@ sub command_line_setup () { 'tmpdir=s' => \$opt_tmpdir, 'vardir=s' => \$opt_vardir, 'benchdir=s' => \$glob_mysql_bench_dir, + 'mem' => \$opt_mem, # Misc 'comment=s' => \$opt_comment, @@ -715,6 +717,32 @@ sub command_line_setup () { } mtr_report("Using binlog format '$used_binlog_format'"); + # -------------------------------------------------------------------------- + # Check if we should speed up tests by trying to run on tmpfs + # -------------------------------------------------------------------------- + if ( $opt_mem ) + { + mtr_error("Can't use --mem and --vardir at the same time ") + if $opt_vardir; + mtr_error("Can't use --mem and --tmpdir at the same time ") + if $opt_tmpdir; + + # Use /dev/shm as the preferred location for vardir and + # thus implicitly also tmpdir. Add other locations to list + my @tmpfs_locations= ("/dev/shm"); + # One could maybe use "mount" to find tmpfs location(s) + foreach my $fs (@tmpfs_locations) + { + if ( -d $fs ) + { + mtr_report("Using tmpfs in $fs"); + $opt_mem= "$fs/var"; + $opt_mem .= $ENV{'MTR_BUILD_THREAD'} if $ENV{'MTR_BUILD_THREAD'}; + last; + } + } + } + # -------------------------------------------------------------------------- # Set the "var/" directory, as it is the base for everything else # -------------------------------------------------------------------------- @@ -1771,6 +1799,8 @@ sub kill_running_server () { sub cleanup_stale_files () { + my $created_by_mem_file= "$glob_mysql_test_dir/var/created_by_mem"; + mtr_report("Removing Stale Files"); if ( $opt_vardir eq "$glob_mysql_test_dir/var" ) @@ -1778,15 +1808,26 @@ sub cleanup_stale_files () { # # Running with "var" in mysql-test dir # - if ( -l "$glob_mysql_test_dir/var" ) + if ( -l $opt_vardir) { - # Some users creates a soft link in mysql-test/var to another area - # - allow it - mtr_report("WARNING: Using the 'mysql-test/var' symlink"); - rmtree("$opt_vardir/log"); - rmtree("$opt_vardir/ndbcluster-$opt_ndbcluster_port"); - rmtree("$opt_vardir/run"); - rmtree("$opt_vardir/tmp"); + # var is a symlink + if (-f $created_by_mem_file) + { + # Remove the directory which the link points at + rmtree(readlink($opt_vardir)); + # Remove the entire "var" dir + rmtree("$opt_vardir/"); + } + else + { + # Some users creates a soft link in mysql-test/var to another area + # - allow it + mtr_report("WARNING: Using the 'mysql-test/var' symlink"); + rmtree("$opt_vardir/log"); + rmtree("$opt_vardir/ndbcluster-$opt_ndbcluster_port"); + rmtree("$opt_vardir/run"); + rmtree("$opt_vardir/tmp"); + } } else { @@ -1808,6 +1849,17 @@ sub cleanup_stale_files () { rmtree("$opt_vardir/"); } + if ( $opt_mem ) + { + # Runinng with var as a link to some "memory" location, normally tmpfs + rmtree($opt_mem); + mkpath($opt_mem); + mtr_verbose("Creating symlink from $opt_vardir to $opt_mem"); + symlink($opt_mem, $opt_vardir); + # Put a small file to recognize this dir was created by --mem + mtr_tofile($created_by_mem_file, $opt_mem); + } + mkpath("$opt_vardir/log"); mkpath("$opt_vardir/run"); mkpath("$opt_vardir/tmp"); @@ -4326,6 +4378,9 @@ Options to control directories to use vardir=DIR The directory where files generated from the test run is stored (default: ./var). Specifying a ramdisk or tmpfs will speed up tests. + mem=DIR Run testsuite in "memory" using tmpfs if + available(default: /dev/shm) + Options to control what test suites or cases to run From 8a27de8371fd9762bb2cb40677093debce6d834b Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 14:38:57 +0200 Subject: [PATCH 52/68] Update result file, --skip-bdb is not passed to mysqld's started by im in 5.1 --- mysql-test/r/im_utils.result | 2 -- 1 file changed, 2 deletions(-) diff --git a/mysql-test/r/im_utils.result b/mysql-test/r/im_utils.result index 4a07faf4331..6e40c9bb1c0 100644 --- a/mysql-test/r/im_utils.result +++ b/mysql-test/r/im_utils.result @@ -22,7 +22,6 @@ server_id VALUE skip-stack-trace VALUE skip-innodb VALUE skip-ndbcluster VALUE -skip-bdb VALUE log-output VALUE SHOW INSTANCE OPTIONS mysqld2; option_name value @@ -41,7 +40,6 @@ server_id VALUE skip-stack-trace VALUE skip-innodb VALUE skip-ndbcluster VALUE -skip-bdb VALUE nonguarded VALUE log-output VALUE START INSTANCE mysqld2; From 6d89bcbcff64c09ad280a43b6c9106cef7868af0 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 18:00:20 +0200 Subject: [PATCH 53/68] Bug #20010 mysql-test-run.pl: --record and --require conflict (test fails) - When --record is passed to mysqltest the whole testcase should be executed as it normally is while the output form the test is acumulating in ds_res. When test has finished ds_res should simply be written to the specified result file(if any) instead of comapring it against the result file. Simplify handling of --require and also the cecking of result files by splitting check_result function into one function 'check_require' that is specialised in checking require's and leave 'check_result' to do just that. - "mysqltest --record" has been considered unsafe, but with this really simple logic, it should be safe to use. client/mysqltest.c: When --record is passed to mysqltest the whole testcase should be executed as it normally is while the output form the test is acumulating in ds_res. When test has finished ds_res should simply be written to the specified result file(if any) instead of comapring it against the result file. Simplify handling of --require and also the cecking of result files by splitting check_result function into one function 'check_require' that is specialised in checking require's and leave 'check_result' to do just that. --- client/mysqltest.c | 181 ++++++++++++++++++++++++--------------------- 1 file changed, 95 insertions(+), 86 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 87d8484dbf8..96bb5d6b80b 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -192,7 +192,7 @@ struct } master_pos; /* if set, all results are concated and compared against this file */ -const char *result_file= 0; +const char *result_file_name= 0; typedef struct st_var { @@ -382,9 +382,9 @@ struct st_command { char *query, *query_buf,*first_argument,*last_argument,*end; int first_word_len, query_len; - my_bool abort_on_error, require_file; + my_bool abort_on_error; struct st_expected_errors expected_errors; - char record_file[FN_REFLEN]; + char require_file[FN_REFLEN]; enum enum_commands type; }; @@ -410,9 +410,10 @@ VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw, my_bool ignore_not_existing); void eval_expr(VAR* v, const char *p, const char** p_end); my_bool match_delimiter(int c, const char *delim, uint length); -void dump_result_to_reject_file(const char *record_file, char *buf, int size); -void dump_result_to_log_file(const char *record_file, char *buf, int size); -void dump_warning_messages(const char *record_file); +void dump_result_to_reject_file(char *buf, int size); +void dump_result_to_log_file(char *buf, int size); +void dump_warning_messages(); +void dump_progress(); void do_eval(DYNAMIC_STRING *query_eval, const char *query, const char *query_end, my_bool pass_through_escape_chars); @@ -742,12 +743,12 @@ void die(const char *fmt, ...) va_end(args); /* Dump the result that has been accumulated so far to .log file */ - if (result_file && ds_res.length) - dump_result_to_log_file(result_file, ds_res.str, ds_res.length); + if (result_file_name && ds_res.length) + dump_result_to_log_file(ds_res.str, ds_res.length); /* Dump warning messages */ - if (result_file && ds_warning_messages.length) - dump_warning_messages(result_file); + if (result_file_name && ds_warning_messages.length) + dump_warning_messages(); /* Clean up and exit */ free_used_memory(); @@ -926,41 +927,32 @@ err: /* - Check the content of ds against content of file fname + Check the content of ds against result file SYNOPSIS check_result ds - content to be checked - fname - name of file to check against - require_option - if set and check fails, the test will be aborted - with the special exit code "not supported test" RETURN VALUES error - the function will not return */ -void check_result(DYNAMIC_STRING* ds, const char *fname, - my_bool require_option) +void check_result(DYNAMIC_STRING* ds) { - int res= dyn_string_cmp(ds, fname); + DBUG_ASSERT(result_file_name); DBUG_ENTER("check_result"); - if (res && require_option) + switch (dyn_string_cmp(ds, result_file_name)) { - char reason[FN_REFLEN]; - fn_format(reason, fname, "", "", MY_REPLACE_EXT | MY_REPLACE_DIR); - abort_not_supported_test("Test requires: '%s'", reason); - } - switch (res) { case RESULT_OK: break; /* ok */ case RESULT_LENGTH_MISMATCH: - dump_result_to_reject_file(fname, ds->str, ds->length); + dump_result_to_reject_file(ds->str, ds->length); die("Result length mismatch"); break; case RESULT_CONTENT_MISMATCH: - dump_result_to_reject_file(fname, ds->str, ds->length); + dump_result_to_reject_file(ds->str, ds->length); die("Result content mismatch"); break; default: /* impossible */ @@ -971,6 +963,35 @@ void check_result(DYNAMIC_STRING* ds, const char *fname, } +/* + Check the content of ds against a require file + If match fails, abort the test with special error code + indicating that test is not supported + + SYNOPSIS + check_result + ds - content to be checked + fname - name of file to check against + + RETURN VALUES + error - the function will not return + +*/ + +void check_require(DYNAMIC_STRING* ds, const char *fname) +{ + DBUG_ENTER("check_require"); + + if (dyn_string_cmp(ds, fname)) + { + char reason[FN_REFLEN]; + fn_format(reason, fname, "", "", MY_REPLACE_EXT | MY_REPLACE_DIR); + abort_not_supported_test("Test requires: '%s'", reason); + } + DBUG_VOID_RETURN; +} + + static byte *get_var_key(const byte* var, uint* len, my_bool __attribute__((unused)) t) { @@ -2389,7 +2410,8 @@ int do_sleep(struct st_command *command, my_bool real_sleep) } -void do_get_file_name(char *filename, struct st_command *command) +void do_get_file_name(struct st_command *command, + char* dest, uint dest_max_len) { char *p= command->first_argument, *name; if (!*p) @@ -2400,7 +2422,7 @@ void do_get_file_name(char *filename, struct st_command *command) if (*p) *p++= 0; command->last_argument= p; - strmake(filename, name, FN_REFLEN); + strmake(dest, name, dest_max_len); } @@ -3672,8 +3694,7 @@ int read_command(struct st_command** command_ptr) insert_dynamic(&q_lines, (gptr) &command)) die(NullS); - command->record_file[0]= 0; - command->require_file= 0; + command->require_file[0]= 0; command->first_word_len= 0; command->query_len= 0; @@ -3767,8 +3788,8 @@ static struct my_option my_long_options[] = {"record", 'r', "Record output of test_file into result file.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"result-file", 'R', "Read/Store result from/in this file.", - (gptr*) &result_file, (gptr*) &result_file, 0, GET_STR, REQUIRED_ARG, - 0, 0, 0, 0, 0, 0}, + (gptr*) &result_file_name, (gptr*) &result_file_name, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"server-arg", 'A', "Send option value to embedded server as a parameter.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"server-file", 'F', "Read embedded server arguments from file.", @@ -4018,35 +4039,35 @@ void str_to_file(const char *fname, char *str, int size) } -void dump_result_to_reject_file(const char *record_file, char *buf, int size) +void dump_result_to_reject_file(char *buf, int size) { char reject_file[FN_REFLEN]; - str_to_file(fn_format(reject_file, record_file, "", ".reject", + str_to_file(fn_format(reject_file, result_file_name, "", ".reject", MY_REPLACE_EXT), buf, size); } -void dump_result_to_log_file(const char *record_file, char *buf, int size) +void dump_result_to_log_file(char *buf, int size) { char log_file[FN_REFLEN]; - str_to_file(fn_format(log_file, record_file, "", ".log", + str_to_file(fn_format(log_file, result_file_name, "", ".log", MY_REPLACE_EXT), buf, size); } -void dump_progress(const char *record_file) +void dump_progress(void) { char log_file[FN_REFLEN]; - str_to_file(fn_format(log_file, record_file, "", ".progress", + str_to_file(fn_format(log_file, result_file_name, "", ".progress", MY_REPLACE_EXT), ds_progress.str, ds_progress.length); } -void dump_warning_messages(const char *record_file) +void dump_warning_messages(void) { char warn_file[FN_REFLEN]; - str_to_file(fn_format(warn_file, record_file, "", ".warnings", + str_to_file(fn_format(warn_file, result_file_name, "", ".warnings", MY_REPLACE_EXT), ds_warning_messages.str, ds_warning_messages.length); } @@ -4608,7 +4629,7 @@ void handle_error(struct st_command *command, DBUG_ENTER("handle_error"); - if (command->require_file) + if (command->require_file[0]) { /* The query after a "--require" failed. This is fine as long the server @@ -5004,12 +5025,12 @@ void run_query(MYSQL *mysql, struct st_command *command, int flags) } /* - When command->record_file is set the output of _this_ query + When command->require_file is set the output of _this_ query should be compared with an already existing file Create a temporary dynamic string to contain the output from this query. */ - if (command->record_file[0]) + if (command->require_file[0]) { init_dynamic_string(&ds_result, "", 1024, 1024); ds= &ds_result; @@ -5144,26 +5165,14 @@ void run_query(MYSQL *mysql, struct st_command *command, int flags) mysql_errno(mysql), mysql_error(mysql)); } - if (command->record_file[0]) + if (command->require_file[0]) { - /* A result file was specified for _this_ query */ - if (record) - { - /* - Recording in progress - Dump the output from _this_ query to the specified record_file - */ - str_to_file(command->record_file, ds->str, ds->length); - - } else { - - /* - The output from _this_ query should be checked against an already - existing file which has been specified using --require or --result - */ - check_result(ds, command->record_file, command->require_file); - } + /* A result file was specified for _this_ query + and the output should be checked against an already + existing file which has been specified using --require or --result + */ + check_require(ds, command->require_file); } dynstr_free(&ds_warnings); @@ -5391,7 +5400,7 @@ void mark_progress(struct st_command* command __attribute__((unused)), int main(int argc, char **argv) { struct st_command *command; - my_bool require_file= 0, q_send_flag= 0; + my_bool q_send_flag= 0; uint command_executed= 0, last_command_executed= 0; char save_file[FN_REFLEN]; MY_STAT res_info; @@ -5442,7 +5451,8 @@ int main(int argc, char **argv) init_dynamic_string(&ds_warning_messages, "", 0, 2048); parse_args(argc, argv); - DBUG_PRINT("info",("result_file: '%s'", result_file ? result_file : "")); + DBUG_PRINT("info",("result_file: '%s'", + result_file_name ? result_file_name : "")); if (mysql_server_init(embedded_server_arg_count, embedded_server_args, (char**) embedded_server_groups)) @@ -5594,9 +5604,8 @@ int main(int argc, char **argv) display_result_vertically= (command->type == Q_QUERY_VERTICAL); if (save_file[0]) { - strmov(command->record_file,save_file); - command->require_file=require_file; - save_file[0]=0; + strmake(command->require_file, save_file, sizeof(save_file)); + save_file[0]= 0; } run_query(&cur_con->mysql, command, QUERY_REAP_FLAG|QUERY_SEND_FLAG); display_result_vertically= old_display_result_vertically; @@ -5626,9 +5635,8 @@ int main(int argc, char **argv) if (save_file[0]) { - strmov(command->record_file,save_file); - command->require_file=require_file; - save_file[0]=0; + strmake(command->require_file, save_file, sizeof(save_file)); + save_file[0]= 0; } run_query(&cur_con->mysql, command, flags); command_executed++; @@ -5660,17 +5668,12 @@ int main(int argc, char **argv) command_executed++; command->last_argument= command->end; break; - case Q_RESULT: - do_get_file_name(save_file, command); - require_file=0; + case Q_REQUIRE: + do_get_file_name(command, save_file, sizeof(save_file)); break; case Q_ERROR: do_get_errcodes(command); break; - case Q_REQUIRE: - do_get_file_name(save_file, command); - require_file=1; - break; case Q_REPLACE: do_get_replace(command); break; @@ -5741,11 +5744,14 @@ int main(int argc, char **argv) else die("Parsing is already enabled"); break; - case Q_DIE: die("%s", command->first_argument); break; + case Q_RESULT: + die("result, deprecated command"); + break; + default: processed= 0; break; @@ -5802,12 +5808,14 @@ int main(int argc, char **argv) */ if (ds_res.length) { - if (result_file) + if (result_file_name) { + /* A result file has been specified */ + if (record) { - /* Dump the output from test to result file */ - str_to_file(result_file, ds_res.str, ds_res.length); + /* Recording - dump the output from test to result file */ + str_to_file(result_file_name, ds_res.str, ds_res.length); } else { @@ -5815,12 +5823,12 @@ int main(int argc, char **argv) - detect missing result file - detect zero size result file */ - check_result(&ds_res, result_file, 0); + check_result(&ds_res); } } else { - /* No result_file specified to compare with, print to stdout */ + /* No result_file_name specified to compare with, print to stdout */ printf("%s", ds_res.str); } } @@ -5829,7 +5837,8 @@ int main(int argc, char **argv) die("The test didn't produce any output"); } - if (!command_executed && result_file && my_stat(result_file, &res_info, 0)) + if (!command_executed && + result_file_name && my_stat(result_file_name, &res_info, 0)) { /* my_stat() successful on result file. Check if we have not run a @@ -5841,12 +5850,12 @@ int main(int argc, char **argv) die("No queries executed but result file found!"); } - if ( opt_mark_progress && result_file ) - dump_progress(result_file); + if ( opt_mark_progress && result_file_name ) + dump_progress(); /* Dump warning messages */ - if (result_file && ds_warning_messages.length) - dump_warning_messages(result_file); + if (result_file_name && ds_warning_messages.length) + dump_warning_messages(); dynstr_free(&ds_res); From 9ba3b3f2efe7e6e93810597f967f22f7b2d39019 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 18:29:59 +0200 Subject: [PATCH 54/68] Use same --slave-load-tmpdir in all versions(backport from 5.0) mysql-test/mysql-test-run.pl: Use same location for slave-load-tmpdir in all versions mysql-test/mysql-test-run.sh: Use same location for slave-load-tmpdir in all versions mysql-test/r/rpl_loaddata.result: Update result after changing slave-load-tmpdir to use a shorter path mysql-test/r/rpl_loaddatalocal.result: Update result after changing slave-load-tmpdir to use a shorter path mysql-test/r/rpl_log.result: Update result after changing slave-load-tmpdir to use a shorter path mysql-test/t/rpl_loaddatalocal.test: Use MYSQLTEST_VARDIR when specifying path to load from(backport from 5.0) Use new command "remove_file" instead of s"ystem rm" --- mysql-test/mysql-test-run.pl | 4 ---- mysql-test/mysql-test-run.sh | 2 +- mysql-test/r/rpl_loaddata.result | 2 +- mysql-test/r/rpl_loaddatalocal.result | 4 ++-- mysql-test/r/rpl_log.result | 12 ++++++------ mysql-test/t/rpl_loaddatalocal.test | 8 +++++--- 6 files changed, 15 insertions(+), 17 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 20123a68159..51989f02f0e 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3153,10 +3153,6 @@ sub mysqld_arguments ($$$$$) { # on the server. The path need to have constant length otherwise # test results will vary, thus a relative path is used. my $slave_load_path= "../tmp"; - if ( $mysql_version_id < 50000 ) - { - $slave_load_path= "../../var/tmp"; - } mtr_add_arg($args, "%s--slave-load-tmpdir=%s", $prefix, $slave_load_path); mtr_add_arg($args, "%s--socket=%s", $prefix, diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh index 15c7470a74c..141a725db2e 100644 --- a/mysql-test/mysql-test-run.sh +++ b/mysql-test/mysql-test-run.sh @@ -188,7 +188,7 @@ SYST=0 REALT=0 FAST_START="" MYSQL_TMP_DIR=$MYSQL_TEST_DIR/var/tmp -SLAVE_LOAD_TMPDIR=../../var/tmp #needs to be same length to test logging +SLAVE_LOAD_TMPDIR=../tmp #needs to be same length to test logging RES_SPACE=" " MYSQLD_SRC_DIRS="strings mysys include extra regex isam merge myisam \ myisammrg heap sql" diff --git a/mysql-test/r/rpl_loaddata.result b/mysql-test/r/rpl_loaddata.result index 65fc9d1b415..d0f2a885dcd 100644 --- a/mysql-test/r/rpl_loaddata.result +++ b/mysql-test/r/rpl_loaddata.result @@ -22,7 +22,7 @@ day id category name 2003-03-22 2416 a bbbbb show master status; File Position Binlog_Do_DB Binlog_Ignore_DB -slave-bin.000001 964 +slave-bin.000001 950 drop table t1; drop table t2; drop table t3; diff --git a/mysql-test/r/rpl_loaddatalocal.result b/mysql-test/r/rpl_loaddatalocal.result index b49ea842485..b2ca868a094 100644 --- a/mysql-test/r/rpl_loaddatalocal.result +++ b/mysql-test/r/rpl_loaddatalocal.result @@ -5,9 +5,9 @@ reset slave; drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; start slave; create table t1(a int); -select * into outfile '../../var/master-data/rpl_loaddatalocal.select_outfile' from t1; +select * into outfile 'MYSQLTEST_VARDIR/master-data/rpl_loaddatalocal.select_outfile' from t1; truncate table t1; -load data local infile './var/master-data/rpl_loaddatalocal.select_outfile' into table t1; +load data local infile 'MYSQLTEST_VARDIR/master-data/rpl_loaddatalocal.select_outfile' into table t1; select a,count(*) from t1 group by a; a count(*) 1 10000 diff --git a/mysql-test/r/rpl_log.result b/mysql-test/r/rpl_log.result index 3833800bfeb..c496f3d540a 100644 --- a/mysql-test/r/rpl_log.result +++ b/mysql-test/r/rpl_log.result @@ -73,7 +73,7 @@ master-bin.000002 276 start slave; show binary logs; Log_name File_size -slave-bin.000001 1285 +slave-bin.000001 1278 slave-bin.000002 170 show binlog events in 'slave-bin.000001' from 4; Log_name Pos Event_type Server_id Orig_log_pos Info @@ -84,11 +84,11 @@ slave-bin.000001 200 Query 1 200 use `test`; insert into t1 values (NULL) slave-bin.000001 263 Query 1 263 use `test`; drop table t1 slave-bin.000001 311 Query 1 311 use `test`; create table t1 (word char(20) not null) slave-bin.000001 386 Create_file 1 386 db=test;table=t1;file_id=1;block_len=581 -slave-bin.000001 1065 Exec_load 1 1065 ;file_id=1 -slave-bin.000001 1088 Query 1 1088 use `test`; drop table t1 -slave-bin.000001 1136 Query 1 1136 use `test`; create table t5 (a int) -slave-bin.000001 1194 Query 1 1194 use `test`; drop table t5 -slave-bin.000001 1242 Rotate 2 1242 slave-bin.000002;pos=4 +slave-bin.000001 1058 Exec_load 1 1058 ;file_id=1 +slave-bin.000001 1081 Query 1 1081 use `test`; drop table t1 +slave-bin.000001 1129 Query 1 1129 use `test`; create table t5 (a int) +slave-bin.000001 1187 Query 1 1187 use `test`; drop table t5 +slave-bin.000001 1235 Rotate 2 1235 slave-bin.000002;pos=4 show binlog events in 'slave-bin.000002' from 4; Log_name Pos Event_type Server_id Orig_log_pos Info slave-bin.000002 4 Query 1 4 use `test`; create table t1 (n int) diff --git a/mysql-test/t/rpl_loaddatalocal.test b/mysql-test/t/rpl_loaddatalocal.test index 2ca142c3b64..fd91aba856a 100644 --- a/mysql-test/t/rpl_loaddatalocal.test +++ b/mysql-test/t/rpl_loaddatalocal.test @@ -20,11 +20,13 @@ while ($1) } set SQL_LOG_BIN=1; enable_query_log; -select * into outfile '../../var/master-data/rpl_loaddatalocal.select_outfile' from t1; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval select * into outfile '$MYSQLTEST_VARDIR/master-data/rpl_loaddatalocal.select_outfile' from t1; #This will generate a 20KB file, now test LOAD DATA LOCAL truncate table t1; -load data local infile './var/master-data/rpl_loaddatalocal.select_outfile' into table t1; -system rm ./var/master-data/rpl_loaddatalocal.select_outfile ; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval load data local infile '$MYSQLTEST_VARDIR/master-data/rpl_loaddatalocal.select_outfile' into table t1; +--remove_file $MYSQLTEST_VARDIR/master-data/rpl_loaddatalocal.select_outfile save_master_pos; connection slave; sync_with_master; From 2430d73ce68d0c64016f88f98431e0a67a19e97f Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 20:21:11 +0200 Subject: [PATCH 55/68] Make use of $opt_mem" and let 4.1 allow --vardir to be set. Still relies on the var/ directory but having it on tmpfs gives a big speedup. mysql-test/mysql-test-run.pl: Make use of opt_mem and let 4.1 allow vardir to be set. Still relies on the var/ directory but having it on tmpfs gives a big speedup. --- mysql-test/mysql-test-run.pl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 51989f02f0e..7778b99ea65 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -754,7 +754,10 @@ sub command_line_setup () { elsif ( $mysql_version_id < 50000 ) { # --vardir was specified - mtr_error("--vardir option not supported until MySQL 5.0"); + # It's only supported in 4.1 as a symlink from var/ + # by setting up $opt_mem that will be created + $opt_mem= $opt_vardir; + $opt_vardir= undef; } $path_vardir_trace= $opt_vardir; From 16b9d8d15ab9f00b02b90bd3a5e50cb3d9543487 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 20:45:16 +0200 Subject: [PATCH 56/68] No need to pass --valgrind to mysqltest anymore, the simple task of creating an environment variable is handled directly from the perl code. --- mysql-test/mysql-test-run.pl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 7778b99ea65..995baf2c0dd 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1749,6 +1749,11 @@ sub environment_setup () { print "Using IM_MYSQLD2_PORT = $ENV{IM_MYSQLD2_PORT}\n"; } } + + # Create an environment variable to make it possible + # to detect that valgrind is being used from test cases + $ENV{'VALGRIND_TEST'}= $opt_valgrind; + } @@ -4009,11 +4014,6 @@ sub run_mysqltest ($) { mtr_add_arg($args, "--big-test"); } - if ( $opt_valgrind ) - { - mtr_add_arg($args, "--valgrind"); - } - if ( $opt_compress ) { mtr_add_arg($args, "--compress"); From 93a9883bed4acacf5d5cad9c04255047f1679ec6 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 22:05:42 +0200 Subject: [PATCH 57/68] Add policy directive about keeping mysqltest framework tools identical in all versions mysql-test/mysql-test-run.pl: Add policy directive about keeping mysqltest framework tools identical in all versions Cleanup the initial comment to reflect current state --- client/mysqltest.c | 2 ++ mysql-test/mysql-test-run.pl | 43 +++++++++++++----------------------- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 96bb5d6b80b..cf3afced427 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -22,6 +22,8 @@ See the "MySQL Test framework manual" for more information http://dev.mysql.com/doc/mysqltest/en/index.html + Please keep the test framework tools identical in all versions! + Written by: Sasha Pachev Matt Wagner diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 995baf2c0dd..6c8befdafa6 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1,30 +1,24 @@ #!/usr/bin/perl # -*- cperl -*- -# This is a transformation of the "mysql-test-run" Bourne shell script -# to Perl. There are reasons this rewrite is not the prettiest Perl -# you have seen # -# - The original script is huge and for most part uncommented, -# not even a usage description of the flags. +############################################################################## # -# - There has been an attempt to write a replacement in C for the -# original Bourne shell script. It was kind of working but lacked -# lot of functionality to really be a replacement. Not to redo -# that mistake and catch all the obscure features of the original -# script, the rewrite in Perl is more close to the original script -# meaning it also share some of the ugly parts as well. +# mysql-test-run.pl # -# - The original intention was that this script was to be a prototype -# to be the base for a new C version with full functionality. Since -# then it was decided that the Perl version should replace the -# Bourne shell version, but the Perl style still reflects the wish -# to make the Perl to C step easy. +# Tool used for executing a suite of .test file # -# Some coding style from the original intent has been kept +# See the "MySQL Test framework manual" for more information +# http://dev.mysql.com/doc/mysqltest/en/index.html +# +# Please keep the test framework tools identical in all versions! +# +############################################################################## +# +# Coding style directions for this perl script # # - To make this Perl script easy to alter even for those that not -# code Perl that often, the coding style is as close as possible to +# code Perl that often, keeep the coding style as close as possible to # the C/C++ MySQL coding standard. # # - All lists of arguments to send to commands are Perl lists/arrays, @@ -42,15 +36,6 @@ # the information. This separates the "find information" from the # "do the work" and makes the program more easy to maintain. # -# - At the moment, there are tons of "global" variables that control -# this script, even accessed from the files in "lib/*.pl". This -# will change over time, for now global variables are used instead -# of using %opt, %path and %exe hashes, because I want more -# compile time checking, that hashes would not give me. Once this -# script is debugged, hashes will be used and passed as parameters -# to functions, to more closely mimic how it would be coded in C -# using structs. -# # - The rule when it comes to the logic of this program is # # command_line_setup() - is to handle the logic between flags @@ -2758,10 +2743,12 @@ sub do_after_run_mysqltest($) my $tinfo= shift; my $tname= $tinfo->{'name'}; + mtr_tofile($path_mysqltest_log,"CURRENT TEST $tname\n"); + # Save info from this testcase run to mysqltest.log mtr_appendfile_to_file($path_timefile, $path_mysqltest_log) if -f $path_timefile; - mtr_tofile($path_mysqltest_log,"CURRENT TEST $tname\n"); + } From c3997a6640e4ff7933691d89b48e3268d45f6281 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 22:06:29 +0200 Subject: [PATCH 58/68] Open input file with "<" not ">>" --- mysql-test/lib/mtr_io.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/lib/mtr_io.pl b/mysql-test/lib/mtr_io.pl index 8a7fc56269c..a1d7ffe87d8 100644 --- a/mysql-test/lib/mtr_io.pl +++ b/mysql-test/lib/mtr_io.pl @@ -176,7 +176,7 @@ sub mtr_appendfile_to_file ($$) { my $to_file= shift; open(TOFILE,">>",$to_file) or mtr_error("can't open file \"$to_file\": $!"); - open(FROMFILE,">>",$from_file) + open(FROMFILE,"<",$from_file) or mtr_error("can't open file \"$from_file\": $!"); print TOFILE while (); close FROMFILE; From 7ec1a292a19e45270e87617ff0431f25d12e468d Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 22:29:16 +0200 Subject: [PATCH 59/68] Print the newly found version number in human readable format --- mysql-test/mysql-test-run.pl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 6c8befdafa6..4162e8cf0ef 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1180,9 +1180,10 @@ sub check_mysqld_features () { # Look for version if ( $line =~ /^$exe_mysqld\s\sVer\s([0-9]*)\.([0-9]*)\.([0-9]*)/ ) { - print "Major: $1 Minor: $2 Build: $3\n"; + #print "Major: $1 Minor: $2 Build: $3\n"; $mysql_version_id= $1*10000 + $2*100 + $3; - print "mysql_version_id: $mysql_version_id\n"; + #print "mysql_version_id: $mysql_version_id\n"; + mtr_report("MySQL Version $1.$2.$3"); } } else From cae534e36f38dde5c34b320057a1df714d69cc5c Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Oct 2006 23:19:32 +0200 Subject: [PATCH 60/68] Add target to Makefile which uses the much faster --mem switch to mysql-test-run.pl --- Makefile.am | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile.am b/Makefile.am index 2aefbd05283..8d0746e6a64 100644 --- a/Makefile.am +++ b/Makefile.am @@ -124,3 +124,8 @@ test-force-pl: cd mysql-test; \ ./mysql-test-run.pl --force && \ ./mysql-test-run.pl --ps-protocol --force + +test-force-pl-mem: + cd mysql-test; \ + ./mysql-test-run.pl --force --mem && \ + ./mysql-test-run.pl --ps-protocol --force --mem From d76252b4a6cb35734571b7ebce54b1e02b9f870d Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 6 Oct 2006 00:06:53 +0200 Subject: [PATCH 61/68] Update partition test to use the new command "file_exist" --- mysql-test/r/partition.result | 36 ++++--------------------- mysql-test/t/partition.test | 50 ++++++++++++++++++++++------------- 2 files changed, 37 insertions(+), 49 deletions(-) diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index d8d433ef216..3585971aaf4 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -1132,41 +1132,15 @@ drop table t1; create table t1 (a int) engine myisam partition by range (a) subpartition by hash (a) -(partition p0 VALUES LESS THAN (1) DATA DIRECTORY = 'hello/master-data/tmpdata' INDEX DIRECTORY = 'hello/master-data/tmpinx' +(partition p0 VALUES LESS THAN (1) DATA DIRECTORY = 'MYSQLTEST_VARDIR/master-data/tmpdata' INDEX DIRECTORY = 'MYSQLTEST_VARDIR/master-data/tmpinx' (SUBPARTITION subpart00, SUBPARTITION subpart01)); -hello/master-data/test/t1.frm -hello/master-data/test/t1.par -hello/master-data/test/t1#P#p0#SP#subpart00.MYD -hello/master-data/test/t1#P#p0#SP#subpart00.MYI -hello/master-data/test/t1#P#p0#SP#subpart01.MYD -hello/master-data/test/t1#P#p0#SP#subpart01.MYI -hello/master-data/tmpdata/t1#P#p0#SP#subpart00.MYD -hello/master-data/tmpdata/t1#P#p0#SP#subpart01.MYD -hello/master-data/tmpinx/t1#P#p0#SP#subpart00.MYI -hello/master-data/tmpinx/t1#P#p0#SP#subpart01.MYI +Checking if file exists before alter ALTER TABLE t1 REORGANIZE PARTITION p0 INTO -(partition p1 VALUES LESS THAN (1) DATA DIRECTORY = 'hello/master-data/tmpdata' INDEX DIRECTORY = 'hello/master-data/tmpinx' +(partition p1 VALUES LESS THAN (1) DATA DIRECTORY = 'MYSQLTEST_VARDIR/master-data/tmpdata' INDEX DIRECTORY = 'MYSQLTEST_VARDIR/master-data/tmpinx' (SUBPARTITION subpart10, SUBPARTITION subpart11), -partition p2 VALUES LESS THAN (2) DATA DIRECTORY = 'hello/master-data/tmpdata' INDEX DIRECTORY = 'hello/master-data/tmpinx' +partition p2 VALUES LESS THAN (2) DATA DIRECTORY = 'MYSQLTEST_VARDIR/master-data/tmpdata' INDEX DIRECTORY = 'MYSQLTEST_VARDIR/master-data/tmpinx' (SUBPARTITION subpart20, SUBPARTITION subpart21)); -hello/master-data/test/t1.frm -hello/master-data/test/t1.par -hello/master-data/test/t1#P#p1#SP#subpart10.MYD -hello/master-data/test/t1#P#p1#SP#subpart10.MYI -hello/master-data/test/t1#P#p1#SP#subpart11.MYD -hello/master-data/test/t1#P#p1#SP#subpart11.MYI -hello/master-data/test/t1#P#p2#SP#subpart20.MYD -hello/master-data/test/t1#P#p2#SP#subpart20.MYI -hello/master-data/test/t1#P#p2#SP#subpart21.MYD -hello/master-data/test/t1#P#p2#SP#subpart21.MYI -hello/master-data/tmpdata/t1#P#p1#SP#subpart10.MYD -hello/master-data/tmpdata/t1#P#p1#SP#subpart11.MYD -hello/master-data/tmpdata/t1#P#p2#SP#subpart20.MYD -hello/master-data/tmpdata/t1#P#p2#SP#subpart21.MYD -hello/master-data/tmpinx/t1#P#p1#SP#subpart10.MYI -hello/master-data/tmpinx/t1#P#p1#SP#subpart11.MYI -hello/master-data/tmpinx/t1#P#p2#SP#subpart20.MYI -hello/master-data/tmpinx/t1#P#p2#SP#subpart21.MYI +Checking if file exists after alter drop table t1; create table t1 (a bigint unsigned not null, primary key(a)) engine = myisam diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index 928d855f9f4..cae5c54849d 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -1332,37 +1332,51 @@ eval SET @inx_dir = 'INDEX DIRECTORY = ''$MYSQLTEST_VARDIR/master-data/tmpinx''' let $inx_directory = `select @inx_dir`; --enable_query_log ---replace_result $MYSQLTEST_VARDIR "hello" +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR eval create table t1 (a int) engine myisam partition by range (a) subpartition by hash (a) (partition p0 VALUES LESS THAN (1) $data_directory $inx_directory (SUBPARTITION subpart00, SUBPARTITION subpart01)); ---replace_result $MYSQLTEST_VARDIR "hello" ---exec ls $MYSQLTEST_VARDIR/master-data/test/t1.* || true ---replace_result $MYSQLTEST_VARDIR "hello" ---exec ls $MYSQLTEST_VARDIR/master-data/test/t1#* || true ---replace_result $MYSQLTEST_VARDIR "hello" ---exec ls $MYSQLTEST_VARDIR/master-data/tmpdata/t1#* || true ---replace_result $MYSQLTEST_VARDIR "hello" ---exec ls $MYSQLTEST_VARDIR/master-data/tmpinx/t1#* || true ---replace_result $MYSQLTEST_VARDIR "hello" +--echo Checking if file exists before alter +--file_exist $MYSQLTEST_VARDIR/master-data/test/t1.frm +--file_exists $MYSQLTEST_VARDIR/master-data/test/t1.par +--file_exists $MYSQLTEST_VARDIR/master-data/test/t1#P#p0#SP#subpart00.MYD +--file_exists $MYSQLTEST_VARDIR/master-data/test/t1#P#p0#SP#subpart00.MYI +--file_exists $MYSQLTEST_VARDIR/master-data/test/t1#P#p0#SP#subpart01.MYD +--file_exists $MYSQLTEST_VARDIR/master-data/test/t1#P#p0#SP#subpart01.MYI +--file_exists $MYSQLTEST_VARDIR/master-data/tmpdata/t1#P#p0#SP#subpart00.MYD +--file_exists $MYSQLTEST_VARDIR/master-data/tmpdata/t1#P#p0#SP#subpart01.MYD +--file_exists $MYSQLTEST_VARDIR/master-data/tmpinx/t1#P#p0#SP#subpart00.MYI +--file_exists $MYSQLTEST_VARDIR/master-data/tmpinx/t1#P#p0#SP#subpart01.MYI +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR eval ALTER TABLE t1 REORGANIZE PARTITION p0 INTO (partition p1 VALUES LESS THAN (1) $data_directory $inx_directory (SUBPARTITION subpart10, SUBPARTITION subpart11), partition p2 VALUES LESS THAN (2) $data_directory $inx_directory (SUBPARTITION subpart20, SUBPARTITION subpart21)); ---replace_result $MYSQLTEST_VARDIR "hello" ---exec ls $MYSQLTEST_VARDIR/master-data/test/t1.* || true ---replace_result $MYSQLTEST_VARDIR "hello" ---exec ls $MYSQLTEST_VARDIR/master-data/test/t1#* || true ---replace_result $MYSQLTEST_VARDIR "hello" ---exec ls $MYSQLTEST_VARDIR/master-data/tmpdata/t1#* || true ---replace_result $MYSQLTEST_VARDIR "hello" ---exec ls $MYSQLTEST_VARDIR/master-data/tmpinx/t1#* || true +--echo Checking if file exists after alter +--file_exists $MYSQLTEST_VARDIR/master-data/test/t1.frm +--file_exists $MYSQLTEST_VARDIR/master-data/test/t1.par +--file_exists $MYSQLTEST_VARDIR/master-data/test/t1#P#p1#SP#subpart10.MYD +--file_exists $MYSQLTEST_VARDIR/master-data/test/t1#P#p1#SP#subpart10.MYI +--file_exists $MYSQLTEST_VARDIR/master-data/test/t1#P#p1#SP#subpart11.MYD +--file_exists $MYSQLTEST_VARDIR/master-data/test/t1#P#p1#SP#subpart11.MYI +--file_exists $MYSQLTEST_VARDIR/master-data/test/t1#P#p2#SP#subpart20.MYD +--file_exists $MYSQLTEST_VARDIR/master-data/test/t1#P#p2#SP#subpart20.MYI +--file_exists $MYSQLTEST_VARDIR/master-data/test/t1#P#p2#SP#subpart21.MYD +--file_exists $MYSQLTEST_VARDIR/master-data/test/t1#P#p2#SP#subpart21.MYI +--file_exists $MYSQLTEST_VARDIR/master-data/tmpdata/t1#P#p1#SP#subpart10.MYD +--file_exists $MYSQLTEST_VARDIR/master-data/tmpdata/t1#P#p1#SP#subpart11.MYD +--file_exists $MYSQLTEST_VARDIR/master-data/tmpdata/t1#P#p2#SP#subpart20.MYD +--file_exists $MYSQLTEST_VARDIR/master-data/tmpdata/t1#P#p2#SP#subpart21.MYD +--file_exists $MYSQLTEST_VARDIR/master-data/tmpinx/t1#P#p1#SP#subpart10.MYI +--file_exists $MYSQLTEST_VARDIR/master-data/tmpinx/t1#P#p1#SP#subpart11.MYI +--file_exists $MYSQLTEST_VARDIR/master-data/tmpinx/t1#P#p2#SP#subpart20.MYI +--file_exists $MYSQLTEST_VARDIR/master-data/tmpinx/t1#P#p2#SP#subpart21.MYI drop table t1; --exec rmdir $MYSQLTEST_VARDIR/master-data/tmpdata || true From 257cfa1ca01a2af8486a295c04b9a5cc4683a8ba Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 6 Oct 2006 00:07:39 +0200 Subject: [PATCH 62/68] Turn on reconnect before crashing server --- mysql-test/t/crash_commit_before.test | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mysql-test/t/crash_commit_before.test b/mysql-test/t/crash_commit_before.test index c46340d44dc..78444b7aa45 100644 --- a/mysql-test/t/crash_commit_before.test +++ b/mysql-test/t/crash_commit_before.test @@ -15,6 +15,9 @@ SET SESSION debug="d,crash_commit_before"; --error 2013 COMMIT; +# Turn on reconnect +--enable_reconnect + # Call script that will poll the server waiting for it to be back online again --source include/wait_until_connected_again.inc From 139b6917cb177fd40c529a2eb1ff1ef862dd3e22 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 6 Oct 2006 00:11:25 +0200 Subject: [PATCH 63/68] partition test relies heavily on innodb, add "have_innodb" requirement --- mysql-test/t/partition.test | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index cae5c54849d..b9cd784ab93 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -4,6 +4,7 @@ # Taken fromm the select test # -- source include/have_partition.inc +-- source include/have_innodb.inc # # This test is disabled on Windows due to BUG#19107 # From fd141956768c7460e40af0b85f83f9e5d1f0803b Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 6 Oct 2006 00:36:25 +0200 Subject: [PATCH 64/68] Fix warning detected, file_exist => file_exists --- mysql-test/t/partition.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index b9cd784ab93..4cdf7a31915 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -1341,7 +1341,7 @@ subpartition by hash (a) (SUBPARTITION subpart00, SUBPARTITION subpart01)); --echo Checking if file exists before alter ---file_exist $MYSQLTEST_VARDIR/master-data/test/t1.frm +--file_exists $MYSQLTEST_VARDIR/master-data/test/t1.frm --file_exists $MYSQLTEST_VARDIR/master-data/test/t1.par --file_exists $MYSQLTEST_VARDIR/master-data/test/t1#P#p0#SP#subpart00.MYD --file_exists $MYSQLTEST_VARDIR/master-data/test/t1#P#p0#SP#subpart00.MYI From 78c5bc62c59b5b168fb0625a70cc465ead4e59ae Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 6 Oct 2006 00:57:10 +0200 Subject: [PATCH 65/68] Add printout of file in which warning was detected Cleanup .progress, .reject, .log and .warnings files produced by mysqltest client/mysqltest.c: Add printout of file in which warning was detected mysql-test/include/ctype_like_escape.inc: Remove warnings, convert -- comments to # comments mysql-test/mysql-test-run.pl: Cleanup all files produced by mysqltest before starting mysqltest again --- client/mysqltest.c | 13 ++++++++++++- mysql-test/include/ctype_like_escape.inc | 4 ++-- mysql-test/mysql-test-run.pl | 15 ++++++++------- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index cf3afced427..8d8c20c3e8f 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -818,6 +818,9 @@ void verbose_msg(const char *fmt, ...) va_start(args, fmt); fprintf(stderr, "mysqltest: "); + if (cur_file && cur_file != file_stack) + fprintf(stderr, "In included file \"%s\": ", + cur_file->file_name); if (start_lineno != 0) fprintf(stderr, "At line %u: ", start_lineno); vfprintf(stderr, fmt, args); @@ -839,7 +842,15 @@ void warning_msg(const char *fmt, ...) dynstr_append(&ds_warning_messages, "mysqltest: "); if (start_lineno != 0) { - len= my_snprintf(buff, sizeof(buff), "Warning detected at line %d: ", + dynstr_append(&ds_warning_messages, "Warning detected "); + if (cur_file && cur_file != file_stack) + { + len= my_snprintf(buff, sizeof(buff), "in included file %s ", + cur_file->file_name); + dynstr_append_mem(&ds_warning_messages, + buff, len); + } + len= my_snprintf(buff, sizeof(buff), "at line %d: ", start_lineno); dynstr_append_mem(&ds_warning_messages, buff, len); diff --git a/mysql-test/include/ctype_like_escape.inc b/mysql-test/include/ctype_like_escape.inc index ac97fbaa1a0..d4abc33c178 100644 --- a/mysql-test/include/ctype_like_escape.inc +++ b/mysql-test/include/ctype_like_escape.inc @@ -11,8 +11,8 @@ insert into t1 values('ab_def'); insert into t1 values('abc_ef'); insert into t1 values('abcd_f'); insert into t1 values('abcde_'); --- should return ab_def +# should return ab_def select c1 as c1u from t1 where c1 like 'ab\_def'; --- should return ab_def +# should return ab_def select c1 as c2h from t1 where c1 like 'ab#_def' escape '#'; drop table t1; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 4162e8cf0ef..23a9b30d76b 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -2704,15 +2704,16 @@ sub do_before_run_mysqltest($) my $tinfo= shift; my $tname= $tinfo->{'name'}; - # Remove old reject file - if ( $opt_suite eq "main" ) + # Remove old files produced by mysqltest + my $result_dir= "r"; + if ( ! $opt_suite eq "main" ) { - unlink("r/$tname.reject"); - } - else - { - unlink("suite/$opt_suite/r/$tname.reject"); + $result_dir= "suite/$opt_suite/r"; } + unlink("$result_dir/$tname.reject"); + unlink("$result_dir/$tname.progress"); + unlink("$result_dir/$tname.log"); + unlink("$result_dir/$tname.warnings"); mtr_tonewfile($path_current_test_log,"$tname\n"); # Always tell where we are From 1822065d1212762adbe00eae14e8e1cb6f4d736d Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 6 Oct 2006 01:22:33 +0200 Subject: [PATCH 66/68] Rename "snapshot_setup" to "datadir_setup" as that is what is done by it. Move call f dunction into "initialize_servers" so it's called also in stress and bench mode --- mysql-test/mysql-test-run.pl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 23a9b30d76b..21f64bc3cb6 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -323,7 +323,7 @@ our $default_mysqld_port; sub main (); sub initial_setup (); sub command_line_setup (); -sub snapshot_setup (); +sub datadir_setup (); sub executable_setup (); sub environment_setup (); sub kill_running_server (); @@ -416,7 +416,6 @@ sub main () { unless $need_ndbcluster; $opt_skip_im= 1 unless $need_im; - snapshot_setup(); initialize_servers(); run_suite($opt_suite, $tests); @@ -1122,7 +1121,7 @@ sub command_line_setup () { $path_snapshot= "$opt_tmpdir/snapshot_$opt_master_myport/"; } -sub snapshot_setup () { +sub datadir_setup () { # Make a list of all data_dirs @data_dir_lst = ( @@ -2370,6 +2369,9 @@ sub run_suite () { ############################################################################## sub initialize_servers () { + + datadir_setup(); + if ( ! $glob_use_running_server ) { kill_running_server(); From 9350dbc4f96c0b58784132e1c0556e0238c53177 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 6 Oct 2006 11:23:19 +0200 Subject: [PATCH 67/68] Improve 'run_testcase_need_master_restart' to require restart if master is not already started Improve 'run_testcase_need_slave_restart' to detect if a slave restart really is necessary. So far all rpl test requires a slave restart, but for all other tests it can be skipped Improve the sort order used by --reorder mysql-test/lib/mtr_cases.pl: Improve the sort order used by reorder mysql-test/mysql-test-run.pl: Improve 'run_testcase_need_master_restart' to require restart if master is not already started Improve 'run_testcase_need_slave_restart' to detect if a slave restart really is necessary. So far all rpl test requires a slave restart, but for all other tests it can be skipped --- mysql-test/lib/mtr_cases.pl | 31 ++++++++------ mysql-test/mysql-test-run.pl | 83 ++++++++++++++---------------------- 2 files changed, 51 insertions(+), 63 deletions(-) diff --git a/mysql-test/lib/mtr_cases.pl b/mysql-test/lib/mtr_cases.pl index bb92730444c..344c80ac0bf 100644 --- a/mysql-test/lib/mtr_cases.pl +++ b/mysql-test/lib/mtr_cases.pl @@ -164,31 +164,36 @@ sub collect_test_cases ($) { # Make a mapping of test name to a string that represents how that test # should be sorted among the other tests. Put the most important criterion # first, then a sub-criterion, then sub-sub-criterion, et c. - foreach $tinfo (@$cases) + foreach $tinfo (@$cases) { my @this_criteria = (); + # # Append the criteria for sorting, in order of importance. - push(@this_criteria, join("!", sort @{$tinfo->{'master_opt'}}) . "~"); # Ending with "~" makes empty sort later than filled + # + push(@this_criteria, "ndb=" . ($tinfo->{'ndb_test'} ? "1" : "0")); push(@this_criteria, "restart=" . ($tinfo->{'master_restart'} ? "1" : "0")); - push(@this_criteria, "big_test=" . ($tinfo->{'big_test'} ? "1" : "0")); - push(@this_criteria, join("|", sort keys %{$tinfo})); # Group similar things together. The values may differ substantially. FIXME? - push(@this_criteria, $tinfo->{'name'}); # Finally, order by the name - + # Group test with similar options together. + # Ending with "~" makes empty sort later than filled + push(@this_criteria, join("!", sort @{$tinfo->{'master_opt'}}) . "~"); + + # Finally, order by the name + push(@this_criteria, $tinfo->{'name'}); + $sort_criteria{$tinfo->{"name"}} = join(" ", @this_criteria); } @$cases = sort { $sort_criteria{$a->{"name"}} cmp $sort_criteria{$b->{"name"}}; } @$cases; ### For debugging the sort-order -# foreach $tinfo (@$cases) -# { -# print $sort_criteria{$tinfo->{"name"}}; -# print " -> \t"; -# print $tinfo->{"name"}; -# print "\n"; -# } + foreach $tinfo (@$cases) + { + print $sort_criteria{$tinfo->{"name"}}; + print " -> \t"; + print $tinfo->{"name"}; + print "\n"; + } } return $cases; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 21f64bc3cb6..31a2c1e32a3 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3522,6 +3522,11 @@ sub run_testcase_need_master_restart($) join(" ", @{$tinfo->{'master_opt'}}) . "' != '" . join(" ", @{$master->[0]->{'start_opts'}}) . "'" ); } + elsif( ! $master->[0]->{'pid'} ) + { + $do_restart= 1; + mtr_verbose("Restart because: master is not started"); + } return $do_restart; } @@ -3533,57 +3538,35 @@ sub run_testcase_need_slave_restart($) # We try to find out if we are to restart the slaves my $do_slave_restart= 0; # Assumes we don't have to - # FIXME only restart slave when necessary - $do_slave_restart= 1; + if ( $max_slave_num == 0) + { + mtr_verbose("No testcase use slaves, no slave restarts"); + } + else + { -# if ( ! $slave->[0]->{'pid'} ) -# { -# # mtr_verbose("Slave not started, no need to check slave restart"); -# } -# elsif ( $do_restart ) -# { -# $do_slave_restart= 1; # Always restart if master restart -# mtr_verbose("Restart slave because: Master restart"); -# } -# elsif ( $tinfo->{'slave_sh'} ) -# { -# $do_slave_restart= 1; # Always restart if script to run -# mtr_verbose("Restart slave because: Always restart if script to run"); -# } -# elsif ( ! $opt_skip_ndbcluster_slave and -# $tinfo->{'ndb_test'} == 0 and -# $clusters->[1]->{'pid'} != 0 ) -# { -# $do_slave_restart= 1; # Restart without slave cluster -# mtr_verbose("Restart slave because: Test does not need slave cluster"); -# } -# elsif ( ! $opt_with_ndbcluster_slave and -# $tinfo->{'ndb_test'} == 1 and -# $clusters->[1]->{'pid'} == 0 ) -# { -# $do_slave_restart= 1; # Restart with slave cluster -# mtr_verbose("Restart slave because: Test need slave cluster"); -# } -# elsif ( $tinfo->{'slave_restart'} ) -# { -# $do_slave_restart= 1; -# mtr_verbose("Restart slave because: slave_restart"); -# } -# elsif ( $slave->[0]->{'running_slave_is_special'} ) -# { -# $do_slave_restart= 1; -# mtr_verbose("Restart slave because: running_slave_is_special"); -# } -# # Check that running slave was started with same options -# # as the current test requires -# elsif (! mtr_same_opts($slave->[0]->{'start_opts'}, -# $tinfo->{'slave_opt'}) ) -# { -# $do_slave_restart= 1; -# mtr_verbose("Restart slave because: running with different options '" . -# join(" ", @{$tinfo->{'slave_opt'}}) . "' != '" . -# join(" ", @{$slave->[0]->{'start_opts'}}) . "'" ); -# } + # Check if any slave is currently started + my $any_slave_started= 0; + foreach my $mysqld (@{$slave}) + { + if ( $mysqld->{'pid'} ) + { + $any_slave_started= 1; + last; + } + } + + if ($any_slave_started) + { + mtr_verbose("Any slave is started, need to restart"); + $do_slave_restart= 1; + } + elsif ( $tinfo->{'slave_num'} ) + { + mtr_verbose("Test need slave, check for restart"); + $do_slave_restart= 1; + } + } return $do_slave_restart; From 4d56a43cb0895da1d56a0a12421a12307fbbe3cf Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 6 Oct 2006 11:49:31 +0200 Subject: [PATCH 68/68] Only print thesort order debug printout if using script-debug --- mysql-test/lib/mtr_cases.pl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mysql-test/lib/mtr_cases.pl b/mysql-test/lib/mtr_cases.pl index 344c80ac0bf..d73c7a83dca 100644 --- a/mysql-test/lib/mtr_cases.pl +++ b/mysql-test/lib/mtr_cases.pl @@ -186,13 +186,13 @@ sub collect_test_cases ($) { @$cases = sort { $sort_criteria{$a->{"name"}} cmp $sort_criteria{$b->{"name"}}; } @$cases; -### For debugging the sort-order - foreach $tinfo (@$cases) + if ( $::opt_script_debug ) { - print $sort_criteria{$tinfo->{"name"}}; - print " -> \t"; - print $tinfo->{"name"}; - print "\n"; + # For debugging the sort-order + foreach $tinfo (@$cases) + { + print("$sort_criteria{$tinfo->{'name'}} -> \t$tinfo->{'name'}\n"); + } } }