diff options
37 files changed, 4220 insertions, 3714 deletions
diff --git a/client/mysqltest.c b/client/mysqltest.c index b8a5acccd34..d49693fa324 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -14,19 +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 "MySQL Test framework manual" for more information - * http://dev.mysql.com/doc/mysqltest/en/index.html - * - * Written by: - * Sasha Pachev <sasha@mysql.com> - * Matt Wagner <matt@mysql.com> - * Monty - * Jani - * Magnus - **/ - -#define MTEST_VERSION "2.7" +/* + mysqltest + + Tool used for executing a .test file + + See the "MySQL Test framework manual" for more information + http://dev.mysql.com/doc/mysqltest/en/index.html + + Written by: + Sasha Pachev <sasha@mysql.com> + Matt Wagner <matt@mysql.com> + Monty + Jani +*/ + +#define MTEST_VERSION "3.0" #include <my_global.h> #include <mysql_embed.h> @@ -35,18 +38,18 @@ #include <mysql.h> #include <mysql_version.h> #include <mysqld_error.h> +#include <errmsg.h> #include <m_ctype.h> #include <my_dir.h> -#include <errmsg.h> /* Error codes */ #include <hash.h> #include <my_getopt.h> #include <stdarg.h> -#include <sys/stat.h> #include <violite.h> -#include "my_regex.h" /* Our own version of lib */ +#include "my_regex.h" /* Our own version of regex */ #ifdef HAVE_SYS_WAIT_H #include <sys/wait.h> #endif + #ifndef WEXITSTATUS # ifdef __WIN__ # define WEXITSTATUS(stat_val) (stat_val) @@ -54,169 +57,116 @@ # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) # endif #endif -/* MAX_QUERY is 256K -- there is a test in sp-big that is >128K */ -#define MAX_QUERY (256*1024) -#define MAX_VAR_NAME 256 -#define MAX_COLUMNS 256 -#define MAX_CONS 128 -#define MAX_INCLUDE_DEPTH 16 -#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 -#define MAX_SERVER_ARGS 64 - - -#define SLAVE_POLL_INTERVAL 300000 /* 0.3 of a sec */ -#define DEFAULT_DELIMITER ";" -#define MAX_DELIMITER 16 - -#define RESULT_OK 0 -#define RESULT_CONTENT_MISMATCH 1 -#define RESULT_LENGTH_MISMATCH 2 - -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}; - -/* ************************************************************************ */ -/* - 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 -}; - -typedef struct -{ - enum match_err_type type; - union - { - uint errnum; - char sqlstate[SQLSTATE_LENGTH+1]; /* \0 terminated string */ - } code; -} match_err; - -typedef struct -{ - const char *name; - long code; -} st_error; -static st_error global_error[] = -{ -#include <mysqld_ername.h> - { 0, 0 } +#define MAX_VAR_NAME_LENGTH 256 +#define MAX_COLUMNS 256 +#define MAX_EMBEDDED_SERVER_ARGS 64 +#define MAX_DELIMITER_LENGTH 16 + +/* Flags controlling send and reap */ +#define QUERY_SEND_FLAG 1 +#define QUERY_REAP_FLAG 2 + + enum { + RESULT_OK= 0, + RESULT_CONTENT_MISMATCH= 1, + RESULT_LENGTH_MISMATCH= 2 + }; + +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 }; -static match_err global_expected_errno[MAX_EXPECTED_ERRORS]; -static uint global_expected_errors; - -/* ************************************************************************ */ - static int record= 0, opt_sleep= -1; -static char *db = 0, *pass=0; -const char *user = 0, *host = 0, *unix_sock = 0, *opt_basedir="./"; +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 port= 0; static int opt_max_connect_retries; -static my_bool opt_big_test= 0, opt_compress= 0, silent= 0, verbose = 0; +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 opt_valgrind_test= 0; -static int parsing_disabled= 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 -{ - FILE* file; - const char *file_name; - uint lineno; /* Current line in file */ -} test_file; +static uint start_lineno= 0; /* Start line of current command */ -static test_file file_stack[MAX_INCLUDE_DEPTH]; -static test_file* cur_file; -static test_file* file_stack_end; -static uint start_lineno= 0; /* Start line of query */ +static char delimiter[MAX_DELIMITER_LENGTH]= ";"; +static uint delimiter_length= 1; -/* Stores regex substitutions */ +static char TMPDIR[FN_REFLEN]; -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 */ +/* Block stack */ +enum block_cmd { + cmd_none, + cmd_if, + cmd_while }; -struct st_replace_regex +struct st_block { - 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; + int line; /* Start line of block */ + my_bool ok; /* Should block be executed */ + enum block_cmd cmd; /* Command owning the block */ }; -struct st_replace_regex *glob_replace_regex= 0; +static struct st_block block_stack[32]; +static struct st_block *cur_block, *block_stack_end; -static char TMPDIR[FN_REFLEN]; -static char delimiter[MAX_DELIMITER]= DEFAULT_DELIMITER; -static uint delimiter_length= 1; - -/* Block stack */ -enum block_cmd { cmd_none, cmd_if, cmd_while }; -typedef struct +/* Open file stack */ +struct st_test_file { - int line; /* Start line of block */ - my_bool ok; /* Should block be executed */ - enum block_cmd cmd; /* Command owning the block */ -} BLOCK; -static BLOCK block_stack[BLOCK_STACK_DEPTH]; -static BLOCK *cur_block, *block_stack_end; + FILE* file; + const char *file_name; + uint lineno; /* Current line in file */ +}; + +static struct st_test_file file_stack[16]; +static struct st_test_file* cur_file; +static struct st_test_file* file_stack_end; + 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 const char *embedded_server_groups[]= +{ + "server", + "embedded", + "mysqltest_SERVER", + NullS +}; -static my_bool display_result_vertically= FALSE, display_metadata= FALSE; +static int embedded_server_arg_count=0; +static char *embedded_server_args[MAX_EMBEDDED_SERVER_ARGS]; -/* See the timer_output() definition for details */ +/* + Timer related variables + See the timer_output() definition for details +*/ static char *timer_file = NULL; -static ulonglong timer_start, progress_start= 0; -static int got_end_timer= FALSE; +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 */ @@ -226,47 +176,25 @@ static void init_re(void); static int match_re(my_regex_t *, char *); static void free_re(void); -static int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace, - char *string, int icase); - -static const char *embedded_server_groups[]= -{ - "server", - "embedded", - "mysqltest_SERVER", - NullS -}; - DYNAMIC_ARRAY q_lines; #include "sslopt-vars.h" -typedef struct -{ - char file[FN_REFLEN]; - ulong pos; -} MASTER_POS ; - -struct connection +struct { - MYSQL mysql; - /* Used when creating views and sp, to avoid implicit commit */ - MYSQL* util_mysql; - char *name; - MYSQL_STMT* stmt; -}; + int read_lines,current_line; +} parser; -typedef struct +struct { - int read_lines,current_line; -} PARSER; + char file[FN_REFLEN]; + ulong pos; +} master_pos; -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; @@ -279,71 +207,68 @@ typedef struct char *env_s; } VAR; -VAR var_reg[10]; /*Perl/shell-like variable registers */ -HASH var_hash; -my_bool disable_query_log=0, disable_result_log=0, disable_warnings=0; -my_bool disable_ps_warnings= 0; -my_bool disable_info= 1; /* By default off */ -my_bool abort_on_error= 1; +VAR var_reg[10]; -struct connection cons[MAX_CONS]; -struct connection* cur_con, *next_con, *cons_end; +HASH var_hash; - /* Add new commands before Q_UNKNOWN !*/ +struct st_connection +{ + 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 + 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 }; -/* this should really be called command */ -struct st_query -{ - char *query, *query_buf,*first_argument,*last_argument,*end; - int first_word_len, query_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; -}; const char *command_names[]= { @@ -421,65 +346,106 @@ const char *command_names[]= 0 }; -TYPELIB command_typelib= {array_elements(command_names),"", - command_names, 0}; -DYNAMIC_STRING ds_res, ds_progress; -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); +/* + 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 +{ + 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; +}; -static void var_free(void* v); +TYPELIB command_typelib= {array_elements(command_names),"", + command_names, 0}; +DYNAMIC_STRING ds_res, ds_progress, ds_warning_messages; + +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) */; +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, + 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); +void dump_warning_messages(const char *record_file); -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); +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); -/* Definitions for replace result */ +#ifdef __WIN__ +void free_tmp_sh_file(); +void free_win_path_patterns(); +#endif -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; +static int eval_result = 0; -struct st_replace; -struct st_replace *init_replace(my_string *from, my_string *to, uint count, - my_string word_end_chars); +/* 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(); -static void free_replace_regex(); -static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name); -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, - const char* query_end, my_bool pass_through_escape_chars); -static void str_to_file(const char *fname, char *str, int size); -#ifdef __WIN__ -static void free_tmp_sh_file(); -static void free_win_path_patterns(); -#endif +/* For replace_regex */ +void do_get_replace_regex(struct st_command *command); +void free_replace_regex(); -struct st_replace *glob_replace; -static int eval_result = 0; -/* For column replace */ -char *replace_column[MAX_COLUMNS]; -uint max_replace_column= 0; +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 @@ -488,35 +454,40 @@ 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 void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val); -static void handle_error(const char *query, struct st_query *q, - unsigned int err_errno, const char *err_error, - 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, - const char *query_end, my_bool pass_through_escape_chars) +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, next_c; register int escaped = 0; - VAR* v; + VAR *v; DBUG_ENTER("do_eval"); - for (p= query; (c = *p) && p < query_end; ++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); } @@ -525,13 +496,13 @@ static void do_eval(DYNAMIC_STRING *query_eval, const char *query, 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; + escaped= 1; if (pass_through_escape_chars) { @@ -558,15 +529,18 @@ enum arg_type }; struct command_arg { - const char* argname; /* Name of argument */ + 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 */ + DYNAMIC_STRING *ds; /* Storage for 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) + +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; @@ -580,25 +554,40 @@ static void check_command_args(struct st_query *command, const char *arguments, switch (arg->type) { - /* A string surrounded by spaces */ + /* A string */ case ARG_STRING: + /* Skip leading spaces */ + while (*ptr && *ptr == ' ') + ptr++; start= ptr; - /* Find end of arg */ - while (*ptr && !my_isspace(charset_info, *ptr)) + /* Find end of arg, terminated by "delimiter_arg" */ + while (*ptr && *ptr != delimiter_arg) ptr++; - init_dynamic_string(arg->ds, 0, 256, 256); - do_eval(arg->ds, start, ptr, FALSE); + 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; - if (*ptr) + + /* Step past the delimiter */ + if (*ptr && *ptr == delimiter_arg) ptr++; + DBUG_PRINT("info", ("val: %s", arg->ds->str)); break; - /* Rest of line */ + /* 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: @@ -616,7 +605,7 @@ static void check_command_args(struct st_query *command, const char *arguments, } -static void handle_command_error(struct st_query *command, uint error) +void handle_command_error(struct st_command *command, uint error) { DBUG_ENTER("handle_command_error"); DBUG_PRINT("enter", ("error: %d", error)); @@ -625,13 +614,14 @@ static void handle_command_error(struct st_query *command, uint error) 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++) + 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_errno[i].code.errnum)); - if ((command->expected_errno[i].type == ERR_ERRNO) && - (command->expected_errno[i].code.errnum == error)) + 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)); @@ -641,22 +631,22 @@ static void handle_command_error(struct st_query *command, uint error) 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) + 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_errno[0].code.errnum); + command->expected_errors.err[0].code.errnum); } DBUG_VOID_RETURN; } -static void close_cons() +void close_connections() { - DBUG_ENTER("close_cons"); - for (--next_con; next_con >= cons; --next_con) + DBUG_ENTER("close_connections"); + for (--next_con; next_con >= connections; --next_con) { if (next_con->stmt) mysql_stmt_close(next_con->stmt); @@ -670,7 +660,7 @@ static void close_cons() } -static void close_files() +void close_files() { DBUG_ENTER("close_files"); for (; cur_file >= file_stack; cur_file--) @@ -685,22 +675,22 @@ static void close_files() } -static void free_used_memory() +void free_used_memory() { uint i; DBUG_ENTER("free_used_memory"); - 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)); @@ -710,8 +700,8 @@ static void free_used_memory() delete_dynamic(&q_lines); dynstr_free(&ds_res); dynstr_free(&ds_progress); - free_replace(); - free_replace_column(); + dynstr_free(&ds_warning_messages); + free_all_replace(); my_free(pass,MYF(MY_ALLOW_ZERO_PTR)); free_defaults(default_argv); mysql_server_end(); @@ -723,15 +713,22 @@ static void free_used_memory() 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\": ", @@ -748,6 +745,10 @@ static 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); @@ -759,10 +760,10 @@ static void die(const char *fmt, ...) } -static void abort_not_supported_test(const char *fmt, ...) +void abort_not_supported_test(const char *fmt, ...) { va_list args; - test_file* err_file= cur_file; + struct st_test_file* err_file= cur_file; DBUG_ENTER("abort_not_supported_test"); /* Print include filestack */ @@ -798,7 +799,14 @@ static void abort_not_supported_test(const char *fmt, ...) exit(62); } -static void verbose_msg(const char *fmt, ...) + +void abort_not_in_this_version() +{ + die("Not available in this version of mysqltest"); +} + + +void verbose_msg(const char *fmt, ...) { va_list args; DBUG_ENTER("verbose_msg"); @@ -817,14 +825,36 @@ static void verbose_msg(const char *fmt, ...) } -void init_parser() +void warning_msg(const char *fmt, ...) { - parser.current_line= parser.read_lines= 0; - memset(&var_reg, 0, sizeof(var_reg)); + 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; } -static int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) +/* + Compare content of the string ds to content of file fname +*/ + +int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) { MY_STAT stat_info; char *tmp, *res_ptr; @@ -853,12 +883,12 @@ static int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) 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, "", stat_info.st_size+256, 256); if (eval_result) @@ -894,6 +924,7 @@ err: DBUG_RETURN(res); } + /* Check the content of ds against content of file fname @@ -902,14 +933,15 @@ err: 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" + with the special exit code "not supported test" RETURN VALUES - error - the function will not return + error - the function will not return */ -static void check_result(DYNAMIC_STRING* ds, const char *fname, - my_bool require_option) + +void check_result(DYNAMIC_STRING* ds, const char *fname, + my_bool require_option) { int res= dyn_string_cmp(ds, fname); DBUG_ENTER("check_result"); @@ -939,13 +971,80 @@ static void check_result(DYNAMIC_STRING* ds, const char *fname, } -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; @@ -964,16 +1063,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; @@ -994,7 +1093,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))) @@ -1004,16 +1104,18 @@ static VAR *var_obtain(const char *name, int len) return v; } + /* - 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 */ -int var_set(const char *var_name, const char *var_name_end, - const char *var_val, const char *var_val_end) + +void var_set(const char *var_name, const char *var_name_end, + const char *var_val, const char *var_val_end) { - int digit, result, env_var= 0; - 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, @@ -1025,15 +1127,15 @@ int var_set(const char *var_name, const char *var_name_end, else var_name++; - digit = *var_name - '0'; + 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; + v= var_reg + digit; - result= eval_expr(v, var_val, (const char**) &var_val_end); + eval_expr(v, var_val, (const char**) &var_val_end); if (env_var) { @@ -1050,7 +1152,183 @@ int var_set(const char *var_name, const char *var_name_end, putenv(v->env_s); my_free((gptr)old_env_s, MYF(MY_ALLOW_ZERO_PTR)); } - DBUG_RETURN(result); + 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= <integer>" +*/ + +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 @<var_name> = `<query>` + + 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 "$<var_name>_<column_name>" + Thus the tab separated output can be read from $<var_name> and + and each individual column can be read as $<var_name>_<col_name> + +*/ + +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 <var_name>_<col_name> */ + 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; } @@ -1081,103 +1359,53 @@ int open_file(const char *name) /* - Check for unexpected "junk" after the end of query - This is normally caused by missing delimiters -*/ - -int 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_RETURN(0); -} - - -/* ugly long name, but we are following the convention */ -int do_wait_for_slave_to_stop(struct st_query *q __attribute__((unused))) -{ - MYSQL* mysql = &cur_con->mysql; - for (;;) - { - MYSQL_RES *res; - MYSQL_ROW row; - int done; - LINT_INIT(res); - - if (mysql_query(mysql,"show status like 'Slave_running'") || - !(res=mysql_store_result(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); - die("Strange result from query while probing slave for stop"); - } - done = !strcmp(row[1],"OFF"); - mysql_free_result(res); - if (done) - break; - my_sleep(SLAVE_POLL_INTERVAL); - } - return 0; -} - - -/* Source and execute the given file SYNOPSIS - do_source() - query called command + do_source() + query called command DESCRIPTION - source <file_name> + source <file_name> - Open the file <file_name> and execute it + Open the file <file_name> and execute it */ -int do_source(struct st_query *query) +void do_source(struct st_command *command) { - 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; + DYNAMIC_STRING ds_filename; + const struct command_arg source_args[] = { + "filename", ARG_STRING, TRUE, &ds_filename, "File to source" + }; + DBUG_ENTER("do_source"); + + check_command_args(command, command->first_argument, source_args, + sizeof(source_args)/sizeof(struct command_arg), + ' '); + /* - If this file has already been sourced, don't source it again. - It's already available in the q_lines cache. + 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); + ; /* Do nothing */ + else + { + DBUG_PRINT("info", ("sourcing file: %s", ds_filename.str)); + open_file(ds_filename.str); + } + + dynstr_free(&ds_filename); + return; } + #ifdef __WIN__ /* Variables used for temuprary sh files used for emulating Unix on Windows */ char tmp_sh_name[64], tmp_sh_cmd[70]; -static void init_tmp_sh_file() +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()); @@ -1185,13 +1413,15 @@ static void init_tmp_sh_file() my_snprintf(tmp_sh_cmd, sizeof(tmp_sh_cmd), "sh %s", tmp_sh_name); } -static void free_tmp_sh_file() + +void free_tmp_sh_file() { my_delete(tmp_sh_name, MYF(0)); } #endif -FILE* my_popen(DYNAMIC_STRING* ds_cmd, const char* mode) + +FILE* my_popen(DYNAMIC_STRING *ds_cmd, const char *mode) { #ifdef __WIN__ /* Dump the command into a sh script file and execute with popen */ @@ -1207,29 +1437,29 @@ FILE* my_popen(DYNAMIC_STRING* ds_cmd, const char* mode) Execute given command. SYNOPSIS - do_exec() - query called command + do_exec() + query called command DESCRIPTION - exec <command> + exec <command> - 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. + 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. + 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. */ -static void do_exec(struct st_query *query) +void do_exec(struct st_command *command) { int error; char buf[1024]; FILE *res_file; - char *cmd= query->first_argument; + char *cmd= command->first_argument; DYNAMIC_STRING ds_cmd; DBUG_ENTER("do_exec"); DBUG_PRINT("enter", ("cmd: '%s'", cmd)); @@ -1238,18 +1468,18 @@ static void do_exec(struct st_query *query) cmd++; if (!*cmd) die("Missing argument in exec"); - query->last_argument= query->end; + command->last_argument= command->end; - init_dynamic_string(&ds_cmd, 0, query->query_len+256, 256); + init_dynamic_string(&ds_cmd, 0, command->query_len+256, 256); /* Eval the command, thus replacing all environment variables */ - do_eval(&ds_cmd, cmd, query->end, TRUE); + do_eval(&ds_cmd, cmd, command->end, TRUE); cmd= ds_cmd.str; DBUG_PRINT("info", ("Executing '%s' as '%s'", - query->first_argument, cmd)); + command->first_argument, cmd)); - if (!(res_file= my_popen(&ds_cmd, "r")) && query->abort_on_error) - die("popen(\"%s\", \"r\") failed", query->first_argument); + 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)) { @@ -1269,230 +1499,70 @@ static void do_exec(struct st_query *query) uint status= WEXITSTATUS(error), i; my_bool ok= 0; - if (query->abort_on_error) - die("command \"%s\" failed", query->first_argument); + 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 < query->expected_errors; i++) + for (i= 0; i < command->expected_errors.count; 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)) + 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", - query->first_argument, status)); + command->first_argument, status)); } } if (!ok) die("command \"%s\" failed with wrong error: %d", - query->first_argument, status); + command->first_argument, status); } - else if (query->expected_errno[0].type == ERR_ERRNO && - query->expected_errno[0].code.errnum != 0) + 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...", - query->first_argument, query->expected_errno[0].code.errnum); + command->first_argument, command->expected_errors.err[0].code.errnum); } dynstr_free(&ds_cmd); - free_replace(); DBUG_VOID_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 @<var_name> = `<query>` - - 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 "$<var_name>_<column_name>" - Thus the tab separated output can be read from $<var_name> and - and each individual column can be read as $<var_name>_<col_name> - -*/ - -int 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 <var_name>_<col_name> */ - uint j; - char var_col_name[MAX_VAR_NAME]; - uint length= snprintf(var_col_name, MAX_VAR_NAME, - "$%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_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 - operator operation to perform on the var + do_modify_var() + query called command + operator operation to perform on the var DESCRIPTION - dec $var_name - inc $var_name + dec $var_name + inc $var_name */ -int do_modify_var(struct st_query *query, +int do_modify_var(struct st_command *command, enum enum_operator operator) { - const char *p= query->first_argument; + const char *p= command->first_argument; VAR* v; if (!*p) - die("Missing argument to %.*s", query->first_word_len, query->query); + die("Missing argument to %.*s", command->first_word_len, command->query); if (*p != '$') die("The argument to %.*s must be a variable (start with $)", - query->first_word_len, query->query); + command->first_word_len, command->query); v= var_get(p, &p, 1, 0); switch (operator) { case DO_DEC: @@ -1506,7 +1576,7 @@ int do_modify_var(struct st_query *query, break; } v->int_dirty= 1; - query->last_argument= (char*)++p; + command->last_argument= (char*)++p; return 0; } @@ -1515,10 +1585,10 @@ int do_modify_var(struct st_query *query, 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. + 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. */ @@ -1535,20 +1605,19 @@ int my_system(DYNAMIC_STRING* ds_cmd) /* - SYNOPSIS do_system - command called command + command called command DESCRIPTION - system <command> + system <command> - Eval the query to expand any $variables in the command. - Execute the command with the "system" command. + Eval the query to expand any $variables in the command. + Execute the command with the "system" command. */ -void do_system(struct st_query *command) +void do_system(struct st_command *command) { DYNAMIC_STRING ds_cmd; DBUG_ENTER("do_system"); @@ -1583,14 +1652,14 @@ void do_system(struct st_query *command) /* SYNOPSIS do_remove_file - command called command + command called command DESCRIPTION - remove_file <file_name> - Remove the file <file_name> + remove_file <file_name> + Remove the file <file_name> */ -static void do_remove_file(struct st_query *command) +void do_remove_file(struct st_command *command) { int error; DYNAMIC_STRING ds_filename; @@ -1600,7 +1669,8 @@ static void do_remove_file(struct st_query *command) DBUG_ENTER("do_remove_file"); check_command_args(command, command->first_argument, - rm_args, sizeof(rm_args)/sizeof(struct command_arg)); + 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; @@ -1613,16 +1683,16 @@ static void do_remove_file(struct st_query *command) /* SYNOPSIS do_copy_file - command command handle + command command handle DESCRIPTION - copy_file <from_file> <to_file> - Copy <from_file> to <to_file> + copy_file <from_file> <to_file> + Copy <from_file> to <to_file> - NOTE! Will fail if <to_file> exists + NOTE! Will fail if <to_file> exists */ -static void do_copy_file(struct st_query *command) +void do_copy_file(struct st_command *command) { int error; DYNAMIC_STRING ds_from_file; @@ -1634,7 +1704,9 @@ static void do_copy_file(struct st_query *command) DBUG_ENTER("do_copy_file"); check_command_args(command, command->first_argument, - copy_file_args, sizeof(copy_file_args)/sizeof(struct command_arg)); + 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, @@ -1649,14 +1721,14 @@ static void do_copy_file(struct st_query *command) /* SYNOPSIS do_file_exists - command called command + command called command DESCRIPTION - fiile_exist <file_name> - Check if file <file_name> exists + fiile_exist <file_name> + Check if file <file_name> exists */ -static void do_file_exist(struct st_query *command) +void do_file_exist(struct st_command *command) { int error; DYNAMIC_STRING ds_filename; @@ -1666,7 +1738,9 @@ static void do_file_exist(struct st_query *command) DBUG_ENTER("do_file_exist"); check_command_args(command, command->first_argument, - file_exist_args, sizeof(file_exist_args)/sizeof(struct command_arg)); + 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); @@ -1678,15 +1752,15 @@ static void do_file_exist(struct st_query *command) /* Read characters from line buffer or file. This is needed to allow - my_ungetc() to buffer MAX_DELIMITER characters for a file + 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. + 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) +int my_getc(FILE *file) { if (line_buffer_pos == line_buffer) return fgetc(file); @@ -1694,46 +1768,22 @@ static int my_getc(FILE *file) } -static void my_ungetc(int c) +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_delimiter(DYNAMIC_STRING *ds, - DYNAMIC_STRING *ds_delimiter) +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) - die("Max delimiter length(%d) exceeded", MAX_DELIMITER); + 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) @@ -1762,31 +1812,31 @@ static void read_until_delimiter(DYNAMIC_STRING *ds, /* SYNOPSIS do_write_file - command called command + command called command DESCRIPTION - write_file <file_name> [<delimiter>]; - <what to write line 1> - <...> - < what to write line n> - EOF + write_file <file_name> [<delimiter>]; + <what to write line 1> + <...> + < what to write line n> + EOF - --write_file <file_name>; - <what to write line 1> - <...> - < what to write line n> - EOF + --write_file <file_name>; + <what to write line 1> + <...> + < what to write line n> + EOF - Write everything between the "write_file" command and 'delimiter' - to "file_name" + Write everything between the "write_file" command and 'delimiter' + to "file_name" - NOTE! Overwrites existing file + NOTE! Overwrites existing file - Default <delimiter> is EOF + Default <delimiter> is EOF */ -static void do_write_file(struct st_query *command) +void do_write_file(struct st_command *command) { DYNAMIC_STRING ds_content; DYNAMIC_STRING ds_filename; @@ -1800,7 +1850,8 @@ static void do_write_file(struct st_query *command) check_command_args(command, command->first_argument, write_file_args, - sizeof(write_file_args)/sizeof(struct command_arg)); + sizeof(write_file_args)/sizeof(struct command_arg), + ' '); /* If no delimiter was provided, use EOF */ if (ds_delimiter.length == 0) @@ -1820,23 +1871,23 @@ static void do_write_file(struct st_query *command) /* SYNOPSIS do_perl - command command handle + command command handle DESCRIPTION - perl [<delimiter>]; - <perlscript line 1> - <...> - <perlscript line n> - EOF + perl [<delimiter>]; + <perlscript line 1> + <...> + <perlscript line n> + EOF - Execute everything after "perl" until <delimiter> as perl. - Useful for doing more advanced things - but still being able to execute it on all platforms. + Execute everything after "perl" until <delimiter> as perl. + Useful for doing more advanced things + but still being able to execute it on all platforms. - Default <delimiter> is EOF + Default <delimiter> is EOF */ -static void do_perl(struct st_query *command) +void do_perl(struct st_command *command) { int error; char buf[FN_REFLEN]; @@ -1851,7 +1902,8 @@ static void do_perl(struct st_query *command) check_command_args(command, command->first_argument, perl_args, - sizeof(perl_args)/sizeof(struct command_arg)); + sizeof(perl_args)/sizeof(struct command_arg), + ' '); /* If no delimiter was provided, use EOF */ if (ds_delimiter.length == 0) @@ -1900,25 +1952,25 @@ static void do_perl(struct st_query *command) for variable names to be escaped using \ SYNOPSIS - do_echo() - command called command + do_echo() + command called command DESCRIPTION - echo text - Print the text after echo until end of command to result file + echo text + Print the text after echo until end of command to result file - echo $<var_name> - Print the content of the variable <var_name> to result file + echo $<var_name> + Print the content of the variable <var_name> to result file - echo Some text $<var_name> - Print "Some text" plus the content of the variable <var_name> to - result file + echo Some text $<var_name> + Print "Some text" plus the content of the variable <var_name> to + result file - echo Some text \$<var_name> - Print "Some text" plus $<var_name> to result file + echo Some text \$<var_name> + Print "Some text" plus $<var_name> to result file */ -int do_echo(struct st_query *command) +int do_echo(struct st_command *command) { DYNAMIC_STRING ds_echo; @@ -1932,11 +1984,41 @@ int do_echo(struct st_query *command) } -int do_sync_with_master2(long offset) +void do_wait_for_slave_to_stop(struct st_command *c __attribute__((unused))) { - MYSQL_RES* res; + static int SLAVE_POLL_INTERVAL= 300000; + MYSQL* mysql = &cur_con->mysql; + for (;;) + { + MYSQL_RES *res; + MYSQL_ROW row; + int done; + LINT_INIT(res); + + 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)); + if (!(row=mysql_fetch_row(res)) || !row[1]) + { + mysql_free_result(res); + die("Strange result from query while probing slave for stop"); + } + done = !strcmp(row[1],"OFF"); + mysql_free_result(res); + if (done) + break; + my_sleep(SLAVE_POLL_INTERVAL); + } + return; +} + + +void 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; @@ -1975,13 +2057,14 @@ wait_for_position: 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) { @@ -1990,20 +2073,22 @@ 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; @@ -2012,8 +2097,8 @@ int do_save_master_pos() #ifdef HAVE_NDB_BINLOG /* - Wait for ndb binlog to be up-to-date with all changes - done on the local mysql server + Wait for ndb binlog to be up-to-date with all changes + done on the local mysql server */ { ulong have_ndbcluster; @@ -2171,27 +2256,29 @@ int do_save_master_pos() Assign the variable <var_name> with <var_val> SYNOPSIS - do_let() - query called command + do_let() + query called command DESCRIPTION - let $<var_name>=<var_val><delimiter> + let $<var_name>=<var_val><delimiter> - <var_name> - is the string string found between the $ and = - <var_val> - is the content between the = and <delimiter>, it may span - multiple line and contain any characters except <delimiter> - <delimiter> - is a string containing of one or more chars, default is ; + <var_name> - is the string string found between the $ and = + <var_val> - is the content between the = and <delimiter>, it may span + multiple line and contain any characters except <delimiter> + <delimiter> - 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 *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 <var_name> */ @@ -2213,31 +2300,18 @@ int do_let(struct st_query *query) while (*p && my_isspace(charset_info,*p)) p++; - do_eval(&let_rhs_expr, p, FALSE); + do_eval(&let_rhs_expr, p, command->end, FALSE); - query->last_argument= query->end; + command->last_argument= command->end; /* Assign var_val to var_name */ - return var_set(var_name, var_name_end, let_rhs_expr.str, - (let_rhs_expr.str + let_rhs_expr.length)); -} - - -/* - 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= <integer>" -*/ - -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); + 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; } -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)) @@ -2246,14 +2320,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; @@ -2261,17 +2335,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 <seconds> - real_sleep <seconds> + sleep <seconds> + real_sleep <seconds> 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. @@ -2282,26 +2356,26 @@ 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 >= 0 && !real_sleep) @@ -2310,13 +2384,14 @@ int do_sleep(struct st_query *query, my_bool real_sleep) DBUG_PRINT("info", ("sleep_val: %f", sleep_val)); if (sleep_val) my_sleep((ulong) (sleep_val * 1000000L)); - query->last_argument= sleep_end; + 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; @@ -2324,13 +2399,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) @@ -2341,78 +2417,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("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 +{ + const char *name; + long code; +} st_error; + +static st_error global_error_names[] = +{ +#include <mysqld_ername.h> + { 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) { - char *p= q->first_argument; + 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 */ - char *end= ++p + SQLSTATE_LENGTH; - char *to_ptr= to[count].code.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); - for (; my_isalnum(charset_info, *p) && p != end; p++) - *to_ptr++= *p; - *to_ptr= 0; + /* 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[count].type= ERR_SQLSTATE; + *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') { - /* SQL error as string */ - st_error *e= global_error; - char *start= p++; + /* Error name string */ - for (; *p == '_' || my_isalnum(charset_info, *p); p++) - ; - 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(start, e->name, (int) (p - start)) && - (uint) strlen(e->name) == (uint) (p - start)) - { - to[count].code.errnum= (uint) e->code; - to[count].type= ERR_ERRNO; - break; - } - } - if (!e->name) - die("Unknown SQL error '%s'", start); + 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; + 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); - if (!(p= str2int(p,10,(long) INT_MIN, (long) INT_MAX, &val))) - die("Invalid argument in %s", q->query); - to[count].code.errnum= (uint) val; - to[count].type= ERR_ERRNO; + 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 ' @@ -2420,11 +2600,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"); @@ -2470,7 +2649,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++; @@ -2493,283 +2672,22 @@ static char *get_string(char **to_ptr, char **from_ptr, DBUG_RETURN(start); } -/* - 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 -*/ - -static struct st_replace_regex* init_replace_regex(char* expr) +void set_reconnect(MYSQL* mysql, int val) { - 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 -*/ - -static 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 - -*/ -static void get_replace_regex(struct st_query *q) -{ - char *expr= q->first_argument; - free_replace_regex(); - if (!(glob_replace_regex=init_replace_regex(expr))) - die("Could not init replace_regex"); - q->last_argument= q->end; -} - - -/* - 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) -{ - 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))) - 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; -} - -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)); - glob_replace_regex=0; - } +#if MYSQL_VERSION_ID < 50000 + mysql->reconnect= val; +#else + int reconnect= val; + mysql_options(mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect); +#endif } -void free_replace() -{ - DBUG_ENTER("free_replace"); - if (glob_replace) - { - my_free((char*) glob_replace,MYF(0)); - glob_replace=0; - } - DBUG_VOID_RETURN; -} - -struct connection * find_connection_by_name(const char *name) +struct st_connection * find_connection_by_name(const char *name) { - struct connection *con; - for (con= cons; con < next_con; con++) + struct st_connection *con; + for (con= connections; con < next_con; con++) { if (!strcmp(con->name, name)) { @@ -2791,10 +2709,10 @@ int select_connection_name(const char *name) } -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) @@ -2804,15 +2722,16 @@ 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)); @@ -2824,13 +2743,17 @@ int close_connection(struct st_query *q) 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) { @@ -2844,59 +2767,19 @@ int close_connection(struct st_query *q) 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 - */ + 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_RETURN(0); + + 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 - - SYNOPSIS - safe_get_param - str - string to get param from - arg - pointer to string where result will be stored - msg - Message to display if param is not found - if msg is 0 this param is not required and param may be empty - - RETURNS - pointer to str after param - -*/ - -char* safe_get_param(char *str, char** arg, const char *msg) -{ - DBUG_ENTER("safe_get_param"); - if(!*str) - { - if (msg) - die(msg); - *arg= str; - DBUG_RETURN(str); - } - while (*str && my_isspace(charset_info,*str)) - str++; - *arg= str; - while (*str && *str != ',' && *str != ')') - str++; - if (msg && !*arg) - die(msg); - - *str++= 0; - DBUG_RETURN(str); } @@ -2904,54 +2787,60 @@ char* safe_get_param(char *str, char** arg, const char *msg) 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 + 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 + 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 ignore any errors during these retries. One should use - connect_n_handle_errors() if he expects a connection error and wants - handle as if it was an error from a usual statement. + 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. - RETURN VALUE - 0 - success, non-0 - failure */ -int safe_connect(MYSQL* mysql, const char *host, const char *user, - const char *pass, const char *db, int port, const char *sock) +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) { - int con_error= 1; - my_bool reconnect= 1; + int failed_attempts= 0; static ulong connection_retry_sleep= 100000; /* Microseconds */ - int i; - for (i= 0; i < opt_max_connect_retries; i++) + + DBUG_ENTER("safe_connect"); + while(!mysql_real_connect(mysql, host,user, pass, db, port, sock, + CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS)) { - if (mysql_real_connect(mysql, host,user, pass, db, port, sock, - CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS)) + /* + Connect failed + + Only allow retry if this was an error indicating the server + could not be contacted + */ + + 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)); } - my_sleep(connection_retry_sleep); + failed_attempts++; } - /* - TODO: change this to 0 in future versions, but the 'kill' test relies on - existing behavior - */ - mysql_options(mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect); - return con_error; + DBUG_VOID_RETURN; } @@ -2959,45 +2848,41 @@ int safe_connect(MYSQL* mysql, const char *host, const char *user, 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 - create_conn - out parameter, set to zero if connection was - not established and is not touched otherwise + 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. + 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 - RETURN VALUE - 0 - success, non-0 - failure */ -int connect_n_handle_errors(struct st_query *q, MYSQL* con, const char* host, +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, - int* create_conn) + const char* db, int port, const char* sock) { DYNAMIC_STRING *ds; - my_bool reconnect= 1; - int error= 0; ds= &ds_res; - if (!disable_query_log) + /* Only log if an error is expected */ + if (!command->abort_on_error && + !disable_query_log) { /* - It is nice to have connect() statement logged in result file - in this case. - QQ: Should we do this only if we are expecting an error ? + Log the connect to result log */ - char port_buff[22]; /* This should be enough for any int */ - char *port_end; dynstr_append_mem(ds, "connect(", 8); replace_dynstr_append(ds, host); dynstr_append_mem(ds, ",", 1); @@ -3008,8 +2893,7 @@ int connect_n_handle_errors(struct st_query *q, MYSQL* con, const char* host, if (db) replace_dynstr_append(ds, db); dynstr_append_mem(ds, ",", 1); - port_end= int10_to_str(port, port_buff, 10); - replace_dynstr_append_mem(ds, port_buff, port_end - port_buff); + replace_dynstr_append_uint(ds, port); dynstr_append_mem(ds, ",", 1); if (sock) replace_dynstr_append(ds, sock); @@ -3020,148 +2904,150 @@ int connect_n_handle_errors(struct st_query *q, MYSQL* con, const char* host, if (!mysql_real_connect(con, host, user, pass, db, port, sock ? sock: 0, CLIENT_MULTI_STATEMENTS)) { - handle_error("connect", q, mysql_errno(con), mysql_error(con), + handle_error(command, mysql_errno(con), mysql_error(con), mysql_sqlstate(con), ds); - *create_conn= 0; - goto err; + return 0; /* Not connected */ } - handle_no_error(q); - - /* - TODO: change this to 0 in future versions, but the 'kill' test relies on - existing behavior - */ - mysql_options(con, MYSQL_OPT_RECONNECT, (char *)&reconnect); - -err: - free_replace(); - free_replace_regex(); - return error; + handle_no_error(command); + return 1; /* Connected */ } /* Open a new connection to MySQL Server with the parameters - specified + specified. Make the new connection the current connection. SYNOPSIS - do_connect() - q called command + do_connect() + q called command DESCRIPTION - connect(<name>,<host>,<user>,<pass>,<db>,[<port>,<sock>[<opts>]]); - - <name> - name of the new connection - <host> - hostname of server - <user> - user to connect as - <pass> - password used when connecting - <db> - initial db when connected - <port> - server port - <sock> - server socket - <opts> - options to use for the connection - SSL - use SSL if available - COMPRESS - use compression if available - - */ - -int do_connect(struct st_query *q) -{ - char *con_name, *con_user,*con_pass, *con_host, *con_port_str, - *con_db, *con_sock, *con_options; - char *con_buf, *p; - char buff[FN_REFLEN]; - int con_port; - bool con_ssl= 0; - bool con_compress= 0; - int free_con_sock= 0; - int error= 0; - int create_conn= 1; - VAR *var_port, *var_sock; + connect(<name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]]); + connect <name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]]; + + <name> - name of the new connection + <host> - hostname of server + <user> - user to connect as + <pass> - password used when connecting + <db> - initial db when connected + <port> - server port + <sock> - server socket + <opts> - 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", q->first_argument)); - - /* Make a copy of query before parsing, safe_get_param will modify */ - if (!(con_buf= my_strdup(q->first_argument, MYF(MY_WME)))) - die("Could not allocate con_buf"); - p= con_buf; - - 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"); + DBUG_PRINT("enter",("connect: %s", command->first_argument)); - /* Port */ - p= safe_get_param(p, &con_port_str, 0); - if (*con_port_str) + /* Remove parenteses around connect arguments */ + if ((ptr= strstr(command->first_argument, "("))) { - if (*con_port_str == '$') + /* Replace it with a space */ + *ptr= ' '; + if ((ptr= strstr(command->first_argument, ")"))) { - 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; + /* Replace it with \0 */ + *ptr= 0; } else - { - con_port= atoi(con_port_str); - if (con_port == 0) - die("Illegal argument for port: '%s'", con_port_str); - } + die("connect - argument list started with '(' must be ended with ')'"); } - else + + check_command_args(command, command->first_argument, connect_args, + sizeof(connect_args)/sizeof(struct command_arg), + ','); + + /* Port */ + if (ds_port.length) { - con_port= port; + con_port= atoi(ds_port.str); + if (con_port == 0) + die("Illegal argument for port: '%s'", ds_port.str); } /* Sock */ - p= safe_get_param(p, &con_sock, 0); - if (*con_sock) + if (ds_sock.length) { - if (*con_sock == '$') + /* + If the socket is specified just as a name without path + append tmpdir in front + */ + if (*ds_sock.str != FN_LIBCHAR) { - 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; + char buff[FN_REFLEN]; + fn_format(buff, ds_sock.str, TMPDIR, "", 0); + dynstr_set(&ds_sock, buff); } } else { - con_sock= (char*) unix_sock; + /* No socket specified, use default */ + dynstr_set(&ds_sock, unix_sock); } + DBUG_PRINT("info", ("socket: %s", ds_sock.str)); + /* Options */ - p= safe_get_param(p, &con_options, 0); + con_options= ds_options.str; while (*con_options) { - char* str= con_options; - while (*str && !my_isspace(charset_info, *str)) - str++; - *str++= 0; - if (!strcmp(con_options, "SSL")) + 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)) con_ssl= 1; - else if (!strcmp(con_options, "COMPRESS")) + else if (!strncmp(con_options, "COMPRESS", 8)) con_compress= 1; else - die("Illegal option to connect: %s", con_options); - con_options= str; + die("Illegal option to connect: %.*s", end - con_options, con_options); + /* Process next option */ + con_options= end; } - /* Note: 'p' is pointing into the copy 'con_buf' */ - q->last_argument= q->first_argument + (p - con_buf); - if (next_con == cons_end) - die("Connection limit exhausted - increase MAX_CONS in mysqltest.c"); + 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(con_name)) - die("Connection %s already exists", con_name); + 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()"); @@ -3173,53 +3059,56 @@ int do_connect(struct st_query *q) #ifdef HAVE_OPENSSL if (opt_use_ssl || con_ssl) { - /* Turn on ssl_verify_server_cert only if host is "localhost" */ - opt_ssl_verify_server_cert= !strcmp(con_host, "localhost"); - 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(ds_connection_name.str, "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; + + /* 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 (con_db && !strcmp(con_db,"*NO-ONE*")) - con_db= 0; - if (q->abort_on_error) - { - 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': %d %s", con_name, - mysql_errno(&next_con->mysql), mysql_error(&next_con->mysql)); - } - else - error= connect_n_handle_errors(q, &next_con->mysql, con_host, con_user, - con_pass, con_db, con_port, con_sock, - &create_conn); + if (ds_database.length && !strcmp(ds_database.str,"*NO-ONE*")) + dynstr_set(&ds_database, ""); - if (create_conn) + + 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)) { - if (!(next_con->name= my_strdup(con_name, MYF(MY_WME)))) - die(NullS); + 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++; } - if (free_con_sock) - my_free(con_sock, MYF(MY_WME)); - my_free(con_buf, MYF(MY_WME)); - DBUG_RETURN(error); + + 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"); } @@ -3245,31 +3134,31 @@ int do_done(struct st_query *q) Process start of a "if" or "while" statement SYNOPSIS - do_block() - cmd Type of block - q called command + do_block() + cmd Type of block + q called command DESCRIPTION - if ([!]<expr>) - { - <block statements> - } + if ([!]<expr>) + { + <block statements> + } - while ([!]<expr>) - { - <block statements> - } + while ([!]<expr>) + { + <block statements> + } - Evaluates the <expr> and if it evaluates to - greater than zero executes the following code block. - A '!' can be used before the <expr> to indicate it should - be executed if it evaluates to zero. + Evaluates the <expr> and if it evaluates to + greater than zero executes the following code block. + A '!' can be used before the <expr> to indicate it should + be executed if it evaluates to zero. - */ +*/ -void do_block(enum block_cmd cmd, struct st_query* q) +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 == cmd_while ? "while" : "if"); @@ -3334,13 +3223,7 @@ void do_block(enum block_cmd cmd, struct st_query* q) } -my_bool end_of_query(int c) -{ - return match_delimiter(c, delimiter, delimiter_length); -} - - -void do_delimiter(struct st_query* command) +void do_delimiter(struct st_command* command) { char* p= command->first_argument; DBUG_ENTER("do_delimiter"); @@ -3361,27 +3244,57 @@ void do_delimiter(struct st_query* command) } +my_bool match_delimiter(int c, const char *delim, uint length) +{ + uint i; + char tmp[MAX_DELIMITER_LENGTH]; + + 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; +} + + +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 and adds them to the - buffer buf. It continues 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. */ @@ -3415,7 +3328,7 @@ int read_line(char *buf, int size) { /* We're back at the first file, check if all { have matching } - */ + */ if (cur_block != block_stack) die("Missing end of block"); @@ -3564,7 +3477,7 @@ int read_line(char *buf, int size) /* - Convert the read query to format version 1 + Convert the read query to result format version 1 That is: After newline, all spaces need to be skipped unless the previous char was a quote @@ -3574,7 +3487,7 @@ int read_line(char *buf, int size) */ -static void convert_to_format_v1(char* query) +void convert_to_format_v1(char* query) { int last_c_was_quote= 0; char *p= query, *write= query; @@ -3616,98 +3529,167 @@ static void convert_to_format_v1(char* query) /* - Create a query from a set of lines + 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 +*/ + +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_query() - q_ptr pointer where to return the new query + read_command() + command_ptr pointer where to return the new query 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. + 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. + 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. */ -static char read_query_buf[MAX_QUERY]; +#define MAX_QUERY (256*1024) /* 256K -- a test in sp-big is >128K */ +static char read_command_buf[MAX_QUERY]; -int read_query(struct st_query** q_ptr) +int read_command(struct st_command** command_ptr) { - char *p= read_query_buf; - struct st_query* q; - DBUG_ENTER("read_query"); + 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; - q->query_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; - read_query_buf[0]= 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))) { - check_eol_junk(read_query_buf); + check_eol_junk(read_command_buf); DBUG_RETURN(1); } - convert_to_format_v1(read_query_buf); + convert_to_format_v1(read_command_buf); - DBUG_PRINT("info", ("query: %s", read_query_buf)); + DBUG_PRINT("info", ("query: %s", read_command_buf)); if (*p == '#') { - q->type= Q_COMMENT; - /* This goto is to avoid losing the "expected error" info. */ - goto end; + command->type= Q_COMMENT; } - if (!parsing_disabled) + else if (p[0] == '-' && p[1] == '-') { - 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); + command->type= Q_COMMENT_WITH_COMMAND; + p+= 2; /* Skip past -- */ } - if (p[0] == '-' && p[1] == '-') - { - q->type= Q_COMMENT_WITH_COMMAND; - p+= 2; /* To calculate first word */ - } - else if (!parsing_disabled) - { - while (*p && my_isspace(charset_info, *p)) - p++ ; - } - -end: + /* Skip leading spaces */ while (*p && my_isspace(charset_info, *p)) p++; - if (!(q->query_buf= q->query= my_strdup(p, MYF(MY_WME)))) - die(NullS); + if (!(command->query_buf= command->query= my_strdup(p, MYF(MY_WME)))) + die("Out of memory"); /* 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); + + /* Skip spaces between command and first argument */ while (*p && my_isspace(charset_info, *p)) p++; - q->first_argument= p; - q->end= strend(q->query); - q->query_len= (q->end - q->query); + command->first_argument= p; + + command->end= strend(command->query); + command->query_len= (command->end - command->query); parser.read_lines++; DBUG_RETURN(0); } @@ -3719,8 +3701,6 @@ static struct my_option my_long_options[] = 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}, @@ -3789,8 +3769,6 @@ static struct my_option my_long_options[] = 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"user", 'u', "User for login.", (gptr*) &user, (gptr*) &user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"valgrind", 'N', "Define VALGRIND_TEST to 1.", (gptr*) &opt_valgrind_test, - (gptr*) &opt_valgrind_test, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"verbose", 'v', "Write more.", (gptr*) &verbose, (gptr*) &verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"version", 'V', "Output version information and exit.", @@ -3804,7 +3782,7 @@ static struct my_option my_long_options[] = #include <help_start.h> -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); @@ -3825,6 +3803,52 @@ void usage() #include <help_end.h> +/* + 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) @@ -3839,35 +3863,35 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), record = 1; 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, "", "", 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; + 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, "", "", MY_UNPACK_FILENAME); - 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) { @@ -3889,7 +3913,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)))) { @@ -3897,8 +3921,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 @@ -3939,16 +3962,16 @@ int parse_args(int argc, char **argv) /* - Write the content of str into file + 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 + 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 */ -static void str_to_file(const char *fname, char *str, int size) +void str_to_file(const char *fname, char *str, int size) { int fd; char buff[FN_REFLEN]; @@ -3960,7 +3983,7 @@ static void str_to_file(const char *fname, char *str, int size) fn_format(buff, fname, "", "", MY_UNPACK_FILENAME); if ((fd= my_open(buff, O_WRONLY | O_CREAT | O_TRUNC, - MYF(MY_WME | MY_FFNF))) < 0) + 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"); @@ -3992,213 +4015,24 @@ void dump_progress(const char *record_file) ds_progress.str, ds_progress.length); } -static void check_regerr(my_regex_t* r, int err) +void dump_warning_messages(const char *record_file) { - char err_buf[1024]; + char warn_file[FN_REFLEN]; - if (err) - { - my_regerror(err,r,err_buf,sizeof(err_buf)); - die("Regex error: %s\n", err_buf); - } + str_to_file(fn_format(warn_file, record_file, "", ".warnings", + MY_REPLACE_EXT), + ds_warning_messages.str, ds_warning_messages.length); } -/* - 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 - */ -static int reg_replace(char** buf_p, int* buf_len_p, char *pattern, - char *replace, char *string, int icase) +void check_regerr(my_regex_t* r, int err) { - 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; + char err_buf[1024]; - 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) + if (err) { - /* 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_regerror(err,r,err_buf,sizeof(err_buf)); + die("Regex error: %s\n", err_buf); } - my_free((gptr)subs, MYF(0)); - my_regfree(&r); - *res_p= 0; - *buf_p= buf; - *buf_len_p= buf_len; - return 0; } @@ -4210,12 +4044,12 @@ 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 + Setup string patterns that will be used to detect filenames that + needs to be converted from Win to Unix format */ -static void init_win_path_patterns() +void init_win_path_patterns() { /* List of string patterns to match in order to find paths */ const char* paths[] = { "$MYSQL_TEST_DIR", @@ -4242,7 +4076,7 @@ static void init_win_path_patterns() p= my_strdup(paths[i], MYF(MY_FAE)); if (insert_dynamic(&patterns, (gptr) &p)) - die(NullS); + die(NullS); DBUG_PRINT("info", ("p: %s", p)); while (*p) @@ -4255,7 +4089,7 @@ static void init_win_path_patterns() DBUG_VOID_RETURN; } -static void free_win_path_patterns() +void free_win_path_patterns() { uint i= 0; for (i=0 ; i < patterns.elements ; i++) @@ -4270,17 +4104,17 @@ static void free_win_path_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 / + 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 / */ -static void fix_win_paths(const char* val, int len) +void fix_win_paths(const char *val, int len) { uint i; char *p; @@ -4312,43 +4146,12 @@ static void fix_win_paths(const char* val, int len) -/* Append the string to ds, with optional replace */ -static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, - const char *val, int len) -{ -#ifdef __WIN__ - fix_win_paths(val, len); -#endif - - if (glob_replace_regex) - { - if (!multi_reg_replace(glob_replace_regex, (char*)val)) - { - val= glob_replace_regex->buf; - len= strlen(val); - } - } - - if (glob_replace) - replace_strings_append(glob_replace, ds, val, len); - else - dynstr_append_mem(ds, val, len); -} - - -/* Append zero-terminated string to ds, with optional replace */ -static void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val) -{ - replace_dynstr_append_mem(ds, val, strlen(val)); -} - - /* Append the result for one field to the dynamic string ds */ -static void append_field(DYNAMIC_STRING *ds, uint col_idx, MYSQL_FIELD* field, - const char* val, ulonglong len, bool is_null) +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]) { @@ -4399,7 +4202,7 @@ static void append_field(DYNAMIC_STRING *ds, uint col_idx, MYSQL_FIELD* field, 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); @@ -4416,7 +4219,6 @@ static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res) if (!display_result_vertically) dynstr_append_mem(ds, "\n", 1); } - free_replace_column(); } @@ -4425,8 +4227,8 @@ static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res) with '\t'. Values may be converted with 'replace_column' */ -static void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, - MYSQL_FIELD *fields, uint num_fields) +void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, + MYSQL_FIELD *fields, uint num_fields) { MYSQL_BIND *bind; my_bool *is_null; @@ -4472,8 +4274,6 @@ static void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, die("fetch didn't end with MYSQL_NO_DATA from statement: %d %s", mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); - free_replace_column(); - for (i= 0; i < num_fields; i++) { /* Free data for output */ @@ -4490,9 +4290,9 @@ static void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, Append metadata for fields to output */ -static void append_metadata(DYNAMIC_STRING *ds, - MYSQL_FIELD *field, - uint num_fields) +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" @@ -4503,44 +4303,36 @@ static void append_metadata(DYNAMIC_STRING *ds, 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); } } @@ -4550,8 +4342,8 @@ static void append_metadata(DYNAMIC_STRING *ds, Append affected row count and other info to output */ -static void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows, - const char *info) +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)); @@ -4566,12 +4358,12 @@ static void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows, /* - Display the table headings with the names tab separated + Display the table headings with the names tab separated */ -static void append_table_headings(DYNAMIC_STRING *ds, - MYSQL_FIELD *field, - uint num_fields) +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++) @@ -4587,10 +4379,10 @@ static void append_table_headings(DYNAMIC_STRING *ds, Fetch warnings from server and append to ds RETURN VALUE - Number of warnings appended to ds + Number of warnings appended to ds */ -static int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql) +int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql) { uint count; MYSQL_RES *warn_res; @@ -4639,9 +4431,9 @@ static int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql) error - function will not return */ -static void run_query_normal(MYSQL *mysql, struct st_query *command, - int flags, char *query, int query_len, - DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings) +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; @@ -4649,20 +4441,20 @@ static void run_query_normal(MYSQL *mysql, struct st_query *command, DBUG_PRINT("enter",("flags: %d", flags)); DBUG_PRINT("enter", ("query: '%-.60s'", query)); - if (flags & QUERY_SEND) + if (flags & QUERY_SEND_FLAG) { /* - Send the query - */ + Send the query + */ if (mysql_send_query(mysql, query, query_len)) { - handle_error(query, command, mysql_errno(mysql), mysql_error(mysql), + handle_error(command, mysql_errno(mysql), mysql_error(mysql), mysql_sqlstate(mysql), ds); goto end; } } - if (!(flags & QUERY_REAP)) + if (!(flags & QUERY_REAP_FLAG)) DBUG_VOID_RETURN; do @@ -4670,22 +4462,22 @@ static void run_query_normal(MYSQL *mysql, struct st_query *command, /* 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(query, command, mysql_errno(mysql), mysql_error(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 - */ + 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(query, command, mysql_errno(mysql), mysql_error(mysql), + handle_error(command, mysql_errno(mysql), mysql_error(mysql), mysql_sqlstate(mysql), ds); goto end; } @@ -4741,7 +4533,7 @@ static void run_query_normal(MYSQL *mysql, struct st_query *command, if (err > 0) { /* We got an error from mysql_next_result, maybe expected */ - handle_error(query, command, mysql_errno(mysql), mysql_error(mysql), + handle_error(command, mysql_errno(mysql), mysql_error(mysql), mysql_sqlstate(mysql), ds); goto end; } @@ -4751,8 +4543,6 @@ static void run_query_normal(MYSQL *mysql, struct st_query *command, handle_no_error(command); end: - free_replace(); - free_replace_regex(); /* We save the return code (mysql_errno(mysql)) from the last call sent @@ -4768,58 +4558,61 @@ end: Handle errors which occurred during execution SYNOPSIS - handle_error() - query - query string - q - query context - err_errno - error number - err_error - error message - err_sqlstate - sql state - ds - dynamic string which is used for output buffer + 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. + If there is an unexpected error this function will abort mysqltest + immediately. RETURN VALUE - error - function will not return + error - function will not return */ -static void handle_error(const char *query, struct st_query *q, - unsigned int err_errno, const char *err_error, - const char *err_sqlstate, DYNAMIC_STRING *ds) +void handle_error(struct st_command *command, + unsigned int err_errno, const char *err_error, + const char *err_sqlstate, DYNAMIC_STRING *ds) { uint i; DBUG_ENTER("handle_error"); - if (q->require_file) + if (command->require_file) { /* 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", query, err_errno, err_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", query); + 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, err_errno, err_error); + 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 (((q->expected_errno[i].type == ERR_ERRNO) && - (q->expected_errno[i].code.errnum == err_errno)) || - ((q->expected_errno[i].type == ERR_SQLSTATE) && - (strcmp(q->expected_errno[i].code.sqlstate, err_sqlstate) == 0))) + 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 (!disable_result_log) { - if (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); @@ -4829,9 +4622,9 @@ static void handle_error(const char *query, struct st_query *q, 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"); } /* OK */ @@ -4839,7 +4632,8 @@ static void handle_error(const char *query, struct st_query *q, } } - DBUG_PRINT("info",("i: %d expected_errors: %d", i, q->expected_errors)); + DBUG_PRINT("info",("i: %d expected_errors: %d", i, + command->expected_errors)); if (!disable_result_log) { @@ -4852,13 +4646,14 @@ static void handle_error(const char *query, struct st_query *q, if (i) { - if (q->expected_errno[0].type == ERR_ERRNO) + if (command->expected_errors.err[0].type == ERR_ERRNO) die("query '%s' failed with wrong errno %d: '%s', instead of %d...", - q->query, err_errno, err_error, q->expected_errno[0].code.errnum); + 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...", - q->query, err_sqlstate, err_error, - q->expected_errno[0].code.sqlstate); + command->query, err_sqlstate, err_error, + command->expected_errors.err[0].code.sqlstate); } DBUG_VOID_RETURN; @@ -4869,30 +4664,30 @@ static void handle_error(const 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 - error - function will not return + error - function will not return */ -static void 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 */ die("query '%s' succeeded - should have failed with errno %d...", - q->query, q->expected_errno[0].code.errnum); + 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 */ die("query '%s' succeeded - should have failed with sqlstate %s...", - q->query, q->expected_errno[0].code.sqlstate); + command->query, command->expected_errors.err[0].code.sqlstate); } DBUG_VOID_RETURN; @@ -4914,9 +4709,9 @@ static void handle_no_error(struct st_query *q) error - function will not return */ -static void run_query_stmt(MYSQL *mysql, struct st_query *command, - char *query, int query_len, DYNAMIC_STRING *ds, - DYNAMIC_STRING *ds_warnings) +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; @@ -4947,7 +4742,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, */ if (mysql_stmt_prepare(stmt, query, query_len)) { - handle_error(query, command, mysql_stmt_errno(stmt), + handle_error(command, mysql_stmt_errno(stmt), mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); goto end; } @@ -4964,6 +4759,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, parameter markers. */ +#if MYSQL_VERSION_ID >= 50000 if (cursor_protocol_enabled) { /* @@ -4974,13 +4770,14 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, 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(query, command, mysql_stmt_errno(stmt), + handle_error(command, mysql_stmt_errno(stmt), mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); goto end; } @@ -4994,8 +4791,8 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, /* 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 + mysql_stmt_store_result(), this is our only way to know how much + buffer to allocate for result data */ { my_bool one= 1; @@ -5010,7 +4807,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, */ if (mysql_stmt_store_result(stmt)) { - handle_error(query, command, mysql_stmt_errno(stmt), + handle_error(command, mysql_stmt_errno(stmt), mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); goto end; } @@ -5080,9 +4877,6 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, } end: - free_replace(); - free_replace_regex(); - if (!disable_warnings) { dynstr_free(&ds_prepare_warnings); @@ -5094,7 +4888,7 @@ end: 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); @@ -5112,7 +4906,7 @@ end: as view, sp etc. */ -static int util_query(MYSQL* org_mysql, const char* query){ +int util_query(MYSQL* org_mysql, const char* query){ MYSQL* mysql; DBUG_ENTER("util_query"); @@ -5123,11 +4917,9 @@ static int util_query(MYSQL* org_mysql, const char* query){ if (!(mysql= mysql_init(mysql))) die("Failed in mysql_init()"); - if (safe_connect(mysql, org_mysql->host, org_mysql->user, - org_mysql->passwd, org_mysql->db, org_mysql->port, - org_mysql->unix_socket)) - die("Could not open util connection: %d %s", - mysql_errno(mysql), mysql_error(mysql)); + 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; } @@ -5141,8 +4933,8 @@ static int util_query(MYSQL* org_mysql, const char* query){ Run query 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 + 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 @@ -5151,7 +4943,7 @@ static int util_query(MYSQL* org_mysql, const char* query){ */ -static void run_query(MYSQL *mysql, struct st_query *command, int flags) +void run_query(MYSQL *mysql, struct st_command *command, int flags) { DYNAMIC_STRING *ds; DYNAMIC_STRING ds_result; @@ -5160,13 +4952,17 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags) char *query; int query_len; my_bool view_created= 0, sp_created= 0; - my_bool complete_query= ((flags & QUERY_SEND) && (flags & QUERY_REAP)); + my_bool complete_query= ((flags & QUERY_SEND_FLAG) && + (flags & QUERY_REAP_FLAG)); 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 - */ + */ if (command->type == Q_EVAL) { init_dynamic_string(&eval_query, "", command->query_len+256, 1024); @@ -5185,7 +4981,7 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags) 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); @@ -5195,9 +4991,9 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags) ds= &ds_res; /* - Log the query into the output buffer + Log the query into the output buffer */ - if (!disable_query_log && (flags & QUERY_SEND)) + if (!disable_query_log && (flags & QUERY_SEND_FLAG)) { replace_dynstr_append_mem(ds, query, query_len); dynstr_append_mem(ds, delimiter, delimiter_length); @@ -5209,8 +5005,8 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags) match_re(&view_re, query)) { /* - Create the query as a view. - Use replace since view can exist from a failed mysqltest run + 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, @@ -5222,7 +5018,7 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags) /* 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))); @@ -5235,14 +5031,14 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags) { /* 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 + 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); } @@ -5328,8 +5124,8 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags) if (record) { /* - Recording in progress - Dump the output from _this_ query to the specified record_file + Recording in progress + Dump the output from _this_ query to the specified record_file */ str_to_file(command->record_file, ds->str, ds->length); @@ -5350,12 +5146,12 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags) dynstr_free(&eval_query); } +/****************************************************************************/ +/* + Functions to detect different SQL statements +*/ -/****************************************************************************\ - * Functions to detect different SQL statements -\****************************************************************************/ - -static char *re_eprint(int err) +char *re_eprint(int err) { static char epbuf[100]; size_t len= my_regerror(REG_ITOA|err, (my_regex_t *)NULL, @@ -5364,7 +5160,7 @@ static char *re_eprint(int err) return(epbuf); } -static void init_re_comp(my_regex_t *re, const char* str) +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); @@ -5377,11 +5173,11 @@ static void init_re_comp(my_regex_t *re, const char* str) } } -static void init_re(void) +void init_re(void) { /* - Filter for queries that can be run using the - MySQL Prepared Statements C API + Filter for queries that can be run using the + MySQL Prepared Statements C API */ const char *ps_re_str = "^(" @@ -5398,13 +5194,13 @@ static void init_re(void) "[[:space:]]*INSERT[[:space:]]+SELECT[[:space:]])"; /* - Filter for queries that can be run using the - Stored procedures + 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 + Filter for queries that can be run as views */ const char *view_re_str = "^(" @@ -5416,7 +5212,7 @@ static void init_re(void) } -static int match_re(my_regex_t *re, char *str) +int match_re(my_regex_t *re, char *str) { int err= my_regexec(re, str, (size_t)0, NULL, 0); @@ -5434,7 +5230,7 @@ static int match_re(my_regex_t *re, char *str) return 0; } -static void free_re(void) +void free_re(void) { my_regfree(&ps_re); my_regfree(&sp_re); @@ -5444,142 +5240,85 @@ static void free_re(void) /****************************************************************************/ -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 (!parsing_disabled && *q->query == '}') + if (*command->query == '}') { - q->type = Q_END_BLOCK; + command->type = Q_END_BLOCK; DBUG_VOID_RETURN; } - if (q->type != Q_COMMENT_WITH_COMMAND) - q->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 */ + command->type=(enum enum_commands) type; /* Found command */ + /* - If queries are disabled, only recognize - --enable_parsing and --disable_parsing + Look for case where "query" was explicitly specified to + force command being sent to server */ - if (parsing_disabled && q->type != Q_ENABLE_PARSING && - q->type != Q_DISABLE_PARSING) - q->type= Q_COMMENT; + if (type == Q_QUERY) + { + /* Skip the "query" part */ + command->query= command->first_argument; + } } - else if (q->type == Q_COMMENT_WITH_COMMAND && - q->first_word_len && - strcmp(q->query + q->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= q->query[q->first_word_len-1]; - q->query[q->first_word_len-1]= 0; - type= find_type(q->query, &command_typelib, 1+2); - q->query[q->first_word_len-1]= save; - if (type > 0) - die("Extra delimiter \";\" found"); - } - 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; -} + /* No mysqltest command matched */ -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+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"); + 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; - 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; -} - -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; -} + /* 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); -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"); - my_hash_insert(&var_hash, (byte*) var_init(0,"BIG_TEST", 0, - (opt_big_test) ? "1" : "0", 0)); - my_hash_insert(&var_hash, (byte*) var_init(0,"VALGRIND_TEST", 0, - (opt_valgrind_test) ? "1" : "0", - 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. @@ -5588,7 +5327,9 @@ static void init_var_hash(MYSQL *mysql) test run completes */ -static void mark_progress(struct st_query* q __attribute__((unused)), int line) + +void mark_progress(struct st_command* command __attribute__((unused)), + int line) { char buf[32], *end; ulonglong timer= timer_now(); @@ -5622,44 +5363,56 @@ 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, - query_executed= 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]); - /* 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; - /* Init cons */ - 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)); - 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; /* 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= cmd_none; - my_init_dynamic_array(&q_lines, sizeof(struct st_query*), INIT_Q_LINES, - INIT_Q_LINES); + 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); + init_dynamic_string(&ds_warning_messages, "", 0, 2048); parse_args(argc, argv); DBUG_PRINT("info",("result_file: '%s'", result_file ? result_file : "")); @@ -5690,29 +5443,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("Could not open connection '%s': %d %s", cur_con->name, - mysql_errno(&cur_con->mysql), 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); - -#ifdef __WIN__ - init_tmp_sh_file(); - init_win_path_patterns(); -#endif + /* Use all time until exit if no explicit 'start_timer' */ + timer_start= timer_now(); /* Initialize $mysql_errno with -1, so we can @@ -5726,26 +5480,35 @@ int main(int argc, char **argv) open_file(opt_include); } - while (!read_query(&q)) + 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 (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) { - q->last_argument= q->first_argument; + command->last_argument= command->first_argument; processed = 1; - switch (q->type) { + switch (command->type) { case Q_CONNECT: - do_connect(q); + do_connect(command); break; - case Q_CONNECTION: select_connection(q); 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; @@ -5760,21 +5523,21 @@ int main(int argc, char **argv) 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_INC: do_modify_var(q, DO_INC); break; - 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_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: - do_delimiter(q); + do_delimiter(command); break; case Q_DISPLAY_VERTICAL_RESULTS: display_result_vertically= TRUE; @@ -5782,124 +5545,135 @@ 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; - /* 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; } - 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; + 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; } - /* - To force something being sent as a query to the mysqld one can - use the prefix "query". Remove "query" from string before executing - */ - if (strncmp(q->query, "query ", 6) == 0) - { - q->query= q->first_argument; - } - 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: - /* 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 + 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. - */ - 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: - get_replace_regex(q); + 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); break; } 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 */ @@ -5908,10 +5682,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; @@ -5920,20 +5693,14 @@ int main(int argc, char **argv) ps_protocol_enabled= ps_protocol; break; case Q_DISABLE_RECONNECT: - { - my_bool reconnect= 0; - mysql_options(&cur_con->mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect); + set_reconnect(&cur_con->mysql, 0); break; - } case Q_ENABLE_RECONNECT: - { - my_bool reconnect= 1; - mysql_options(&cur_con->mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect); + set_reconnect(&cur_con->mysql, 1); break; - } case Q_DISABLE_PARSING: if (parsing_disabled == 0) - parsing_disabled++; + parsing_disabled= 1; else die("Parsing is already disabled"); break; @@ -5942,14 +5709,14 @@ int main(int argc, char **argv) Ensure we don't get parsing_disabled < 0 as this would accidentally disable code we don't want to have disabled */ - if (parsing_disabled > 0) - parsing_disabled--; + if (parsing_disabled == 1) + parsing_disabled= 0; else die("Parsing is already enabled"); break; case Q_DIE: - die("%s", q->first_argument); + die("%s", command->first_argument); break; default: @@ -5961,29 +5728,39 @@ int main(int argc, char **argv) if (!processed) { current_line_inc= 0; - switch (q->type) { - case Q_WHILE: do_block(cmd_while, q); break; - case Q_IF: do_block(cmd_if, q); break; - case Q_END_BLOCK: do_done(q); break; + 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 && + 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 */ - 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(q, parser.current_line); + mark_progress(command, parser.current_line); } start_lineno= 0; @@ -6010,7 +5787,7 @@ int main(int argc, char **argv) /* 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); } } @@ -6025,7 +5802,7 @@ int main(int argc, char **argv) die("The test didn't produce any output"); } - if (!query_executed && result_file && my_stat(result_file, &res_info, 0)) + 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 @@ -6037,14 +5814,16 @@ 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); - if (!got_end_timer) - timer_output(); /* No end_timer cmd, end it */ + timer_output(); free_used_memory(); my_end(MY_CHECK_ERROR); @@ -6057,101 +5836,188 @@ int main(int argc, char **argv) /* - 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=<filename> 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. -static int read_server_arguments(const char *name) -{ - char argument[1024],buff[FN_REFLEN], *str=0; - FILE *file; + - If only 'start_timer' is given we measure how long we execute + from that point until we terminate mysqltest. - if (!test_if_hard_path(name)) + - 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. +*/ + +void timer_output(void) +{ + if (timer_file) { - strxmov(buff, opt_basedir, name, NullS); - name=buff; + char buf[32], *end; + 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; } - fn_format(buff, name, "", "", MY_UNPACK_FILENAME); +} - if (!embedded_server_arg_count) + +ulonglong timer_now(void) +{ + return my_getsystime() / 10000; +} + + +/* + 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) { - embedded_server_arg_count=1; - embedded_server_args[0]= (char*) ""; /* Progname */ + 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); } - 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))) + my_free(start, MYF(0)); + command->last_argument= command->end; +} + + +void free_replace_column() +{ + uint i; + for (i=0 ; i < max_replace_column ; i++) { - *(strend(str)-1)=0; /* Remove end newline */ - if (!(embedded_server_args[embedded_server_arg_count]= - (char*) my_strdup(str,MYF(MY_WME)))) + if (replace_column[i]) { - my_fclose(file,MYF(0)); - return 1; + my_free(replace_column[i], 0); + replace_column[i]= 0; } - 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; + max_replace_column= 0; } -/****************************************************************************\ - * - * A primitive timer that give results in milliseconds if the - * --timer-file=<filename> 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) + +/****************************************************************************/ +/* + 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) { - if (timer_file) + 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 buf[32], *end; - ulonglong timer= timer_now() - timer_start; - end= longlong2str(timer, buf, 10); - str_to_file(timer_file,buf, (int) (end-buf)); + 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; } -static ulonglong timer_now(void) + +void free_replace() { - return my_getsystime() / 10000; + DBUG_ENTER("free_replace"); + if (glob_replace) + { + my_free((char*) glob_replace,MYF(0)); + glob_replace=0; + } + DBUG_VOID_RETURN; } -/**************************************************************************** -* Handle replacement of strings -****************************************************************************/ - -#define PC_MALLOC 256 /* Bytes for pointers */ -#define PS_MALLOC 512 /* Bytes for data */ - -#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; @@ -6165,99 +6031,514 @@ typedef struct st_replace_found { int from_offset; } REPLACE_STRING; -#ifndef WORD_BIT -#define WORD_BIT (8*sizeof(uint)) -#endif + +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; + } +} -static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name) +/* + Regex replace functions +*/ + + +/* Stores regex substitutions */ + +struct st_regex { - uint i,length,old_count; - byte *new_pos; - const char **new_array; - DBUG_ENTER("insert_pointer_name"); + 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 */ +}; - if (! pa->typelib.count) +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) { - 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)))) + bzero(®,sizeof(reg)); + /* find the start of the statement */ + while (p < expr_end) { - my_free((gptr) pa->typelib.type_names,MYF(0)); - DBUG_RETURN (-1); + if (*p == '/') + break; + p++; } - 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; + + 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"); } - length=(uint) strlen(name)+1; - if (pa->length+length >= pa->max_length) + 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++) { - 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) + 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)) { - 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; + /* 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; } - pa->max_length+=PS_MALLOC; } - if (pa->typelib.count >= pa->max_count-1) + + 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) { - 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)); + 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; } - 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) +/* + 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) { - if (pa->typelib.count) + 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))) { - 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)); + check_regerr(&r,err_code); + return 1; } -} /* free_pointer_array */ + 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); - /* Code for replace rutines */ + /* 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 #define SET_MALLOC_HUNC 64 +#define LAST_CHAR_CODE 259 typedef struct st_rep_set { uint *bits; /* Pointer to used sets */ @@ -6289,32 +6570,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]; @@ -6367,7 +6664,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] == '^') @@ -6550,7 +6847,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)+ @@ -6593,7 +6890,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); @@ -6609,16 +6906,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; @@ -6636,7 +6933,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; @@ -6655,34 +6952,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++) @@ -6690,22 +6987,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; @@ -6726,11 +7023,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 its - 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++) @@ -6744,14 +7041,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 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++) @@ -6764,145 +7061,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; } +/**************************************************************************** + * Handle replacement of strings + ****************************************************************************/ -static uint replace_len(my_string str) -{ - uint len=0; - while (*str) - { - if (str[0] == '\\' && str[1]) - str++; - str++; - len++; - } - return len; -} - +#define PC_MALLOC 256 /* Bytes for pointers */ +#define PS_MALLOC 512 /* Bytes for data */ -/* Replace strings while appending to ds */ -void replace_strings_append(REPLACE *rep, DYNAMIC_STRING* ds, - const char *str, int len) +int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name) { - reg1 REPLACE *rep_pos; - reg2 REPLACE_STRING *rep_str; - const char *start, *from; - DBUG_ENTER("replace_strings_append"); + uint i,length,old_count; + byte *new_pos; + const char **new_array; + DBUG_ENTER("insert_pointer_name"); - start= from= str; - rep_pos=rep+1; - for (;;) + if (! pa->typelib.count) { - /* 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) + 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)))) { - /* 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; + my_free((gptr) pa->typelib.type_names,MYF(0)); + DBUG_RETURN (-1); } - - /* 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) + 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) { - /* End of from string */ - DBUG_PRINT("exit", ("Found end of from string")); - DBUG_VOID_RETURN; + 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; } - DBUG_ASSERT(from <= str+len); - start= from; - rep_pos=rep; + 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 */ -/**************************************************************************** - Replace results for a column -*****************************************************************************/ +/* free pointer array */ -static void free_replace_column() +void free_pointer_array(POINTER_ARRAY *pa) { - uint i; - for (i=0 ; i < max_replace_column ; i++) + if (pa->typelib.count) { - if (replace_column[i]) - { - my_free(replace_column[i], 0); - replace_column[i]= 0; - } + 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)); } - max_replace_column= 0; -} +} /* free_pointer_array */ -/* - 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"); +/* Functions that uses replace and replace_regex */ - free_replace_column(); - if (!*from) - die("Missing argument in %s", q->query); +/* 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 - /* Allocate a buffer for results */ - start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE)); - while (*from) + if (glob_replace_regex) { - char *to; - uint column_number; + /* Regex replace */ + if (!multi_reg_replace(glob_replace_regex, (char*)val)) + { + val= glob_replace_regex->buf; + len= strlen(val); + } + } - 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); + if (glob_replace) + { + /* Normal replace */ + replace_strings_append(glob_replace, ds, val, len); } - my_free(start, MYF(0)); - q->last_argument= q->end; + else + dynstr_append_mem(ds, val, len); } +/* 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/extra/rpl_tests/rpl_flsh_tbls.test b/mysql-test/extra/rpl_tests/rpl_flsh_tbls.test index 7a097fd1eae..ee6b0ed1426 100644 --- a/mysql-test/extra/rpl_tests/rpl_flsh_tbls.test +++ b/mysql-test/extra/rpl_tests/rpl_flsh_tbls.test @@ -7,6 +7,8 @@ -- source include/master-slave.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/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 diff --git a/mysql-test/lib/mtr_cases.pl b/mysql-test/lib/mtr_cases.pl index f63e8a4bfd4..bb92730444c 100644 --- a/mysql-test/lib/mtr_cases.pl +++ b/mysql-test/lib/mtr_cases.pl @@ -514,13 +514,6 @@ sub collect_one_test_case($$$$$$$) { $tinfo->{'comment'}= "Test need debug binaries"; return; } - - 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/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 e8dcb0262c9..8a7fc56269c 100644 --- a/mysql-test/lib/mtr_io.pl +++ b/mysql-test/lib/mtr_io.pl @@ -12,6 +12,7 @@ sub mtr_fromfile ($); sub mtr_tofile ($@); sub mtr_tonewfile($@); sub mtr_lastlinefromfile($); +sub mtr_appendfile_to_file ($$); ############################################################################## # @@ -170,4 +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 (<FROMFILE>); + close FROMFILE; + close TOFILE; +} + + 1; diff --git a/mysql-test/lib/mtr_process.pl b/mysql-test/lib/mtr_process.pl index 9f8eb8e92bd..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; @@ -26,28 +25,6 @@ sub mtr_kill_processes ($); sub mtr_ping_with_timeout($); sub mtr_ping_port ($); -# 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 ($$); - # static in C sub spawn_impl ($$$$$$$$); @@ -115,8 +92,6 @@ sub spawn_impl ($$$$$$$$) { my $pid_file= shift; # FIXME my $spawn_opts= shift; - mtr_error("Can't spawn with empty \"path\"") unless defined $path; - if ( $::opt_script_debug ) { print STDERR "\n"; @@ -140,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(); @@ -358,9 +336,16 @@ sub mtr_process_exit_status { # kill IM manager first, else it will restart the servers sub mtr_kill_leftovers () { + mtr_report("Killing Possible Leftover Processes"); mtr_debug("mtr_kill_leftovers(): started."); - mtr_im_stop($::instance_manager, 'mtr_kill_leftovers'); + mkpath("$::opt_vardir/log"); # Needed for mysqladmin log + + # 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(). @@ -392,40 +377,41 @@ sub mtr_kill_leftovers () { $srv->{'pid'}= 0; # Assume we are done with it } - # Start shutdown of clusters. - - mtr_debug("Shutting down cluster..."); - - foreach my $cluster (@{$::clusters}) + if ( ! $::opt_skip_ndbcluster ) { - 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'} - }); + # Start shutdown of clusters. + mtr_debug("Shutting down cluster..."); - $cluster->{'pid'}= 0; # Assume we are done with it + foreach my $cluster (@{$::clusters}) + { + mtr_debug(" - cluster " . + "(pid: $cluster->{pid}; " . + "pid file: '$cluster->{path_pid})"); + my $pid= mtr_ndbmgm_start($cluster, "shutdown"); - foreach my $ndbd (@{$cluster->{'ndbds'}}) - { - mtr_debug(" - ndbd " . - "(pid: $ndbd->{pid}; " . - "pid file: '$ndbd->{path_pid})"); + # Save the pid of the ndb_mgm process + $admin_pids{$pid}= 1; push(@kill_pids,{ - pid => $ndbd->{'pid'}, - pidfile => $ndbd->{'path_pid'}, + pid => $cluster->{'pid'}, + pidfile => $cluster->{'path_pid'} }); - $ndbd->{'pid'}= 0; # Assume we are done with it + + $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 + } } } @@ -1095,7 +1081,7 @@ sub mtr_kill_processes ($) { { foreach my $sig (15, 9) { - last if mtr_im_kill_process([ $pid ], $sig, 10); + last if mtr_im_kill_process([ $pid ], $sig, 10, 1); } } } @@ -1130,679 +1116,6 @@ sub mtr_exit ($) { exit($code); } -############################################################################## -# -# Instance Manager management routines. -# -############################################################################## - -sub mtr_im_kill_process ($$$) { - my $pid_lst= shift; - my $signal= shift; - my $timeout= shift; - my $total_attempts= $timeout * 10; - - my %pids; - - foreach my $pid (@{$pid_lst}) - { - $pids{$pid}= 1; - } - - for (my $cur_attempt= 1; $cur_attempt <= $total_attempts; ++$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 100ms waiting for processes to die..."); - - select(undef, undef, undef, 0.1); - } - - mtr_debug("Process(es) " . - join(' ', keys %pids) . - " is still alive after $total_attempts " . - "of sending signal $signal."); -} - -########################################################################### - -sub mtr_im_load_pids($) { - my $instance_manager= shift; - - mtr_debug("Loading PID files..."); - - # Obtain mysqld-process pids. - - my $instances = $instance_manager->{'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: '$instance_manager->{path_pid}'."); - - if (-f $instance_manager->{'path_pid'}) - { - $instance_manager->{'pid'} = - mtr_get_pid_from_file($instance_manager->{'path_pid'}); - - mtr_debug("IM-main PID: $instance_manager->{pid}."); - } - else - { - mtr_debug("IM-main: no PID file."); - $instance_manager->{'pid'}= undef; - } - - # - IM-angel - - mtr_debug("IM-angel PID file: '$instance_manager->{path_angel_pid}'."); - - if (-f $instance_manager->{'path_angel_pid'}) - { - $instance_manager->{'angel_pid'} = - mtr_get_pid_from_file($instance_manager->{'path_angel_pid'}); - - mtr_debug("IM-angel PID: $instance_manager->{'angel_pid'}."); - } - else - { - mtr_debug("IM-angel: no PID file."); - $instance_manager->{'angel_pid'} = undef; - } -} - -########################################################################### - -sub mtr_im_terminate($) { - my $instance_manager= shift; - - # Load pids from pid-files. We should do it first of all, because IM deletes - # them on shutdown. - - mtr_im_load_pids($instance_manager); - - 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 $instance_manager->{'pid'}) - { - mtr_debug("IM-main pid: $instance_manager->{pid}."); - mtr_debug("Stopping IM-main..."); - - mtr_im_kill_process([ $instance_manager->{'pid'} ], 'TERM', 10); - } - else - { - mtr_debug("IM-main pid: n/a."); - } - - # If IM-angel was alive, wait for it to die. - - if (defined $instance_manager->{'angel_pid'}) - { - mtr_debug("IM-angel pid: $instance_manager->{'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, $instance_manager->{'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($instance_manager); -} - -########################################################################### - -sub mtr_im_check_alive($) { - my $instance_manager= shift; - - mtr_debug("Checking whether IM-components are alive..."); - - return 1 if mtr_im_check_main_alive($instance_manager); - - return 1 if mtr_im_check_angel_alive($instance_manager); - - return 1 if mtr_im_check_mysqlds_alive($instance_manager); - - return 0; -} - -########################################################################### - -sub mtr_im_check_main_alive($) { - my $instance_manager= shift; - - # Check that the process, that we know to be IM's, is dead. - - if (defined $instance_manager->{'pid'}) - { - if (kill (0, $instance_manager->{'pid'})) - { - mtr_debug("IM-main (PID: $instance_manager->{pid}) is alive."); - return 1; - } - else - { - mtr_debug("IM-main (PID: $instance_manager->{pid}) is dead."); - } - } - else - { - mtr_debug("No PID file for IM-main."); - } - - # Check that IM does not accept client connections. - - if (mtr_ping_port($instance_manager->{'port'})) - { - mtr_debug("IM-main (port: $instance_manager->{port}) " . - "is accepting connections."); - - mtr_im_errlog("IM-main is accepting connections on port " . - "$instance_manager->{port}, but there is no " . - "process information."); - return 1; - } - else - { - mtr_debug("IM-main (port: $instance_manager->{port}) " . - "does not accept connections."); - return 0; - } -} - -########################################################################### - -sub mtr_im_check_angel_alive($) { - my $instance_manager= shift; - - # Check that the process, that we know to be the Angel, is dead. - - if (defined $instance_manager->{'angel_pid'}) - { - if (kill (0, $instance_manager->{'angel_pid'})) - { - mtr_debug("IM-angel (PID: $instance_manager->{angel_pid}) is alive."); - return 1; - } - else - { - mtr_debug("IM-angel (PID: $instance_manager->{angel_pid}) is dead."); - return 0; - } - } - else - { - mtr_debug("No PID file for IM-angel."); - return 0; - } -} - -########################################################################### - -sub mtr_im_check_mysqlds_alive($) { - my $instance_manager= shift; - - mtr_debug("Checking for IM-guarded mysqld instances..."); - - my $instances = $instance_manager->{'instances'}; - - for (my $idx= 0; $idx < 2; ++$idx) - { - mtr_debug("Checking mysqld[$idx]..."); - - return 1 - if mtr_im_check_mysqld_alive($instance_manager, $instances->[$idx]); - } -} - -########################################################################### - -sub mtr_im_check_mysqld_alive($$) { - my $instance_manager= shift; - my $mysqld_instance= shift; - - # Check that the process is dead. - - if (defined $instance_manager->{'pid'}) - { - if (kill (0, $instance_manager->{'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 $instance_manager= shift; - - mtr_im_rm_file($instance_manager->{'path_pid'}); - mtr_im_rm_file($instance_manager->{'path_sock'}); - - mtr_im_rm_file($instance_manager->{'path_angel_pid'}); - - for (my $idx= 0; $idx < 2; ++$idx) - { - mtr_im_rm_file($instance_manager->{'instances'}->[$idx]->{'path_pid'}); - mtr_im_rm_file($instance_manager->{'instances'}->[$idx]->{'path_sock'}); - } -} - -########################################################################### - -sub mtr_im_rm_file($) -{ - my $file_path= shift; - - if (-f $file_path) - { - mtr_debug("Removing '$file_path'..."); - - mtr_warning("Can not remove '$file_path'.") - unless unlink($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 $instance_manager= shift; - - # Re-load PIDs. That can be useful because some processes could have been - # restarted. - - mtr_im_load_pids($instance_manager); - - # Ignoring SIGCHLD so that all children could rest in peace. - - start_reap_all(); - - # Kill IM-angel first of all. - - if (defined $instance_manager->{'angel_pid'}) - { - mtr_debug("Killing IM-angel (PID: $instance_manager->{angel_pid})..."); - mtr_im_kill_process([ $instance_manager->{'angel_pid'} ], 'KILL', 10); - } - else - { - mtr_debug("IM-angel is dead."); - } - - # Re-load PIDs again. - - mtr_im_load_pids($instance_manager); - - # Kill IM-main. - - if (defined $instance_manager->{'pid'}) - { - mtr_debug("Killing IM-main (PID: $instance_manager->pid})..."); - mtr_im_kill_process([ $instance_manager->{'pid'} ], 'KILL', 10); - } - else - { - mtr_debug("IM-main is dead."); - } - - # Re-load PIDs again. - - mtr_im_load_pids($instance_manager); - - # 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= $instance_manager->{'instances'}->[$idx]->{'pid'}; - - next unless defined $pid; - - 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); - } - - # That's all. - - stop_reap_all(); -} - -############################################################################## - -sub mtr_im_wait_for_connection($$$) { - my $instance_manager= shift; - my $total_attempts= shift; - my $connect_timeout= shift; - - mtr_debug("Waiting for IM on port $instance_manager->{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($instance_manager->{'port'})) - { - mtr_debug("IM is accepting connections " . - "on port $instance_manager->{port}."); - return 1; - } - - mtr_debug("Sleeping $connect_timeout..."); - sleep($connect_timeout); - } - - mtr_debug("IM does not accept connections " . - "on port $instance_manager->{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; -} - -############################################################################## - -sub mtr_im_start($$) { - my $instance_manager = shift; - my $opts = shift; - - mtr_debug("Starting Instance Manager..."); - - my $args; - mtr_init_args(\$args); - mtr_add_arg($args, "--defaults-file=%s", - $instance_manager->{'defaults_file'}); - - foreach my $opt (@{$opts}) - { - mtr_add_arg($args, $opt); - } - - $instance_manager->{'pid'} = - mtr_spawn( - $::exe_im, # path to the executable - $args, # cmd-line args - '', # stdin - $instance_manager->{'path_log'}, # stdout - $instance_manager->{'path_err'}, # stderr - '', # pid file path (not used) - { append_log_file => 1 } # append log files - ); - - if ( ! $instance_manager->{'pid'} ) - { - mtr_report('Could not start Instance Manager'); - return; - } - - # 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. - - if ( ! sleep_until_file_created( - $instance_manager->{'path_pid'}, - $instance_manager->{'start_timeout'}, - -1)) # real PID is still unknown - { - mtr_report("Instance Manager PID file is missing"); - return; - } - - $instance_manager->{'pid'} = - mtr_get_pid_from_file($instance_manager->{'path_pid'}); - - mtr_debug("Instance Manager started. PID: $instance_manager->{pid}."); - - # Wait until we can connect to IM. - - my $IM_CONNECT_TIMEOUT= 30; - - unless (mtr_im_wait_for_connection($instance_manager, - $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_error("Can not connect to Instance Manager " . - "in $IM_CONNECT_TIMEOUT seconds after start."); - } - - # Wait until we can connect to guarded mysqld-instances - # (in other words -- wait for IM to start guarded instances). - - for (my $idx= 0; $idx < 2; ++$idx) - { - my $mysqld= $instance_manager->{'instances'}->[$idx]; - - next if exists $mysqld->{'nonguarded'}; - - mtr_debug("Waiting for mysqld[$idx] to start..."); - - 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_error("Can not connect to mysqld[$idx] " . - "in $IM_CONNECT_TIMEOUT seconds after start."); - } - - mtr_debug("mysqld[$idx] started."); - } - - mtr_debug("Instance Manager started."); - - mtr_im_load_pids($instance_manager); -} - -############################################################################## - -sub mtr_im_stop($$) { - my $instance_manager= shift; - my $where= shift; - - mtr_debug("Stopping Instance Manager..."); - - # Try graceful shutdown. - - mtr_im_terminate($instance_manager); - - # Check that all processes died. - - unless (mtr_im_check_alive($instance_manager)) - { - mtr_debug("Instance Manager has been stopped successfully."); - mtr_im_cleanup($instance_manager); - return 1; - } - - # Instance Manager don't want to die. We should kill it. - - mtr_im_errlog("[$where] Instance Manager did not shutdown gracefully."); - - mtr_im_kill($instance_manager); - - # Check again that all IM-related processes have been killed. - - my $im_is_alive= mtr_im_check_alive($instance_manager); - - mtr_im_cleanup($instance_manager); - - if ($im_is_alive) - { - mtr_error("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/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 662afcd3bbf..3ad083dbd0c 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"; @@ -129,10 +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 $mysql_version_id; our $glob_mysql_test_dir= undef; our $glob_mysql_bench_dir= undef; our $glob_hostname= undef; @@ -146,42 +144,36 @@ 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_snapshot; -our $path_slave_load_tmpdir; # What is this?! 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 $opt_vardir_trace; # unix formatted opt_vardir for trace files +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 our $opt_verbose= 0; # Verbose output, enable with --verbose -# Options FIXME not all.... - our $exe_master_mysqld; our $exe_mysql; our $exe_mysqladmin; our $exe_mysqlbinlog; our $exe_mysql_client_test; our $exe_mysqld; -our $exe_mysqlcheck; # Called from test case -our $exe_mysqldump; # Called from test case -our $exe_mysqlslap; # 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; @@ -238,7 +230,7 @@ our $opt_gprof_dir; our $opt_gprof_master; our $opt_gprof_slave; -our $master; # Will be struct in C +our $master; our $slave; our $clusters; @@ -268,7 +260,7 @@ 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; @@ -325,14 +317,16 @@ our $exe_ndb_waiter; our $path_ndb_tools_dir; our $path_ndb_examples_dir; our $exe_ndb_example; -our $file_ndb_testrun_log; +our $path_ndb_testrun_log; our @data_dir_lst; our $used_binlog_format; our $debug_compiled_binaries; our $glob_tot_real_time= 0; -our $glob_innodb_supported; + +# Default values read from mysqld +our $default_mysqld_port; ###################################################################### # @@ -348,10 +342,9 @@ sub executable_setup (); sub environment_setup (); sub kill_running_server (); sub cleanup_stale_files (); -sub check_ssl_support (); +sub check_ssl_support ($); sub check_running_as_root(); -sub check_ndbcluster_support (); -sub check_innodb_support (); +sub check_ndbcluster_support ($); sub rm_ndbcluster_tables ($); sub ndbcluster_start_install ($); sub ndbcluster_start ($$); @@ -390,12 +383,6 @@ sub main () { initial_setup(); command_line_setup(); executable_setup(); - - check_ndbcluster_support(); - check_innodb_support(); - check_ssl_support(); - check_debug_support(); - environment_setup(); signal_setup(); @@ -465,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" . @@ -495,13 +477,11 @@ sub initial_setup () { chomp($glob_mysql_test_dir); } $glob_basedir= dirname($glob_mysql_test_dir); + # 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; - # needs to be same length to test logging (FIXME what???) - $path_slave_load_tmpdir= "../../var/tmp"; - $path_my_basedir= $opt_source_dist ? $glob_mysql_test_dir : $glob_basedir; @@ -668,7 +648,6 @@ sub command_line_setup () { 'comment=s' => \$opt_comment, 'debug' => \$opt_debug, 'fast' => \$opt_fast, - 'netware' => \$opt_netware, 'reorder' => \$opt_reorder, 'enable-disabled' => \$opt_enable_disabled, 'script-debug' => \$opt_script_debug, @@ -744,9 +723,15 @@ sub command_line_setup () { { $opt_vardir= "$glob_mysql_test_dir/var"; } - $opt_vardir_trace= $opt_vardir; + 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/... - $opt_vardir_trace=~ s/^\w://; + $path_vardir_trace=~ s/^\w://; # We make the path absolute, as the server will do a chdir() before usage unless ( $opt_vardir =~ m,^/, or @@ -767,12 +752,6 @@ sub command_line_setup () { # Do sanity checks of command line arguments # -------------------------------------------------------------------------- - 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 # -------------------------------------------------------------------------- @@ -1054,6 +1033,7 @@ sub command_line_setup () { 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 }; @@ -1065,6 +1045,7 @@ sub command_line_setup () { 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 }; @@ -1120,6 +1101,7 @@ sub command_line_setup () { $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/"; } @@ -1152,6 +1134,96 @@ sub snapshot_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/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 =~ /^([\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); + check_debug_support(\%mysqld_variables); + + if ( $mysql_version_id < 50000 ) + { + # 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 + $opt_socket= %mysqld_variables->{'socket'}; + $default_mysqld_port = %mysqld_variables->{'port'}; +} + + sub executable_setup () { # @@ -1169,161 +1241,131 @@ sub executable_setup () { } } - if ( $opt_source_dist ) - { - if ( $glob_win32 ) - { - $path_client_bindir= mtr_path_exists("$glob_basedir/client_release", - "$glob_basedir/client_debug", - "$glob_basedir/bin", - # New CMake locations. - "$glob_basedir/client/release", - "$glob_basedir/client/debug"); - $exe_mysqld= mtr_exe_exists ("$path_client_bindir/mysqld-max-nt", - "$path_client_bindir/mysqld-max", - "$path_client_bindir/mysqld-nt", - "$path_client_bindir/mysqld", - "$path_client_bindir/mysqld-max", - "$path_client_bindir/mysqld-debug", - "$glob_basedir/sql/release/mysqld", - "$glob_basedir/sql/debug/mysqld"); - $path_language= mtr_path_exists("$glob_basedir/share/english/", - "$glob_basedir/sql/share/english/"); - $path_charsetsdir= mtr_path_exists("$glob_basedir/share/charsets", - "$glob_basedir/sql/share/charsets"); - $exe_my_print_defaults= - mtr_exe_exists("$path_client_bindir/my_print_defaults", - "$glob_basedir/extra/release/my_print_defaults", - "$glob_basedir/extra/debug/my_print_defaults"); - $exe_perror= - mtr_exe_exists("$path_client_bindir/perror", - "$glob_basedir/extra/release/perror", - "$glob_basedir/extra/debug/perror"); - } - else - { - $path_client_bindir= mtr_path_exists("$glob_basedir/client"); - $exe_mysqld= mtr_exe_exists ("$glob_basedir/sql/mysqld"); - $exe_mysqlslap= mtr_exe_exists ("$path_client_bindir/mysqlslap"); - $path_language= mtr_path_exists("$glob_basedir/sql/share/english/"); - $path_charsetsdir= mtr_path_exists("$glob_basedir/sql/share/charsets"); - - $exe_im= mtr_exe_exists( - "$glob_basedir/server-tools/instance-manager/mysqlmanager"); - $exe_my_print_defaults= - mtr_exe_exists("$glob_basedir/extra/my_print_defaults"); - $exe_perror= - mtr_exe_exists("$glob_basedir/extra/perror"); - } + # 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"); + + # 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"); - 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 - { - $exe_mysqltest= mtr_exe_exists("$path_client_bindir/mysqltest"); - $exe_mysql_client_test= - mtr_exe_exists("$glob_basedir/tests/mysql_client_test", - "$glob_basedir/tests/release/mysql_client_test", - "$glob_basedir/tests/debug/mysql_client_test", - "$path_client_bindir/mysql_client_test", - "/usr/bin/false"); - } - $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"); - $exe_mysql_fix_system_tables= - mtr_script_exists("$glob_basedir/scripts/mysql_fix_privilege_tables", - "/usr/bin/false"); - $path_ndb_tools_dir= mtr_path_exists("$glob_basedir/storage/ndb/tools"); - $path_ndb_examples_dir= mtr_path_exists("$glob_basedir/storage/ndb/ndbapi-examples"); - $exe_ndb_example= mtr_file_exists("$path_ndb_examples_dir/ndbapi_simple/ndbapi_simple"); - $exe_ndb_mgm= "$glob_basedir/storage/ndb/src/mgmclient/ndb_mgm"; - $exe_ndb_waiter= "$glob_basedir/storage/ndb/tools/ndb_waiter"; - $exe_ndbd= "$glob_basedir/storage/ndb/src/kernel/ndbd"; - $exe_ndb_mgmd= "$glob_basedir/storage/ndb/src/mgmsrv/ndb_mgmd"; - $lib_udf_example= - mtr_file_exists("$glob_basedir/sql/.libs/udf_example.so", - "$glob_basedir/sql/release/udf_example.dll", - "$glob_basedir/sql/debug/udf_example.dll"); + $exe_master_mysqld= $exe_master_mysqld || $exe_mysqld; + $exe_slave_mysqld= $exe_slave_mysqld || $exe_mysqld; + + # 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_skip_ndbcluster) + { + # Look for ndb tols and binaries + 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("$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 + $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 { - $path_client_bindir= mtr_path_exists("$glob_basedir/bin"); - $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"); - $exe_mysql_fix_system_tables= - mtr_script_exists("$path_client_bindir/mysql_fix_privilege_tables", - "$glob_basedir/scripts/mysql_fix_privilege_tables", - "/usr/bin/false"); - $exe_my_print_defaults= - mtr_exe_exists("$path_client_bindir/my_print_defaults"); - $exe_perror= - mtr_exe_exists("$path_client_bindir/perror"); - - $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"); - - 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"); - $exe_mysqlslap= mtr_exe_exists("$path_client_bindir/mysqlslap"); - } - $exe_im= mtr_exe_exists("$glob_basedir/libexec/mysqlmanager", - "$glob_basedir/bin/mysqlmanager"); - 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", - "$glob_basedir/tests/release/mysql_client_test", - "$glob_basedir/tests/debug/mysql_client_test", - "/usr/bin/false"); # FIXME temporary - } - $path_ndb_tools_dir= "$glob_basedir/bin"; - $path_ndb_examples_dir= "$glob_basedir/ndbapi-examples"; - $exe_ndb_mgm= "$glob_basedir/bin/ndb_mgm"; - $exe_ndb_waiter= "$glob_basedir/bin/ndb_waiter"; - $exe_ndbd= "$glob_basedir/bin/ndbd"; - $exe_ndb_mgmd= "$glob_basedir/bin/ndb_mgmd"; - } - $exe_master_mysqld= $exe_master_mysqld || $exe_mysqld; - $exe_slave_mysqld= $exe_slave_mysqld || $exe_mysqld; + } - $file_ndb_testrun_log= "$opt_vardir/log/ndb_testrun.log"; + # 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"); + } } @@ -1422,8 +1464,7 @@ sub environment_setup () { $ENV{'SLAVE_MYPORT'}= $slave->[0]->{'port'}; $ENV{'SLAVE_MYPORT1'}= $slave->[1]->{'port'}; $ENV{'SLAVE_MYPORT2'}= $slave->[2]->{'port'}; -# $ENV{'MYSQL_TCP_PORT'}= '@MYSQL_TCP_PORT@'; # FIXME - $ENV{'MYSQL_TCP_PORT'}= 3306; + $ENV{'MYSQL_TCP_PORT'}= $default_mysqld_port; $ENV{MTR_BUILD_THREAD}= 0 unless $ENV{MTR_BUILD_THREAD}; # Set if not set @@ -1440,12 +1481,12 @@ sub environment_setup () { $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'}= $file_ndb_testrun_log; + $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'}= $file_ndb_testrun_log; + $ENV{'NDB_EXAMPLES_OUTPUT'}= $path_ndb_testrun_log; # ---------------------------------------------------- # Setup env for IM @@ -1475,7 +1516,7 @@ sub environment_setup () { if ( $opt_debug ) { $cmdline_mysqlcheck .= - " --debug=d:t:A,$opt_vardir_trace/log/mysqlcheck.trace"; + " --debug=d:t:A,$path_vardir_trace/log/mysqlcheck.trace"; } $ENV{'MYSQL_CHECK'}= $cmdline_mysqlcheck; @@ -1488,9 +1529,9 @@ sub environment_setup () { if ( $opt_debug ) { $cmdline_mysqldump .= - " --debug=d:t:A,$opt_vardir_trace/log/mysqldump-master.trace"; + " --debug=d:t:A,$path_vardir_trace/log/mysqldump-master.trace"; $cmdline_mysqldumpslave .= - " --debug=d:t:A,$opt_vardir_trace/log/mysqldump-slave.trace"; + " --debug=d:t:A,$path_vardir_trace/log/mysqldump-slave.trace"; } $ENV{'MYSQL_DUMP'}= $cmdline_mysqldump; $ENV{'MYSQL_DUMP_SLAVE'}= $cmdline_mysqldumpslave; @@ -1510,7 +1551,7 @@ sub environment_setup () { if ( $opt_debug ) { $cmdline_mysqlslap .= - " --debug=d:t:A,$opt_vardir_trace/log/mysqlslap.trace"; + " --debug=d:t:A,$path_vardir_trace/log/mysqlslap.trace"; } $ENV{'MYSQL_SLAP'}= $cmdline_mysqlslap; } @@ -1526,7 +1567,7 @@ sub environment_setup () { if ( $opt_debug ) { $cmdline_mysqlimport .= - " --debug=d:t:A,$opt_vardir_trace/log/mysqlimport.trace"; + " --debug=d:t:A,$path_vardir_trace/log/mysqlimport.trace"; } $ENV{'MYSQL_IMPORT'}= $cmdline_mysqlimport; @@ -1542,7 +1583,7 @@ sub environment_setup () { if ( $opt_debug ) { $cmdline_mysqlshow .= - " --debug=d:t:A,$opt_vardir_trace/log/mysqlshow.trace"; + " --debug=d:t:A,$path_vardir_trace/log/mysqlshow.trace"; } $ENV{'MYSQL_SHOW'}= $cmdline_mysqlshow; @@ -1551,13 +1592,16 @@ sub environment_setup () { # ---------------------------------------------------- my $cmdline_mysqlbinlog= "$exe_mysqlbinlog" . - " --no-defaults --local-load=$opt_tmpdir" . - " --character-sets-dir=$path_charsetsdir"; + " --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,$opt_vardir_trace/log/mysqlbinlog.trace"; + " --debug=d:t:A,$path_vardir_trace/log/mysqlbinlog.trace"; } $ENV{'MYSQL_BINLOG'}= $cmdline_mysqlbinlog; @@ -1578,13 +1622,16 @@ sub environment_setup () { my $cmdline_mysql_client_test= "$exe_mysql_client_test --no-defaults --testcase --user=root --silent " . "--port=$master->[0]->{'port'} " . - "--vardir=$opt_vardir " . "--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,$opt_vardir_trace/log/mysql_client_test.trace"; + " --debug=d:t:A,$path_vardir_trace/log/mysql_client_test.trace"; } if ( $glob_use_embedded_server ) @@ -1601,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'}"; @@ -1641,11 +1689,20 @@ sub environment_setup () { 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"; - print "Using NDBCLUSTER_PORT_SLAVE = $ENV{NDBCLUSTER_PORT_SLAVE}\n"; - 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"; + 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"; + } } } @@ -1694,11 +1751,7 @@ sub kill_running_server () { # 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(); - } } @@ -1801,8 +1854,8 @@ sub check_running_as_root () { } - -sub check_ssl_support () { +sub check_ssl_support ($) { + my $mysqld_variables= shift; if ($opt_skip_ssl || $opt_extern) { @@ -1812,13 +1865,7 @@ sub check_ssl_support () { return; } - # check ssl support by testing using a switch - # that is only available in that case - if ( mtr_run($exe_mysqld, - ["--no-defaults", - "--ssl", - "--help"], - "", "/dev/null", "/dev/null", "") != 0 ) + if ( ! $mysqld_variables->{'ssl'} ) { if ( $opt_ssl) { @@ -1835,17 +1882,12 @@ sub check_ssl_support () { } -sub check_debug_support () { +sub check_debug_support ($) { + my $mysqld_variables= shift; - # check debug support by testing using a switch - # that is only available in that case - if ( mtr_run($exe_mysqld, - ["--no-defaults", - "--debug", - "--help"], - "", "/dev/null", "/dev/null", "") != 0 ) + if ( $mysqld_variables->{'debug'} ) { - # mtr_report("Binaries are not debug compiled"); + #mtr_report("Binaries are not debug compiled"); $debug_compiled_binaries= 0; if ( $opt_debug ) @@ -1858,32 +1900,14 @@ 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 # ############################################################################## -sub check_ndbcluster_support () { +sub check_ndbcluster_support ($) { + my $mysqld_variables= shift; if ($opt_skip_ndbcluster) { @@ -1892,13 +1916,7 @@ sub check_ndbcluster_support () { return; } - # check ndbcluster support by runnning mysqld 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 ) + if ( ! $mysqld_variables->{'ndb-connectstring'} ) { mtr_report("Skipping ndbcluster, mysqld not compiled with ndbcluster"); $opt_skip_ndbcluster= 1; @@ -1964,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"; @@ -2085,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"); @@ -2186,8 +2212,6 @@ sub run_benchmarks ($) { chdir($glob_mysql_bench_dir) or mtr_error("Couldn't chdir to '$glob_mysql_bench_dir': $!"); - # FIXME write shorter.... - if ( ! $benchmark ) { mtr_add_arg($args, "--log"); @@ -2219,9 +2243,6 @@ sub run_benchmarks ($) { # ############################################################################## -# FIXME how to specify several suites to run? Comma separated list? - - sub run_suite () { my ($suite, $tests)= @_; @@ -2316,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; @@ -2419,10 +2441,10 @@ sub install_db ($$) { if ( $opt_debug ) { mtr_add_arg($args, "--debug=d:t:i:A,%s/log/bootstrap_%s.trace", - $opt_vardir_trace, $type); + $path_vardir_trace, $type); } - if ( ! $opt_netware ) + if ( ! $glob_netware ) { mtr_add_arg($args, "--language=%s", $path_language); mtr_add_arg($args, "--character-sets-dir=%s", $path_charsetsdir); @@ -2612,12 +2634,10 @@ sub do_before_run_mysqltest($) unlink("suite/$opt_suite/r/$tname.reject"); } - -# MASV cleanup... 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($file_ndb_testrun_log,"CURRENT TEST $tname\n"); + mtr_tofile($path_ndb_testrun_log,"CURRENT TEST $tname\n"); mtr_tofile($master->[0]->{'path_myerr'},"CURRENT_TEST: $tname\n"); if ( $master->[1]->{'pid'} ) @@ -2631,12 +2651,11 @@ sub do_after_run_mysqltest($) my $tinfo= shift; my $tname= $tinfo->{'name'}; - #MASV cleanup - # Save info from this testcase run to mysqltest.log - my $testcase_log= mtr_fromfile($path_timefile) if -f $path_timefile; - mtr_tofile($path_mysqltest_log,"CURRENT TEST $tname\n"); - mtr_tofile($path_mysqltest_log, $testcase_log); - } + # 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"); +} ############################################################################## @@ -2728,7 +2747,10 @@ sub run_testcase ($) { if ( ! $glob_use_running_server and $tinfo->{'component_id'} eq 'im' ) { - mtr_im_stop($instance_manager, $tinfo->{'name'}); + unless ( mtr_im_stop($instance_manager, $tinfo->{'name'}) ) + { + mtr_error("Failed to stop Instance Manager.") + } } } @@ -2951,7 +2973,11 @@ sub mysqld_arguments ($$$$$) { 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--log-bin-trust-function-creators", $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); @@ -3000,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); + } } } @@ -3018,7 +3047,7 @@ sub mysqld_arguments ($$$$$) { $opt_vardir, $sidx); # FIXME use own dir for binlogs mtr_add_arg($args, "%s--log-slave-updates", $prefix); } - # FIXME option duplicated for slave + mtr_add_arg($args, "%s--log=%s", $prefix, $slave->[$idx]->{'path_mylog'}); mtr_add_arg($args, "%s--master-retry-count=10", $prefix); @@ -3075,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 @@ -3084,12 +3116,12 @@ 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_trace, $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_trace, $sidx); + $prefix, $path_vardir_trace, $sidx); } } @@ -3097,7 +3129,6 @@ sub mysqld_arguments ($$$$$) { 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); - mtr_add_arg($args, "%s--log-bin-trust-function-creators", $prefix); if ( $opt_ssl_supported ) { @@ -3275,8 +3306,14 @@ sub stop_all_servers () { print "Stopping All Servers\n"; - print "Shutting-down Instance Manager\n"; - mtr_im_stop($instance_manager, "stop_all_servers"); + if ( ! $opt_skip_im ) + { + print "Shutting-down Instance Manager\n"; + unless (mtr_im_stop($instance_manager, "stop_all_servers")) + { + mtr_error("Failed to stop Instance Manager.") + } + } my %admin_pids; # hash of admin processes that requests shutdown my @kill_pids; # list of processes to shutdown/kill @@ -3627,6 +3664,11 @@ sub run_testcase_start_servers($) { return; } + # ------------------------------------------------------- + # Init variables that can change between server starts + # ------------------------------------------------------- + $ENV{'TZ'}= $tinfo->{'timezone'}; + if ( $tinfo->{'component_id'} eq 'mysqld' ) { if ( ! $opt_skip_ndbcluster and @@ -3650,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"); @@ -3681,7 +3728,14 @@ sub run_testcase_start_servers($) { im_create_defaults_file($instance_manager); - mtr_im_start($instance_manager, $tinfo->{im_opts}); + 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."); + return; + } } # ---------------------------------------------------------------------- @@ -3885,7 +3939,7 @@ sub run_mysqltest ($) { if ( $opt_debug ) { mtr_add_arg($args, "--debug=d:t:A,%s/log/mysqltest.trace", - $opt_vardir_trace); + $path_vardir_trace); } if ( $opt_ssl_supported ) @@ -3982,11 +4036,6 @@ sub run_mysqltest ($) { } } - # ------------------------------------------------------- - # Init variables that change for each testcase - # ------------------------------------------------------- - $ENV{'TZ'}= $tinfo->{'timezone'}; - my $res = mtr_run_test($exe,$args,"","",$path_timefile,""); if ( $opt_check_testcases ) @@ -4216,9 +4265,7 @@ sub usage ($) { print STDERR <<HERE; -mysql-test-run [ OPTIONS ] [ TESTCASE ] - -FIXME when is TESTCASE arg used or not?! +$0 [ OPTIONS ] [ TESTCASE ] Options to control what engine/variation to run diff --git a/mysql-test/r/binlog_stm_mix_innodb_myisam.result b/mysql-test/r/binlog_stm_mix_innodb_myisam.result index 95b6eb953a2..8ab6840d441 100644 --- a/mysql-test/r/binlog_stm_mix_innodb_myisam.result +++ b/mysql-test/r/binlog_stm_mix_innodb_myisam.result @@ -195,7 +195,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/check.result b/mysql-test/r/check.result index 4c565f4f1b1..60806e7393e 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/drop.result b/mysql-test/r/drop.result index 53b2ea8c84a..24f1b654733 100644 --- a/mysql-test/r/drop.result +++ b/mysql-test/r/drop.result @@ -79,9 +79,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 a7f5e5e8fec..7eb7fd16edb 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: Can't execute the query because you have a conflicting read lock - 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 @@ -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 e4a9dfcfff1..56f8acab896 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 @@ -45,7 +45,7 @@ flush tables with read lock; show master status; File Position Binlog_Do_DB Binlog_Ignore_DB master-bin.000001 102 - commit; +commit; show master status; File Position Binlog_Do_DB Binlog_Ignore_DB master-bin.000001 102 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/func_misc.result b/mysql-test/r/func_misc.result index f383a717a85..3ebf071a5bb 100644 --- a/mysql-test/r/func_misc.result +++ b/mysql-test/r/func_misc.result @@ -63,7 +63,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 79ba073a856..4b089c1d5e9 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_myisam.result b/mysql-test/r/handler_myisam.result index 8edf191b1b0..beb1a40c318 100644 --- a/mysql-test/r/handler_myisam.result +++ b/mysql-test/r/handler_myisam.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/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/multi_update.result b/mysql-test/r/multi_update.result index 3ed0222fcdb..6d8e5dfdf2c 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 ce4182945a9..628ba7f9c88 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -154,6 +154,19 @@ mysqltest: At line 1: End of line junk detected: "sleep 7 " 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: 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 MySQL "MySQL" MySQL: The world''s most popular open source database @@ -239,7 +252,7 @@ mysqltest: At line 1: Missing assignment operator in let 1 # Execute: echo $success ; 1 -mysqltest: At line 1: Missing file name in source +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 "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 @@ -332,7 +345,7 @@ Counter is greater than 0, (counter=10) Counter is not 0, (counter=0) 1 Testing while with not -mysqltest: In included file "./include/mysqltest_while.inc": At line 64: Nesting too deeply +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" @@ -371,17 +384,15 @@ 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: Syntax error in connect - expected '(' found 'mysqltest: At line 1: Missing connection host -mysqltest: At line 1: Missing connection host -mysqltest: At line 1: Missing connection user -mysqltest: At line 1: Missing connection user -mysqltest: At line 1: Missing connection password -mysqltest: At line 1: Missing connection db -mysqltest: At line 1: Could not open connection 'con2': 1049 Unknown database 'illegal_db' +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 - increase MAX_CONS in mysqltest.c +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); @@ -449,7 +460,6 @@ 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 -End of 5.0 tests select "b" as col1, "c" as col2; col1 col2 b c @@ -492,4 +502,3 @@ mysqltest: At line 1: Max delimiter length(16) exceeded hello hello End of tests -End of 5.1 tests 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/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/rpl_err_ignoredtable.result b/mysql-test/r/rpl_err_ignoredtable.result index 27db9c0372c..f211d5d9a2f 100644 --- a/mysql-test/r/rpl_err_ignoredtable.result +++ b/mysql-test/r/rpl_err_ignoredtable.result @@ -26,7 +26,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_master_pos_wait.result b/mysql-test/r/rpl_master_pos_wait.result index 81d9043c8ce..951e944fddc 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 filtered Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: Note 1003 select 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/rpl_stm_000001.result b/mysql-test/r/rpl_stm_000001.result index ef4226e07b5..3b4cd05f640 100644 --- a/mysql-test/r/rpl_stm_000001.result +++ b/mysql-test/r/rpl_stm_000001.result @@ -44,7 +44,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/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/synchronization.result b/mysql-test/r/synchronization.result index 697eb064998..4543a829494 100644 --- a/mysql-test/r/synchronization.result +++ b/mysql-test/r/synchronization.result @@ -1,6 +1,6 @@ drop table if exists t1; 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 @@ -8,7 +8,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 @@ -16,7 +16,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 @@ -24,7 +24,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 @@ -32,7 +32,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 @@ -40,7 +40,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 @@ -48,7 +48,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 @@ -56,7 +56,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 @@ -64,7 +64,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 @@ -72,7 +72,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 @@ -80,7 +80,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 @@ -88,7 +88,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 @@ -96,7 +96,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 @@ -104,7 +104,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 @@ -112,7 +112,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 @@ -120,7 +120,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 @@ -128,7 +128,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 @@ -136,7 +136,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 @@ -144,7 +144,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 @@ -152,7 +152,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/t/connect.test b/mysql-test/t/connect.test index 5ee3d64e56f..2e66c24d877 100644 --- a/mysql-test/t/connect.test +++ b/mysql-test/t/connect.test @@ -59,6 +59,7 @@ flush privileges; connect (con10,localhost,test,gambling2,); connect (con5,localhost,test,gambling2,mysql); +connection con5; set password=""; --error 1372 set password='gambling3'; diff --git a/mysql-test/t/init_file.test b/mysql-test/t/init_file.test index 6b5e032fd99..31a6ef5a541 100644 --- a/mysql-test/t/init_file.test +++ b/mysql-test/t/init_file.test @@ -4,7 +4,7 @@ # # See mysql-test/std_data/init_file.dat and # mysql-test/t/init_file-master.opt for the actual test -# +# --echo ok --echo end of 4.1 tests diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test index 2e7f7d96bf8..5b0df28981f 100644 --- a/mysql-test/t/mysqltest.test +++ b/mysql-test/t/mysqltest.test @@ -317,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 @@ -388,6 +387,67 @@ select 3 from t1 ; --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 @@ -610,6 +670,7 @@ echo $var3_var3; # Fix win paths --replace_result \\ / +# Source a nonexisting file --error 1 --exec echo "source non_existingFile;" | $MYSQL_TEST 2>&1 @@ -627,13 +688,16 @@ echo $var3_var3; # Test execution of source in a while loop +--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 include/sourced.inc + --source $MYSQLTEST_VARDIR/tmp/sourced.inc eval SELECT '$outer = outer loop variable before dec' AS ""; dec $outer; @@ -661,11 +725,12 @@ let $num= 9; while ($num) { SELECT 'In loop' AS ""; - --source include/sourced1.inc + --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 @@ -817,10 +882,150 @@ while (!$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 \\ / +--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 @@ -925,12 +1130,6 @@ select "a" as col1, "c" as col2; --error 1 --exec echo "connect (con2,);" | $MYSQL_TEST 2>&1 --error 1 ---exec echo "connect (con2,localhost);" | $MYSQL_TEST 2>&1 ---error 1 ---exec echo "connect (con2, localhost, root);" | $MYSQL_TEST 2>&1 ---error 1 ---exec echo "connect (con2, localhost, root,);" | $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 @@ -938,13 +1137,15 @@ select "a" as col1, "c" as col2; --exec echo "connect (con1,localhost,root,,,,,SMTP POP);" | $MYSQL_TEST 2>&1 # Repeat connect/disconnect ---system echo "let \$i=100;" > $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 +--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 @@ -1149,8 +1350,6 @@ query sleep; --error 1065 query ; ---echo End of 5.0 tests - # test for replace_regex --replace_regex /at/b/ select "at" as col1, "c" as col2; @@ -1289,4 +1488,3 @@ EOF --echo End of tests ---echo End of 5.1 tests diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index e86420f4364..14788d9000f 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -354,14 +354,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 - -set @v=0; execute stmt using @v; +set @v=0; --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 c0b81796731..4c0f411c758 100644 --- a/mysql-test/t/ps_1general.test +++ b/mysql-test/t/ps_1general.test @@ -316,8 +316,8 @@ prepare stmt4 from ' show table status from test like ''t9%'' '; --replace_column 8 # 12 # 13 # 14 # # 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 4c48b4d151f..b25facdb418 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/sp-error.test b/mysql-test/t/sp-error.test index e829a71c45a..2c85c6ae643 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; |