diff options
150 files changed, 13266 insertions, 4811 deletions
diff --git a/.bzrignore b/.bzrignore index c4ae17ed95e..348093af390 100644 --- a/.bzrignore +++ b/.bzrignore @@ -1064,3 +1064,6 @@ vio/test-ssl vio/test-sslclient vio/test-sslserver vio/viotest-ssl +include/check_abi +include/mysql_h.ic +mysql-test/r/blackhole.log diff --git a/BitKeeper/etc/collapsed b/BitKeeper/etc/collapsed new file mode 100644 index 00000000000..60be7fa5dc6 --- /dev/null +++ b/BitKeeper/etc/collapsed @@ -0,0 +1 @@ +452a92d0-31-8wSzSfZi165fcGcXPA diff --git a/Makefile.am b/Makefile.am index 2aefbd05283..8d0746e6a64 100644 --- a/Makefile.am +++ b/Makefile.am @@ -124,3 +124,8 @@ test-force-pl: cd mysql-test; \ ./mysql-test-run.pl --force && \ ./mysql-test-run.pl --ps-protocol --force + +test-force-pl-mem: + cd mysql-test; \ + ./mysql-test-run.pl --force --mem && \ + ./mysql-test-run.pl --ps-protocol --force --mem diff --git a/client/Makefile.am b/client/Makefile.am index c0569d5fa6f..fe150defe03 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -30,7 +30,9 @@ mysql_SOURCES = mysql.cc readline.cc sql_string.cc completion_hash.cc mysqladmin_SOURCES = mysqladmin.cc mysql_LDADD = @readline_link@ @TERMCAP_LIB@ $(LDADD) $(CXXLDFLAGS) mysqlbinlog_LDADD = $(LDADD) $(CXXLDFLAGS) -mysqltest_SOURCES= mysqltest.c $(top_srcdir)/mysys/my_getsystime.c +mysqltest_SOURCES= mysqltest.c \ + $(top_srcdir)/mysys/my_getsystime.c \ + $(top_srcdir)/mysys/my_copy.c mysqltest_LDADD = $(top_builddir)/regex/libregex.a $(LDADD) mysqlbinlog_SOURCES = mysqlbinlog.cc ../mysys/mf_tempdir.c mysqlmanagerc_SOURCES = mysqlmanagerc.c diff --git a/client/mysql.cc b/client/mysql.cc index c9ebf7d09f5..d8810ba3c28 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -372,6 +372,21 @@ int main(int argc,char *argv[]) else status.add_to_history=1; status.exit_status=1; + + { + /* + The file descriptor-layer may be out-of-sync with the file-number layer, + so we make sure that "stdout" is really open. If its file is closed then + explicitly close the FD layer. + */ + int stdout_fileno_copy; + stdout_fileno_copy= dup(fileno(stdout)); /* Okay if fileno fails. */ + if (stdout_fileno_copy == -1) + fclose(stdout); + else + close(stdout_fileno_copy); /* Clean up dup(). */ + } + load_defaults("my",load_default_groups,&argc,&argv); defaults_argv=argv; if (get_options(argc, (char **) argv)) @@ -1997,6 +2012,8 @@ com_go(String *buffer,char *line __attribute__((unused))) (long) mysql_num_rows(result), (long) mysql_num_rows(result) == 1 ? "row" : "rows"); end_pager(); + if (mysql_errno(&mysql)) + error= put_error(&mysql); } } else if (mysql_affected_rows(&mysql) == ~(ulonglong) 0) diff --git a/client/mysqldump.c b/client/mysqldump.c index 7f495ccdafb..2d2fe439f76 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -785,6 +785,7 @@ static void DBerror(MYSQL *mysql, const char *when) DBUG_ENTER("DBerror"); fprintf(stderr, "%s: Got error: %d: %s %s\n", my_progname, mysql_errno(mysql), mysql_error(mysql), when); + fflush(stderr); safe_exit(EX_MYSQLERR); DBUG_VOID_RETURN; } /* DBerror */ @@ -2416,7 +2417,7 @@ static int do_reset_master(MYSQL *mysql_con) } -static int start_transaction(MYSQL *mysql_con, my_bool consistent_read_now) +static int start_transaction(MYSQL *mysql_con) { /* We use BEGIN for old servers. --single-transaction --master-data will fail @@ -2431,10 +2432,8 @@ static int start_transaction(MYSQL *mysql_con, my_bool consistent_read_now) "SET SESSION TRANSACTION ISOLATION " "LEVEL REPEATABLE READ") || mysql_query_with_error_report(mysql_con, 0, - consistent_read_now ? "START TRANSACTION " - "WITH CONSISTENT SNAPSHOT" : - "BEGIN")); + "/*!40100 WITH CONSISTENT SNAPSHOT */")); } @@ -2661,7 +2660,7 @@ int main(int argc, char **argv) if ((opt_lock_all_tables || opt_master_data) && do_flush_tables_read_lock(sock)) goto err; - if (opt_single_transaction && start_transaction(sock, test(opt_master_data))) + if (opt_single_transaction && start_transaction(sock)) goto err; if (opt_delete_master_logs && do_reset_master(sock)) goto err; diff --git a/client/mysqltest.c b/client/mysqltest.c index ad0f9f857bb..7c1ce19283f 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -14,35 +14,24 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* mysqltest test tool - * See the manual for more information - * TODO: document better how mysqltest works - * - * Written by: - * Sasha Pachev <sasha@mysql.com> - * Matt Wagner <matt@mysql.com> - * Monty - * Jani - **/ - -/********************************************************************** - TODO: +/* + mysqltest -- Do comparison line by line, instead of doing a full comparison of - the text file. This will save space as we don't need to keep many - results in memory. It will also make it possible to do simple - 'comparison' fixes like accepting the result even if a float differed - in the last decimals. + Tool used for executing a .test file -- Don't buffer lines from the test that you don't expect to need - again. + See the "MySQL Test framework manual" for more information + http://dev.mysql.com/doc/mysqltest/en/index.html -- Change 'read_line' to be faster by using the readline.cc code; - We can do better than calling feof() for each character! + Please keep the test framework tools identical in all versions! -**********************************************************************/ + Written by: + Sasha Pachev <sasha@mysql.com> + Matt Wagner <matt@mysql.com> + Monty + Jani +*/ -#define MTEST_VERSION "2.5" +#define MTEST_VERSION "3.0" #include <my_global.h> #include <mysql_embed.h> @@ -51,18 +40,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) @@ -70,166 +59,144 @@ # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) # endif #endif -#define MAX_QUERY 131072 -#define MAX_VAR_NAME 256 -#define MAX_COLUMNS 256 -#define PAD_SIZE 128 -#define MAX_CONS 128 -#define MAX_INCLUDE_DEPTH 16 -#define LAZY_GUESS_BUF_SIZE 8192 -#define INIT_Q_LINES 1024 -#define MIN_VAR_ALLOC 32 -#define BLOCK_STACK_DEPTH 32 -#define MAX_EXPECTED_ERRORS 10 -#define QUERY_SEND 1 -#define QUERY_REAP 2 -#ifndef MYSQL_MANAGER_PORT -#define MYSQL_MANAGER_PORT 23546 -#endif -#define MAX_SERVER_ARGS 64 -/* - Sometimes in a test the client starts before - the server - to solve the problem, we try again - after some sleep if connection fails the first - time -*/ -#define CON_RETRY_SLEEP 2 -#define MAX_CON_TRIES 5 +#define MAX_VAR_NAME_LENGTH 256 +#define MAX_COLUMNS 256 +#define MAX_EMBEDDED_SERVER_ARGS 64 +#define MAX_DELIMITER_LENGTH 16 + +/* 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 +}; -#define SLAVE_POLL_INTERVAL 300000 /* 0.3 of a sec */ -#define DEFAULT_DELIMITER ";" -#define MAX_DELIMITER 16 +static int record= 0, opt_sleep= -1; +static char *db= 0, *pass= 0; +const char *user= 0, *host= 0, *unix_sock= 0, *opt_basedir= "./"; +const char *opt_include= 0; +static int port= 0; +static int opt_max_connect_retries; +static my_bool opt_compress= 0, silent= 0, verbose= 0; +static my_bool tty_password= 0; +static my_bool opt_mark_progress= 0; +static my_bool ps_protocol= 0, ps_protocol_enabled= 0; +static my_bool sp_protocol= 0, sp_protocol_enabled= 0; +static my_bool view_protocol= 0, view_protocol_enabled= 0; +static my_bool cursor_protocol= 0, cursor_protocol_enabled= 0; +static my_bool parsing_disabled= 0; +static my_bool display_result_vertically= FALSE, display_metadata= FALSE; +static my_bool disable_query_log= 0, disable_result_log= 0; +static my_bool disable_warnings= 0, disable_ps_warnings= 0; +static my_bool disable_info= 1; +static my_bool abort_on_error= 1; -#define RESULT_OK 0 -#define RESULT_CONTENT_MISMATCH 1 -#define RESULT_LENGTH_MISMATCH 2 +static char **default_argv; +static const char *load_default_groups[]= { "mysqltest", "client", 0 }; +static char line_buffer[MAX_DELIMITER_LENGTH], *line_buffer_pos= line_buffer; -enum {OPT_MANAGER_USER=256,OPT_MANAGER_HOST,OPT_MANAGER_PASSWD, - OPT_MANAGER_PORT,OPT_MANAGER_WAIT_TIMEOUT, OPT_SKIP_SAFEMALLOC, - OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA, OPT_SSL_CAPATH, - OPT_SSL_CIPHER,OPT_PS_PROTOCOL}; +static uint start_lineno= 0; /* Start line of current command */ -/* ************************************************************************ */ -/* - 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 +static char delimiter[MAX_DELIMITER_LENGTH]= ";"; +static uint delimiter_length= 1; + +static char TMPDIR[FN_REFLEN]; + +/* Block stack */ +enum block_cmd { + cmd_none, + cmd_if, + cmd_while }; -typedef struct +struct st_block { - enum match_err_type type; - union - { - uint errnum; - char sqlstate[SQLSTATE_LENGTH+1]; /* \0 terminated string */ - } code; -} match_err; - -static match_err global_expected_errno[MAX_EXPECTED_ERRORS]; -static uint global_expected_errors; - -/* ************************************************************************ */ - -static int record = 0, opt_sleep=0; -static char *db = 0, *pass=0; -const char *user = 0, *host = 0, *unix_sock = 0, *opt_basedir="./"; -static int port = 0; -static my_bool opt_big_test= 0, opt_compress= 0, silent= 0, verbose = 0; -static my_bool tty_password= 0, ps_protocol= 0, ps_protocol_enabled= 0; -static uint start_lineno= 0, *lineno; -const char *manager_user="root",*manager_host=0; -char *manager_pass=0; -int manager_port=MYSQL_MANAGER_PORT; -int manager_wait_timeout=3; -MYSQL_MANAGER* manager=0; + int line; /* Start line of block */ + my_bool ok; /* Should block be executed */ + enum block_cmd cmd; /* Command owning the block */ +}; -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 struct st_block block_stack[32]; +static struct st_block *cur_block, *block_stack_end; -typedef struct +/* Open file stack */ +struct st_test_file { FILE* file; const char *file_name; -} test_file; + uint lineno; /* Current line in file */ +}; -static test_file file_stack[MAX_INCLUDE_DEPTH]; -static test_file* cur_file; -static test_file* file_stack_end; +static struct st_test_file file_stack[16]; +static struct st_test_file* cur_file; +static struct st_test_file* file_stack_end; -static uint lineno_stack[MAX_INCLUDE_DEPTH]; -static char TMPDIR[FN_REFLEN]; -static char delimiter[MAX_DELIMITER]= DEFAULT_DELIMITER; -static uint delimiter_length= 1; static CHARSET_INFO *charset_info= &my_charset_latin1; /* Default charset */ static const char *charset_name= "latin1"; /* Default character set name */ -static int embedded_server_arg_count=0; -static char *embedded_server_args[MAX_SERVER_ARGS]; +static 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; -static int got_end_timer= FALSE; static void timer_output(void); static ulonglong timer_now(void); -static my_regex_t ps_re; /* Holds precompiled re for valid PS statements */ -static void ps_init_re(void); -static int ps_match_re(char *); -static char *ps_eprint(int); -static void ps_free_reg(void); +static ulonglong progress_start= 0; -static const char *embedded_server_groups[] = { - "server", - "embedded", - "mysqltest_SERVER", - NullS -}; +/* Precompiled re's */ +static my_regex_t ps_re; /* the query can be run using PS protocol */ +static my_regex_t sp_re; /* the query can be run as a SP */ +static my_regex_t view_re; /* the query can be run as a view*/ + +static void init_re(void); +static int match_re(my_regex_t *, char *); +static void free_re(void); DYNAMIC_ARRAY q_lines; #include "sslopt-vars.h" -typedef struct -{ - char file[FN_REFLEN]; - ulong pos; -} MASTER_POS ; - -struct connection -{ - MYSQL mysql; - char *name; -}; - -typedef struct +struct { int read_lines,current_line; -} PARSER; +} parser; -MYSQL_RES *last_result=0; +struct +{ + 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_name= 0; -typedef struct +typedef struct st_var { char *name; int name_len; @@ -239,81 +206,71 @@ typedef struct int alloced_len; int int_dirty; /* do not update string if int is updated until first read */ int alloced; + char *env_s; } VAR; -#if defined(__NETWARE__) || defined(__WIN__) -/* - Netware doesn't proved environment variable substitution that is done - by the shell in unix environments. We do this in the following function: -*/ - -static char *subst_env_var(const char *cmd); -static FILE *my_popen(const char *cmd, const char *mode); -#define popen(A,B) my_popen((A),(B)) -#endif /* __NETWARE__ */ - -VAR var_reg[10]; /*Perl/shell-like variable registers */ -HASH var_hash; -my_bool disable_query_log=0, disable_result_log=0, disable_warnings=0; -my_bool disable_info= 1; /* By default off */ -my_bool abort_on_error= 1; +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_SERVER_START, Q_SERVER_STOP,Q_REQUIRE_MANAGER, -Q_WAIT_FOR_SLAVE_TO_STOP, -Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS, -Q_ENABLE_INFO, Q_DISABLE_INFO, -Q_ENABLE_METADATA, Q_DISABLE_METADATA, -Q_EXEC, Q_DELIMITER, -Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR, -Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS, -Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, -Q_START_TIMER, Q_END_TIMER, -Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL, -Q_EXIT, -Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT, -Q_IF, - -Q_UNKNOWN, /* Unknown command. */ -Q_COMMENT, /* Comments, ignored. */ -Q_COMMENT_WITH_COMMAND + 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_EXIT, + + 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; - 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[]= { @@ -348,16 +305,17 @@ const char *command_names[]= "enable_rpl_parse", "disable_rpl_parse", "eval_result", + /* Enable/disable that the _query_ is logged to result file */ "enable_query_log", "disable_query_log", + /* Enable/disable that the _result_ from a query is logged to result file */ "enable_result_log", "disable_result_log", - "server_start", - "server_stop", - "require_manager", "wait_for_slave_to_stop", "enable_warnings", "disable_warnings", + "enable_ps_warnings", + "disable_ps_warnings", "enable_info", "disable_info", "enable_metadata", @@ -375,80 +333,124 @@ const char *command_names[]= "character_set", "disable_ps_protocol", "enable_ps_protocol", - "exit", "disable_reconnect", "enable_reconnect", "if", + "disable_parsing", + "enable_parsing", + "replace_regex", + "remove_file", + "file_exists", + "write_file", + "copy_file", + "perl", + "die", + /* Don't execute any more commands, compare result */ + "exit", 0 }; -/* Block stack */ -typedef struct + +/* + The list of error codes to --error are stored in an internal array of + structs. This struct can hold numeric SQL error codes, error names or + SQLSTATE codes as strings. The element next to the last active element + in the list is set to type ERR_EMPTY. When an SQL statement returns an + error, we use this list to check if this is an expected error. +*/ +enum match_err_type { - int line; /* Start line of block */ - my_bool ok; /* Should block be executed */ - enum enum_commands cmd; /* Command owning the block */ -} BLOCK; -static BLOCK block_stack[BLOCK_STACK_DEPTH]; -static BLOCK *cur_block, *block_stack_end; + ERR_EMPTY= 0, + ERR_ERRNO, + ERR_SQLSTATE +}; -TYPELIB command_typelib= {array_elements(command_names),"", - command_names, 0}; +struct st_match_err +{ + enum match_err_type type; + union + { + uint errnum; + char sqlstate[SQLSTATE_LENGTH+1]; /* \0 terminated string */ + } code; +}; -DYNAMIC_STRING ds_res; -static void die(const char *fmt, ...); -static void init_var_hash(); -static VAR* var_from_env(const char *, const char *); -static byte* get_var_key(const byte* rec, uint* len, my_bool t); -static VAR* var_init(VAR* v, const char *name, int name_len, const char *val, - int val_len); +struct st_expected_errors +{ + struct st_match_err err[10]; + uint count; +}; +static struct st_expected_errors saved_expected_errors; -static void var_free(void* v); +struct st_command +{ + char *query, *query_buf,*first_argument,*last_argument,*end; + int first_word_len, query_len; + my_bool abort_on_error; + struct st_expected_errors expected_errors; + char require_file[FN_REFLEN]; + enum enum_commands type; +}; -int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname); -void reject_dump(const char *record_file, char *buf, int size); +TYPELIB command_typelib= {array_elements(command_names),"", + command_names, 0}; -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); +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(char *buf, int size); +void dump_result_to_log_file(char *buf, int size); +void dump_warning_messages(); +void dump_progress(); + +void do_eval(DYNAMIC_STRING *query_eval, const char *query, + const char *query_end, my_bool pass_through_escape_chars); +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); -uint replace_strings(struct st_replace *rep, my_string *start, - uint *max_length, const char *from); +/* 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 int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name); -void free_pointer_array(POINTER_ARRAY *pa); -static int initialize_replace_buffer(void); -static void free_replace_buffer(void); -static void do_eval(DYNAMIC_STRING *query_eval, const char *query); -void str_to_file(const char *fname, char *str, int size); -int do_server_op(struct st_query *q,const char *op); -struct st_replace *glob_replace; -static char *out_buff; -static uint out_length; -static int eval_result = 0; +/* For replace_regex */ +void do_get_replace_regex(struct st_command *command); +void free_replace_regex(); + -/* 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 @@ -457,42 +459,64 @@ void mysql_disable_rpl_parse(MYSQL* mysql __attribute__((unused))) {} int mysql_rpl_parse_enabled(MYSQL* mysql __attribute__((unused))) { return 1; } my_bool mysql_rpl_probe(MYSQL *mysql __attribute__((unused))) { return 1; } #endif -static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val, - int len); -static int handle_no_error(struct st_query *q); -static void do_eval(DYNAMIC_STRING* query_eval, const char *query) +void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val, + int len); +void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val); +void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val); + +void handle_error(struct st_command*, + unsigned int err_errno, const char *err_error, + const char *err_sqlstate, DYNAMIC_STRING *ds); +void handle_no_error(struct st_command*); + + + +void do_eval(DYNAMIC_STRING *query_eval, const char *query, + const char *query_end, my_bool pass_through_escape_chars) { const char *p; - register char c; + register char c, next_c; register int escaped = 0; - VAR* v; + VAR *v; DBUG_ENTER("do_eval"); - for (p= query; (c = *p); ++p) + for (p= query; (c= *p) && p < query_end; ++p) { switch(c) { case '$': if (escaped) { - escaped = 0; + escaped= 0; dynstr_append_mem(query_eval, p, 1); } else { - if (!(v = var_get(p, &p, 0, 0))) + if (!(v= var_get(p, &p, 0, 0))) die("Bad variable in eval"); dynstr_append_mem(query_eval, v->str_val, v->str_val_len); } break; case '\\': + next_c= *(p+1); if (escaped) { - escaped = 0; + escaped= 0; dynstr_append_mem(query_eval, p, 1); } + else if (next_c == '\\' || next_c == '$' || next_c == '"') + { + /* Set escaped only if next char is \, " or $ */ + escaped= 1; + + if (pass_through_escape_chars) + { + /* The escape char should be added to the output string. */ + dynstr_append_mem(query_eval, p, 1); + } + } else - escaped = 1; + dynstr_append_mem(query_eval, p, 1); break; default: dynstr_append_mem(query_eval, p, 1); @@ -503,28 +527,154 @@ static void do_eval(DYNAMIC_STRING* query_eval, const char *query) } -static void close_cons() +enum arg_type +{ + ARG_STRING, + ARG_REST +}; + +struct command_arg { + const char *argname; /* Name of argument */ + enum arg_type type; /* Type of argument */ + my_bool required; /* Argument required */ + DYNAMIC_STRING *ds; /* Storage for argument */ + const char *description; /* Description of the argument */ +}; + + +void check_command_args(struct st_command *command, + const char *arguments, + const struct command_arg *args, + int num_args, const char delimiter_arg) +{ + int i; + const char *ptr= arguments; + const char *start; + + DBUG_ENTER("check_command_args"); + DBUG_PRINT("enter", ("num_args: %d", num_args)); + for (i= 0; i < num_args; i++) + { + const struct command_arg *arg= &args[i]; + + switch (arg->type) + { + /* A string */ + case ARG_STRING: + /* Skip leading spaces */ + while (*ptr && *ptr == ' ') + ptr++; + start= ptr; + /* Find end of arg, terminated by "delimiter_arg" */ + while (*ptr && *ptr != delimiter_arg) + ptr++; + if (ptr > start) + { + init_dynamic_string(arg->ds, 0, ptr-start, 32); + do_eval(arg->ds, start, ptr, FALSE); + } + else + { + /* Empty string */ + init_dynamic_string(arg->ds, "", 0, 0); + } + command->last_argument= (char*)ptr; + + /* Step past the delimiter */ + if (*ptr && *ptr == delimiter_arg) + ptr++; + DBUG_PRINT("info", ("val: %s", arg->ds->str)); + break; + + /* Rest of line */ + case ARG_REST: + start= ptr; + init_dynamic_string(arg->ds, 0, command->query_len, 256); + do_eval(arg->ds, start, command->end, FALSE); + command->last_argument= command->end; + DBUG_PRINT("info", ("val: %s", arg->ds->str)); + break; + + default: + DBUG_ASSERT("Unknown argument type"); + break; + } + + /* Check required arg */ + if (arg->ds->length == 0 && arg->required) + die("Missing required argument '%s' to command '%.*s'", arg->argname, + command->first_word_len, command->query); + + } + DBUG_VOID_RETURN; +} + + +void handle_command_error(struct st_command *command, uint error) +{ + DBUG_ENTER("handle_command_error"); + DBUG_PRINT("enter", ("error: %d", error)); + if (error != 0) + { + uint i; + + if (command->abort_on_error) + die("command \"%.*s\" failed with error %d", + command->first_word_len, command->query, error); + for (i= 0; i < command->expected_errors.count; i++) + { + DBUG_PRINT("info", ("expected error: %d", + command->expected_errors.err[i].code.errnum)); + if ((command->expected_errors.err[i].type == ERR_ERRNO) && + (command->expected_errors.err[i].code.errnum == error)) + { + DBUG_PRINT("info", ("command \"%.*s\" failed with expected error: %d", + command->first_word_len, command->query, error)); + DBUG_VOID_RETURN; + } + } + die("command \"%.*s\" failed with wrong error: %d", + command->first_word_len, command->query, error); + } + else if (command->expected_errors.err[0].type == ERR_ERRNO && + command->expected_errors.err[0].code.errnum != 0) + { + /* Error code we wanted was != 0, i.e. not an expected success */ + die("command \"%.*s\" succeeded - should have failed with errno %d...", + command->first_word_len, command->query, + command->expected_errors.err[0].code.errnum); + } + DBUG_VOID_RETURN; +} + + +void close_connections() { - DBUG_ENTER("close_cons"); - if (last_result) - mysql_free_result(last_result); - 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); + next_con->stmt= 0; mysql_close(&next_con->mysql); + if (next_con->util_mysql) + mysql_close(next_con->util_mysql); my_free(next_con->name, MYF(MY_ALLOW_ZERO_PTR)); } DBUG_VOID_RETURN; } -static void close_files() +void close_files() { DBUG_ENTER("close_files"); - for (; cur_file != (file_stack-1) ; cur_file--) + for (; cur_file >= file_stack; cur_file--) { - DBUG_PRINT("info", ("file_name: %s", cur_file->file_name)); if (cur_file->file && cur_file->file != stdin) + { + DBUG_PRINT("info", ("closing file: %s", cur_file->file_name)); my_fclose(cur_file->file, MYF(0)); + } my_free((gptr)cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR)); cur_file->file_name= 0; } @@ -532,25 +682,22 @@ static void close_files() } -static void free_used_memory() +void free_used_memory() { uint i; DBUG_ENTER("free_used_memory"); -#ifndef EMBEDDED_LIBRARY - if (manager) - mysql_manager_close(manager); -#endif - close_cons(); + + close_connections(); close_files(); hash_free(&var_hash); - for (i=0 ; i < q_lines.elements ; i++) + for (i= 0 ; i < q_lines.elements ; i++) { - struct st_query **q= dynamic_element(&q_lines, i, struct st_query**); + struct st_command **q= dynamic_element(&q_lines, i, struct st_command**); my_free((gptr) (*q)->query_buf,MYF(MY_ALLOW_ZERO_PTR)); my_free((gptr) (*q),MYF(0)); } - for (i=0; i < 10; i++) + for (i= 0; i < 10; i++) { if (var_reg[i].alloced_len) my_free(var_reg[i].str_val, MYF(MY_WME)); @@ -559,20 +706,28 @@ static void free_used_memory() my_free(embedded_server_args[--embedded_server_arg_count],MYF(0)); delete_dynamic(&q_lines); dynstr_free(&ds_res); - free_replace(); - free_replace_column(); + dynstr_free(&ds_progress); + dynstr_free(&ds_warning_messages); + free_all_replace(); my_free(pass,MYF(MY_ALLOW_ZERO_PTR)); free_defaults(default_argv); mysql_server_end(); - if (ps_protocol) - ps_free_reg(); + free_re(); +#ifdef __WIN__ + free_tmp_sh_file(); + free_win_path_patterns(); +#endif DBUG_VOID_RETURN; } -static void die(const char *fmt, ...) + +void die(const char *fmt, ...) { va_list args; DBUG_ENTER("die"); + DBUG_PRINT("enter", ("start_lineno: %d", start_lineno)); + + /* Print the error message */ va_start(args, fmt); if (fmt) { @@ -587,25 +742,73 @@ static void die(const char *fmt, ...) fflush(stderr); } va_end(args); + + /* Dump the result that has been accumulated so far to .log file */ + if (result_file_name && ds_res.length) + dump_result_to_log_file(ds_res.str, ds_res.length); + + /* Dump warning messages */ + if (result_file_name && ds_warning_messages.length) + dump_warning_messages(); + + /* Clean up and exit */ free_used_memory(); my_end(MY_CHECK_ERROR); + + if (!silent) + printf("not ok\n"); + exit(1); } -/* Note that we will get some memory leaks when calling this! */ -static void abort_not_supported_test() +void abort_not_supported_test(const char *fmt, ...) { + va_list args; + struct st_test_file* err_file= cur_file; DBUG_ENTER("abort_not_supported_test"); - fprintf(stderr, "This test is not supported by this installation\n"); - if (!silent) - printf("skipped\n"); + + /* Print include filestack */ + fprintf(stderr, "The test '%s' is not supported by this installation\n", + file_stack->file_name); + fprintf(stderr, "Detected in file %s at line %d\n", + err_file->file_name, err_file->lineno); + while (err_file != file_stack) + { + err_file--; + fprintf(stderr, "included from %s at line %d\n", + err_file->file_name, err_file->lineno); + } + + /* Print error message */ + va_start(args, fmt); + if (fmt) + { + fprintf(stderr, "reason: "); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + fflush(stderr); + } + va_end(args); + + /* Clean up and exit */ free_used_memory(); my_end(MY_CHECK_ERROR); + + if (!silent) + printf("skipped\n"); + exit(62); } -static void verbose_msg(const char *fmt, ...) + +void abort_not_in_this_version() +{ + die("Not available in this version of mysqltest"); +} + + +void verbose_msg(const char *fmt, ...) { va_list args; DBUG_ENTER("verbose_msg"); @@ -613,24 +816,59 @@ static void verbose_msg(const char *fmt, ...) DBUG_VOID_RETURN; va_start(args, fmt); - fprintf(stderr, "mysqltest: "); - if (start_lineno > 0) + if (cur_file && cur_file != file_stack) + fprintf(stderr, "In included file \"%s\": ", + cur_file->file_name); + if (start_lineno != 0) fprintf(stderr, "At line %u: ", start_lineno); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); + DBUG_VOID_RETURN; } -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) + { + dynstr_append(&ds_warning_messages, "Warning detected "); + if (cur_file && cur_file != file_stack) + { + len= my_snprintf(buff, sizeof(buff), "in included file %s ", + cur_file->file_name); + dynstr_append_mem(&ds_warning_messages, + buff, len); + } + len= my_snprintf(buff, sizeof(buff), "at line %d: ", + start_lineno); + dynstr_append_mem(&ds_warning_messages, + buff, len); + } +#ifndef __WIN__ + len= vsnprintf(buff, sizeof(buff), fmt, args); + dynstr_append_mem(&ds_warning_messages, buff, len); +#endif + dynstr_append(&ds_warning_messages, "\n"); + va_end(args); + + DBUG_VOID_RETURN; } +/* + 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; @@ -645,33 +883,35 @@ int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) if (!test_if_hard_path(fname)) { strxmov(eval_file, opt_basedir, fname, NullS); - fn_format(eval_file, eval_file,"","",4); + fn_format(eval_file, eval_file, "", "", MY_UNPACK_FILENAME); } else - fn_format(eval_file, fname,"","",4); + fn_format(eval_file, fname, "", "", MY_UNPACK_FILENAME); if (!my_stat(eval_file, &stat_info, MYF(MY_WME))) die(NullS); if (!eval_result && (uint) stat_info.st_size != ds->length) { - DBUG_PRINT("info",("Size differs: result size: %u file size: %u", + DBUG_PRINT("info",("Size differs: result size: %u file size: %llu", ds->length, stat_info.st_size)); + DBUG_PRINT("info",("result: '%s'", ds->str)); DBUG_RETURN(RESULT_LENGTH_MISMATCH); } if (!(tmp = (char*) my_malloc(stat_info.st_size + 1, MYF(MY_WME)))) - die(NullS); + die("Out of memory"); if ((fd = my_open(eval_file, O_RDONLY, MYF(MY_WME))) < 0) - die(NullS); + die("Failed to open file %s", eval_file); if (my_read(fd, (byte*)tmp, stat_info.st_size, MYF(MY_WME|MY_NABP))) - die(NullS); + die("Failed to read from file %s, errno: %d", eval_file, errno); tmp[stat_info.st_size] = 0; - init_dynamic_string(&res_ds, "", 0, 65536); + init_dynamic_string(&res_ds, "", stat_info.st_size+256, 256); if (eval_result) { - do_eval(&res_ds, tmp); - res_ptr = res_ds.str; - if ((res_len = res_ds.length) != ds->length) + do_eval(&res_ds, tmp, tmp + stat_info.st_size, FALSE); + res_ptr= res_ds.str; + res_len= res_ds.length; + if (res_len != ds->length) { res= RESULT_LENGTH_MISMATCH; goto err; @@ -688,51 +928,158 @@ int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) err: if (res && eval_result) - str_to_file(fn_format(eval_file, fname, "", ".eval",2), res_ptr, - res_len); + str_to_file(fn_format(eval_file, fname, "", ".eval", + MY_REPLACE_EXT), + res_ptr, res_len); + dynstr_free(&res_ds); my_free((gptr) tmp, MYF(0)); my_close(fd, MYF(MY_WME)); - dynstr_free(&res_ds); DBUG_RETURN(res); } -static int check_result(DYNAMIC_STRING* ds, const char *fname, - my_bool require_option) + +/* + Check the content of ds against result file + + SYNOPSIS + check_result + ds - content to be checked + + RETURN VALUES + error - the function will not return + +*/ + +void check_result(DYNAMIC_STRING* ds) { - int error= RESULT_OK; - int res= dyn_string_cmp(ds, fname); + DBUG_ENTER("check_result"); + DBUG_ASSERT(result_file_name); - if (res && require_option) - abort_not_supported_test(); - switch (res) { + switch (dyn_string_cmp(ds, result_file_name)) + { case RESULT_OK: break; /* ok */ case RESULT_LENGTH_MISMATCH: - verbose_msg("Result length mismatch"); - error= RESULT_LENGTH_MISMATCH; + dump_result_to_reject_file(ds->str, ds->length); + die("Result length mismatch"); break; case RESULT_CONTENT_MISMATCH: - verbose_msg("Result content mismatch"); - error= RESULT_CONTENT_MISMATCH; + dump_result_to_reject_file(ds->str, ds->length); + die("Result content mismatch"); break; default: /* impossible */ die("Unknown error code from dyn_string_cmp()"); } - if (error) - reject_dump(fname, ds->str, ds->length); - return error; + + DBUG_VOID_RETURN; +} + + +/* + Check the content of ds against a require file + If match fails, abort the test with special error code + indicating that test is not supported + + SYNOPSIS + check_result + ds - content to be checked + fname - name of file to check against + + RETURN VALUES + error - the function will not return + +*/ + +void check_require(DYNAMIC_STRING* ds, const char *fname) +{ + DBUG_ENTER("check_require"); + + if (dyn_string_cmp(ds, fname)) + { + char reason[FN_REFLEN]; + fn_format(reason, fname, "", "", MY_REPLACE_EXT | MY_REPLACE_DIR); + abort_not_supported_test("Test requires: '%s'", reason); + } + DBUG_VOID_RETURN; +} + + +static byte *get_var_key(const byte* var, uint* len, + my_bool __attribute__((unused)) t) +{ + 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; } -VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw, +void var_free(void *v) +{ + my_free(((VAR*) v)->str_val, MYF(MY_WME)); + if (((VAR*)v)->alloced) + my_free((char*) v, MYF(MY_WME)); +} + + +VAR* var_from_env(const char *name, const char *def_val) +{ + const char *tmp; + VAR *v; + if (!(tmp = getenv(name))) + tmp = def_val; + + v = var_init(0, name, strlen(name), tmp, strlen(tmp)); + my_hash_insert(&var_hash, (byte*)v); + return v; +} + + +VAR* var_get(const char *var_name, const char **var_name_end, my_bool raw, my_bool ignore_not_existing) { int digit; - VAR* v; + VAR *v; DBUG_ENTER("var_get"); - DBUG_PRINT("enter",("var_name: %s",var_name)); + DBUG_PRINT("enter", ("var_name: %s",var_name)); if (*var_name != '$') goto err; @@ -751,16 +1098,16 @@ VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw, die("Empty variable"); } length= (uint) (var_name - save_var_name); - if (length >= MAX_VAR_NAME) + if (length >= MAX_VAR_NAME_LENGTH) die("Too long variable name: %s", save_var_name); if (!(v = (VAR*) hash_search(&var_hash, save_var_name, length))) { - char buff[MAX_VAR_NAME+1]; + char buff[MAX_VAR_NAME_LENGTH+1]; strmake(buff, save_var_name, length); v= var_from_env(buff, ""); } - var_name--; /* Point at last character */ + var_name--; /* Point at last character */ } else v = var_reg + digit; @@ -781,7 +1128,8 @@ err: DBUG_RETURN(0); } -static VAR *var_obtain(const char *name, int len) + +VAR *var_obtain(const char *name, int len) { VAR* v; if ((v = (VAR*)hash_search(&var_hash, name, len))) @@ -791,200 +1139,332 @@ static VAR *var_obtain(const char *name, int len) return v; } -int var_set(const char *var_name, const char *var_name_end, - const char *var_val, const char *var_val_end) + +/* + - if variable starts with a $ it is regarded as a local test varable + - if not it is treated as a environment variable, and the corresponding + environment variable will be updated +*/ + +void var_set(const char *var_name, const char *var_name_end, + const char *var_val, const char *var_val_end) { - int digit; - VAR* v; + int digit, env_var= 0; + VAR *v; DBUG_ENTER("var_set"); DBUG_PRINT("enter", ("var_name: '%.*s' = '%.*s' (length: %d)", (int) (var_name_end - var_name), var_name, (int) (var_val_end - var_val), var_val, (int) (var_val_end - var_val))); - if (*var_name++ != '$') - { - var_name--; - die("Variable name in %s does not start with '$'", var_name); - } - digit = *var_name - '0'; + if (*var_name != '$') + env_var= 1; + else + var_name++; + + digit= *var_name - '0'; if (!(digit < 10 && digit >= 0)) { - v = var_obtain(var_name, (uint) (var_name_end - var_name)); + v= var_obtain(var_name, (uint) (var_name_end - var_name)); } else - v = var_reg + digit; - DBUG_RETURN(eval_expr(v, var_val, (const char**)&var_val_end)); -} + v= var_reg + digit; + eval_expr(v, var_val, (const char**) &var_val_end); -int open_file(const char *name) -{ - char buff[FN_REFLEN]; - DBUG_ENTER("open_file"); - DBUG_PRINT("enter", ("name: %s", name)); - if (!test_if_hard_path(name)) + if (env_var) { - strxmov(buff, opt_basedir, name, NullS); - name=buff; + char buf[1024], *old_env_s= v->env_s; + if (v->int_dirty) + { + sprintf(v->str_val, "%d", v->int_val); + v->int_dirty= 0; + v->str_val_len= strlen(v->str_val); + } + strxmov(buf, v->name, "=", v->str_val, NullS); + if (!(v->env_s= my_strdup(buf, MYF(MY_WME)))) + die("Out of memory"); + putenv(v->env_s); + my_free((gptr)old_env_s, MYF(MY_ALLOW_ZERO_PTR)); } - fn_format(buff,name,"","",4); + DBUG_VOID_RETURN; +} - if (cur_file == file_stack_end) - die("Source directives are nesting too deep"); - cur_file++; - if (!(cur_file->file = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0)))) - { - cur_file--; - die("Could not open file %s", buff); - } - cur_file->file_name= my_strdup(buff, MYF(MY_FAE)); - *++lineno=1; - DBUG_RETURN(0); +/* + 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; } /* - Check for unexpected "junk" after the end of query - This is normally caused by missing delimiters + 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 check_eol_junk(const char *eol) +void var_query_set(VAR *var, const char *query, const char** query_end) { - 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++; + 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); - /* Check for extra delimiter */ - if (*p && !strncmp(p, delimiter, delimiter_length)) - die("Extra delimiter \"%s\" found", delimiter); + while (end > query && *end != '`') + --end; + if (query == end) + die("Syntax error in query, missing '`'"); + ++query; - /* Allow trailing # comment */ - if (*p && *p != '#') + if (mysql_real_query(mysql, query, (int)(end - query)) || + !(res = mysql_store_result(mysql))) { - if (*p == '\n') - die("Missing delimiter"); - die("End of line junk detected: \"%s\"", p); + *end = 0; + die("Error running query '%s': %d %s", query, + mysql_errno(mysql), mysql_error(mysql)); } - 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 (;;) + if ((row = mysql_fetch_row(res)) && row[0]) { - MYSQL_RES *res; - MYSQL_ROW row; - int done; - LINT_INIT(res); + /* + 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 - 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]) + init_dynamic_string(&result, "", 2048, 2048); + lengths= mysql_fetch_lengths(res); + for (i=0; i < mysql_num_fields(res); i++) { - mysql_free_result(res); - die("Strange result from query while probing slave for stop"); + 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); } - done = !strcmp(row[1],"OFF"); - mysql_free_result(res); - if (done) - break; - my_sleep(SLAVE_POLL_INTERVAL); + end= result.str + result.length-1; + eval_expr(var, result.str, (const char**) &end); + dynstr_free(&result); } - return 0; -} + else + eval_expr(var, "", 0); -int do_require_manager(struct st_query *query __attribute__((unused)) ) -{ - if (!manager) - abort_not_supported_test(); - return 0; + mysql_free_result(res); + DBUG_VOID_RETURN; } -#ifndef EMBEDDED_LIBRARY -int do_server_start(struct st_query *q) + +void var_copy(VAR *dest, VAR *src) { - return do_server_op(q, "start"); + 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 do_server_stop(struct st_query *q) + +void eval_expr(VAR *v, const char *p, const char **p_end) { - return do_server_op(q, "stop"); + 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; } -int do_server_op(struct st_query *q, const char *op) + +int open_file(const char *name) { - char *p= q->first_argument; - char com_buf[256], *com_p; - if (!manager) + char buff[FN_REFLEN]; + DBUG_ENTER("open_file"); + DBUG_PRINT("enter", ("name: %s", name)); + if (!test_if_hard_path(name)) { - die("Manager is not initialized, manager commands are not possible"); + strxmov(buff, opt_basedir, name, NullS); + name=buff; } - com_p= strmov(com_buf,op); - com_p= strmov(com_p,"_exec "); - if (!*p) - die("Missing server name in server_%s", op); - while (*p && !my_isspace(charset_info, *p)) - *com_p++= *p++; - *com_p++= ' '; - com_p= int10_to_str(manager_wait_timeout, com_p, 10); - *com_p++= '\n'; - *com_p= 0; - if (mysql_manager_command(manager, com_buf, (int)(com_p-com_buf))) - die("Error in command: %s(%d)", manager->last_error, manager->last_errno); - while (!manager->eof) - { - if (mysql_manager_fetch_line(manager, com_buf, sizeof(com_buf))) - die("Error fetching result line: %s(%d)", manager->last_error, - manager->last_errno); - } - - q->last_argument= p; - return 0; + fn_format(buff, name, "", "", MY_UNPACK_FILENAME); + + if (cur_file == file_stack_end) + die("Source directives are nesting too deep"); + cur_file++; + if (!(cur_file->file = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0)))) + { + cur_file--; + die("Could not open file %s", buff); + } + cur_file->file_name= my_strdup(buff, MYF(MY_FAE)); + cur_file->lineno=1; + DBUG_RETURN(0); } -#endif /* 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; + static 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 temporary sh files used for emulating Unix on Windows */ +char tmp_sh_name[64], tmp_sh_cmd[70]; + +void init_tmp_sh_file() +{ + /* Format a name for the tmp sh file that is unique for this process */ + my_snprintf(tmp_sh_name, sizeof(tmp_sh_name), "tmp_%d.sh", getpid()); + /* Format the command to execute in order to run the script */ + my_snprintf(tmp_sh_cmd, sizeof(tmp_sh_cmd), "sh %s", tmp_sh_name); +} + + +void free_tmp_sh_file() +{ + my_delete(tmp_sh_name, MYF(0)); +} +#endif + + +FILE* my_popen(DYNAMIC_STRING *ds_cmd, const char *mode) +{ +#ifdef __WIN__ + /* Dump the command into a sh script file and execute with popen */ + str_to_file(tmp_sh_name, ds_cmd->str, ds_cmd->length); + return popen(tmp_sh_cmd, mode); +#else + return popen(ds_cmd->str, mode); +#endif } @@ -992,361 +1472,588 @@ int do_source(struct st_query *query) 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. */ -static void do_exec(struct st_query *query) +void do_exec(struct st_command *command) { int error; - DYNAMIC_STRING *ds= NULL; - DYNAMIC_STRING ds_tmp; 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)); + /* Skip leading space */ while (*cmd && my_isspace(charset_info, *cmd)) cmd++; if (!*cmd) die("Missing argument in exec"); - query->last_argument= query->end; + command->last_argument= command->end; + + init_dynamic_string(&ds_cmd, 0, command->query_len+256, 256); + /* Eval the command, thus replacing all environment variables */ + do_eval(&ds_cmd, cmd, command->end, TRUE); - DBUG_PRINT("info", ("Executing '%s'", cmd)); + DBUG_PRINT("info", ("Executing '%s' as '%s'", + command->first_argument, cmd)); - if (!(res_file= popen(cmd, "r")) && query->abort_on_error) - die("popen(\"%s\", \"r\") failed", cmd); + if (!(res_file= my_popen(&ds_cmd, "r")) && command->abort_on_error) + die("popen(\"%s\", \"r\") failed", command->first_argument); - if (disable_result_log) + while (fgets(buf, sizeof(buf), res_file)) { - while (fgets(buf, sizeof(buf), res_file)) + if (disable_result_log) { buf[strlen(buf)-1]=0; DBUG_PRINT("exec_result",("%s", buf)); } - } - else - { - if (query->record_file[0]) + else { - init_dynamic_string(&ds_tmp, "", 16384, 65536); - ds= &ds_tmp; + replace_dynstr_append(&ds_res, buf); } - else - ds= &ds_res; - - while (fgets(buf, sizeof(buf), res_file)) - replace_dynstr_append_mem(ds, buf, strlen(buf)); } error= pclose(res_file); - if (error != 0) + if (error > 0) { uint status= WEXITSTATUS(error), i; my_bool ok= 0; - if (query->abort_on_error) - die("command \"%s\" failed", cmd); + 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", - cmd, status)); + command->first_argument, status)); } } if (!ok) die("command \"%s\" failed with wrong error: %d", - cmd, 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...", - cmd, query->expected_errno[0].code.errnum); + command->first_argument, command->expected_errors.err[0].code.errnum); } - if (!disable_result_log) - { - if (glob_replace) - free_replace(); + dynstr_free(&ds_cmd); + DBUG_VOID_RETURN; +} - if (record) - { - if (!query->record_file[0] && !result_file) - die("Missing result file"); - if (!result_file) - str_to_file(query->record_file, ds->str, ds->length); - } - else if (query->record_file[0]) - { - error= check_result(ds, query->record_file, query->require_file); - } - if (ds == &ds_tmp) - dynstr_free(&ds_tmp); +enum enum_operator +{ + DO_DEC, + DO_INC +}; + + +/* + Decrease or increase the value of a variable + + SYNOPSIS + do_modify_var() + query called command + operator operation to perform on the var + + DESCRIPTION + dec $var_name + inc $var_name + +*/ + +int do_modify_var(struct st_command *command, + enum enum_operator operator) +{ + const char *p= command->first_argument; + VAR* v; + if (!*p) + die("Missing argument to %.*s", command->first_word_len, command->query); + if (*p != '$') + die("The argument to %.*s must be a variable (start with $)", + command->first_word_len, command->query); + v= var_get(p, &p, 1, 0); + switch (operator) { + case DO_DEC: + v->int_val--; + break; + case DO_INC: + v->int_val++; + break; + default: + die("Invalid operator to do_modify_var"); + break; } + v->int_dirty= 1; + command->last_argument= (char*)++p; + return 0; } -int var_query_set(VAR* v, const char *p, const char** p_end) +/* + Wrapper for 'system' function + + NOTE + If mysqltest is executed from cygwin shell, the command will be + executed in the "windows command interpreter" cmd.exe and we prepend "sh" + to make it be executed by cygwins "bash". Thus commands like "rm", + "mkdir" as well as shellscripts can executed by "system" in Windows. + +*/ + +int my_system(DYNAMIC_STRING* ds_cmd) { - char* end = (char*)((p_end && *p_end) ? *p_end : p + strlen(p)); - MYSQL_RES *res; - MYSQL_ROW row; - MYSQL* mysql = &cur_con->mysql; - LINT_INIT(res); +#ifdef __WIN__ + /* Dump the command into a sh script file and execute with system */ + str_to_file(tmp_sh_name, ds_cmd->str, ds_cmd->length); + return system(tmp_sh_cmd); +#else + return system(ds_cmd->str); +#endif +} - while (end > p && *end != '`') - --end; - if (p == end) - die("Syntax error in query, missing '`'"); - ++p; - if (mysql_real_query(mysql, p, (int)(end - p)) || - !(res = mysql_store_result(mysql))) - { - *end = 0; - die("Error running query '%s': %s", p, mysql_error(mysql)); - } +/* + SYNOPSIS + do_system + command called command - if ((row = mysql_fetch_row(res)) && row[0]) + DESCRIPTION + system <command> + + Eval the query to expand any $variables in the command. + Execute the command with the "system" command. + +*/ + +void do_system(struct st_command *command) +{ + DYNAMIC_STRING ds_cmd; + DBUG_ENTER("do_system"); + + if (strlen(command->first_argument) == 0) + die("Missing arguments to system, nothing to do!"); + + init_dynamic_string(&ds_cmd, 0, command->query_len + 64, 256); + + /* Eval the system command, thus replacing all environment variables */ + do_eval(&ds_cmd, command->first_argument, command->end, TRUE); + + DBUG_PRINT("info", ("running system command '%s' as '%s'", + command->first_argument, ds_cmd.str)); + if (my_system(&ds_cmd)) { - /* - 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; + if (command->abort_on_error) + die("system command '%s' failed", command->first_argument); - init_dynamic_string(&result, "", 16384, 65536); - lengths= mysql_fetch_lengths(res); - for (i=0; i < mysql_num_fields(res); i++) - { - if (row[0]) - dynstr_append_mem(&result, row[i], lengths[i]); - dynstr_append_mem(&result, "\t", 1); - } - end= result.str + result.length-1; - eval_expr(v, result.str, (const char**) &end); - dynstr_free(&result); + /* If ! abort_on_error, log message and continue */ + dynstr_append(&ds_res, "system command '"); + replace_dynstr_append(&ds_res, command->first_argument); + dynstr_append(&ds_res, "' failed\n"); } - else - eval_expr(v, "", 0); - mysql_free_result(res); - return 0; + command->last_argument= command->end; + dynstr_free(&ds_cmd); + DBUG_VOID_RETURN; } -void var_copy(VAR *dest, VAR *src) + +/* + SYNOPSIS + do_remove_file + command called command + + DESCRIPTION + remove_file <file_name> + Remove the file <file_name> +*/ + +void do_remove_file(struct st_command *command) { - dest->int_val= src->int_val; - dest->int_dirty= src->int_dirty; + int error; + static DYNAMIC_STRING ds_filename; + const struct command_arg rm_args[] = { + "filename", ARG_STRING, TRUE, &ds_filename, "File to delete" + }; + DBUG_ENTER("do_remove_file"); + + check_command_args(command, command->first_argument, + rm_args, sizeof(rm_args)/sizeof(struct command_arg), + ' '); + + DBUG_PRINT("info", ("removing file: %s", ds_filename.str)); + error= my_delete(ds_filename.str, MYF(0)) != 0; + handle_command_error(command, error); + dynstr_free(&ds_filename); + DBUG_VOID_RETURN; +} - /* 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); +/* + SYNOPSIS + do_copy_file + command command handle + + DESCRIPTION + copy_file <from_file> <to_file> + Copy <from_file> to <to_file> + + NOTE! Will fail if <to_file> exists +*/ + +void do_copy_file(struct st_command *command) +{ + int error; + static DYNAMIC_STRING ds_from_file; + static DYNAMIC_STRING ds_to_file; + const struct command_arg copy_file_args[] = { + "from_file", ARG_STRING, TRUE, &ds_from_file, "Filename to copy from", + "to_file", ARG_STRING, TRUE, &ds_to_file, "Filename to copy to" + }; + DBUG_ENTER("do_copy_file"); + + check_command_args(command, command->first_argument, + copy_file_args, + sizeof(copy_file_args)/sizeof(struct command_arg), + ' '); + + DBUG_PRINT("info", ("Copy %s to %s", ds_from_file.str, ds_to_file.str)); + error= (my_copy(ds_from_file.str, ds_to_file.str, + MYF(MY_DONT_OVERWRITE_FILE)) != 0); + handle_command_error(command, error); + dynstr_free(&ds_from_file); + dynstr_free(&ds_to_file); + DBUG_VOID_RETURN; } -int eval_expr(VAR* v, const char *p, const char** p_end) + +/* + SYNOPSIS + do_file_exists + command called command + + DESCRIPTION + fiile_exist <file_name> + Check if file <file_name> exists +*/ + +void do_file_exist(struct st_command *command) { - VAR* vp; - if (*p == '$') + int error; + static DYNAMIC_STRING ds_filename; + const struct command_arg file_exist_args[] = { + "filename", ARG_STRING, TRUE, &ds_filename, "File to check if it exist" + }; + DBUG_ENTER("do_file_exist"); + + check_command_args(command, command->first_argument, + file_exist_args, + sizeof(file_exist_args)/sizeof(struct command_arg), + ' '); + + DBUG_PRINT("info", ("Checking for existence of file: %s", ds_filename.str)); + error= (access(ds_filename.str, F_OK) != 0); + handle_command_error(command, error); + dynstr_free(&ds_filename); + DBUG_VOID_RETURN; +} + + +/* + Read characters from line buffer or file. This is needed to allow + my_ungetc() to buffer MAX_DELIMITER_LENGTH characters for a file + + NOTE: + This works as long as one doesn't change files (with 'source file_name') + when there is things pushed into the buffer. This should however not + happen for any tests in the test suite. +*/ + +int my_getc(FILE *file) +{ + if (line_buffer_pos == line_buffer) + return fgetc(file); + return *--line_buffer_pos; +} + + +void my_ungetc(int c) +{ + *line_buffer_pos++= (char) c; +} + + +void read_until_delimiter(DYNAMIC_STRING *ds, + DYNAMIC_STRING *ds_delimiter) +{ + char c; + DBUG_ENTER("read_until_delimiter"); + DBUG_PRINT("enter", ("delimiter: %s, length: %d", + ds_delimiter->str, ds_delimiter->length)); + + if (ds_delimiter->length > MAX_DELIMITER_LENGTH) + die("Max delimiter length(%d) exceeded", MAX_DELIMITER_LENGTH); + + /* Read from file until delimiter is found */ + while (1) { - if ((vp = var_get(p,p_end,0,0))) + c= my_getc(cur_file->file); + + if (c == '\n') + cur_file->lineno++; + + if (feof(cur_file->file)) + die("End of file encountered before '%s' delimiter was found", + ds_delimiter->str); + + if (match_delimiter(c, ds_delimiter->str, ds_delimiter->length)) { - var_copy(v, vp); - return 0; + DBUG_PRINT("exit", ("Found delimiter '%s'", ds_delimiter->str)); + break; } + dynstr_append_mem(ds, (const char*)&c, 1); } - 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; + DBUG_PRINT("exit", ("ds: %s", ds->str)); + DBUG_VOID_RETURN; } -enum enum_operator -{ - DO_DEC, - DO_INC -}; - /* - Decrease or increase the value of a variable - SYNOPSIS - do_modify_var() - query called command - name human readable name of operator - operator operation to perform on the var + do_write_file + command called command DESCRIPTION - dec $var_name - inc $var_name + 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 everything between the "write_file" command and 'delimiter' + to "file_name" + + NOTE! Overwrites existing file + + Default <delimiter> is EOF */ -int do_modify_var(struct st_query *query, const char *name, - enum enum_operator operator) +void do_write_file(struct st_command *command) { - const char *p= query->first_argument; - VAR* v; - if (!*p) - die("Missing arguments to %s", name); - if (*p != '$') - die("First argument to %s must be a variable (start with $)", name); - v= var_get(p, &p, 1, 0); - switch (operator){ - case DO_DEC: - v->int_val--; - break; - case DO_INC: - v->int_val++; - break; - default: - die("Invalid operator to do_operator"); - break; - } - v->int_dirty= 1; - query->last_argument= (char*)++p; - return 0; + static DYNAMIC_STRING ds_content; + static DYNAMIC_STRING ds_filename; + static DYNAMIC_STRING ds_delimiter; + const struct command_arg write_file_args[] = { + "filename", ARG_STRING, TRUE, &ds_filename, "File to write to", + "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until" + }; + DBUG_ENTER("do_write_file"); + + check_command_args(command, + command->first_argument, + write_file_args, + sizeof(write_file_args)/sizeof(struct command_arg), + ' '); + + /* If no delimiter was provided, use EOF */ + if (ds_delimiter.length == 0) + dynstr_set(&ds_delimiter, "EOF"); + + init_dynamic_string(&ds_content, "", 1024, 1024); + read_until_delimiter(&ds_content, &ds_delimiter); + DBUG_PRINT("info", ("Writing to file: %s", ds_filename.str)); + str_to_file(ds_filename.str, ds_content.str, ds_content.length); + dynstr_free(&ds_content); + dynstr_free(&ds_filename); + dynstr_free(&ds_delimiter); + DBUG_VOID_RETURN; } -int do_system(struct st_query *q) +/* + SYNOPSIS + do_perl + command command handle + + DESCRIPTION + 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. + + Default <delimiter> is EOF +*/ + +void do_perl(struct st_command *command) { - char *p=q->first_argument; - VAR v; - var_init(&v, 0, 0, 0, 0); - eval_expr(&v, p, 0); /* NULL terminated */ - if (v.str_val_len) - { - char expr_buf[1024]; - if ((uint)v.str_val_len > sizeof(expr_buf) - 1) - v.str_val_len = sizeof(expr_buf) - 1; - memcpy(expr_buf, v.str_val, v.str_val_len); - expr_buf[v.str_val_len] = 0; - DBUG_PRINT("info", ("running system command '%s'", expr_buf)); - if (system(expr_buf)) + int error; + char buf[FN_REFLEN]; + FILE *res_file; + static DYNAMIC_STRING ds_script; + static DYNAMIC_STRING ds_delimiter; + const struct command_arg perl_args[] = { + "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until" + }; + DBUG_ENTER("do_perl"); + + check_command_args(command, + command->first_argument, + perl_args, + sizeof(perl_args)/sizeof(struct command_arg), + ' '); + + /* If no delimiter was provided, use EOF */ + if (ds_delimiter.length == 0) + dynstr_set(&ds_delimiter, "EOF"); + + init_dynamic_string(&ds_script, "", 1024, 1024); + read_until_delimiter(&ds_script, &ds_delimiter); + + DBUG_PRINT("info", ("Executing perl: %s", ds_script.str)); + + /* Format a name for a tmp .pl file that is unique for this process */ + my_snprintf(buf, sizeof(buf), "%s/tmp/tmp_%d.pl", + getenv("MYSQLTEST_VARDIR"), getpid()); + str_to_file(buf, ds_script.str, ds_script.length); + + /* Format the perl <filename> command */ + my_snprintf(buf, sizeof(buf), "perl %s/tmp/tmp_%d.pl", + getenv("MYSQLTEST_VARDIR"), getpid()); + + if (!(res_file= popen(buf, "r")) && command->abort_on_error) + die("popen(\"%s\", \"r\") failed", buf); + + while (fgets(buf, sizeof(buf), res_file)) + { + if (disable_result_log) + { + buf[strlen(buf)-1]=0; + DBUG_PRINT("exec_result",("%s", buf)); + } + else { - if (q->abort_on_error) - die("system command '%s' failed", expr_buf); - /* If ! abort_on_error, display message and continue */ - verbose_msg("system command '%s' failed", expr_buf); + replace_dynstr_append(&ds_res, buf); } } - else - die("Missing arguments to system, nothing to do!"); - var_free(&v); - q->last_argument= q->end; - return 0; + error= pclose(res_file); + handle_command_error(command, WEXITSTATUS(error)); + dynstr_free(&ds_script); + dynstr_free(&ds_delimiter); + DBUG_VOID_RETURN; } /* Print the content between echo and <delimiter> to result file. - If content is a variable, the variable value will be retrieved + Evaluate all variables in the string before printing, allow + for variable names to be escaped using \ SYNOPSIS - do_echo() - q called command + do_echo() + command called command DESCRIPTION - Usage 1: - 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 - Usage 2: - 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 $<var_name> to result file */ -int do_echo(struct st_query *q) +int do_echo(struct st_command *command) { - char *p= q->first_argument; - DYNAMIC_STRING *ds; - DYNAMIC_STRING ds_tmp; - VAR v; - var_init(&v,0,0,0,0); + DYNAMIC_STRING ds_echo; + + init_dynamic_string(&ds_echo, "", command->query_len, 256); + do_eval(&ds_echo, command->first_argument, command->end, FALSE); + dynstr_append_mem(&ds_res, ds_echo.str, ds_echo.length); + dynstr_append_mem(&ds_res, "\n", 1); + dynstr_free(&ds_echo); + command->last_argument= command->end; + return(0); +} - if (q->record_file[0]) + +void do_wait_for_slave_to_stop(struct st_command *c __attribute__((unused))) +{ + static int SLAVE_POLL_INTERVAL= 300000; + MYSQL* mysql = &cur_con->mysql; + for (;;) { - init_dynamic_string(&ds_tmp, "", 256, 512); - ds= &ds_tmp; - } - else - ds= &ds_res; + MYSQL_RES *res; + MYSQL_ROW row; + int done; + LINT_INIT(res); - eval_expr(&v, p, 0); /* NULL terminated */ - if (v.str_val_len) - dynstr_append_mem(ds, v.str_val, v.str_val_len); - dynstr_append_mem(ds, "\n", 1); - var_free(&v); - if (ds == &ds_tmp) - dynstr_free(&ds_tmp); - q->last_argument= q->end; - return 0; + 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; } -int do_sync_with_master2(long offset) +void do_sync_with_master2(long offset) { - MYSQL_RES* res; + MYSQL_RES *res; MYSQL_ROW row; - MYSQL* mysql= &cur_con->mysql; + MYSQL *mysql= &cur_con->mysql; char query_buf[FN_REFLEN+128]; int tries= 0; int rpl_parse; @@ -1362,10 +2069,10 @@ int do_sync_with_master2(long offset) wait_for_position: if (mysql_query(mysql, query_buf)) - die("failed in %s: %d: %s", query_buf, mysql_errno(mysql), + die("failed in '%s': %d: %s", query_buf, mysql_errno(mysql), mysql_error(mysql)); - if (!(last_result= res= mysql_store_result(mysql))) + if (!(res= mysql_store_result(mysql))) die("mysql_store_result() returned NULL for '%s'", query_buf); if (!(row= mysql_fetch_row(res))) die("empty result in %s", query_buf); @@ -1375,24 +2082,24 @@ wait_for_position: It may be that the slave SQL thread has not started yet, though START SLAVE has been issued ? */ - if (tries++ == 3) + if (tries++ == 30) die("could not sync with master ('%s' returned NULL)", query_buf); - sleep(1); /* So at most we will wait 3 seconds and make 4 tries */ + sleep(1); /* So at most we will wait 30 seconds and make 31 tries */ mysql_free_result(res); goto wait_for_position; } mysql_free_result(res); - last_result=0; if (rpl_parse) mysql_enable_rpl_parse(mysql); - return 0; + return; } -int do_sync_with_master(struct st_query *query) + +void do_sync_with_master(struct st_command *command) { long offset= 0; - char *p= query->first_argument; + char *p= command->first_argument; const char *offset_start= p; if (*offset_start) { @@ -1401,33 +2108,177 @@ int do_sync_with_master(struct st_query *query) if(*p && !my_isspace(charset_info, *p)) die("Invalid integer argument \"%s\"", offset_start); - query->last_argument= p; + command->last_argument= p; } - return do_sync_with_master2(offset); + do_sync_with_master2(offset); + return; } + +/* + when ndb binlog is on, this call will wait until last updated epoch + (locally in the mysqld) has been received into the binlog +*/ int do_save_master_pos() { - MYSQL_RES* res; + MYSQL_RES *res; MYSQL_ROW row; - MYSQL* mysql = &cur_con->mysql; + MYSQL *mysql = &cur_con->mysql; const char *query; int rpl_parse; rpl_parse = mysql_rpl_parse_enabled(mysql); mysql_disable_rpl_parse(mysql); +#ifdef HAVE_NDB_BINLOG + /* + Wait for ndb binlog to be up-to-date with all changes + done on the local mysql server + */ + { + ulong have_ndbcluster; + if (mysql_query(mysql, query= "show variables like 'have_ndbcluster'")) + die("'%s' failed: %d %s", query, + mysql_errno(mysql), mysql_error(mysql)); + if (!(res= mysql_store_result(mysql))) + die("mysql_store_result() returned NULL for '%s'", query); + if (!(row= mysql_fetch_row(res))) + die("Query '%s' returned empty result", query); + + have_ndbcluster= strcmp("YES", row[1]) == 0; + mysql_free_result(res); + + if (have_ndbcluster) + { + ulonglong start_epoch= 0, applied_epoch= 0, + latest_epoch=0, latest_trans_epoch=0, + latest_handled_binlog_epoch= 0, latest_received_binlog_epoch= 0, + latest_applied_binlog_epoch= 0; + int count= 0; + int do_continue= 1; + while (do_continue) + { + const char binlog[]= "binlog"; + const char latest_epoch_str[]= + "latest_epoch="; + const char latest_trans_epoch_str[]= + "latest_trans_epoch="; + const char latest_received_binlog_epoch_str[]= + "latest_received_binlog_epoch"; + const char latest_handled_binlog_epoch_str[]= + "latest_handled_binlog_epoch="; + const char latest_applied_binlog_epoch_str[]= + "latest_applied_binlog_epoch="; + if (count) + sleep(1); + if (mysql_query(mysql, query= "show engine ndb status")) + die("failed in '%s': %d %s", query, + mysql_errno(mysql), mysql_error(mysql)); + if (!(res= mysql_store_result(mysql))) + die("mysql_store_result() returned NULL for '%s'", query); + while ((row= mysql_fetch_row(res))) + { + if (strcmp(row[1], binlog) == 0) + { + const char *status= row[2]; + + /* latest_epoch */ + while (*status && strncmp(status, latest_epoch_str, + sizeof(latest_epoch_str)-1)) + status++; + if (*status) + { + status+= sizeof(latest_epoch_str)-1; + latest_epoch= strtoull(status, (char**) 0, 10); + } + else + die("result does not contain '%s' in '%s'", + latest_epoch_str, query); + /* latest_trans_epoch */ + while (*status && strncmp(status, latest_trans_epoch_str, + sizeof(latest_trans_epoch_str)-1)) + status++; + if (*status) + { + status+= sizeof(latest_trans_epoch_str)-1; + latest_trans_epoch= strtoull(status, (char**) 0, 10); + } + else + die("result does not contain '%s' in '%s'", + latest_trans_epoch_str, query); + /* latest_received_binlog_epoch */ + while (*status && + strncmp(status, latest_received_binlog_epoch_str, + sizeof(latest_received_binlog_epoch_str)-1)) + status++; + if (*status) + { + status+= sizeof(latest_received_binlog_epoch_str)-1; + latest_received_binlog_epoch= strtoull(status, (char**) 0, 10); + } + else + die("result does not contain '%s' in '%s'", + latest_received_binlog_epoch_str, query); + /* latest_handled_binlog */ + while (*status && + strncmp(status, latest_handled_binlog_epoch_str, + sizeof(latest_handled_binlog_epoch_str)-1)) + status++; + if (*status) + { + status+= sizeof(latest_handled_binlog_epoch_str)-1; + latest_handled_binlog_epoch= strtoull(status, (char**) 0, 10); + } + else + die("result does not contain '%s' in '%s'", + latest_handled_binlog_epoch_str, query); + /* latest_applied_binlog_epoch */ + while (*status && + strncmp(status, latest_applied_binlog_epoch_str, + sizeof(latest_applied_binlog_epoch_str)-1)) + status++; + if (*status) + { + status+= sizeof(latest_applied_binlog_epoch_str)-1; + latest_applied_binlog_epoch= strtoull(status, (char**) 0, 10); + } + else + die("result does not contain '%s' in '%s'", + latest_applied_binlog_epoch_str, query); + if (count == 0) + start_epoch= latest_trans_epoch; + break; + } + } + if (!row) + die("result does not contain '%s' in '%s'", + binlog, query); + if (latest_applied_binlog_epoch > applied_epoch) + count= 0; + applied_epoch= latest_applied_binlog_epoch; + count++; + if (latest_handled_binlog_epoch >= start_epoch) + do_continue= 0; + else if (count > 30) + { + break; + } + mysql_free_result(res); + } + } + } +#endif if (mysql_query(mysql, query= "show master status")) - die("failed in show master status: %d: %s", + die("failed in 'show master status': %d %s", mysql_errno(mysql), mysql_error(mysql)); - if (!(last_result =res = mysql_store_result(mysql))) + if (!(res = mysql_store_result(mysql))) die("mysql_store_result() retuned NULL for '%s'", query); if (!(row = mysql_fetch_row(res))) die("empty result in show master status"); strnmov(master_pos.file, row[0], sizeof(master_pos.file)-1); master_pos.pos = strtoul(row[1], (char**) 0, 10); - mysql_free_result(res); last_result=0; + mysql_free_result(res); if (rpl_parse) mysql_enable_rpl_parse(mysql); @@ -1440,25 +2291,30 @@ 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 *var_name, *var_name_end, *var_val_start; + char *p= command->first_argument; + char *var_name, *var_name_end; + DYNAMIC_STRING let_rhs_expr; + + DBUG_ENTER("do_let"); + + init_dynamic_string(&let_rhs_expr, "", 512, 2048); /* Find <var_name> */ if (!*p) @@ -1467,7 +2323,8 @@ int do_let(struct st_query *query) while (*p && (*p != '=') && !my_isspace(charset_info,*p)) p++; var_name_end= p; - if (var_name+1 == var_name_end) + if (var_name == var_name_end || + (var_name+1 == var_name_end && *var_name == '$')) die("Missing variable name in let"); while (my_isspace(charset_info,*p)) p++; @@ -1477,29 +2334,19 @@ int do_let(struct st_query *query) /* Find start of <var_val> */ while (*p && my_isspace(charset_info,*p)) p++; - var_val_start= p; - query->last_argument= query->end; - /* Assign var_val to var_name */ - return var_set(var_name, var_name_end, var_val_start, query->end); -} + do_eval(&let_rhs_expr, p, command->end, FALSE); -/* - 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); + command->last_argument= command->end; + /* Assign var_val to var_name */ + 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)) @@ -1508,14 +2355,14 @@ int do_rpl_probe(struct st_query *query __attribute__((unused))) } -int do_enable_rpl_parse(struct st_query *query __attribute__((unused))) +int do_enable_rpl_parse(struct st_command *command __attribute__((unused))) { mysql_enable_rpl_parse(&cur_con->mysql); return 0; } -int do_disable_rpl_parse(struct st_query *query __attribute__((unused))) +int do_disable_rpl_parse(struct st_command *command __attribute__((unused))) { mysql_disable_rpl_parse(&cur_con->mysql); return 0; @@ -1523,17 +2370,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. @@ -1544,39 +2391,43 @@ int do_disable_rpl_parse(struct st_query *query __attribute__((unused))) used for cpu-independent delays. */ -int do_sleep(struct st_query *query, my_bool real_sleep) +int do_sleep(struct st_command *command, my_bool real_sleep) { int error= 0; - char *p= query->first_argument; - char *sleep_start, *sleep_end= query->end; + char *p= command->first_argument; + char *sleep_start, *sleep_end= command->end; double sleep_val; while (my_isspace(charset_info, *p)) p++; if (!*p) - die("Missing argument to %.*s", query->first_word_len, query->query); + die("Missing argument to %.*s", command->first_word_len, command->query); sleep_start= p; /* Check that arg starts with a digit, not handled by my_strtod */ if (!my_isdigit(charset_info, *sleep_start)) - die("Invalid argument to %.*s \"%s\"", query->first_word_len, query->query, - query->first_argument); + die("Invalid argument to %.*s \"%s\"", command->first_word_len, + command->query,command->first_argument); sleep_val= my_strtod(sleep_start, &sleep_end, &error); if (error) - die("Invalid argument to %.*s \"%s\"", query->first_word_len, query->query, - query->first_argument); + die("Invalid argument to %.*s \"%s\"", command->first_word_len, + command->query, command->first_argument); /* Fixed sleep time selected by --sleep option */ - if (opt_sleep && !real_sleep) + if (opt_sleep >= 0 && !real_sleep) sleep_val= opt_sleep; - my_sleep((ulong) (sleep_val * 1000000L)); - query->last_argument= sleep_end; + DBUG_PRINT("info", ("sleep_val: %f", sleep_val)); + if (sleep_val) + my_sleep((ulong) (sleep_val * 1000000L)); + command->last_argument= sleep_end; return 0; } -static void get_file_name(char *filename, struct st_query *q) + +void do_get_file_name(struct st_command *command, + char* dest, uint dest_max_len) { - char *p= q->first_argument, *name; + char *p= command->first_argument, *name; if (!*p) die("Missing file name argument"); name= p; @@ -1584,13 +2435,14 @@ static void get_file_name(char *filename, struct st_query *q) p++; if (*p) *p++= 0; - q->last_argument= p; - strmake(filename, name, FN_REFLEN); + command->last_argument= p; + strmake(dest, name, dest_max_len); } -static void set_charset(struct st_query *q) + +void do_set_charset(struct st_command *command) { - char *charset_name= q->first_argument; + char *charset_name= command->first_argument; char *p; if (!charset_name || !*charset_name) @@ -1601,49 +2453,182 @@ static void set_charset(struct st_query *q) p++; if(*p) *p++= 0; - q->last_argument= p; + command->last_argument= p; charset_info= get_charset_by_csname(charset_name,MY_CS_PRIMARY,MYF(MY_WME)); if (!charset_info) - abort_not_supported_test(); + abort_not_supported_test("Test requires charset '%s'", charset_name); +} + + +#if MYSQL_VERSION_ID >= 50000 +/* List of error names to error codes, available from 5.0 */ +typedef struct +{ + const char *name; + uint code; +} st_error; + +static st_error global_error_names[] = +{ +#include <mysqld_ername.h> + { 0, 0 } +}; + +uint get_errcode_from_name(char *error_name, char *error_end) +{ + /* SQL error as string */ + st_error *e= global_error_names; + + DBUG_ENTER("get_errcode_from_name"); + 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)) + { + DBUG_RETURN(e->code); + } + } + if (!e->name) + die("Unknown SQL error name '%s'", error_name); + DBUG_RETURN(0); } +#else +uint get_errcode_from_name(char *error_name __attribute__((unused)), + char *error_end __attribute__((unused))) +{ + abort_not_in_this_version(); + return 0; /* Never reached */ +} +#endif + + -static uint get_errcodes(match_err *to,struct st_query *q) +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 */ - int i; - p++; - for (i = 0; my_isalnum(charset_info, *p) && i < SQLSTATE_LENGTH; p++, i++) - to[count].code.sqlstate[i]= *p; - to[count].code.sqlstate[i]= '\0'; - to[count].type= ERR_SQLSTATE; + char *to_ptr= to->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); + + /* Check sqlstate string validity */ + while (*p && p < end) + { + if (my_isdigit(charset_info, *p) || my_isupper(charset_info, *p)) + *to_ptr++= *p++; + else + die("The sqlstate may only consist of digits[0-9] " \ + "and _uppercase_ letters"); + } + + *to_ptr= 0; + to->type= ERR_SQLSTATE; + DBUG_PRINT("info", ("ERR_SQLSTATE: %s", to->code.sqlstate)); + } + else if (*p == 's') + { + die("The sqlstate definition must start with an uppercase S"); + } + else if (*p == 'E') + { + /* Error name string */ + + DBUG_PRINT("info", ("Error name: %s", p)); + to->code.errnum= get_errcode_from_name(p, end); + to->type= ERR_ERRNO; + DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum)); + } + else if (*p == 'e') + { + die("The error name definition must start with an uppercase E"); } else { long val; - p=str2int(p,10,(long) INT_MIN, (long) INT_MAX, &val); - if (p == NULL) - die("Invalid argument in %s", q->query); - to[count].code.errnum= (uint) val; - to[count].type= ERR_ERRNO; + char *start= p; + /* Check that the string passed to str2int only contain digits */ + while (*p && p != end) + { + if (!my_isdigit(charset_info, *p)) + die("Invalid argument to error: '%s' - "\ + "the errno may only consist of digits[0-9]", + command->first_argument); + p++; + } + + /* Convert the sting to int */ + if (!str2int(start, 10, (long) INT_MIN, (long) INT_MAX, &val)) + die("Invalid argument to error: '%s'", command->first_argument); + + to->code.errnum= (uint) val; + to->type= ERR_ERRNO; + DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum)); } + to++; count++; - } while (*(p++) == ','); - q->last_argument= (p - 1); - to[count].type= ERR_EMPTY; /* End of data */ - DBUG_RETURN(count); + + if (count >= (sizeof(saved_expected_errors.err) / + sizeof(struct st_match_err))) + die("Too many errorcodes specified"); + + /* Set pointer to the end of the last error code */ + p= end; + + /* Find next ',' */ + while (*p && *p != ',') + p++; + + if (*p) + p++; /* Step past ',' */ + + } while (*p); + + command->last_argument= p; + to->type= ERR_EMPTY; /* End of data */ + + DBUG_PRINT("info", ("Expected errors: %d", count)); + saved_expected_errors.count= count; + DBUG_VOID_RETURN; } + /* Get a string; Return ptr to end of string Strings may be surrounded by " or ' @@ -1651,11 +2636,10 @@ static uint get_errcodes(match_err *to,struct st_query *q) If string is a '$variable', return the value of the variable. */ - -static char *get_string(char **to_ptr, char **from_ptr, - struct st_query *q) +char *get_string(char **to_ptr, char **from_ptr, + struct st_command *command) { - reg1 char c,sep; + char c, sep; char *to= *to_ptr, *from= *from_ptr, *start=to; DBUG_ENTER("get_string"); @@ -1701,7 +2685,7 @@ static char *get_string(char **to_ptr, char **from_ptr, *to++=c; } if (*from != ' ' && *from) - die("Wrong string argument in %s", q->query); + die("Wrong string argument in %s", command->query); while (my_isspace(charset_info,*from)) /* Point to next string */ from++; @@ -1725,93 +2709,49 @@ static char *get_string(char **to_ptr, char **from_ptr, } -/* - Get arguments for replace. The syntax is: - replace from to [from to ...] - Where each argument may be quoted with ' or " - A argument may also be a variable, in which case the value of the - variable is replaced. -*/ - -static void get_replace(struct st_query *q) +void set_reconnect(MYSQL* mysql, int val) { - uint i; - char *from= q->first_argument; - char *buff,*start; - char word_end_chars[256],*pos; - POINTER_ARRAY to_array,from_array; - DBUG_ENTER("get_replace"); - - free_replace(); - - bzero((char*) &to_array,sizeof(to_array)); - bzero((char*) &from_array,sizeof(from_array)); - if (!*from) - die("Missing argument in %s", q->query); - start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE)); - while (*from) - { - char *to=buff; - to=get_string(&buff, &from, q); - if (!*from) - die("Wrong number of arguments to replace_result in '%s'", q->query); - insert_pointer_name(&from_array,to); - to=get_string(&buff, &from, q); - insert_pointer_name(&to_array,to); - } - for (i=1,pos=word_end_chars ; i < 256 ; i++) - if (my_isspace(charset_info,i)) - *pos++= i; - *pos=0; /* End pointer */ - if (!(glob_replace=init_replace((char**) from_array.typelib.type_names, - (char**) to_array.typelib.type_names, - (uint) from_array.typelib.count, - word_end_chars)) || - initialize_replace_buffer()) - die("Can't initialize replace from '%s'", q->query); - free_pointer_array(&from_array); - free_pointer_array(&to_array); - my_free(start, MYF(0)); - q->last_argument= q->end; + my_bool reconnect= val; + DBUG_ENTER("set_reconnect"); + DBUG_PRINT("info", ("val: %d", val)); +#if MYSQL_VERSION_ID < 50000 + mysql->reconnect= reconnect; +#else + mysql_options(mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect); +#endif DBUG_VOID_RETURN; } -void free_replace() + +struct st_connection * find_connection_by_name(const char *name) { - DBUG_ENTER("free_replace"); - if (glob_replace) + struct st_connection *con; + for (con= connections; con < next_con; con++) { - my_free((char*) glob_replace,MYF(0)); - glob_replace=0; - free_replace_buffer(); + if (!strcmp(con->name, name)) + { + return con; + } } - DBUG_VOID_RETURN; + return 0; /* Connection not found */ } int select_connection_name(const char *name) { - struct connection *con; DBUG_ENTER("select_connection2"); DBUG_PRINT("enter",("name: '%s'", name)); - for (con= cons; con < next_con; con++) - { - if (!strcmp(con->name, name)) - { - cur_con= con; - DBUG_RETURN(0); - } - } - die("connection '%s' not found in connection pool", name); - DBUG_RETURN(1); /* Never reached */ + if (!(cur_con= find_connection_by_name(name))) + die("connection '%s' not found in connection pool", name); + DBUG_RETURN(0); } -int select_connection(struct st_query *query) +int select_connection(struct st_command *command) { char *name; - char *p= query->first_argument; + char *p= command->first_argument; DBUG_ENTER("select_connection"); if (!*p) @@ -1821,33 +2761,38 @@ int select_connection(struct st_query *query) p++; if (*p) *p++= 0; - query->last_argument= p; + command->last_argument= p; return select_connection_name(name); } -int close_connection(struct st_query *q) +void do_close_connection(struct st_command *command) { - char *p= q->first_argument, *name; - struct connection *con; + char *p= command->first_argument, *name; + struct st_connection *con; + DBUG_ENTER("close_connection"); DBUG_PRINT("enter",("name: '%s'",p)); if (!*p) - die("Missing connection name in connect"); + die("Missing connection name in disconnect"); name= p; while (*p && !my_isspace(charset_info,*p)) p++; if (*p) *p++= 0; - q->last_argument= p; - for (con= cons; con < next_con; con++) + command->last_argument= p; + + /* Loop through connection pool for connection to close */ + for (con= connections; con < next_con; con++) { + DBUG_PRINT("info", ("con->name: %s", con->name)); if (!strcmp(con->name, name)) { + DBUG_PRINT("info", ("Closing connection %s", con->name)); #ifndef EMBEDDED_LIBRARY - if (q->type == Q_DIRTY_CLOSE) + if (command->type == Q_DIRTY_CLOSE) { if (con->mysql.net.vio) { @@ -1857,172 +2802,358 @@ int close_connection(struct st_query *q) } #endif mysql_close(&con->mysql); - DBUG_RETURN(0); + if (con->util_mysql) + mysql_close(con->util_mysql); + con->util_mysql= 0; + my_free(con->name, MYF(0)); + + /* + When the connection is closed set name to "closed_connection" + to make it possible to reuse the connection name. + The connection slot will not be reused + */ + if (!(con->name = my_strdup("closed_connection", MYF(MY_WME)))) + die("Out of memory"); + + DBUG_VOID_RETURN; } } die("connection '%s' not found in connection pool", name); - DBUG_RETURN(1); /* Never reached */ } /* - This one now is a hack - we may want to improve in in the - future to handle quotes. For now we assume that anything that is not - a comma, a space or ) belongs to the argument. space is a chopper, comma or - ) are delimiters/terminators + Connect to a server doing several retries if needed. + + SYNOPSIS + safe_connect() + con - connection structure to be used + host, user, pass, - connection parameters + db, port, sock + + NOTE + + Sometimes in a test the client starts before + the server - to solve the problem, we try again + after some sleep if connection fails the first + time + + This function will try to connect to the given server + "opt_max_connect_retries" times and sleep "connection_retry_sleep" + seconds between attempts before finally giving up. + This helps in situation when the client starts + before the server (which happens sometimes). + It will only ignore connection errors during these retries. + */ -char* safe_get_param(char *str, char** arg, const char *msg) +void safe_connect(MYSQL* mysql, const char *name, const char *host, + const char *user, const char *pass, const char *db, + int port, const char *sock) { - DBUG_ENTER("safe_get_param"); - while (*str && my_isspace(charset_info,*str)) - str++; - *arg= str; - for (; *str && *str != ',' && *str != ')' ; str++) + int failed_attempts= 0; + static ulong connection_retry_sleep= 100000; /* Microseconds */ + + DBUG_ENTER("safe_connect"); + while(!mysql_real_connect(mysql, host,user, pass, db, port, sock, + CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS)) { - if (my_isspace(charset_info,*str)) - *str= 0; - } - if (!*str) - die(msg); + /* + Connect failed - *str++= 0; - DBUG_RETURN(str); + Only allow retry if this was an error indicating the server + could not be contacted + */ + + if (mysql_errno(mysql) == CR_CONNECTION_ERROR && + failed_attempts < opt_max_connect_retries) + my_sleep(connection_retry_sleep); + else + { + 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)); + } + failed_attempts++; + } + DBUG_VOID_RETURN; } -#ifndef EMBEDDED_LIBRARY -void init_manager() -{ - if (!(manager=mysql_manager_init(0))) - die("Failed in mysql_manager_init()"); - if (!mysql_manager_connect(manager,manager_host,manager_user, - manager_pass,manager_port)) - die("Could not connect to MySQL manager: %s(%d)",manager->last_error, - manager->last_errno); -} -#endif +/* + Connect to a server and handle connection errors in case they occur. + + SYNOPSIS + connect_n_handle_errors() + q - context of connect "query" (command) + con - connection structure to be used + host, user, pass, - connection parameters + db, port, sock + + DESCRIPTION + This function will try to establish a connection to server and handle + possible errors in the same manner as if "connect" was usual SQL-statement + (If error is expected it will ignore it once it occurs and log the + "statement" to the query log). + Unlike safe_connect() it won't do several attempts. -int safe_connect(MYSQL* con, const char *host, const char *user, - const char *pass, - const char *db, int port, const char *sock) + RETURN VALUES + 1 - Connected + 0 - Not connected + +*/ + +int connect_n_handle_errors(struct st_command *command, + MYSQL* con, const char* host, + const char* user, const char* pass, + const char* db, int port, const char* sock) { - int con_error = 1; - int i; - for (i = 0; i < MAX_CON_TRIES; ++i) + DYNAMIC_STRING *ds; + + ds= &ds_res; + + /* Only log if an error is expected */ + if (!command->abort_on_error && + !disable_query_log) { - if (mysql_real_connect(con, host,user, pass, db, port, sock, - CLIENT_MULTI_STATEMENTS)) - { - con_error = 0; - break; - } - sleep(CON_RETRY_SLEEP); + /* + Log the connect to result log + */ + dynstr_append_mem(ds, "connect(", 8); + replace_dynstr_append(ds, host); + dynstr_append_mem(ds, ",", 1); + replace_dynstr_append(ds, user); + dynstr_append_mem(ds, ",", 1); + replace_dynstr_append(ds, pass); + dynstr_append_mem(ds, ",", 1); + if (db) + replace_dynstr_append(ds, db); + dynstr_append_mem(ds, ",", 1); + replace_dynstr_append_uint(ds, port); + dynstr_append_mem(ds, ",", 1); + if (sock) + replace_dynstr_append(ds, sock); + dynstr_append_mem(ds, ")", 1); + dynstr_append_mem(ds, delimiter, delimiter_length); + dynstr_append_mem(ds, "\n", 1); + } + if (!mysql_real_connect(con, host, user, pass, db, port, sock ? sock: 0, + CLIENT_MULTI_STATEMENTS)) + { + handle_error(command, mysql_errno(con), mysql_error(con), + mysql_sqlstate(con), ds); + return 0; /* Not connected */ } - return con_error; + + handle_no_error(command); + return 1; /* Connected */ } -int do_connect(struct st_query *q) +/* + Open a new connection to MySQL Server with the parameters + specified. Make the new connection the current connection. + + SYNOPSIS + do_connect() + q called command + + DESCRIPTION + connect(<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) { - char *con_name, *con_user,*con_pass, *con_host, *con_port_str, - *con_db, *con_sock; - char *p= q->first_argument; - char buff[FN_REFLEN]; - int con_port; - int free_con_sock= 0; + int con_port= port; + char *con_options; + bool con_ssl= 0, con_compress= 0; + char *ptr; + + static DYNAMIC_STRING ds_connection_name; + static DYNAMIC_STRING ds_host; + static DYNAMIC_STRING ds_user; + static DYNAMIC_STRING ds_password; + static DYNAMIC_STRING ds_database; + static DYNAMIC_STRING ds_port; + static DYNAMIC_STRING ds_sock; + static DYNAMIC_STRING ds_options; + const struct command_arg connect_args[] = { + "connection name", ARG_STRING, TRUE, &ds_connection_name, + "Name of the connection", + "host", ARG_STRING, TRUE, &ds_host, "Host to connect to", + "user", ARG_STRING, FALSE, &ds_user, "User to connect as", + "passsword", ARG_STRING, FALSE, &ds_password, + "Password used when connecting", + "database", ARG_STRING, FALSE, &ds_database, + "Dtabase to select after connect", + "port", ARG_STRING, FALSE, &ds_port, "Port to connect to", + "socket", ARG_STRING, FALSE, &ds_sock, "Socket to connect with", + "options", ARG_STRING, FALSE, &ds_options, + "Options to use while connecting" + }; DBUG_ENTER("do_connect"); - DBUG_PRINT("enter",("connect: %s",p)); + DBUG_PRINT("enter",("connect: %s", command->first_argument)); - if (*p != '(') - die("Syntax error in connect - expected '(' found '%c'", *p); - p++; - p= safe_get_param(p, &con_name, "missing connection name"); - p= safe_get_param(p, &con_host, "missing connection host"); - p= safe_get_param(p, &con_user, "missing connection user"); - p= safe_get_param(p, &con_pass, "missing connection password"); - p= safe_get_param(p, &con_db, "missing connection db"); - if (!*p || *p == ';') /* Default port and sock */ + /* Remove parenteses around connect arguments */ + if ((ptr= strstr(command->first_argument, "("))) { - con_port= port; - con_sock= (char*) unix_sock; - } - else - { - VAR* var_port, *var_sock; - p= safe_get_param(p, &con_port_str, "missing connection port"); - 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); - p= safe_get_param(p, &con_sock, "missing connection socket"); - if (*con_sock == '$') + die("connect - argument list started with '(' must be ended with ')'"); + } + + check_command_args(command, command->first_argument, connect_args, + sizeof(connect_args)/sizeof(struct command_arg), + ','); + + /* Port */ + if (ds_port.length) + { + con_port= atoi(ds_port.str); + if (con_port == 0) + die("Illegal argument for port: '%s'", ds_port.str); + } + + /* Sock */ + if (ds_sock.length) + { + /* + If the socket is specified just as a name without path + append tmpdir in front + */ + if (*ds_sock.str != FN_LIBCHAR) { - 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); } } - q->last_argument= p; + else + { + /* No socket specified, use default */ + dynstr_set(&ds_sock, unix_sock); + } + DBUG_PRINT("info", ("socket: %s", ds_sock.str)); + + + /* Options */ + con_options= ds_options.str; + while (*con_options) + { + char* end; + /* Step past any spaces in beginning of option*/ + while (*con_options && my_isspace(charset_info, *con_options)) + con_options++; + /* Find end of this option */ + end= con_options; + while (*end && !my_isspace(charset_info, *end)) + end++; + if (!strncmp(con_options, "SSL", 3)) + con_ssl= 1; + else if (!strncmp(con_options, "COMPRESS", 8)) + con_compress= 1; + else + die("Illegal option to connect: %.*s", end - con_options, con_options); + /* Process next option */ + con_options= end; + } + + if (next_con == connections_end) + die("Connection limit exhausted, you can have max %d connections", + (sizeof(connections)/sizeof(struct st_connection))); - if (next_con == cons_end) - die("Connection limit exhausted - increase MAX_CONS in mysqltest.c"); + if (find_connection_by_name(ds_connection_name.str)) + die("Connection %s already exists", ds_connection_name.str); if (!mysql_init(&next_con->mysql)) die("Failed on mysql_init()"); - if (opt_compress) - mysql_options(&next_con->mysql,MYSQL_OPT_COMPRESS,NullS); + if (opt_compress || con_compress) + mysql_options(&next_con->mysql, MYSQL_OPT_COMPRESS, NullS); mysql_options(&next_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0); mysql_options(&next_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name); #ifdef HAVE_OPENSSL - if (opt_use_ssl) + if (opt_use_ssl || con_ssl) + { mysql_ssl_set(&next_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); +#if MYSQL_VERSION_ID >= 50000 + /* Turn on ssl_verify_server_cert only if host is "localhost" */ + opt_ssl_verify_server_cert= !strcmp(ds_connection_name.str, "localhost"); + mysql_options(&next_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, + &opt_ssl_verify_server_cert); #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; + } +#endif + + /* 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 ((safe_connect(&next_con->mysql, con_host, - con_user, con_pass, - con_db, con_port, con_sock ? con_sock: 0))) - die("Could not open connection '%s': %s", con_name, - mysql_error(&next_con->mysql)); - - if (!(next_con->name= my_strdup(con_name, MYF(MY_WME)))) - die(NullS); - cur_con= next_con++; - if (free_con_sock) - my_free(con_sock, MYF(MY_WME)); - DBUG_RETURN(0); + if (ds_database.length && !strcmp(ds_database.str,"*NO-ONE*")) + dynstr_set(&ds_database, ""); + + + if (connect_n_handle_errors(command, &next_con->mysql, + ds_host.str,ds_user.str, + ds_password.str, ds_database.str, + con_port, ds_sock.str)) + { + DBUG_PRINT("info", ("Inserting connection %s in connection pool", + ds_connection_name.str)); + if (!(next_con->name= my_strdup(ds_connection_name.str, MYF(MY_WME)))) + die("Out of memory"); + cur_con= next_con++; + } + + dynstr_free(&ds_connection_name); + dynstr_free(&ds_host); + dynstr_free(&ds_user); + dynstr_free(&ds_password); + dynstr_free(&ds_database); + dynstr_free(&ds_port); + dynstr_free(&ds_sock); + dynstr_free(&ds_options); + DBUG_VOID_RETURN; } -int do_done(struct st_query *q) +int do_done(struct st_command *command) { /* Check if empty block stack */ if (cur_block == block_stack) { - if (*q->query != '}') + if (*command->query != '}') die("Stray 'end' command - end of block before beginning"); die("Stray '}' - end of block before beginning"); } /* Test if inner block has been executed */ - if (cur_block->ok && cur_block->cmd == Q_WHILE) + if (cur_block->ok && cur_block->cmd == cmd_while) { /* Pop block from stack, re-execute outer block */ cur_block--; @@ -2038,12 +3169,41 @@ int do_done(struct st_query *q) } -int do_block(enum enum_commands cmd, struct st_query *q) +/* + Process start of a "if" or "while" statement + + SYNOPSIS + do_block() + cmd Type of block + q called command + + DESCRIPTION + if ([!]<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. + +*/ + +void do_block(enum block_cmd cmd, struct st_command* command) { - char *p= q->first_argument; + char *p= command->first_argument; const char *expr_start, *expr_end; VAR v; - const char *cmd_name= (cmd == Q_WHILE ? "while" : "if"); + const char *cmd_name= (cmd == cmd_while ? "while" : "if"); + my_bool not_expr= FALSE; + DBUG_ENTER("do_block"); + DBUG_PRINT("enter", ("%s", cmd_name)); /* Check stack overflow */ if (cur_block == block_stack_end) @@ -2059,13 +3219,21 @@ int do_block(enum enum_commands cmd, struct st_query *q) cur_block++; cur_block->cmd= cmd; cur_block->ok= FALSE; - return 0; + DBUG_VOID_RETURN; } /* Parse and evaluate test expression */ expr_start= strchr(p, '('); - if (!expr_start) + if (!expr_start++) die("missing '(' in %s", cmd_name); + + /* Check for !<expr> */ + if (*expr_start == '!') + { + not_expr= TRUE; + expr_start++; /* Step past the '!' */ + } + /* Find ending ')' */ expr_end= strrchr(expr_start, ')'); if (!expr_end) die("missing ')' in %s", cmd_name); @@ -2073,61 +3241,62 @@ int do_block(enum enum_commands cmd, struct st_query *q) while (*p && my_isspace(charset_info, *p)) p++; - if (*p == '{') - die("Missing newline between %s and '{'", cmd_name); - if (*p) + if (*p && *p != '{') die("Missing '{' after %s. Found \"%s\"", cmd_name, p); var_init(&v,0,0,0,0); - eval_expr(&v, ++expr_start, &expr_end); + eval_expr(&v, expr_start, &expr_end); /* Define inner block */ cur_block++; cur_block->cmd= cmd; cur_block->ok= (v.int_val ? TRUE : FALSE); + if (not_expr) + cur_block->ok = !cur_block->ok; + + DBUG_PRINT("info", ("OK: %d", cur_block->ok)); + var_free(&v); - return 0; + DBUG_VOID_RETURN; } -/* - Read characters from line buffer or file. This is needed to allow - my_ungetc() to buffer MAX_DELIMITER characters for a file +void do_delimiter(struct st_command* command) +{ + char* p= command->first_argument; + DBUG_ENTER("do_delimiter"); + DBUG_PRINT("enter", ("first_argument: %s", command->first_argument)); - 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. -*/ + while (*p && my_isspace(charset_info, *p)) + p++; -int my_getc(FILE *file) -{ - if (line_buffer_pos == line_buffer) - return fgetc(file); - return *--line_buffer_pos; -} + if (!(*p)) + die("Can't set empty delimiter"); -void my_ungetc(int c) -{ - *line_buffer_pos++= (char) c; + strmake(delimiter, p, sizeof(delimiter) - 1); + delimiter_length= strlen(delimiter); + + DBUG_PRINT("exit", ("delimiter: %s", delimiter)); + command->last_argument= p + delimiter_length; + DBUG_VOID_RETURN; } -my_bool end_of_query(int c) +my_bool match_delimiter(int c, const char *delim, uint length) { uint i; - char tmp[MAX_DELIMITER]; + char tmp[MAX_DELIMITER_LENGTH]; - if (c != *delimiter) + if (c != *delim) return 0; - for (i= 1; i < delimiter_length && - (c= my_getc(cur_file->file)) == *(delimiter + i); + for (i= 1; i < length && + (c= my_getc(cur_file->file)) == *(delim + i); i++) tmp[i]= c; - if (i == delimiter_length) + if (i == length) return 1; /* Found delimiter */ /* didn't find delimiter, push back things that we read */ @@ -2138,44 +3307,51 @@ my_bool end_of_query(int c) } +my_bool end_of_query(int c) +{ + return match_delimiter(c, delimiter, delimiter_length); +} + + /* Read one "line" from the file SYNOPSIS - read_line - buf buffer for the read line - size size of the buffer i.e max size to read + read_line + buf buffer for the read line + size size of the buffer i.e max size to read DESCRIPTION - This function actually reads several lines an adds them to the - buffer buf. It will continue to read until it finds what it believes - is a complete query. + This function actually reads several lines and adds them to the + buffer buf. It continues to read until it finds what it believes + is a complete query. - Normally that means it will read lines until it reaches the - "delimiter" that marks end of query. Default delimiter is ';' - The function should be smart enough not to detect delimiter's - found inside strings surrounded with '"' and '\'' escaped strings. + Normally that means it will read lines until it reaches the + "delimiter" that marks end of query. Default delimiter is ';' + The function should be smart enough not to detect delimiter's + found inside strings surrounded with '"' and '\'' escaped strings. - If the first line in a query starts with '#' or '-' this line is treated - as a comment. A comment is always terminated when end of line '\n' is - reached. + If the first line in a query starts with '#' or '-' this line is treated + as a comment. A comment is always terminated when end of line '\n' is + reached. */ int read_line(char *buf, int size) { - int c; + char c, last_quote; char *p= buf, *buf_end= buf + size - 1; - int no_save= 0; - enum {R_NORMAL, R_Q1, R_ESC_Q_Q1, R_ESC_Q_Q2, - R_ESC_SLASH_Q1, R_ESC_SLASH_Q2, - R_Q2, R_COMMENT, R_LINE_START} state= R_LINE_START; + int skip_char= 0; + enum {R_NORMAL, R_Q, R_SLASH_IN_Q, + R_COMMENT, R_LINE_START} state= R_LINE_START; DBUG_ENTER("read_line"); + LINT_INIT(last_quote); - start_lineno= *lineno; + start_lineno= cur_file->lineno; + DBUG_PRINT("info", ("Starting to read at lineno: %d", start_lineno)); for (; p < buf_end ;) { - no_save= 0; + skip_char= 0; c= my_getc(cur_file->file); if (feof(cur_file->file)) { @@ -2187,127 +3363,130 @@ int read_line(char *buf, int size) } my_free((gptr)cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR)); cur_file->file_name= 0; - lineno--; - start_lineno= *lineno; if (cur_file == file_stack) { /* We're back at the first file, check if all { have matching } - */ + */ if (cur_block != block_stack) - { - start_lineno= *(lineno+1); die("Missing end of block"); - } - DBUG_RETURN(1); + + *p= 0; + DBUG_PRINT("info", ("end of file at line %d", cur_file->lineno)); + DBUG_RETURN(1); } cur_file--; + start_lineno= cur_file->lineno; continue; } - /* Line counting is independent of state */ if (c == '\n') - (*lineno)++; + { + /* Line counting is independent of state */ + cur_file->lineno++; + + /* Convert cr/lf to lf */ + if (p != buf && *(p-1) == '\r') + p--; + } switch(state) { case R_NORMAL: - /* Only accept '{' in the beginning of a line */ if (end_of_query(c)) { *p= 0; + DBUG_PRINT("exit", ("Found delimiter '%s' at line %d", + delimiter, cur_file->lineno)); DBUG_RETURN(0); } - else if (c == '\'') - state = R_Q1; - else if (c == '"') - state = R_Q2; - else if (c == '\n') + else if ((c == '{' && + (!my_strnncoll_simple(charset_info, "while", 5, + buf, min(5, p - buf), 0) || + !my_strnncoll_simple(charset_info, "if", 2, + buf, min(2, p - buf), 0)))) { - state = R_LINE_START; + /* Only if and while commands can be terminated by { */ + *p++= c; + *p= 0; + DBUG_PRINT("exit", ("Found '{' indicating start of block at line %d", + cur_file->lineno)); + DBUG_RETURN(0); + } + else if (c == '\'' || c == '"' || c == '`') + { + last_quote= c; + state= R_Q; } break; + case R_COMMENT: if (c == '\n') { + /* Comments are terminated by newline */ *p= 0; + DBUG_PRINT("exit", ("Found newline in comment at line: %d", + cur_file->lineno)); DBUG_RETURN(0); } break; + case R_LINE_START: - /* Only accept start of comment if this is the first line in query */ - if ((*lineno == start_lineno) && (c == '#' || c == '-')) + if (c == '#' || c == '-') { + /* A # or - in the first position of the line - this is a comment */ state = R_COMMENT; } else if (my_isspace(charset_info, c)) { + /* Skip all space at begining of line */ if (c == '\n') - start_lineno= *lineno; /* Query hasn't started yet */ - no_save= 1; + { + /* Query hasn't started yet */ + start_lineno= cur_file->lineno; + DBUG_PRINT("info", ("Query hasn't started yet, start_lineno: %d", + start_lineno)); + } + skip_char= 1; } - else if (c == '}') + else if (end_of_query(c)) { - *buf++= '}'; - *buf= 0; + *p= 0; + DBUG_PRINT("exit", ("Found delimiter '%s' at line: %d", + delimiter, cur_file->lineno)); DBUG_RETURN(0); } - else if (end_of_query(c) || c == '{') + else if (c == '}') { + /* A "}" need to be by itself in the begining of a line to terminate */ + *p++= c; *p= 0; + DBUG_PRINT("exit", ("Found '}' in begining of a line at line: %d", + cur_file->lineno)); DBUG_RETURN(0); } - else if (c == '\'') - state= R_Q1; - else if (c == '"') - state= R_Q2; - else - state= R_NORMAL; - break; - - case R_Q1: - if (c == '\'') - state= R_ESC_Q_Q1; - else if (c == '\\') - state= R_ESC_SLASH_Q1; - break; - case R_ESC_Q_Q1: - if (end_of_query(c)) + else if (c == '\'' || c == '"' || c == '`') { - *p= 0; - DBUG_RETURN(0); + last_quote= c; + state= R_Q; } - if (c != '\'') - state= R_NORMAL; else - state= R_Q1; - break; - case R_ESC_SLASH_Q1: - state= R_Q1; + state= R_NORMAL; break; - case R_Q2: - if (c == '"') - state= R_ESC_Q_Q2; - else if (c == '\\') - state= R_ESC_SLASH_Q2; - break; - case R_ESC_Q_Q2: - if (end_of_query(c)) - { - *p= 0; - DBUG_RETURN(0); - } - if (c != '"') + case R_Q: + if (c == last_quote) state= R_NORMAL; - else - state= R_Q2; + else if (c == '\\') + state= R_SLASH_IN_Q; break; - case R_ESC_SLASH_Q2: - state= R_Q2; + + case R_SLASH_IN_Q: + state= R_Q; break; + } - if (!no_save) + if (!skip_char) { /* Could be a multibyte character */ /* This code is based on the code in "sql_load.cc" */ @@ -2325,7 +3504,7 @@ int read_line(char *buf, int size) for (i= 1; i < charlen; i++) { if (feof(cur_file->file)) - goto found_eof; /* FIXME: could we just break here?! */ + goto found_eof; c= my_getc(cur_file->file); *p++ = c; } @@ -2342,102 +3521,253 @@ int read_line(char *buf, int size) *p++= c; } } - *p= 0; /* Always end with \0 */ - DBUG_RETURN(feof(cur_file->file)); + die("The input buffer is too small for this query.x\n" \ + "check your query or increase MAX_QUERY and recompile"); + DBUG_RETURN(0); } + /* - Create a query from a set of lines + Convert the read query to result format version 1 + + That is: After newline, all spaces need to be skipped + unless the previous char was a quote + + This is due to an old bug that has now been fixed, but the + version 1 output format is preserved by using this function + +*/ + +void convert_to_format_v1(char* query) +{ + int last_c_was_quote= 0; + char *p= query, *write= query; + char *end= strend(query); + char last_c; + + while (p <= end) + { + if (*p == '\n' && !last_c_was_quote) + { + *write++ = *p++; /* Save the newline */ + + /* Skip any spaces on next line */ + while (*p && my_isspace(charset_info, *p)) + p++; + + last_c_was_quote= 0; + } + else if (*p == '\'' || *p == '"' || *p == '`') + { + last_c= *p; + *write++ = *p++; + + /* Copy anything until the next quote of same type */ + while (*p && *p != last_c) + *write++ = *p++; + + *write++ = *p++; + + last_c_was_quote= 1; + } + else + { + *write++ = *p++; + last_c_was_quote= 0; + } + } +} + + +/* + 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 or when + switching between different delimiters +*/ + +void check_eol_junk_line(const char *line) +{ + const char *p= line; + DBUG_ENTER("check_eol_junk_line"); + DBUG_PRINT("enter", ("line: %s", line)); + + /* Check for extra delimiter */ + if (*p && !strncmp(p, delimiter, delimiter_length)) + 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; +} + +void check_eol_junk(const char *eol) +{ + const char *p= eol; + DBUG_ENTER("check_eol_junk"); + DBUG_PRINT("enter", ("eol: %s", eol)); + + /* Skip past all spacing chars and comments */ + while (*p && (my_isspace(charset_info, *p) || *p == '#' || *p == '\n')) + { + /* Skip past comments started with # and ended with newline */ + if (*p && *p == '#') + { + p++; + while (*p && *p != '\n') + p++; + } + + /* Check this line */ + if (*p && *p == '\n') + check_eol_junk_line(p); + + if (*p) + p++; + } + + check_eol_junk_line(p); + + DBUG_VOID_RETURN; +} + + + +/* + 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. - If query starts with @<file_name> this will specify a file to .... */ -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, *p1; - 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; + command->require_file[0]= 0; + command->first_word_len= 0; + command->query_len= 0; - q->type= Q_UNKNOWN; - q->query_buf= q->query= 0; - if (read_line(read_query_buf, sizeof(read_query_buf))) + command->type= Q_UNKNOWN; + command->query_buf= command->query= 0; + read_command_buf[0]= 0; + if (read_line(read_command_buf, sizeof(read_command_buf))) { - DBUG_PRINT("warning",("too long query")); + check_eol_junk(read_command_buf); DBUG_RETURN(1); } - DBUG_PRINT("info", ("query: %s", read_query_buf)); + + convert_to_format_v1(read_command_buf); + + DBUG_PRINT("info", ("query: %s", read_command_buf)); if (*p == '#') { - q->type= Q_COMMENT; - /* This goto is to avoid losing the "expected error" info. */ - goto end; + command->type= Q_COMMENT; } - memcpy((gptr) q->expected_errno, (gptr) global_expected_errno, - sizeof(global_expected_errno)); - q->expected_errors= global_expected_errors; - q->abort_on_error= (global_expected_errors == 0 && abort_on_error); - if (p[0] == '-' && p[1] == '-') + else if (p[0] == '-' && p[1] == '-') { - q->type= Q_COMMENT_WITH_COMMAND; - p+= 2; /* To calculate first word */ - } - else - { - while (*p && my_isspace(charset_info, *p)) - p++ ; - if (*p == '@') - { - p++; - p1 = q->record_file; - while (!my_isspace(charset_info, *p) && - p1 < q->record_file + sizeof(q->record_file) - 1) - *p1++ = *p++; - *p1 = 0; - } + command->type= Q_COMMENT_WITH_COMMAND; + p+= 2; /* Skip past -- */ } -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); + command->first_argument= p; + + command->end= strend(command->query); + command->query_len= (command->end - command->query); parser.read_lines++; DBUG_RETURN(0); } @@ -2445,39 +3775,41 @@ end: static struct my_option my_long_options[] = { - {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.", - 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, - {"database", 'D', "Database to use.", (gptr*) &db, (gptr*) &db, 0, - GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, + 0, 0, 0, 0, 0, 0}, {"basedir", 'b', "Basedir for tests.", (gptr*) &opt_basedir, (gptr*) &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"big-test", 'B', "Define BIG_TEST to 1.", (gptr*) &opt_big_test, - (gptr*) &opt_big_test, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"compress", 'C', "Use the compressed server/client protocol.", (gptr*) &opt_compress, (gptr*) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, - 0, 0, 0, 0, 0, 0}, + {"cursor-protocol", OPT_CURSOR_PROTOCOL, "Use cursors for prepared statements.", + (gptr*) &cursor_protocol, (gptr*) &cursor_protocol, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"database", 'D', "Database to use.", (gptr*) &db, (gptr*) &db, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#ifdef DBUG_OFF + {"debug", '#', "This is a non-debug version. Catch this and exit", + 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#else + {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.", + 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#endif {"host", 'h', "Connect to host.", (gptr*) &host, (gptr*) &host, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"manager-user", OPT_MANAGER_USER, "Undocumented: Used for debugging.", - (gptr*) &manager_user, (gptr*) &manager_user, 0, GET_STR, REQUIRED_ARG, 0, - 0, 0, 0, 0, 0}, - {"manager-host", OPT_MANAGER_HOST, "Undocumented: Used for debugging.", - (gptr*) &manager_host, (gptr*) &manager_host, 0, GET_STR, REQUIRED_ARG, - 0, 0, 0, 0, 0, 0}, - {"manager-password", OPT_MANAGER_PASSWD, "Undocumented: Used for debugging.", - 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"manager-port", OPT_MANAGER_PORT, "Undocumented: Used for debugging.", - (gptr*) &manager_port, (gptr*) &manager_port, 0, GET_INT, REQUIRED_ARG, - MYSQL_MANAGER_PORT, 0, 0, 0, 0, 0}, - {"manager-wait-timeout", OPT_MANAGER_WAIT_TIMEOUT, - "Undocumented: Used for debugging.", (gptr*) &manager_wait_timeout, - (gptr*) &manager_wait_timeout, 0, GET_INT, REQUIRED_ARG, 3, 0, 0, 0, 0, 0}, + {"include", 'i', "Include SQL before each test case.", (gptr*) &opt_include, + (gptr*) &opt_include, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"mark-progress", OPT_MARK_PROGRESS, + "Write linenumber and elapsed time to <testname>.progress ", + (gptr*) &opt_mark_progress, (gptr*) &opt_mark_progress, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"max-connect-retries", OPT_MAX_CONNECT_RETRIES, + "Max number of connection attempts when connecting to server", + (gptr*) &opt_max_connect_retries, (gptr*) &opt_max_connect_retries, 0, + GET_INT, REQUIRED_ARG, 500, 1, 10000, 0, 0, 0}, {"password", 'p', "Password to use when connecting to server.", 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"port", 'P', "Port number to use for connection.", (gptr*) &port, - (gptr*) &port, 0, GET_INT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0, 0}, + (gptr*) &port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"ps-protocol", OPT_PS_PROTOCOL, "Use prepared statements protocol for communication", (gptr*) &ps_protocol, (gptr*) &ps_protocol, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -2486,8 +3818,8 @@ static struct my_option my_long_options[] = {"record", 'r', "Record output of test_file into result file.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"result-file", 'R', "Read/Store result from/in this file.", - (gptr*) &result_file, (gptr*) &result_file, 0, GET_STR, REQUIRED_ARG, - 0, 0, 0, 0, 0, 0}, + (gptr*) &result_file_name, (gptr*) &result_file_name, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"server-arg", 'A', "Send option value to embedded server as a parameter.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"server-file", 'F', "Read embedded server arguments from file.", @@ -2498,11 +3830,14 @@ static struct my_option my_long_options[] = "Don't use the memory allocation checking.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"sleep", 'T', "Sleep always this many seconds on sleep commands.", - (gptr*) &opt_sleep, (gptr*) &opt_sleep, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, + (gptr*) &opt_sleep, (gptr*) &opt_sleep, 0, GET_INT, REQUIRED_ARG, -1, 0, 0, 0, 0, 0}, {"socket", 'S', "Socket file to use for connection.", (gptr*) &unix_sock, (gptr*) &unix_sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"sp-protocol", OPT_SP_PROTOCOL, "Use stored procedures for select", + (gptr*) &sp_protocol, (gptr*) &sp_protocol, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #include "sslopt-longopts.h" {"test-file", 'x', "Read test from/in this file (default stdin).", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -2516,13 +3851,16 @@ static struct my_option my_long_options[] = GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"view-protocol", OPT_VIEW_PROTOCOL, "Use views for select", + (gptr*) &view_protocol, (gptr*) &view_protocol, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; #include <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); @@ -2543,6 +3881,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) @@ -2556,40 +3940,36 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case 'r': record = 1; break; - case (int)OPT_MANAGER_PASSWD: - my_free(manager_pass,MYF(MY_ALLOW_ZERO_PTR)); - manager_pass=my_strdup(argument, MYF(MY_FAE)); - while (*argument) *argument++= 'x'; /* Destroy argument */ - break; case 'x': + { + char buff[FN_REFLEN]; + if (!test_if_hard_path(argument)) { - char buff[FN_REFLEN]; - if (!test_if_hard_path(argument)) - { - strxmov(buff, opt_basedir, argument, NullS); - argument= buff; - } - fn_format(buff, argument, "", "", 4); - DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0); - if (!(cur_file->file= - my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0)))) - die("Could not open %s: errno = %d", buff, errno); - cur_file->file_name= my_strdup(buff, MYF(MY_FAE)); - break; + strxmov(buff, opt_basedir, argument, NullS); + argument= buff; } + fn_format(buff, argument, "", "", MY_UNPACK_FILENAME); + DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0); + if (!(cur_file->file= + my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0)))) + die("Could not open %s: errno = %d", buff, errno); + cur_file->file_name= my_strdup(buff, MYF(MY_FAE)); + cur_file->lineno= 1; + break; + } case 'm': + { + static char buff[FN_REFLEN]; + if (!test_if_hard_path(argument)) { - static char buff[FN_REFLEN]; - if (!test_if_hard_path(argument)) - { - strxmov(buff, opt_basedir, argument, NullS); - argument= buff; - } - fn_format(buff, argument, "", "", 4); - timer_file= buff; - unlink(timer_file); /* Ignore error, may not exist */ - break; + strxmov(buff, opt_basedir, argument, NullS); + argument= buff; } + fn_format(buff, argument, "", "", MY_UNPACK_FILENAME); + timer_file= buff; + unlink(timer_file); /* Ignore error, may not exist */ + break; + } case 'p': if (argument) { @@ -2611,7 +3991,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), embedded_server_arg_count=1; embedded_server_args[0]= (char*) ""; } - if (embedded_server_arg_count == MAX_SERVER_ARGS-1 || + if (embedded_server_arg_count == MAX_EMBEDDED_SERVER_ARGS-1 || !(embedded_server_args[embedded_server_arg_count++]= my_strdup(argument, MYF(MY_FAE)))) { @@ -2619,8 +3999,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), } break; case 'F': - if (read_server_arguments(argument)) - die(NullS); + read_embedded_server_arguments(argument); break; case OPT_SKIP_SAFEMALLOC: #ifdef SAFEMALLOC @@ -2659,14 +4038,16 @@ int parse_args(int argc, char **argv) return 0; } -char* safe_str_append(char *buf, const char *str, int size) -{ - int i,c ; - for (i = 0; (c = *str++) && i < size - 1; i++) - *buf++ = c; - *buf = 0; - return buf; -} + +/* + Write the content of str into file + + SYNOPSIS + str_to_file + fname - name of file to truncate/create and write to + str - content to write to file + size - size of content witten to file +*/ void str_to_file(const char *fname, char *str, int size) { @@ -2675,38 +4056,222 @@ void str_to_file(const char *fname, char *str, int size) if (!test_if_hard_path(fname)) { strxmov(buff, opt_basedir, fname, NullS); - fname=buff; + fname= buff; } - fn_format(buff,fname,"","",4); - - if ((fd = my_open(buff, O_WRONLY | O_CREAT | O_TRUNC, - MYF(MY_WME | MY_FFNF))) < 0) + fn_format(buff, fname, "", "", MY_UNPACK_FILENAME); + + if ((fd= my_open(buff, O_WRONLY | O_CREAT | O_TRUNC, + MYF(MY_WME | MY_FFNF))) < 0) die("Could not open %s: errno = %d", buff, errno); if (my_write(fd, (byte*)str, size, MYF(MY_WME|MY_FNABP))) die("write failed"); my_close(fd, MYF(0)); } -void reject_dump(const char *record_file, char *buf, int size) + +void dump_result_to_reject_file(char *buf, int size) { char reject_file[FN_REFLEN]; - str_to_file(fn_format(reject_file, record_file,"",".reject",2), buf, size); + str_to_file(fn_format(reject_file, result_file_name, "", ".reject", + MY_REPLACE_EXT), + buf, size); } +void dump_result_to_log_file(char *buf, int size) +{ + char log_file[FN_REFLEN]; + str_to_file(fn_format(log_file, result_file_name, "", ".log", + MY_REPLACE_EXT), + buf, size); +} -/* Append the string to ds, with optional replace */ +void dump_progress(void) +{ + char log_file[FN_REFLEN]; + str_to_file(fn_format(log_file, result_file_name, "", ".progress", + MY_REPLACE_EXT), + ds_progress.str, ds_progress.length); +} -static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val, - int len) +void dump_warning_messages(void) { - if (glob_replace) + char warn_file[FN_REFLEN]; + + str_to_file(fn_format(warn_file, result_file_name, "", ".warnings", + MY_REPLACE_EXT), + ds_warning_messages.str, ds_warning_messages.length); +} + +void check_regerr(my_regex_t* r, int err) +{ + char err_buf[1024]; + + if (err) + { + my_regerror(err,r,err_buf,sizeof(err_buf)); + die("Regex error: %s\n", err_buf); + } +} + + +#ifdef __WIN__ + +DYNAMIC_ARRAY patterns; + +/* + init_win_path_patterns + + DESCRIPTION + Setup string patterns that will be used to detect filenames that + needs to be converted from Win to Unix format + +*/ + +void init_win_path_patterns() +{ + /* List of string patterns to match in order to find paths */ + const char* paths[] = { "$MYSQL_TEST_DIR", + "$MYSQL_TMP_DIR", + "./test/", 0 }; + int num_paths= 3; + int i; + char* p; + + DBUG_ENTER("init_win_path_patterns"); + + my_init_dynamic_array(&patterns, sizeof(const char*), 16, 16); + + /* Loop through all paths in the array */ + for (i= 0; i < num_paths; i++) + { + VAR* v; + if (*(paths[i]) == '$') + { + v= var_get(paths[i], 0, 0, 0); + p= my_strdup(v->str_val, MYF(MY_FAE)); + } + else + p= my_strdup(paths[i], MYF(MY_FAE)); + + if (insert_dynamic(&patterns, (gptr) &p)) + die(NullS); + + DBUG_PRINT("info", ("p: %s", p)); + while (*p) + { + if (*p == '/') + *p='\\'; + p++; + } + } + DBUG_VOID_RETURN; +} + +void free_win_path_patterns() +{ + uint i= 0; + for (i=0 ; i < patterns.elements ; i++) + { + const char** pattern= dynamic_element(&patterns, i, const char**); + my_free((gptr) *pattern, MYF(0)); + } + delete_dynamic(&patterns); +} + +/* + fix_win_paths + + DESCRIPTION + Search the string 'val' for the patterns that are known to be + strings that contain filenames. Convert all \ to / in the + filenames that are found. + + Ex: + val = 'Error "c:\mysql\mysql-test\var\test\t1.frm" didn't exist' + => $MYSQL_TEST_DIR is found by strstr + => all \ from c:\mysql\m... until next space is converted into / +*/ + +void fix_win_paths(const char *val, int len) +{ + uint i; + char *p; + + DBUG_ENTER("fix_win_paths"); + for (i= 0; i < patterns.elements; i++) + { + const char** pattern= dynamic_element(&patterns, i, const char**); + DBUG_PRINT("info", ("pattern: %s", *pattern)); + if (strlen(*pattern) == 0) continue; + /* Search for the path in string */ + while ((p= strstr(val, *pattern))) + { + DBUG_PRINT("info", ("Found %s in val p: %s", *pattern, p)); + + while (*p && !my_isspace(charset_info, *p)) + { + if (*p == '\\') + *p= '/'; + p++; + } + DBUG_PRINT("info", ("Converted \\ to /, p: %s", p)); + } + } + DBUG_PRINT("exit", (" val: %s, len: %d", val, len)); + DBUG_VOID_RETURN; +} +#endif + + + +/* + Append the result for one field to the dynamic string ds +*/ + +void append_field(DYNAMIC_STRING *ds, uint col_idx, MYSQL_FIELD* field, + const char* val, ulonglong len, bool is_null) +{ + if (col_idx < max_replace_column && replace_column[col_idx]) + { + val= replace_column[col_idx]; + len= strlen(val); + } + else if (is_null) + { + val= "NULL"; + len= 4; + } +#ifdef __WIN__ + else if ((field->type == MYSQL_TYPE_DOUBLE || + field->type == MYSQL_TYPE_FLOAT ) && + field->decimals >= 31) + { + /* Convert 1.2e+018 to 1.2e+18 and 1.2e-018 to 1.2e-18 */ + char *start= strchr(val, 'e'); + if (start && strlen(start) >= 5 && + (start[1] == '-' || start[1] == '+') && start[2] == '0') + { + start+=2; /* Now points at first '0' */ + /* Move all chars after the first '0' one step left */ + memmove(start, start + 1, strlen(start)); + len--; + } + } +#endif + + if (!display_result_vertically) + { + if (col_idx) + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append_mem(ds, val, (int)len); + } + else { - len=(int) replace_strings(glob_replace, &out_buff, &out_length, val); - if (len == -1) - die("Out of memory in replace"); - val=out_buff; + dynstr_append(ds, field->name); + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append_mem(ds, val, (int)len); + dynstr_append_mem(ds, "\n", 1); } - dynstr_append_mem(ds, val, len); } @@ -2715,309 +4280,347 @@ static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val, Values may be converted with 'replace_column' */ -static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res) +void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res) { MYSQL_ROW row; uint num_fields= mysql_num_fields(res); - MYSQL_FIELD *fields= !display_result_vertically ? 0 : mysql_fetch_fields(res); - unsigned long *lengths; + MYSQL_FIELD *fields= mysql_fetch_fields(res); + ulong *lengths; + while ((row = mysql_fetch_row(res))) { uint i; lengths = mysql_fetch_lengths(res); for (i = 0; i < num_fields; i++) - { - const char *val= row[i]; - ulonglong len= lengths[i]; - - if (i < max_replace_column && replace_column[i]) - { - val= replace_column[i]; - len= strlen(val); - } - if (!val) - { - val= "NULL"; - len= 4; - } - if (!display_result_vertically) - { - if (i) - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_mem(ds, val, len); - } - else - { - dynstr_append(ds, fields[i].name); - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_mem(ds, val, len); - dynstr_append_mem(ds, "\n", 1); - } - } + append_field(ds, i, &fields[i], + (const char*)row[i], lengths[i], !row[i]); if (!display_result_vertically) dynstr_append_mem(ds, "\n", 1); } - free_replace_column(); } /* -* flags control the phased/stages of query execution to be performed -* if QUERY_SEND bit is on, the query will be sent. If QUERY_REAP is on -* the result will be read - for regular query, both bits must be on + Append all results from ps execution to the dynamic string separated + with '\t'. Values may be converted with 'replace_column' */ -static int run_query_normal(MYSQL *mysql, struct st_query *q, int flags); -static int run_query_stmt (MYSQL *mysql, struct st_query *q, int flags); -static void run_query_stmt_handle_warnings(MYSQL *mysql, DYNAMIC_STRING *ds); -static int run_query_stmt_handle_error(char *query, struct st_query *q, - MYSQL_STMT *stmt, DYNAMIC_STRING *ds); -static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields, - DYNAMIC_STRING *ds); - -static int run_query(MYSQL *mysql, struct st_query *q, int flags) +void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, + MYSQL_FIELD *fields, uint num_fields) { + MYSQL_BIND *bind; + my_bool *is_null; + ulong *length; + uint i; - /* - Try to find out if we can run this statement using the prepared - statement protocol. + /* Allocate array with bind structs, lengths and NULL flags */ + bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND), + MYF(MY_WME | MY_FAE | MY_ZEROFILL)); + length= (ulong*) my_malloc(num_fields * sizeof(ulong), + MYF(MY_WME | MY_FAE)); + is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool), + MYF(MY_WME | MY_FAE)); + + /* Allocate data for the result of each field */ + for (i= 0; i < num_fields; i++) + { + uint max_length= fields[i].max_length + 1; + bind[i].buffer_type= MYSQL_TYPE_STRING; + bind[i].buffer= (char *)my_malloc(max_length, MYF(MY_WME | MY_FAE)); + bind[i].buffer_length= max_length; + bind[i].is_null= &is_null[i]; + bind[i].length= &length[i]; + + DBUG_PRINT("bind", ("col[%d]: buffer_type: %d, buffer_length: %lu", + i, bind[i].buffer_type, bind[i].buffer_length)); + } - We don't have a mysql_stmt_send_execute() so we only handle - complete SEND+REAP. + if (mysql_stmt_bind_result(stmt, bind)) + die("mysql_stmt_bind_result failed: %d: %s", + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); - If it is a '?' in the query it may be a SQL level prepared - statement already and we can't do it twice - */ + while (mysql_stmt_fetch(stmt) == 0) + { + for (i= 0; i < num_fields; i++) + append_field(ds, i, &fields[i], (const char *) bind[i].buffer, + *bind[i].length, *bind[i].is_null); + if (!display_result_vertically) + dynstr_append_mem(ds, "\n", 1); + } + + if (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + die("fetch didn't end with MYSQL_NO_DATA from statement: %d %s", + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); - if (ps_protocol_enabled && disable_info && - (flags & QUERY_SEND) && (flags & QUERY_REAP) && ps_match_re(q->query)) - return run_query_stmt(mysql, q, flags); - return run_query_normal(mysql, q, flags); + for (i= 0; i < num_fields; i++) + { + /* Free data for output */ + my_free((gptr)bind[i].buffer, MYF(MY_WME | MY_FAE)); + } + /* Free array with bind structs, lengths and NULL flags */ + my_free((gptr)bind , MYF(MY_WME | MY_FAE)); + my_free((gptr)length , MYF(MY_WME | MY_FAE)); + my_free((gptr)is_null , MYF(MY_WME | MY_FAE)); } -static int run_query_normal(MYSQL* mysql, struct st_query* q, int flags) +/* + Append metadata for fields to output +*/ + +void append_metadata(DYNAMIC_STRING *ds, + MYSQL_FIELD *field, + uint num_fields) { - MYSQL_RES* res= 0; - uint i; - int error= 0, err= 0, counter= 0; - DYNAMIC_STRING *ds; - DYNAMIC_STRING ds_tmp; - DYNAMIC_STRING eval_query; - char* query; - int query_len, got_error_on_send= 0; - DBUG_ENTER("run_query_normal"); - DBUG_PRINT("enter",("flags: %d", flags)); - - if (q->type != Q_EVAL) - { - query = q->query; - query_len = strlen(query); - } - else + MYSQL_FIELD *field_end; + dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\t" + "Column_alias\tType\tLength\tMax length\tIs_null\t" + "Flags\tDecimals\tCharsetnr\n"); + + for (field_end= field+num_fields ; + field < field_end ; + field++) { - init_dynamic_string(&eval_query, "", 16384, 65536); - do_eval(&eval_query, q->query); - query = eval_query.str; - query_len = eval_query.length; + dynstr_append_mem(ds, field->catalog, + 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); + dynstr_append_mem(ds, "\t", 1); + dynstr_append_mem(ds, field->table, + field->table_length); + dynstr_append_mem(ds, "\t", 1); + dynstr_append_mem(ds, field->org_name, + 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); + replace_dynstr_append_uint(ds, field->type); + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append_uint(ds, field->length); + dynstr_append_mem(ds, "\t", 1); + 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); + replace_dynstr_append_uint(ds, field->flags); + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append_uint(ds, field->decimals); + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append_uint(ds, field->charsetnr); + dynstr_append_mem(ds, "\n", 1); } - DBUG_PRINT("enter", ("query: '%-.60s'", query)); +} - if (q->record_file[0]) + +/* + Append affected row count and other info to output +*/ + +void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows, + const char *info) +{ + char buf[40], buff2[21]; + sprintf(buf,"affected rows: %s\n", llstr(affected_rows, buff2)); + dynstr_append(ds, buf); + if (info) { - init_dynamic_string(&ds_tmp, "", 16384, 65536); - ds = &ds_tmp; + dynstr_append(ds, "info: "); + dynstr_append(ds, info); + dynstr_append_mem(ds, "\n", 1); } - else - ds= &ds_res; +} + + +/* + Display the table headings with the names tab separated +*/ - if (flags & QUERY_SEND) +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++) { - got_error_on_send= mysql_send_query(mysql, query, query_len); - if (got_error_on_send && q->expected_errno[0].type == ERR_EMPTY) - die("unable to send query '%s' (mysql_errno=%d , errno=%d)", - query, mysql_errno(mysql), errno); + if (col_idx) + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append(ds, field[col_idx].name); } + dynstr_append_mem(ds, "\n", 1); +} - do +/* + Fetch warnings from server and append to ds + + RETURN VALUE + Number of warnings appended to ds +*/ + +int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql) +{ + uint count; + MYSQL_RES *warn_res; + DBUG_ENTER("append_warnings"); + + if (!(count= mysql_warning_count(mysql))) + DBUG_RETURN(0); + + /* + If one day we will support execution of multi-statements + through PS API we should not issue SHOW WARNINGS until + we have not read all results... + */ + DBUG_ASSERT(!mysql_more_results(mysql)); + + if (mysql_real_query(mysql, "SHOW WARNINGS", 13)) + die("Error running query \"SHOW WARNINGS\": %s", mysql_error(mysql)); + + if (!(warn_res= mysql_store_result(mysql))) + die("Warning count is %u but didn't get any warnings", + count); + + append_result(ds, warn_res); + mysql_free_result(warn_res); + + DBUG_PRINT("warnings", ("%s", ds->str)); + + DBUG_RETURN(count); +} + + + +/* + Run query using MySQL C API + + SYNPOSIS + run_query_normal + mysql - mysql handle + command - currrent command pointer + flags -flags indicating wheter to SEND and/or REAP + query - query string to execute + query_len - length query string to execute + ds - output buffer wherte to store result form query + + RETURN VALUE + error - function will not return +*/ + +void run_query_normal(MYSQL *mysql, struct st_command *command, + int flags, char *query, int query_len, + DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings) +{ + MYSQL_RES *res= 0; + int err= 0, counter= 0; + DBUG_ENTER("run_query_normal"); + DBUG_PRINT("enter",("flags: %d", flags)); + DBUG_PRINT("enter", ("query: '%-.60s'", query)); + + if (flags & QUERY_SEND_FLAG) { - if ((flags & QUERY_SEND) && !disable_query_log && !counter) + /* + Send the query + */ + if (mysql_send_query(mysql, query, query_len)) { - replace_dynstr_append_mem(ds,query, query_len); - dynstr_append_mem(ds, delimiter, delimiter_length); - dynstr_append_mem(ds, "\n", 1); + handle_error(command, mysql_errno(mysql), mysql_error(mysql), + mysql_sqlstate(mysql), ds); + goto end; } - if (!(flags & QUERY_REAP)) - DBUG_RETURN(0); + } - if (got_error_on_send || - (!counter && (*mysql->methods->read_query_result)(mysql)) || - (!(last_result= res= mysql_store_result(mysql)) && - mysql_field_count(mysql))) - { - if (q->require_file) - { - abort_not_supported_test(); - } - if (q->abort_on_error) - die("query '%s' failed: %d: %s", query, - mysql_errno(mysql), mysql_error(mysql)); + if (!(flags & QUERY_REAP_FLAG)) + DBUG_VOID_RETURN; - for (i=0 ; (uint) i < q->expected_errors ; i++) - { - if (((q->expected_errno[i].type == ERR_ERRNO) && - (q->expected_errno[i].code.errnum == mysql_errno(mysql))) || - ((q->expected_errno[i].type == ERR_SQLSTATE) && - (strcmp(q->expected_errno[i].code.sqlstate,mysql_sqlstate(mysql)) == 0))) - { - if (i == 0 && q->expected_errors == 1) - { - /* Only log error if there is one possible error */ - dynstr_append_mem(ds,"ERROR ",6); - replace_dynstr_append_mem(ds, mysql_sqlstate(mysql), - strlen(mysql_sqlstate(mysql))); - dynstr_append_mem(ds, ": ", 2); - replace_dynstr_append_mem(ds,mysql_error(mysql), - strlen(mysql_error(mysql))); - dynstr_append_mem(ds,"\n",1); - } - /* Don't log error if we may not get an error */ - else if (q->expected_errno[0].type == ERR_SQLSTATE || - (q->expected_errno[0].type == ERR_ERRNO && - q->expected_errno[0].code.errnum != 0)) - dynstr_append(ds,"Got one of the listed errors\n"); - goto end; /* Ok */ - } - } - DBUG_PRINT("info",("i: %d expected_errors: %d", i, - q->expected_errors)); - dynstr_append_mem(ds, "ERROR ",6); - replace_dynstr_append_mem(ds, mysql_sqlstate(mysql), - strlen(mysql_sqlstate(mysql))); - dynstr_append_mem(ds,": ",2); - replace_dynstr_append_mem(ds, mysql_error(mysql), - strlen(mysql_error(mysql))); - dynstr_append_mem(ds,"\n",1); - if (i) - { - if (q->expected_errno[0].type == ERR_ERRNO) - verbose_msg("query '%s' failed with wrong errno %d instead of %d...", - q->query, mysql_errno(mysql), q->expected_errno[0].code.errnum); - else - verbose_msg("query '%s' failed with wrong sqlstate %s instead of %s...", - q->query, mysql_sqlstate(mysql), q->expected_errno[0].code.sqlstate); - error= 1; - goto end; - } - verbose_msg("query '%s' failed: %d: %s", q->query, mysql_errno(mysql), - mysql_error(mysql)); - /* - if we do not abort on error, failure to run the query does - not fail the whole test case - */ + do + { + /* + When on first result set, call mysql_read_query_result to retrieve + answer to the query sent earlier + */ + if ((counter==0) && mysql_read_query_result(mysql)) + { + handle_error(command, mysql_errno(mysql), mysql_error(mysql), + mysql_sqlstate(mysql), ds); goto end; } - if (handle_no_error(q)) + /* + 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)) { - error= 1; + handle_error(command, mysql_errno(mysql), mysql_error(mysql), + mysql_sqlstate(mysql), ds); goto end; } if (!disable_result_log) { - ulong affected_rows; /* Ok to be undef if 'disable_info' is set */ + ulonglong affected_rows; /* Ok to be undef if 'disable_info' is set */ LINT_INIT(affected_rows); if (res) { - MYSQL_FIELD *field= mysql_fetch_fields(res); + MYSQL_FIELD *fields= mysql_fetch_fields(res); uint num_fields= mysql_num_fields(res); if (display_metadata) - run_query_display_metadata(field, num_fields, ds); + append_metadata(ds, fields, num_fields); if (!display_result_vertically) - { - for (i = 0; i < num_fields; i++) - { - if (i) - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_mem(ds, field[i].name, - strlen(field[i].name)); - } - dynstr_append_mem(ds, "\n", 1); - } + append_table_headings(ds, fields, num_fields); + append_result(ds, res); } /* - Need to call mysql_affected_rows() before the new + Need to call mysql_affected_rows() before the "new" query to find the warnings */ if (!disable_info) - affected_rows= (ulong)mysql_affected_rows(mysql); + affected_rows= mysql_affected_rows(mysql); - /* Add all warnings to the result */ - if (!disable_warnings && mysql_warning_count(mysql)) + /* + Add all warnings to the result. We can't do this if we are in + the middle of processing results from multi-statement, because + this will break protocol. + */ + if (!disable_warnings && !mysql_more_results(mysql)) { - MYSQL_RES *warn_res=0; - uint count= mysql_warning_count(mysql); - if (!mysql_real_query(mysql, "SHOW WARNINGS", 13)) - { - warn_res= mysql_store_result(mysql); - } - if (!warn_res) - die("Warning count is %u but didn't get any warnings\n", - count); - else + if (append_warnings(ds_warnings, mysql) || ds_warnings->length) { dynstr_append_mem(ds, "Warnings:\n", 10); - append_result(ds, warn_res); - mysql_free_result(warn_res); + dynstr_append_mem(ds, ds_warnings->str, ds_warnings->length); } } + if (!disable_info) - { - char buf[40]; - sprintf(buf,"affected rows: %lu\n", affected_rows); - dynstr_append(ds, buf); - if (mysql_info(mysql)) - { - dynstr_append(ds, "info: "); - dynstr_append(ds, mysql_info(mysql)); - dynstr_append_mem(ds, "\n", 1); - } - } + append_info(ds, affected_rows, mysql_info(mysql)); } - if (record) - { - if (!q->record_file[0] && !result_file) - die("Missing result file"); - if (!result_file) - str_to_file(q->record_file, ds->str, ds->length); - } - else if (q->record_file[0]) - { - error = check_result(ds, q->record_file, q->require_file); - } if (res) mysql_free_result(res); - last_result= 0; counter++; } while (!(err= mysql_next_result(mysql))); - if (err >= 1) - mysql_error(mysql); + if (err > 0) + { + /* We got an error from mysql_next_result, maybe expected */ + handle_error(command, mysql_errno(mysql), mysql_error(mysql), + mysql_sqlstate(mysql), ds); + goto end; + } + DBUG_ASSERT(err == -1); /* Successful and there are no more results */ + + /* If we come here the query is both executed and read successfully */ + handle_no_error(command); end: - free_replace(); - last_result=0; - if (ds == &ds_tmp) - dynstr_free(&ds_tmp); - if (q->type == Q_EVAL) - dynstr_free(&eval_query); /* We save the return code (mysql_errno(mysql)) from the last call sent @@ -3025,562 +4628,624 @@ end: variable then can be used from the test case itself. */ var_set_errno(mysql_errno(mysql)); - DBUG_RETURN(error); + DBUG_VOID_RETURN; } -/****************************************************************************\ - * If --ps-protocol run ordinary statements using prepared statemnt C API -\****************************************************************************/ +/* + Handle errors which occurred during execution + + SYNOPSIS + handle_error() + q - query context + err_errno - error number + err_error - error message + err_sqlstate - sql state + ds - dynamic string which is used for output buffer + + NOTE + If there is an unexpected error this function will abort mysqltest + immediately. + + RETURN VALUE + error - function will not return +*/ + +void handle_error(struct st_command *command, + unsigned int err_errno, const char *err_error, + const char *err_sqlstate, DYNAMIC_STRING *ds) +{ + uint i; + + DBUG_ENTER("handle_error"); + + if (command->require_file[0]) + { + /* + The query after a "--require" failed. This is fine as long the server + returned a valid reponse. Don't allow 2013 or 2006 to trigger an + abort_not_supported_test + */ + if (err_errno == CR_SERVER_LOST || + err_errno == CR_SERVER_GONE_ERROR) + die("require query '%s' failed: %d: %s", command->query, + err_errno, err_error); + + /* Abort the run of this test, pass the failed query as reason */ + abort_not_supported_test("Query '%s' failed, required functionality" \ + "not supported", command->query); + } + + if (command->abort_on_error) + die("query '%s' failed: %d: %s", command->query, err_errno, err_error); + + DBUG_PRINT("info", ("expected_errors.count: %d", + command->expected_errors.count)); + for (i= 0 ; (uint) i < command->expected_errors.count ; i++) + { + if (((command->expected_errors.err[i].type == ERR_ERRNO) && + (command->expected_errors.err[i].code.errnum == err_errno)) || + ((command->expected_errors.err[i].type == ERR_SQLSTATE) && + (strncmp(command->expected_errors.err[i].code.sqlstate, + err_sqlstate, SQLSTATE_LENGTH) == 0))) + { + if (!disable_result_log) + { + if (command->expected_errors.count == 1) + { + /* Only log error if there is one possible error */ + dynstr_append_mem(ds, "ERROR ", 6); + replace_dynstr_append(ds, err_sqlstate); + dynstr_append_mem(ds, ": ", 2); + replace_dynstr_append(ds, err_error); + dynstr_append_mem(ds,"\n",1); + } + /* Don't log error if we may not get an error */ + 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 */ + DBUG_VOID_RETURN; + } + } + + DBUG_PRINT("info",("i: %d expected_errors: %d", i, + command->expected_errors.count)); + + if (!disable_result_log) + { + dynstr_append_mem(ds, "ERROR ",6); + replace_dynstr_append(ds, err_sqlstate); + dynstr_append_mem(ds, ": ", 2); + replace_dynstr_append(ds, err_error); + dynstr_append_mem(ds, "\n", 1); + } + + if (i) + { + if (command->expected_errors.err[0].type == ERR_ERRNO) + die("query '%s' failed with wrong errno %d: '%s', instead of %d...", + command->query, err_errno, err_error, + command->expected_errors.err[0].code.errnum); + else + die("query '%s' failed with wrong sqlstate %s: '%s', instead of %s...", + command->query, err_sqlstate, err_error, + command->expected_errors.err[0].code.sqlstate); + } + + DBUG_VOID_RETURN; +} + /* - We don't have a mysql_stmt_send_execute() so we only handle - complete SEND+REAP + Handle absence of errors after execution + + SYNOPSIS + handle_no_error() + q - context of query + + RETURN VALUE + error - function will not return */ -static int run_query_stmt(MYSQL *mysql, struct st_query *q, int flags) +void handle_no_error(struct st_command *command) { - int error= 0; /* Function return code if "goto end;" */ - int err; /* Temporary storage of return code from calls */ - int query_len, got_error_on_execute; - uint num_rows; - char *query; - MYSQL_RES *res= NULL; /* Note that here 'res' is meta data result set */ - DYNAMIC_STRING *ds; - DYNAMIC_STRING ds_tmp; - DYNAMIC_STRING eval_query; - MYSQL_STMT *stmt; - DBUG_ENTER("run_query_stmt"); + DBUG_ENTER("handle_no_error"); - /* - We must allocate a new stmt for each query in this program becasue this - may be a new connection. - */ - if (!(stmt= mysql_stmt_init(mysql))) - die("unable init stmt structure"); - - if (q->type != Q_EVAL) + if (command->expected_errors.err[0].type == ERR_ERRNO && + command->expected_errors.err[0].code.errnum != 0) { - query= q->query; - query_len= strlen(query); + /* Error code we wanted was != 0, i.e. not an expected success */ + die("query '%s' succeeded - should have failed with errno %d...", + command->query, command->expected_errors.err[0].code.errnum); } - else + else if (command->expected_errors.err[0].type == ERR_SQLSTATE && + strcmp(command->expected_errors.err[0].code.sqlstate,"00000") != 0) { - init_dynamic_string(&eval_query, "", 16384, 65536); - do_eval(&eval_query, q->query); - query= eval_query.str; - query_len= eval_query.length; + /* SQLSTATE we wanted was != "00000", i.e. not an expected success */ + die("query '%s' succeeded - should have failed with sqlstate %s...", + command->query, command->expected_errors.err[0].code.sqlstate); } + + DBUG_VOID_RETURN; +} + + +/* + Run query using prepared statement C API + + SYNPOSIS + run_query_stmt + mysql - mysql handle + command - currrent command pointer + query - query string to execute + query_len - length query string to execute + ds - output buffer where to store result form query + + RETURN VALUE + error - function will not return +*/ + +void run_query_stmt(MYSQL *mysql, struct st_command *command, + char *query, int query_len, DYNAMIC_STRING *ds, + DYNAMIC_STRING *ds_warnings) +{ + MYSQL_RES *res= NULL; /* Note that here 'res' is meta data result set */ + MYSQL_STMT *stmt; + DYNAMIC_STRING ds_prepare_warnings; + DYNAMIC_STRING ds_execute_warnings; + DBUG_ENTER("run_query_stmt"); DBUG_PRINT("query", ("'%-.60s'", query)); - if (q->record_file[0]) + /* + Init a new stmt if it's not already one created for this connection + */ + if(!(stmt= cur_con->stmt)) { - init_dynamic_string(&ds_tmp, "", 16384, 65536); - ds= &ds_tmp; + if (!(stmt= mysql_stmt_init(mysql))) + die("unable to init stmt structure"); + cur_con->stmt= stmt; } - else - ds= &ds_res; - /* Store the query into the output buffer if not disabled */ - if (!disable_query_log) + /* Init dynamic strings for warnings */ + if (!disable_warnings) { - replace_dynstr_append_mem(ds,query, query_len); - dynstr_append_mem(ds, delimiter, delimiter_length); - dynstr_append_mem(ds, "\n", 1); + init_dynamic_string(&ds_prepare_warnings, NULL, 0, 256); + init_dynamic_string(&ds_execute_warnings, NULL, 0, 256); } /* - We use the prepared statement interface but there is actually no - '?' in the query. If unpreparable we fall back to use normal - C API. + Prepare the query */ - if ((err= mysql_stmt_prepare(stmt, query, query_len)) == CR_NO_PREPARE_STMT) - return run_query_normal(mysql, q, flags); - - if (err != 0) + if (mysql_stmt_prepare(stmt, query, query_len)) { - if (q->abort_on_error) - { - die("query '%s' failed: %d: %s", query, - mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); - } - else - { - /* - Preparing is part of normal execution and some errors may be expected - */ - error= run_query_stmt_handle_error(query, q, stmt, ds); - goto end; - } + handle_error(command, mysql_stmt_errno(stmt), + mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); + goto end; } - /* We may have got warnings already, collect them if any */ - /* FIXME we only want this if the statement succeeds I think */ - run_query_stmt_handle_warnings(mysql, ds); + /* + Get the warnings from mysql_stmt_prepare and keep them in a + separate string + */ + if (!disable_warnings) + append_warnings(&ds_prepare_warnings, mysql); /* - No need to call mysql_stmt_bind_param() because we have no + No need to call mysql_stmt_bind_param() because we have no parameter markers. - - To optimize performance we use a global 'stmt' that is initiated - once. A new prepare will implicitely close the old one. When we - terminate we will lose the connection, this also closes the last - prepared statement. */ - if ((got_error_on_execute= mysql_stmt_execute(stmt)) != 0) /* 0 == Success */ +#if MYSQL_VERSION_ID >= 50000 + if (cursor_protocol_enabled) { - if (q->abort_on_error) - { - /* We got an error, unexpected */ - die("unable to execute statement '%s': " - "%s (mysql_stmt_errno=%d returned=%d)", - query, mysql_stmt_error(stmt), - mysql_stmt_errno(stmt), got_error_on_execute); - } - else - { - /* We got an error, maybe expected */ - error= run_query_stmt_handle_error(query, q, stmt, ds); - goto end; - } + /* + Use cursor when retrieving result + */ + ulong type= CURSOR_TYPE_READ_ONLY; + if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type)) + die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s", + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); } +#endif + + /* + Execute the query + */ + if (mysql_stmt_execute(stmt)) + { + handle_error(command, mysql_stmt_errno(stmt), + mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); + goto end; + } + + /* + When running in cursor_protocol get the warnings from execute here + and keep them in a separate string for later. + */ + if (cursor_protocol_enabled && !disable_warnings) + append_warnings(&ds_execute_warnings, mysql); /* We instruct that we want to update the "max_length" field in - mysql_stmt_store_result(), this is our only way to know how much - buffer to allocate for result data + mysql_stmt_store_result(), this is our only way to know how much + buffer to allocate for result data */ { my_bool one= 1; - if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, - (void*) &one) != 0) - die("unable to set stmt attribute " - "'STMT_ATTR_UPDATE_MAX_LENGTH': %s (returned=%d)", - query, err); + if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one)) + die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s", + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); } /* If we got here the statement succeeded and was expected to do so, get data. Note that this can still give errors found during execution! */ - if ((err= mysql_stmt_store_result(stmt)) != 0) + if (mysql_stmt_store_result(stmt)) { - if (q->abort_on_error) - { - /* We got an error, unexpected */ - die("unable to execute statement '%s': " - "%s (mysql_stmt_errno=%d returned=%d)", - query, mysql_stmt_error(stmt), - mysql_stmt_errno(stmt), got_error_on_execute); - } - else - { - /* We got an error, maybe expected */ - error= run_query_stmt_handle_error(query, q, stmt, ds); - goto end; - } + handle_error(command, mysql_stmt_errno(stmt), + mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); + goto end; } - /* If we got here the statement was both executed and read succeesfully */ - if (handle_no_error(q)) + /* If we got here the statement was both executed and read successfully */ + handle_no_error(command); + if (!disable_result_log) { - error= 1; - goto end; - } + /* + Not all statements creates a result set. If there is one we can + now create another normal result set that contains the meta + data. This set can be handled almost like any other non prepared + statement result set. + */ + if ((res= mysql_stmt_result_metadata(stmt)) != NULL) + { + /* Take the column count from meta info */ + MYSQL_FIELD *fields= mysql_fetch_fields(res); + uint num_fields= mysql_num_fields(res); - num_rows= mysql_stmt_num_rows(stmt); + if (display_metadata) + append_metadata(ds, fields, num_fields); - /* - Not all statements creates a result set. If there is one we can - now create another normal result set that contains the meta - data. This set can be handled almost like any other non prepared - statement result set. - */ - if (!disable_result_log && ((res= mysql_stmt_result_metadata(stmt)) != NULL)) - { - /* Take the column count from meta info */ - MYSQL_FIELD *field= mysql_fetch_fields(res); - uint num_fields= mysql_num_fields(res); + if (!display_result_vertically) + append_table_headings(ds, fields, num_fields); - /* FIXME check error from the above? */ + append_stmt_result(ds, stmt, fields, num_fields); - if (display_metadata) - run_query_display_metadata(field, num_fields, ds); + mysql_free_result(res); /* Free normal result set with meta data */ - if (!display_result_vertically) + /* Clear prepare warnings */ + dynstr_set(&ds_prepare_warnings, NULL); + } + else { - /* Display the table heading with the names tab separated */ - uint col_idx; - for (col_idx= 0; col_idx < num_fields; col_idx++) - { - if (col_idx) - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_mem(ds, field[col_idx].name, - strlen(field[col_idx].name)); - } - dynstr_append_mem(ds, "\n", 1); + /* + This is a query without resultset + */ } - /* Now we are to put the real result into the output buffer */ - /* FIXME when it works, create function append_stmt_result() */ + if (!disable_warnings) { - MYSQL_BIND *bind; - my_bool *is_null; - unsigned long *length; - /* FIXME we don't handle vertical display ..... */ - uint col_idx, row_idx; - - /* Allocate array with bind structs, lengths and NULL flags */ - bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND), - MYF(MY_WME | MY_FAE)); - length= (unsigned long*) my_malloc(num_fields * sizeof(unsigned long), - MYF(MY_WME | MY_FAE)); - is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool), - MYF(MY_WME | MY_FAE)); - - for (col_idx= 0; col_idx < num_fields; col_idx++) - { - /* Allocate data for output */ - /* - FIXME it may be a bug that for non string/blob types - 'max_length' is 0, should try out 'length' in that case - */ - uint max_length= max(field[col_idx].max_length + 1, 1024); - char *str_data= (char *) my_malloc(max_length, MYF(MY_WME | MY_FAE)); - - bind[col_idx].buffer_type= MYSQL_TYPE_STRING; - bind[col_idx].buffer= (char *)str_data; - bind[col_idx].buffer_length= max_length; - bind[col_idx].is_null= &is_null[col_idx]; - bind[col_idx].length= &length[col_idx]; - } - - /* Fill in the data into the structures created above */ - if ((err= mysql_stmt_bind_result(stmt, bind)) != 0) - die("unable to bind result to statement '%s': " - "%s (mysql_stmt_errno=%d returned=%d)", - query, - mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err); - - /* Read result from each row */ - for (row_idx= 0; row_idx < num_rows; row_idx++) - { - if ((err= mysql_stmt_fetch(stmt)) != 0) - die("unable to fetch all rows from statement '%s': " - "%s (mysql_stmt_errno=%d returned=%d)", - query, - mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err); - - /* Read result from each column */ - for (col_idx= 0; col_idx < num_fields; col_idx++) - { - const char *val; - ulonglong len; - if (col_idx < max_replace_column && replace_column[col_idx]) - { - val= replace_column[col_idx]; - len= strlen(val); - } - else if (*bind[col_idx].is_null) - { - val= "NULL"; - len= 4; - } - else - { - /* FIXME is string terminated? */ - val= (const char *) bind[col_idx].buffer; - len= *bind[col_idx].length; - } - if (!display_result_vertically) - { - if (col_idx) /* No tab before first col */ - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_mem(ds, val, len); - } - else - { - dynstr_append(ds, field[col_idx].name); - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_mem(ds, val, len); - dynstr_append_mem(ds, "\n", 1); - } - } - if (!display_result_vertically) - dynstr_append_mem(ds, "\n", 1); - } - - if ((err= mysql_stmt_fetch(stmt)) != MYSQL_NO_DATA) - die("fetch didn't end with MYSQL_NO_DATA from statement " - "'%s': %s (mysql_stmt_errno=%d returned=%d)", - query, - mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err); + /* Get the warnings from execute */ - free_replace_column(); - - for (col_idx= 0; col_idx < num_fields; col_idx++) + /* Append warnings to ds - if there are any */ + if (append_warnings(&ds_execute_warnings, mysql) || + ds_execute_warnings.length || + ds_prepare_warnings.length || + ds_warnings->length) { - /* Free data for output */ - my_free((gptr)bind[col_idx].buffer, MYF(MY_WME | MY_FAE)); + dynstr_append_mem(ds, "Warnings:\n", 10); + if (ds_warnings->length) + dynstr_append_mem(ds, ds_warnings->str, + ds_warnings->length); + if (ds_prepare_warnings.length) + dynstr_append_mem(ds, ds_prepare_warnings.str, + ds_prepare_warnings.length); + if (ds_execute_warnings.length) + dynstr_append_mem(ds, ds_execute_warnings.str, + ds_execute_warnings.length); } - /* Free array with bind structs, lengths and NULL flags */ - my_free((gptr)bind , MYF(MY_WME | MY_FAE)); - my_free((gptr)length , MYF(MY_WME | MY_FAE)); - my_free((gptr)is_null , MYF(MY_WME | MY_FAE)); } - /* Add all warnings to the result */ - run_query_stmt_handle_warnings(mysql, ds); - if (!disable_info) - { - char buf[40]; - sprintf(buf,"affected rows: %lu\n",(ulong) mysql_affected_rows(mysql)); - dynstr_append(ds, buf); - if (mysql_info(mysql)) - { - dynstr_append(ds, "info: "); - dynstr_append(ds, mysql_info(mysql)); - dynstr_append_mem(ds, "\n", 1); - } - } - } - run_query_stmt_handle_warnings(mysql, ds); + append_info(ds, mysql_affected_rows(mysql), mysql_info(mysql)); - if (record) - { - if (!q->record_file[0] && !result_file) - die("Missing result file"); - if (!result_file) - str_to_file(q->record_file, ds->str, ds->length); } - else if (q->record_file[0]) + +end: + if (!disable_warnings) { - error= check_result(ds, q->record_file, q->require_file); + dynstr_free(&ds_prepare_warnings); + dynstr_free(&ds_execute_warnings); } - if (res) - mysql_free_result(res); /* Free normal result set with meta data */ - last_result= 0; /* FIXME have no idea what this is about... */ - if (err >= 1) - mysql_error(mysql); /* FIXME strange, has no effect... */ + /* + We save the return code (mysql_stmt_errno(stmt)) from the last call sent + to the server into the mysqltest builtin variable $mysql_errno. This + variable then can be used from the test case itself. + */ -end: - free_replace(); - last_result=0; - if (ds == &ds_tmp) - dynstr_free(&ds_tmp); - if (q->type == Q_EVAL) - dynstr_free(&eval_query); var_set_errno(mysql_stmt_errno(stmt)); +#ifndef BUG15518_FIXED mysql_stmt_close(stmt); - DBUG_RETURN(error); + cur_con->stmt= NULL; +#endif + DBUG_VOID_RETURN; } -/****************************************************************************\ - * Broken out sub functions to run_query_stmt() -\****************************************************************************/ -static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields, - DYNAMIC_STRING *ds) -{ - MYSQL_FIELD *field_end; - dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\t" - "Column_alias\tType\tLength\tMax length\tIs_null\t" - "Flags\tDecimals\tCharsetnr\n"); +/* + Create a util connection if one does not already exists + and use that to run the query + This is done to avoid implict commit when creating/dropping objects such + as view, sp etc. +*/ - for (field_end= field+num_fields ; - field < field_end ; - field++) +int util_query(MYSQL* org_mysql, const char* query){ + + MYSQL* mysql; + DBUG_ENTER("util_query"); + + if(!(mysql= cur_con->util_mysql)) { - char buff[22]; - dynstr_append_mem(ds, field->catalog, - 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); - dynstr_append_mem(ds, "\t", 1); - dynstr_append_mem(ds, field->table, - field->table_length); - dynstr_append_mem(ds, "\t", 1); - dynstr_append_mem(ds, field->org_name, - 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); - dynstr_append_mem(ds, "\t", 1); - longlong10_to_str((unsigned int) field->length, buff, 10); - dynstr_append(ds, buff); - dynstr_append_mem(ds, "\t", 1); - longlong10_to_str((unsigned int) field->max_length, buff, 10); - dynstr_append(ds, buff); - 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); + DBUG_PRINT("info", ("Creating util_mysql")); + if (!(mysql= mysql_init(mysql))) + die("Failed in mysql_init()"); - int10_to_str((int) field->flags, buff, 10); - dynstr_append(ds, buff); - dynstr_append_mem(ds, "\t", 1); - int10_to_str((int) field->decimals, buff, 10); - dynstr_append(ds, buff); - dynstr_append_mem(ds, "\t", 1); - int10_to_str((int) field->charsetnr, buff, 10); - dynstr_append(ds, buff); - dynstr_append_mem(ds, "\n", 1); + safe_connect(mysql, "util", org_mysql->host, org_mysql->user, + org_mysql->passwd, org_mysql->db, org_mysql->port, + org_mysql->unix_socket); + + cur_con->util_mysql= mysql; } + + return mysql_query(mysql, query); } -static void run_query_stmt_handle_warnings(MYSQL *mysql, DYNAMIC_STRING *ds) -{ - uint count; - DBUG_ENTER("run_query_stmt_handle_warnings"); - if (!disable_warnings && (count= mysql_warning_count(mysql))) - { - if (mysql_real_query(mysql, "SHOW WARNINGS", 13) == 0) - { - MYSQL_RES *warn_res= mysql_store_result(mysql); - if (!warn_res) - die("Warning count is %u but didn't get any warnings\n", - count); - else - { - dynstr_append_mem(ds, "Warnings:\n", 10); - append_result(ds, warn_res); - mysql_free_result(warn_res); - } - } - } - DBUG_VOID_RETURN; -} +/* + Run query + flags control the phased/stages of query execution to be performed + if QUERY_SEND_FLAG bit is on, the query will be sent. If QUERY_REAP_FLAG + is on the result will be read - for regular query, both bits must be on -static int run_query_stmt_handle_error(char *query, struct st_query *q, - MYSQL_STMT *stmt, DYNAMIC_STRING *ds) + SYNPOSIS + run_query + mysql - mysql handle + command - currrent command pointer + +*/ + +void run_query(MYSQL *mysql, struct st_command *command, int flags) { - if (q->require_file) /* FIXME don't understand this one */ + DYNAMIC_STRING *ds; + DYNAMIC_STRING ds_result; + DYNAMIC_STRING ds_warnings; + DYNAMIC_STRING eval_query; + char *query; + int query_len; + my_bool view_created= 0, sp_created= 0; + my_bool complete_query= ((flags & QUERY_SEND_FLAG) && + (flags & QUERY_REAP_FLAG)); + + init_dynamic_string(&ds_warnings, NULL, 0, 256); + + /* 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); + do_eval(&eval_query, command->query, command->end, FALSE); + query = eval_query.str; + query_len = eval_query.length; + } + else { - abort_not_supported_test(); + query = command->query; + query_len = strlen(query); } - if (q->abort_on_error) - die("query '%s' failed: %d: %s", query, - mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); + /* + When command->require_file is set the output of _this_ query + should be compared with an already existing file + Create a temporary dynamic string to contain the output from + this query. + */ + if (command->require_file[0]) + { + init_dynamic_string(&ds_result, "", 1024, 1024); + ds= &ds_result; + } else + ds= &ds_res; + + /* + Log the query into the output buffer + */ + if (!disable_query_log && (flags & QUERY_SEND_FLAG)) { - int i; + replace_dynstr_append_mem(ds, query, query_len); + dynstr_append_mem(ds, delimiter, delimiter_length); + dynstr_append_mem(ds, "\n", 1); + } - for (i=0 ; (uint) i < q->expected_errors ; i++) + if (view_protocol_enabled && + complete_query && + match_re(&view_re, query)) + { + /* + Create the query as a view. + Use replace since view can exist from a failed mysqltest run + */ + DYNAMIC_STRING query_str; + init_dynamic_string(&query_str, + "CREATE OR REPLACE VIEW mysqltest_tmp_v AS ", + query_len+64, 256); + dynstr_append_mem(&query_str, query, query_len); + if (util_query(mysql, query_str.str)) { - if (((q->expected_errno[i].type == ERR_ERRNO) && - (q->expected_errno[i].code.errnum == mysql_stmt_errno(stmt))) || - ((q->expected_errno[i].type == ERR_SQLSTATE) && - (strcmp(q->expected_errno[i].code.sqlstate, - mysql_stmt_sqlstate(stmt)) == 0))) - { - if (i == 0 && q->expected_errors == 1) - { - /* Only log error if there is one possible error */ - dynstr_append_mem(ds,"ERROR ",6); - replace_dynstr_append_mem(ds, mysql_stmt_sqlstate(stmt), - strlen(mysql_stmt_sqlstate(stmt))); - dynstr_append_mem(ds, ": ", 2); - replace_dynstr_append_mem(ds,mysql_stmt_error(stmt), - strlen(mysql_stmt_error(stmt))); - dynstr_append_mem(ds,"\n",1); - } - /* Don't log error if we may not get an error */ - else if (q->expected_errno[0].type == ERR_SQLSTATE || - (q->expected_errno[0].type == ERR_ERRNO && - q->expected_errno[0].code.errnum != 0)) - dynstr_append(ds,"Got one of the listed errors\n"); - return 0; /* Ok */ - } + /* + Failed to create the view, this is not fatal + just run the query the normal way + */ + DBUG_PRINT("view_create_error", + ("Failed to create view '%s': %d: %s", query_str.str, + mysql_errno(mysql), mysql_error(mysql))); + + /* Log error to create view */ + verbose_msg("Failed to create view '%s' %d: %s", query_str.str, + mysql_errno(mysql), mysql_error(mysql)); } - DBUG_PRINT("info",("i: %d expected_errors: %d", i, - q->expected_errors)); - dynstr_append_mem(ds, "ERROR ",6); - replace_dynstr_append_mem(ds, mysql_stmt_sqlstate(stmt), - strlen(mysql_stmt_sqlstate(stmt))); - dynstr_append_mem(ds,": ",2); - replace_dynstr_append_mem(ds, mysql_stmt_error(stmt), - strlen(mysql_stmt_error(stmt))); - dynstr_append_mem(ds,"\n",1); - if (i) + else { - if (q->expected_errno[0].type == ERR_ERRNO) - verbose_msg("query '%s' failed with wrong errno %d instead of %d...", - q->query, mysql_stmt_errno(stmt), - q->expected_errno[0].code.errnum); - else - verbose_msg("query '%s' failed with wrong sqlstate %s instead of %s...", - q->query, mysql_stmt_sqlstate(stmt), - q->expected_errno[0].code.sqlstate); - return 1; /* Error */ + /* + Yes, it was possible to create this query as a view + */ + view_created= 1; + query= (char*)"SELECT * FROM mysqltest_tmp_v"; + query_len = strlen(query); + + /* + Collect warnings from create of the view that should otherwise + have been produced when the SELECT was executed + */ + append_warnings(&ds_warnings, cur_con->util_mysql); } - verbose_msg("query '%s' failed: %d: %s", q->query, mysql_stmt_errno(stmt), - mysql_stmt_error(stmt)); + + dynstr_free(&query_str); + + } + + if (sp_protocol_enabled && + complete_query && + match_re(&sp_re, query)) + { /* - if we do not abort on error, failure to run the query does - not fail the whole test case + Create the query as a stored procedure + Drop first since sp can exist from a failed mysqltest run */ - return 0; - } + DYNAMIC_STRING query_str; + init_dynamic_string(&query_str, + "DROP PROCEDURE IF EXISTS mysqltest_tmp_sp;", + query_len+64, 256); + util_query(mysql, query_str.str); + dynstr_set(&query_str, "CREATE PROCEDURE mysqltest_tmp_sp()\n"); + dynstr_append_mem(&query_str, query, query_len); + if (util_query(mysql, query_str.str)) + { + /* + Failed to create the stored procedure for this query, + this is not fatal just run the query the normal way + */ + DBUG_PRINT("sp_create_error", + ("Failed to create sp '%s': %d: %s", query_str.str, + mysql_errno(mysql), mysql_error(mysql))); - return 0; -} + /* Log error to create sp */ + verbose_msg("Failed to create sp '%s' %d: %s", query_str.str, + mysql_errno(mysql), mysql_error(mysql)); + } + else + { + sp_created= 1; -/* - Handle absence of errors after execution + query= (char*)"CALL mysqltest_tmp_sp()"; + query_len = strlen(query); + } + dynstr_free(&query_str); + } - SYNOPSIS - handle_no_error() - q - context of query + /* + Find out how to run this query - RETURN VALUE - 0 - OK - 1 - Some error was expected from this query. -*/ + Always run with normal C API if it's not a complete + SEND + REAP -static int handle_no_error(struct st_query *q) -{ - DBUG_ENTER("handle_no_error"); + If it is a '?' in the query it may be a SQL level prepared + statement already and we can't do it twice + */ + if (ps_protocol_enabled && + complete_query && + match_re(&ps_re, query)) + run_query_stmt(mysql, command, query, query_len, ds, &ds_warnings); + else + run_query_normal(mysql, command, flags, query, query_len, + ds, &ds_warnings); - if (q->expected_errno[0].type == ERR_ERRNO && - q->expected_errno[0].code.errnum != 0) + if (sp_created) { - /* Error code we wanted was != 0, i.e. not an expected success */ - verbose_msg("query '%s' succeeded - should have failed with errno %d...", - q->query, q->expected_errno[0].code.errnum); - DBUG_RETURN(1); + if (util_query(mysql, "DROP PROCEDURE mysqltest_tmp_sp ")) + die("Failed to drop sp: %d: %s", mysql_errno(mysql), mysql_error(mysql)); + } + + if (view_created) + { + if (util_query(mysql, "DROP VIEW mysqltest_tmp_v ")) + die("Failed to drop view: %d: %s", + mysql_errno(mysql), mysql_error(mysql)); } - else if (q->expected_errno[0].type == ERR_SQLSTATE && - strcmp(q->expected_errno[0].code.sqlstate,"00000") != 0) + + if (command->require_file[0]) { - /* SQLSTATE we wanted was != "00000", i.e. not an expected success */ - verbose_msg("query '%s' succeeded - should have failed with sqlstate %s...", - q->query, q->expected_errno[0].code.sqlstate); - DBUG_RETURN(1); + + /* A result file was specified for _this_ query + and the output should be checked against an already + existing file which has been specified using --require or --result + */ + check_require(ds, command->require_file); } - DBUG_RETURN(0); + dynstr_free(&ds_warnings); + if (ds == &ds_result) + dynstr_free(&ds_result); + if (command->type == Q_EVAL) + dynstr_free(&eval_query); +} + +/****************************************************************************/ +/* + Functions to detect different SQL statements +*/ + +char *re_eprint(int err) +{ + static char epbuf[100]; + size_t len= my_regerror(REG_ITOA|err, (my_regex_t *)NULL, + epbuf, sizeof(epbuf)); + assert(len <= sizeof(epbuf)); + return(epbuf); } -/****************************************************************************\ - * Functions to match SQL statements that can be prepared -\****************************************************************************/ +void init_re_comp(my_regex_t *re, const char* str) +{ + int err= my_regcomp(re, str, (REG_EXTENDED | REG_ICASE | REG_NOSUB), + &my_charset_latin1); + if (err) + { + char erbuf[100]; + int len= my_regerror(err, re, erbuf, sizeof(erbuf)); + die("error %s, %d/%d `%s'\n", + re_eprint(err), len, (int)sizeof(erbuf), erbuf); + } +} -static void ps_init_re(void) +void init_re(void) { + /* + Filter for queries that can be run using the + MySQL Prepared Statements C API + */ const char *ps_re_str = "^(" "[[:space:]]*REPLACE[[:space:]]|" @@ -3595,199 +5260,233 @@ static void ps_init_re(void) "[[:space:]]*UPDATE[[:space:]]+MULTI[[:space:]]|" "[[:space:]]*INSERT[[:space:]]+SELECT[[:space:]])"; - int err= my_regcomp(&ps_re, ps_re_str, - (REG_EXTENDED | REG_ICASE | REG_NOSUB), - &my_charset_latin1); - if (err) - { - char erbuf[100]; - int len= my_regerror(err, &ps_re, erbuf, sizeof(erbuf)); - fprintf(stderr, "error %s, %d/%d `%s'\n", - ps_eprint(err), len, (int)sizeof(erbuf), erbuf); - exit(1); - } + /* + Filter for queries that can be run using the + Stored procedures + */ + const char *sp_re_str =ps_re_str; + + /* + Filter for queries that can be run as views + */ + const char *view_re_str = + "^(" + "[[:space:]]*SELECT[[:space:]])"; + + init_re_comp(&ps_re, ps_re_str); + init_re_comp(&sp_re, sp_re_str); + init_re_comp(&view_re, view_re_str); } -static int ps_match_re(char *stmt_str) +int match_re(my_regex_t *re, char *str) { - int err= my_regexec(&ps_re, stmt_str, (size_t)0, NULL, 0); + int err= my_regexec(re, str, (size_t)0, NULL, 0); if (err == 0) return 1; else if (err == REG_NOMATCH) return 0; - else + { char erbuf[100]; - int len= my_regerror(err, &ps_re, erbuf, sizeof(erbuf)); - fprintf(stderr, "error %s, %d/%d `%s'\n", - ps_eprint(err), len, (int)sizeof(erbuf), erbuf); - exit(1); + int len= my_regerror(err, re, erbuf, sizeof(erbuf)); + die("error %s, %d/%d `%s'\n", + re_eprint(err), len, (int)sizeof(erbuf), erbuf); } + return 0; } -static char *ps_eprint(int err) -{ - static char epbuf[100]; - size_t len= my_regerror(REG_ITOA|err, (my_regex_t *)NULL, epbuf, sizeof(epbuf)); - assert(len <= sizeof(epbuf)); - return(epbuf); -} - - -static void ps_free_reg(void) +void free_re(void) { my_regfree(&ps_re); + my_regfree(&sp_re); + my_regfree(&view_re); + my_regex_end(); } /****************************************************************************/ -void get_query_type(struct st_query* q) +void get_command_type(struct st_command* command) { char save; uint type; - DBUG_ENTER("get_query_type"); + DBUG_ENTER("get_command_type"); - if (*q->query == '}') + if (*command->query == '}') { - q->type = Q_END_BLOCK; + command->type = Q_END_BLOCK; DBUG_VOID_RETURN; } - if (q->type != Q_COMMENT_WITH_COMMAND) - q->type = Q_QUERY; - save=q->query[q->first_word_len]; - q->query[q->first_word_len]=0; - type=find_type(q->query, &command_typelib, 1+2); - q->query[q->first_word_len]=save; + save= command->query[command->first_word_len]; + command->query[command->first_word_len]= 0; + type= find_type(command->query, &command_typelib, 1+2); + command->query[command->first_word_len]= save; if (type > 0) - q->type=(enum enum_commands) type; /* Found command */ - DBUG_VOID_RETURN; -} + { + command->type=(enum enum_commands) type; /* Found command */ + /* + Look for case where "query" was explicitly specified to + force command being sent to server + */ + if (type == Q_QUERY) + { + /* Skip the "query" part */ + command->query= command->first_argument; + } + } + else + { + /* No mysqltest command matched */ -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; + if (command->type != Q_COMMENT_WITH_COMMAND) + { + /* A query that will sent to mysqld */ + command->type= Q_QUERY; + } + else + { + /* -- comment that didn't contain a mysqltest command */ + command->type= Q_COMMENT; + warning_msg("Suspicious command '--%s' detected, was this intentional? "\ + "Use # instead of -- to avoid this warning", + command->query); + + if (command->first_word_len && + strcmp(command->query + command->first_word_len - 1, delimiter) == 0) + { + /* + Detect comment with command using extra delimiter + Ex --disable_query_log; + ^ Extra delimiter causing the command + to be skipped + */ + save= command->query[command->first_word_len-1]; + command->query[command->first_word_len-1]= 0; + if (find_type(command->query, &command_typelib, 1+2) > 0) + die("Extra delimiter \";\" found"); + command->query[command->first_word_len-1]= save; + + } + } + } + + /* Set expected error on command */ + memcpy(&command->expected_errors, &saved_expected_errors, + sizeof(saved_expected_errors)); + DBUG_PRINT("info", ("There are %d expected errors", + command->expected_errors.count)); + command->abort_on_error= (command->expected_errors.count == 0 && + abort_on_error); + + DBUG_VOID_RETURN; } -static VAR *var_init(VAR *v, const char *name, int name_len, const char *val, - int val_len) -{ - int val_alloc_len; - VAR *tmp_var; - if (!name_len && name) - name_len = strlen(name); - if (!val_len && val) - val_len = strlen(val) ; - val_alloc_len = val_len + 16; /* room to grow */ - if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var) - + name_len, MYF(MY_WME)))) - die("Out of memory"); - tmp_var->name = (name) ? (char*) tmp_var + sizeof(*tmp_var) : 0; - tmp_var->alloced = (v == 0); - if (!(tmp_var->str_val = my_malloc(val_alloc_len+1, MYF(MY_WME)))) - die("Out of memory"); +/* + 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. - 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; - return tmp_var; -} + The ds_progress will be dumped to <test_name>.progress when + test run completes -static void var_free(void *v) +*/ + +void mark_progress(struct st_command* command __attribute__((unused)), + int line) { - my_free(((VAR*) v)->str_val, MYF(MY_WME)); - if (((VAR*)v)->alloced) - my_free((char*) v, MYF(MY_WME)); -} + char buf[32], *end; + ulonglong timer= timer_now(); + if (!progress_start) + progress_start= timer; + timer-= progress_start; + /* Milliseconds since start */ + end= longlong2str(timer, buf, 10); + dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); + dynstr_append_mem(&ds_progress, "\t", 1); -static VAR* var_from_env(const char *name, const char *def_val) -{ - const char *tmp; - VAR *v; - if (!(tmp = getenv(name))) - tmp = def_val; + /* Parser line number */ + end= int10_to_str(line, buf, 10); + dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); + dynstr_append_mem(&ds_progress, "\t", 1); - v = var_init(0, name, strlen(name), tmp, strlen(tmp)); - my_hash_insert(&var_hash, (byte*)v); - return v; -} + /* Filename */ + dynstr_append(&ds_progress, cur_file->file_name); + dynstr_append_mem(&ds_progress, ":", 1); + /* Line in file */ + end= int10_to_str(cur_file->lineno, buf, 10); + dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); + + + dynstr_append_mem(&ds_progress, "\n", 1); -static void init_var_hash(MYSQL *mysql) -{ - VAR *v; - DBUG_ENTER("init_var_hash"); - if (hash_init(&var_hash, charset_info, - 1024, 0, 0, get_var_key, var_free, MYF(0))) - die("Variable hash initialization failed"); - if (opt_big_test) - my_hash_insert(&var_hash, (byte*) var_init(0,"BIG_TEST", 0, "1",0)); - v= var_init(0,"MAX_TABLES", 0, (sizeof(ulong) == 4) ? "31" : "62",0); - my_hash_insert(&var_hash, (byte*) v); - v= var_init(0,"SERVER_VERSION", 0, mysql_get_server_info(mysql), 0); - my_hash_insert(&var_hash, (byte*) v); - v= var_init(0,"DB", 2, db, 0); - my_hash_insert(&var_hash, (byte*) v); - DBUG_VOID_RETURN; } int main(int argc, char **argv) { - int error = 0; - struct st_query *q; - my_bool require_file=0, q_send_flag=0, query_executed= 0, abort_flag= 0; + struct st_command *command; + my_bool q_send_flag= 0, abort_flag= 0; + uint command_executed= 0, last_command_executed= 0; char save_file[FN_REFLEN]; MY_STAT res_info; MY_INIT(argv[0]); - { - DBUG_ENTER("main"); - DBUG_PROCESS(argv[0]); - /* Use all time until exit if no explicit 'start_timer' */ - timer_start= timer_now(); + save_file[0]= 0; + TMPDIR[0]= 0; + + /* Init expected errors */ + memset(&saved_expected_errors, 0, sizeof(saved_expected_errors)); - save_file[0]=0; - TMPDIR[0]=0; - memset(cons, 0, sizeof(cons)); - cons_end = cons + MAX_CONS; - next_con = cons + 1; - cur_con = cons; + /* Init connections */ + memset(connections, 0, sizeof(connections)); + connections_end= connections + + (sizeof(connections)/sizeof(struct st_connection)) - 1; + next_con= connections + 1; + cur_con= connections; + /* Init file stack */ memset(file_stack, 0, sizeof(file_stack)); - memset(&master_pos, 0, sizeof(master_pos)); - file_stack_end= file_stack + MAX_INCLUDE_DEPTH - 1; + file_stack_end= + file_stack + (sizeof(file_stack)/sizeof(struct st_test_file)) - 1; cur_file= file_stack; - lineno = lineno_stack; - my_init_dynamic_array(&q_lines, sizeof(struct st_query*), INIT_Q_LINES, - INIT_Q_LINES); + /* Init block stack */ memset(block_stack, 0, sizeof(block_stack)); - block_stack_end= block_stack + BLOCK_STACK_DEPTH - 1; + block_stack_end= + block_stack + (sizeof(block_stack)/sizeof(struct st_block)) - 1; cur_block= block_stack; cur_block->ok= TRUE; /* Outer block should always be executed */ - cur_block->cmd= Q_UNKNOWN; + cur_block->cmd= cmd_none; + + 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, "", 0, 65536); + 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_name ? result_file_name : "")); if (mysql_server_init(embedded_server_arg_count, embedded_server_args, (char**) embedded_server_groups)) @@ -3796,17 +5495,17 @@ int main(int argc, char **argv) { cur_file->file= stdin; cur_file->file_name= my_strdup("<stdin>", MYF(MY_WME)); + cur_file->lineno= 1; } - *lineno=1; -#ifndef EMBEDDED_LIBRARY - if (manager_host) - init_manager(); -#endif - if (ps_protocol) - { + init_re(); + ps_protocol_enabled= ps_protocol; + sp_protocol_enabled= sp_protocol; + view_protocol_enabled= view_protocol; + cursor_protocol_enabled= cursor_protocol; + /* Cursor protcol implies ps protocol */ + if (cursor_protocol_enabled) ps_protocol_enabled= 1; - ps_init_re(); - } + if (!( mysql_init(&cur_con->mysql))) die("Failed in mysql_init()"); if (opt_compress) @@ -3815,18 +5514,30 @@ int main(int argc, char **argv) mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name); #ifdef HAVE_OPENSSL + +#if MYSQL_VERSION_ID >= 50000 + opt_ssl_verify_server_cert= TRUE; /* Always on in mysqltest */ +#endif + if (opt_use_ssl) + { mysql_ssl_set(&cur_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); +#if MYSQL_VERSION_ID >= 50000 + mysql_options(&cur_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, + &opt_ssl_verify_server_cert); +#endif + } #endif if (!(cur_con->name = my_strdup("default", MYF(MY_WME)))) die("Out of memory"); - if (safe_connect(&cur_con->mysql, host, user, pass, db, port, unix_sock)) - die("Failed in mysql_real_connect(): %s", mysql_error(&cur_con->mysql)); + safe_connect(&cur_con->mysql, cur_con->name, host, user, pass, + db, port, unix_sock); - init_var_hash(&cur_con->mysql); + /* Use all time until exit if no explicit 'start_timer' */ + timer_start= timer_now(); /* Initialize $mysql_errno with -1, so we can @@ -3835,24 +5546,40 @@ int main(int argc, char **argv) */ var_set_errno(-1); - while (!abort_flag && !read_query(&q)) + if (opt_include) + { + open_file(opt_include); + } + + while (!read_command(&command) && !abort_flag) { 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) { - case Q_CONNECT: do_connect(q); break; - case Q_CONNECTION: select_connection(q); break; + switch (command->type) { + case Q_CONNECT: + do_connect(command); + break; + case Q_CONNECTION: select_connection(command); break; case Q_DISCONNECT: case Q_DIRTY_CLOSE: - close_connection(q); break; - case Q_RPL_PROBE: do_rpl_probe(q); break; - case Q_ENABLE_RPL_PARSE: do_enable_rpl_parse(q); break; - case Q_DISABLE_RPL_PARSE: do_disable_rpl_parse(q); break; + do_close_connection(command); break; + case Q_RPL_PROBE: do_rpl_probe(command); break; + case Q_ENABLE_RPL_PARSE: do_enable_rpl_parse(command); break; + case Q_DISABLE_RPL_PARSE: do_disable_rpl_parse(command); break; case Q_ENABLE_QUERY_LOG: disable_query_log=0; break; case Q_DISABLE_QUERY_LOG: disable_query_log=1; break; case Q_ENABLE_ABORT_ON_ERROR: abort_on_error=1; break; @@ -3861,27 +5588,27 @@ int main(int argc, char **argv) case Q_DISABLE_RESULT_LOG: disable_result_log=1; break; case Q_ENABLE_WARNINGS: disable_warnings=0; break; case Q_DISABLE_WARNINGS: disable_warnings=1; break; + case Q_ENABLE_PS_WARNINGS: disable_ps_warnings=0; break; + case Q_DISABLE_PS_WARNINGS: disable_ps_warnings=1; break; case Q_ENABLE_INFO: disable_info=0; break; case Q_DISABLE_INFO: disable_info=1; break; case Q_ENABLE_METADATA: display_metadata=1; break; case Q_DISABLE_METADATA: display_metadata=0; break; - case Q_SOURCE: do_source(q); break; - case Q_SLEEP: do_sleep(q, 0); break; - case Q_REAL_SLEEP: do_sleep(q, 1); break; - case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(q); break; - case Q_REQUIRE_MANAGER: do_require_manager(q); break; -#ifndef EMBEDDED_LIBRARY - case Q_SERVER_START: do_server_start(q); break; - case Q_SERVER_STOP: do_server_stop(q); break; -#endif - case Q_INC: do_modify_var(q, "inc", DO_INC); break; - case Q_DEC: do_modify_var(q, "dec", DO_DEC); break; - case Q_ECHO: do_echo(q); break; - case Q_SYSTEM: do_system(q); break; + case Q_SOURCE: do_source(command); break; + case Q_SLEEP: do_sleep(command, 0); break; + case Q_REAL_SLEEP: do_sleep(command, 1); break; + case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(command); break; + case Q_INC: do_modify_var(command, DO_INC); break; + case Q_DEC: do_modify_var(command, DO_DEC); break; + case Q_ECHO: do_echo(command); command_executed++; break; + case Q_SYSTEM: do_system(command); break; + case Q_REMOVE_FILE: do_remove_file(command); break; + case Q_FILE_EXIST: do_file_exist(command); break; + case Q_WRITE_FILE: do_write_file(command); break; + case Q_COPY_FILE: do_copy_file(command); break; + case Q_PERL: do_perl(command); break; case Q_DELIMITER: - strmake(delimiter, q->first_argument, sizeof(delimiter) - 1); - delimiter_length= strlen(delimiter); - q->last_argument= q->first_argument+delimiter_length; + do_delimiter(command); break; case Q_DISPLAY_VERTICAL_RESULTS: display_result_vertically= TRUE; @@ -3889,130 +5616,128 @@ int main(int argc, char **argv) case Q_DISPLAY_HORIZONTAL_RESULTS: display_result_vertically= FALSE; break; - case Q_LET: do_let(q); break; + case Q_LET: do_let(command); break; case Q_EVAL_RESULT: eval_result = 1; break; case Q_EVAL: - if (q->query == q->query_buf) + if (command->query == command->query_buf) { - q->query= q->first_argument; - q->first_word_len= 0; + command->query= command->first_argument; + command->first_word_len= 0; } /* fall through */ case Q_QUERY_VERTICAL: case Q_QUERY_HORIZONTAL: { my_bool old_display_result_vertically= display_result_vertically; - if (!q->query[q->first_word_len]) - { - /* This happens when we use 'query_..' on it's own line */ - q_send_flag=1; - DBUG_PRINT("info", - ("query: '%s' first_word_len: %d send_flag=1", - q->query, q->first_word_len)); - break; - } - /* fix up query pointer if this is * first iteration for this line */ - if (q->query == q->query_buf) - q->query += q->first_word_len + 1; - display_result_vertically= (q->type==Q_QUERY_VERTICAL); + + /* Remove "query_*" if this is first iteration */ + if (command->query == command->query_buf) + command->query= command->first_argument; + + display_result_vertically= (command->type == Q_QUERY_VERTICAL); if (save_file[0]) { - strmov(q->record_file,save_file); - q->require_file=require_file; - save_file[0]=0; + strmake(command->require_file, save_file, sizeof(save_file)); + save_file[0]= 0; } - error|= run_query(&cur_con->mysql, q, QUERY_REAP|QUERY_SEND); + run_query(&cur_con->mysql, command, QUERY_REAP_FLAG|QUERY_SEND_FLAG); display_result_vertically= old_display_result_vertically; - q->last_argument= q->end; - query_executed= 1; + command->last_argument= command->end; + command_executed++; break; } case Q_QUERY: case Q_REAP: { - /* - We read the result always regardless of the mode for both full - query and read-result only (reap) - */ - int flags = QUERY_REAP; - if (q->type != Q_REAP) /* for a full query, enable the send stage */ - flags |= QUERY_SEND; - if (q_send_flag) - { - flags= QUERY_SEND; - q_send_flag=0; - } + int flags; + if (q_send_flag) + { + /* Last command was an empty 'send' */ + flags= QUERY_SEND_FLAG; + q_send_flag= 0; + } + else if (command->type == Q_REAP) + { + flags= QUERY_REAP_FLAG; + } + else + { + /* full query, both reap and send */ + flags= QUERY_REAP_FLAG | QUERY_SEND_FLAG; + } + if (save_file[0]) { - strmov(q->record_file,save_file); - q->require_file=require_file; - save_file[0]=0; + strmake(command->require_file, save_file, sizeof(save_file)); + save_file[0]= 0; } - error |= run_query(&cur_con->mysql, q, flags); - query_executed= 1; - q->last_argument= q->end; + run_query(&cur_con->mysql, command, flags); + command_executed++; + command->last_argument= command->end; break; } case Q_SEND: - if (!q->query[q->first_word_len]) - { - /* This happens when we use 'send' on it's own line */ - q_send_flag=1; - break; - } - /* fix up query pointer if this is * first iteration for this line */ - if (q->query == q->query_buf) - q->query += q->first_word_len; + if (!*command->first_argument) + { + /* + This is a send without arguments, it indicates that _next_ query + should be send only + */ + q_send_flag= 1; + break; + } + + /* Remove "send" if this is first iteration */ + if (command->query == command->query_buf) + command->query= command->first_argument; + /* - run_query() can execute a query partially, depending on the flags - QUERY_SEND flag without QUERY_REAP tells it to just send the - query and read the result some time later when reap instruction + run_query() can execute a query partially, depending on the flags. + QUERY_SEND_FLAG flag without QUERY_REAP_FLAG tells it to just send + the query and read the result some time later when reap instruction is given on this connection. - */ - error |= run_query(&cur_con->mysql, q, QUERY_SEND); - query_executed= 1; - q->last_argument= q->end; + */ + run_query(&cur_con->mysql, command, QUERY_SEND_FLAG); + command_executed++; + command->last_argument= command->end; break; - case Q_RESULT: - get_file_name(save_file,q); - require_file=0; + case Q_REQUIRE: + do_get_file_name(command, save_file, sizeof(save_file)); break; case Q_ERROR: - global_expected_errors=get_errcodes(global_expected_errno,q); - break; - case Q_REQUIRE: - get_file_name(save_file,q); - require_file=1; + do_get_errcodes(command); break; case Q_REPLACE: - get_replace(q); + do_get_replace(command); break; + case Q_REPLACE_REGEX: + do_get_replace_regex(command); + break; case Q_REPLACE_COLUMN: - get_replace_column(q); + do_get_replace_column(command); break; case Q_SAVE_MASTER_POS: do_save_master_pos(); break; - case Q_SYNC_WITH_MASTER: do_sync_with_master(q); break; + case Q_SYNC_WITH_MASTER: do_sync_with_master(command); break; case Q_SYNC_SLAVE_WITH_MASTER: { do_save_master_pos(); - if (*q->first_argument) - select_connection(q); + if (*command->first_argument) + select_connection(command); else select_connection_name("slave"); do_sync_with_master2(0); 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 */ @@ -4021,10 +5746,9 @@ int main(int argc, char **argv) case Q_END_TIMER: /* End timer before ending mysqltest */ timer_output(); - got_end_timer= TRUE; break; case Q_CHARACTER_SET: - set_charset(q); + do_set_charset(command); break; case Q_DISABLE_PS_PROTOCOL: ps_protocol_enabled= 0; @@ -4033,190 +5757,342 @@ int main(int argc, char **argv) ps_protocol_enabled= ps_protocol; break; case Q_DISABLE_RECONNECT: - cur_con->mysql.reconnect= 0; + set_reconnect(&cur_con->mysql, 0); break; case Q_ENABLE_RECONNECT: - cur_con->mysql.reconnect= 1; + set_reconnect(&cur_con->mysql, 1); + break; + case Q_DISABLE_PARSING: + if (parsing_disabled == 0) + parsing_disabled= 1; + else + die("Parsing is already disabled"); + break; + case Q_ENABLE_PARSING: + /* + Ensure we don't get parsing_disabled < 0 as this would accidentally + disable code we don't want to have disabled + */ + if (parsing_disabled == 1) + parsing_disabled= 0; + else + die("Parsing is already enabled"); + break; + case Q_DIE: + /* Abort test with error code and error message */ + die("%s", command->first_argument); break; case Q_EXIT: + /* Stop processing any more commands */ abort_flag= 1; break; - default: processed = 0; break; + case Q_RESULT: + die("result, deprecated command"); + break; + + default: + processed= 0; + break; } } if (!processed) { - current_line_inc = 0; - switch (q->type) { - case Q_WHILE: do_block(Q_WHILE, q); break; - case Q_IF: do_block(Q_IF, q); break; - case Q_END_BLOCK: do_done(q); break; + current_line_inc= 0; + switch (command->type) { + case Q_WHILE: do_block(cmd_while, command); break; + case Q_IF: do_block(cmd_if, command); break; + case Q_END_BLOCK: do_done(command); break; default: current_line_inc = 1; break; } } else - check_eol_junk(q->last_argument); + check_eol_junk(command->last_argument); - if (q->type != Q_ERROR) + if (command->type != Q_ERROR && + 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(command, parser.current_line); } start_lineno= 0; - if (!query_executed && result_file && my_stat(result_file, &res_info, 0)) - { - /* - my_stat() successful on result file. Check if we have not run a - single query, but we do have a result file that contains data. - Note that we don't care, if my_stat() fails. For example, for a - non-existing or non-readable file, we assume it's fine to have - no query output from the test file, e.g. regarded as no error. - */ - if (res_info.st_size) - error|= (RESULT_CONTENT_MISMATCH | RESULT_LENGTH_MISMATCH); - } - if (ds_res.length && !error) + if (parsing_disabled) + die("Test ended with parsing disabled"); + + /* + The whole test has been executed _sucessfully_. + Time to compare result or save it to record file. + The entire output from test is now kept in ds_res. + */ + if (ds_res.length) { - if (result_file) + if (result_file_name) { - if (!record) - error |= check_result(&ds_res, result_file, q->require_file); + /* A result file has been specified */ + + if (record) + { + /* Recording - dump the output from test to result file */ + str_to_file(result_file_name, ds_res.str, ds_res.length); + } else - str_to_file(result_file, ds_res.str, ds_res.length); + { + /* Check that the output from test is equal to result file + - detect missing result file + - detect zero size result file + */ + check_result(&ds_res); + } } else { - /* Print the result to stdout */ + /* No result_file_name specified to compare with, print to stdout */ printf("%s", ds_res.str); } } - dynstr_free(&ds_res); + else + { + die("The test didn't produce any output"); + } - if (!silent) + if (!command_executed && + result_file_name && my_stat(result_file_name, &res_info, 0)) { - if (error) - printf("not ok\n"); - else - printf("ok\n"); + /* + my_stat() successful on result file. Check if we have not run a + single query, but we do have a result file that contains data. + Note that we don't care, if my_stat() fails. For example, for a + non-existing or non-readable file, we assume it's fine to have + no query output from the test file, e.g. regarded as no error. + */ + die("No queries executed but result file found!"); } - if (!got_end_timer) - timer_output(); /* No end_timer cmd, end it */ + if ( opt_mark_progress && result_file_name ) + dump_progress(); + + /* Dump warning messages */ + if (result_file_name && ds_warning_messages.length) + dump_warning_messages(); + + dynstr_free(&ds_res); + + timer_output(); free_used_memory(); my_end(MY_CHECK_ERROR); - exit(error ? 1 : 0); - return error ? 1 : 0; /* Keep compiler happy */ - } + + /* Yes, if we got this far the test has suceeded! Sakila smiles */ + if (!silent) + printf("ok\n"); + exit(0); + return 0; /* Keep compiler happy */ } /* - Read arguments for embedded server and put them into - embedded_server_args_count and embedded_server_args[] -*/ + A primitive timer that give results in milliseconds if the + --timer-file=<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,"","",4); +} - 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; @@ -4230,99 +6106,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)); -static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name) + /* Append part of original string before replace string */ + dynstr_append_mem(ds, start, (from - rep_str->to_offset) - start); + + /* Append replace string */ + dynstr_append_mem(ds, rep_str->replace_string, + strlen(rep_str->replace_string)); + + if (!*(from-=rep_str->from_offset) && rep_pos->found != 2) + { + /* End of from string */ + DBUG_PRINT("exit", ("Found end of from string")); + DBUG_VOID_RETURN; + } + DBUG_ASSERT(from <= str+len); + start= from; + rep_pos=rep; + } +} + + +/* + Regex replace functions +*/ + + +/* Stores regex substitutions */ + +struct st_regex { - 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); + + /* 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; +} - /* Code for replace rutines */ +#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 */ @@ -4354,32 +6645,48 @@ typedef struct st_follow { } FOLLOWS; -static int init_sets(REP_SETS *sets,uint states); -static REP_SET *make_new_set(REP_SETS *sets); -static void make_sets_invisible(REP_SETS *sets); -static void free_last_set(REP_SETS *sets); -static void free_sets(REP_SETS *sets); -static void internal_set_bit(REP_SET *set, uint bit); -static void internal_clear_bit(REP_SET *set, uint bit); -static void or_bits(REP_SET *to,REP_SET *from); -static void copy_bits(REP_SET *to,REP_SET *from); -static int cmp_bits(REP_SET *set1,REP_SET *set2); -static int get_next_bit(REP_SET *set,uint lastpos); -static int find_set(REP_SETS *sets,REP_SET *find); -static int find_found(FOUND_SET *found_set,uint table_offset, - int found_offset); -static uint start_at_word(my_string pos); -static uint end_of_word(my_string pos); -static uint replace_len(my_string pos); +int init_sets(REP_SETS *sets,uint states); +REP_SET *make_new_set(REP_SETS *sets); +void make_sets_invisible(REP_SETS *sets); +void free_last_set(REP_SETS *sets); +void free_sets(REP_SETS *sets); +void internal_set_bit(REP_SET *set, uint bit); +void internal_clear_bit(REP_SET *set, uint bit); +void or_bits(REP_SET *to,REP_SET *from); +void copy_bits(REP_SET *to,REP_SET *from); +int cmp_bits(REP_SET *set1,REP_SET *set2); +int get_next_bit(REP_SET *set,uint lastpos); +int find_set(REP_SETS *sets,REP_SET *find); +int find_found(FOUND_SET *found_set,uint table_offset, + int found_offset); +uint start_at_word(my_string pos); +uint end_of_word(my_string pos); static uint found_sets=0; - /* Init a replace structure for further calls */ +uint replace_len(my_string str) +{ + uint len=0; + while (*str) + { + if (str[0] == '\\' && str[1]) + str++; + str++; + len++; + } + return len; +} + +/* Init a replace structure for further calls */ REPLACE *init_replace(my_string *from, my_string *to,uint count, my_string word_end_chars) { + static const int SPACE_CHAR= 256; + static const int START_OF_LINE= 257; + static const int END_OF_LINE= 258; + uint i,j,states,set_nr,len,result_len,max_length,found_end,bits_set,bit_nr; int used_sets,chr,default_state; char used_chars[LAST_CHAR_CODE],is_word_end[256]; @@ -4432,7 +6739,7 @@ REPLACE *init_replace(my_string *from, my_string *to,uint count, DBUG_RETURN(0); } - /* Init follow_ptr[] */ + /* Init follow_ptr[] */ for (i=0, states=1, follow_ptr=follow+1 ; i < count ; i++) { if (from[i][0] == '\\' && from[i][1] == '^') @@ -4615,7 +6922,7 @@ REPLACE *init_replace(my_string *from, my_string *to,uint count, } } - /* Alloc replace structure for the replace-state-machine */ + /* Alloc replace structure for the replace-state-machine */ if ((replace=(REPLACE*) my_malloc(sizeof(REPLACE)*(sets.count)+ sizeof(REPLACE_STRING)*(found_sets+1)+ @@ -4658,7 +6965,7 @@ REPLACE *init_replace(my_string *from, my_string *to,uint count, } -static int init_sets(REP_SETS *sets,uint states) +int init_sets(REP_SETS *sets,uint states) { bzero((char*) sets,sizeof(*sets)); sets->size_of_bits=((states+7)/8); @@ -4674,16 +6981,16 @@ static int init_sets(REP_SETS *sets,uint states) return 0; } - /* Make help sets invisible for nicer codeing */ +/* Make help sets invisible for nicer codeing */ -static void make_sets_invisible(REP_SETS *sets) +void make_sets_invisible(REP_SETS *sets) { sets->invisible=sets->count; sets->set+=sets->count; sets->count=0; } -static REP_SET *make_new_set(REP_SETS *sets) +REP_SET *make_new_set(REP_SETS *sets) { uint i,count,*bit_buffer; REP_SET *set; @@ -4701,7 +7008,7 @@ static REP_SET *make_new_set(REP_SETS *sets) } count=sets->count+sets->invisible+SET_MALLOC_HUNC; if (!(set=(REP_SET*) my_realloc((gptr) sets->set_buffer, - sizeof(REP_SET)*count, + sizeof(REP_SET)*count, MYF(MY_WME)))) return 0; sets->set_buffer=set; @@ -4720,34 +7027,34 @@ static REP_SET *make_new_set(REP_SETS *sets) return make_new_set(sets); } -static void free_last_set(REP_SETS *sets) +void free_last_set(REP_SETS *sets) { sets->count--; sets->extra++; return; } -static void free_sets(REP_SETS *sets) +void free_sets(REP_SETS *sets) { my_free((gptr)sets->set_buffer,MYF(0)); my_free((gptr)sets->bit_buffer,MYF(0)); return; } -static void internal_set_bit(REP_SET *set, uint bit) +void internal_set_bit(REP_SET *set, uint bit) { set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT); return; } -static void internal_clear_bit(REP_SET *set, uint bit) +void internal_clear_bit(REP_SET *set, uint bit) { set->bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT)); return; } -static void or_bits(REP_SET *to,REP_SET *from) +void or_bits(REP_SET *to,REP_SET *from) { reg1 uint i; for (i=0 ; i < to->size_of_bits ; i++) @@ -4755,22 +7062,22 @@ static void or_bits(REP_SET *to,REP_SET *from) return; } -static void copy_bits(REP_SET *to,REP_SET *from) +void copy_bits(REP_SET *to,REP_SET *from) { memcpy((byte*) to->bits,(byte*) from->bits, (size_t) (sizeof(uint) * to->size_of_bits)); } -static int cmp_bits(REP_SET *set1,REP_SET *set2) +int cmp_bits(REP_SET *set1,REP_SET *set2) { return bcmp((byte*) set1->bits,(byte*) set2->bits, sizeof(uint) * set1->size_of_bits); } - /* Get next set bit from set. */ +/* Get next set bit from set. */ -static int get_next_bit(REP_SET *set,uint lastpos) +int get_next_bit(REP_SET *set,uint lastpos) { uint pos,*start,*end,bits; @@ -4791,11 +7098,11 @@ static int get_next_bit(REP_SET *set,uint lastpos) return pos; } - /* find if there is a same set in sets. If there is, use it and - free given set, else put in given set in sets and return it's - position */ +/* find if there is a same set in sets. If there is, use it and + free given set, else put in given set in sets and return its + position */ -static int find_set(REP_SETS *sets,REP_SET *find) +int find_set(REP_SETS *sets,REP_SET *find) { uint i; for (i=0 ; i < sets->count-1 ; i++) @@ -4809,14 +7116,14 @@ static int find_set(REP_SETS *sets,REP_SET *find) return i; /* return new postion */ } - /* find if there is a found_set with same table_offset & found_offset - If there is return offset to it, else add new offset and return pos. - Pos returned is -offset-2 in found_set_structure because it's is - saved in set->next and set->next[] >= 0 points to next set and - set->next[] == -1 is reserved for end without replaces. - */ +/* find if there is a found_set with same table_offset & found_offset + If there is return offset to it, else add new offset and return pos. + Pos returned is -offset-2 in found_set_structure because it is + saved in set->next and set->next[] >= 0 points to next set and + set->next[] == -1 is reserved for end without replaces. +*/ -static int find_found(FOUND_SET *found_set,uint table_offset, int found_offset) +int find_found(FOUND_SET *found_set,uint table_offset, int found_offset) { int i; for (i=0 ; (uint) i < found_sets ; i++) @@ -4829,251 +7136,153 @@ 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; -} - - -static uint replace_len(my_string str) -{ - uint len=0; - while (*str) - { - if (str[0] == '\\' && str[1]) - str++; - str++; - len++; - } - return len; + 1 : 0; } +/**************************************************************************** + * Handle replacement of strings + ****************************************************************************/ - /* Replace strings; Return length of result string */ +#define PC_MALLOC 256 /* Bytes for pointers */ +#define PS_MALLOC 512 /* Bytes for data */ -uint replace_strings(REPLACE *rep, my_string *start,uint *max_length, - const char *from) +int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name) { - reg1 REPLACE *rep_pos; - reg2 REPLACE_STRING *rep_str; - my_string to,end,pos,new_str; + uint i,length,old_count; + byte *new_pos; + const char **new_array; + DBUG_ENTER("insert_pointer_name"); - end=(to= *start) + *max_length-1; - rep_pos=rep+1; - for (;;) + if (! pa->typelib.count) { - while (!rep_pos->found) - { - rep_pos= rep_pos->next[(uchar) *from]; - if (to == end) - { - (*max_length)+=8192; - if (!(new_str=my_realloc(*start,*max_length,MYF(MY_WME)))) - return (uint) -1; - to=new_str+(to - *start); - end=(*start=new_str)+ *max_length-1; - } - *to++= *from++; - } - if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string) - return (uint) (to - *start)-1; - to-=rep_str->to_offset; - for (pos=rep_str->replace_string; *pos ; pos++) + if (!(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)))) { - if (to == end) - { - (*max_length)*=2; - if (!(new_str=my_realloc(*start,*max_length,MYF(MY_WME)))) - return (uint) -1; - to=new_str+(to - *start); - end=(*start=new_str)+ *max_length-1; - } - *to++= *pos; + my_free((gptr) pa->typelib.type_names,MYF(0)); + DBUG_RETURN (-1); } - if (!*(from-=rep_str->from_offset) && rep_pos->found != 2) - return (uint) (to - *start); - rep_pos=rep; + 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; } -} - -static int initialize_replace_buffer(void) -{ - out_length=8192; - if (!(out_buff=my_malloc(out_length,MYF(MY_WME)))) - return(1); - return 0; -} - -static void free_replace_buffer(void) -{ - my_free(out_buff,MYF(MY_WME)); -} - - -/**************************************************************************** - Replace results for a column -*****************************************************************************/ - -static void free_replace_column() -{ - uint i; - for (i=0 ; i < max_replace_column ; i++) + length=(uint) strlen(name)+1; + if (pa->length+length >= pa->max_length) { - if (replace_column[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) { - my_free(replace_column[i], 0); - replace_column[i]= 0; + my_ptrdiff_t diff=PTR_BYTE_DIFF(new_pos,pa->str); + for (i=0 ; i < pa->typelib.count ; i++) + pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff, + char*); + pa->str=new_pos; } + pa->max_length+=PS_MALLOC; } - max_replace_column= 0; -} - -/* - Get arguments for replace_columns. The syntax is: - replace-column column_number to_string [column_number to_string ...] - Where each argument may be quoted with ' or " - A argument may also be a variable, in which case the value of the - variable is replaced. -*/ - -static void get_replace_column(struct st_query *q) -{ - char *from=q->first_argument; - char *buff,*start; - DBUG_ENTER("get_replace_columns"); - - free_replace_column(); - if (!*from) - die("Missing argument in %s", q->query); - - /* Allocate a buffer for results */ - start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE)); - while (*from) + if (pa->typelib.count >= pa->max_count-1) { - char *to; - uint column_number; - - to= get_string(&buff, &from, q); - if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS) - die("Wrong column number to replace_column in '%s'", q->query); - if (!*from) - die("Wrong number of arguments to replace_column in '%s'", q->query); - to= get_string(&buff, &from, q); - my_free(replace_column[column_number-1], MY_ALLOW_ZERO_PTR); - replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE)); - set_if_bigger(max_replace_column, column_number); + 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)); } - my_free(start, MYF(0)); - q->last_argument= q->end; -} + 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 */ -#if defined(__NETWARE__) || defined(__WIN__) -/* - Substitute environment variables with text. - SYNOPSIS - subst_env_var() - arg String that should be substitute +/* free pointer array */ - DESCRIPTION - This function takes a string as an input and replaces the - environment variables, that starts with '$' character, with it value. +void free_pointer_array(POINTER_ARRAY *pa) +{ + if (pa->typelib.count) + { + pa->typelib.count=0; + my_free((gptr) pa->typelib.type_names,MYF(0)); + pa->typelib.type_names=0; + my_free((gptr) pa->str,MYF(0)); + } +} /* free_pointer_array */ - NOTES - Return string must be freed with my_free() - RETURN - String with environment variables replaced. -*/ +/* Functions that uses replace and replace_regex */ -static char *subst_env_var(const char *str) +/* Append the string to ds, with optional replace */ +void replace_dynstr_append_mem(DYNAMIC_STRING *ds, + const char *val, int len) { - char *result; - char *pos; +#ifdef __WIN__ + fix_win_paths(val, len); +#endif - result= pos= my_malloc(MAX_QUERY, MYF(MY_FAE)); - while (*str) + if (glob_replace_regex) { - /* - need this only when we want to provide the functionality of - escaping through \ 'backslash' - if ((result == pos && *str=='$') || - (result != pos && *str=='$' && str[-1] !='\\')) - */ - if (*str == '$') + /* Regex replace */ + if (!multi_reg_replace(glob_replace_regex, (char*)val)) { - char env_var[256], *env_pos= env_var, *subst; - - /* Search for end of environment variable */ - for (str++; - *str && !isspace(*str) && *str != '\\' && *str != '/' && - *str != '$'; - str++) - *env_pos++= *str; - *env_pos= 0; - - if (!(subst= getenv(env_var))) - { - my_free(result, MYF(0)); - die("MYSQLTEST.NLM: Environment variable %s is not defined", - env_var); - } - - /* get the string to be substitued for env_var */ - pos= strmov(pos, subst); - /* Process delimiter in *str again */ + val= glob_replace_regex->buf; + len= strlen(val); } - else - *pos++= *str++; } - *pos= 0; - return result; -} - - -/* - popen replacement for Netware - SYNPOSIS - my_popen() - name Command to execute (with possible env variables) - mode Mode for popen. - - NOTES - Environment variable expansion does not take place for popen function - on NetWare, so we use this function to wrap around popen to do this. - - For the moment we ignore 'mode' and always use 'r0' - - RETURN - # >= 0 File handle - -1 Error -*/ + if (glob_replace) + { + /* Normal replace */ + replace_strings_append(glob_replace, ds, val, len); + } + else + dynstr_append_mem(ds, val, len); +} -#undef popen /* Remove wrapper */ -#ifdef __WIN__ -#define popen _popen /* redefine for windows */ -#endif -FILE *my_popen(const char *cmd, const char *mode __attribute__((unused))) +/* Append zero-terminated string to ds, with optional replace */ +void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val) { - char *subst_cmd; - FILE *res_file; - - subst_cmd= subst_env_var(cmd); - res_file= popen(subst_cmd, "r0"); - my_free(subst_cmd, MYF(0)); - return res_file; + replace_dynstr_append_mem(ds, val, strlen(val)); } -#endif /* __NETWARE__ or __WIN__*/ +/* 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/configure.in b/configure.in index eb1d601cf95..013676b0045 100644 --- a/configure.in +++ b/configure.in @@ -508,6 +508,10 @@ AC_SUBST(DOXYGEN) AC_SUBST(PDFLATEX) AC_SUBST(MAKEINDEX) +# icheck, used for ABI check +AC_PATH_PROG(ICHECK, icheck, no) +AC_SUBST(ICHECK) + # Lock for PS AC_PATH_PROG(PS, ps, ps) AC_MSG_CHECKING("how to check if pid exists") diff --git a/include/Makefile.am b/include/Makefile.am index 3fa7b04d69a..a17ef377e78 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -15,14 +15,14 @@ # Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, # MA 02111-1307, USA -BUILT_SOURCES = mysql_version.h m_ctype.h my_config.h +BUILT_SOURCES = mysql_version.h my_config.h abi_check pkginclude_HEADERS = my_dbug.h m_string.h my_sys.h my_list.h my_xml.h \ mysql.h mysql_com.h mysqld_error.h mysql_embed.h \ my_semaphore.h my_pthread.h my_no_pthread.h raid.h \ errmsg.h my_global.h my_net.h my_alloc.h \ my_getopt.h sslopt-longopts.h my_dir.h typelib.h \ sslopt-vars.h sslopt-case.h sql_common.h keycache.h \ - sql_state.h mysql_time.h $(BUILT_SOURCES) + sql_state.h mysql_time.h m_ctype.h $(BUILT_SOURCES) noinst_HEADERS = config-win.h config-os2.h config-netware.h \ nisam.h heap.h merge.h my_bitmap.h\ myisam.h myisampack.h myisammrg.h ft_global.h\ @@ -32,8 +32,11 @@ noinst_HEADERS = config-win.h config-os2.h config-netware.h \ thr_lock.h t_ctype.h violite.h md5.h \ mysql_version.h.in my_handler.h my_time.h +CLEANFILES = abi_check +EXTRA_DIST = mysql_h.ic + # mysql_version.h are generated -SUPERCLEANFILES = mysql_version.h my_config.h +SUPERCLEANFILES = mysql_version.h my_config.h $(CLEANFILES) # Some include files that may be moved and patched by configure DISTCLEANFILES = sched.h $(SUPERCLEANFILES) @@ -55,5 +58,22 @@ link_sources: dist-hook: $(RM) -f $(distdir)/mysql_version.h $(distdir)/my_config.h +# +# Rules for checking that ABI has not changed +# + +# Create a icheck file and compare it to the reference +abi_check: mysql.h mysql_version.h mysql_com.h mysql_time.h my_list.h \ + my_alloc.h typelib.h + @set -ex; \ + if [ @ICHECK@ != no ] ; then \ + @ICHECK@ --canonify --skip-from-re /usr/ -o $@.ic mysql.h; \ + @ICHECK@ --compare mysql_h.ic $@.ic; \ + fi; \ + touch abi_check; + +all: abi_check + + # Don't update the files from bitkeeper %::SCCS/s.% diff --git a/include/my_dbug.h b/include/my_dbug.h index 711ece4335c..d2f6e40f3c6 100644 --- a/include/my_dbug.h +++ b/include/my_dbug.h @@ -35,7 +35,8 @@ extern void _db_enter_(const char *_func_,const char *_file_,uint _line_, extern void _db_return_(uint _line_,const char **_sfunc_,const char **_sfile_, uint *_slevel_); extern void _db_pargs_(uint _line_,const char *keyword); -extern void _db_doprnt_ _VARARGS((const char *format,...)); +extern void _db_doprnt_ _VARARGS((const char *format,...)) + ATTRIBUTE_FORMAT(printf, 1, 2); extern void _db_dump_(uint _line_,const char *keyword,const char *memory, uint length); extern void _db_output_(); diff --git a/include/my_time.h b/include/my_time.h index 94701e159c4..11c653f70d0 100644 --- a/include/my_time.h +++ b/include/my_time.h @@ -44,12 +44,24 @@ typedef long my_time_t; #define TIME_FUZZY_DATE 1 #define TIME_DATETIME_ONLY 2 +#define MYSQL_TIME_WARN_TRUNCATED 1 +#define MYSQL_TIME_WARN_OUT_OF_RANGE 2 + +/* Limits for the TIME data type */ +#define TIME_MAX_HOUR 838 +#define TIME_MAX_MINUTE 59 +#define TIME_MAX_SECOND 59 +#define TIME_MAX_VALUE (TIME_MAX_HOUR*10000 + TIME_MAX_MINUTE*100 + \ + TIME_MAX_SECOND) + enum enum_mysql_timestamp_type str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, uint flags, int *was_cut); bool str_to_time(const char *str,uint length, MYSQL_TIME *l_time, - int *was_cut); + int *warning); + +int check_time_range(struct st_mysql_time *time, int *warning); long calc_daynr(uint year,uint month,uint day); diff --git a/include/mysql.h b/include/mysql.h index 143f6752c46..bc99b6f6ba1 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -14,6 +14,17 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + This file defines the client API to MySQL and also the ABI of the + dynamically linked libmysqlclient. + + The ABI should never be changed in a released product of MySQL + thus you need to take great care when changing the file. In case + the file is changed so the ABI is broken, you must also + update the SHAREDLIB_MAJOR_VERSION in configure.in . + +*/ + #ifndef _mysql_h #define _mysql_h diff --git a/include/mysql_h.ic b/include/mysql_h.ic new file mode 100644 index 00000000000..30ef44a1ccb --- /dev/null +++ b/include/mysql_h.ic @@ -0,0 +1,914 @@ +struct rand_struct; +struct st_list; +struct st_mem_root; +struct st_mysql; +struct st_mysql_bind; +struct st_mysql_data; +struct st_mysql_field; +struct st_mysql_manager; +struct st_mysql_methods; +struct st_mysql_options; +struct st_mysql_parameters; +struct st_mysql_res; +struct st_mysql_rows; +struct st_mysql_stmt; +struct st_mysql_time; +struct st_net; +struct st_typelib; +struct st_udf_args; +struct st_udf_init; +struct st_used_mem; +enum Item_result; +enum enum_field_types; +enum enum_mysql_set_option; +enum enum_mysql_stmt_state; +enum enum_mysql_timestamp_type; +enum enum_server_command; +enum enum_stmt_attr_type; +enum mysql_enum_shutdown_level; +enum mysql_option; +enum mysql_protocol_type; +enum mysql_rpl_type; +enum mysql_status; +# 131 "mysql.h" +typedef struct st_mysql_rows MYSQL_ROWS; +# 24 "my_list.h" +typedef struct st_list LIST; +# 232 "mysql.h" +typedef struct st_mysql MYSQL; +# 571 "mysql.h" +typedef struct st_mysql_bind MYSQL_BIND; +# 93 "mysql.h" +typedef struct st_mysql_field MYSQL_FIELD; +# 117 "mysql.h" +typedef unsigned int MYSQL_FIELD_OFFSET; +# 323 "mysql.h" +typedef struct st_mysql_manager MYSQL_MANAGER; +# 337 "mysql.h" +typedef struct st_mysql_parameters MYSQL_PARAMETERS; +# 292 "mysql.h" +typedef struct st_mysql_res MYSQL_RES; +# 116 "mysql.h" +typedef char * * MYSQL_ROW; +# 137 "mysql.h" +typedef MYSQL_ROWS * MYSQL_ROW_OFFSET; +# 596 "mysql.h" +typedef struct st_mysql_stmt MYSQL_STMT; +# 151 "mysql_com.h" +typedef struct st_net NET; +# 21 "typelib.h" +typedef struct st_typelib TYPELIB; +# 141 "mysql_com.h" +typedef struct st_vio Vio; +# 57 "mysql.h" +typedef char * gptr; +# 29 "my_list.h" +typedef int (* list_walk_action)(void *, void *); +# 48 "mysql.h" +typedef char my_bool; +# 63 "mysql.h" +typedef int my_socket; +# 125 "mysql.h" +typedef unsigned long long int my_ulonglong; +# 35 "my_alloc.h" +typedef struct st_mem_root MEM_ROOT; +# 141 "mysql.h" +typedef struct st_mysql_data MYSQL_DATA; +# 648 "mysql.h" +typedef struct st_mysql_methods MYSQL_METHODS; +# 48 "mysql_time.h" +typedef struct st_mysql_time MYSQL_TIME; +# 315 "mysql_com.h" +typedef struct st_udf_args UDF_ARGS; +# 326 "mysql_com.h" +typedef struct st_udf_init UDF_INIT; +# 27 "my_alloc.h" +typedef struct st_used_mem USED_MEM; +# 302 "mysql_com.h" +struct __attribute__((aligned(__alignof__(unsigned long int)), aligned(__alignof__(double)))) rand_struct + { + unsigned long int seed1; + unsigned long int seed2; + unsigned long int max_value; + double max_value_dbl; + }; +# 24 "my_list.h" +struct __attribute__((aligned(__alignof__(void *)))) st_list + { + struct st_list * prev; + struct st_list * next; + void * data; + }; +# 35 "my_alloc.h" +struct __attribute__((aligned(__alignof__(void *)), aligned(__alignof__(unsigned int)))) st_mem_root + { + USED_MEM * free; + USED_MEM * used; + USED_MEM * pre_alloc; + unsigned int min_malloc; + unsigned int block_size; + unsigned int block_num; + unsigned int first_block_usage; + void (* error_handler)(void); + }; +# 232 "mysql.h" +struct __attribute__((aligned(__alignof__(void *)), aligned(__alignof__(unsigned long long int)))) st_mysql + { + NET net; + gptr connector_fd; + char * host; + char * user; + char * passwd; + char * unix_socket; + char * server_version; + char * host_info; + char * info; + char * db; + struct charset_info_st * charset; + MYSQL_FIELD * fields; + MEM_ROOT field_alloc; + my_ulonglong affected_rows; + my_ulonglong insert_id; + my_ulonglong extra_info; + unsigned long int thread_id; + unsigned long int packet_length; + unsigned int port; + unsigned long int client_flag; + unsigned long int server_capabilities; + unsigned int protocol_version; + unsigned int field_count; + unsigned int server_status; + unsigned int server_language; + unsigned int warning_count; + struct st_mysql_options options; + enum mysql_status status; + my_bool free_me; + my_bool reconnect; + char scramble[(20 + 1)]; + my_bool rpl_pivot; + struct st_mysql * master; + struct st_mysql * next_slave; + struct st_mysql * last_used_slave; + struct st_mysql * last_used_con; + LIST * stmts; + struct st_mysql_methods const * methods; + void * thd; + my_bool * unbuffered_fetch_owner; + struct st_mysql_stmt * current_stmt; + }; +# 571 "mysql.h" +struct __attribute__((aligned(__alignof__(void *)), aligned(__alignof__(unsigned long int)))) st_mysql_bind + { + unsigned long int * length; + my_bool * is_null; + void * buffer; + enum enum_field_types buffer_type; + unsigned long int buffer_length; + unsigned char * inter_buffer; + unsigned long int offset; + unsigned long int internal_length; + unsigned int param_number; + unsigned int pack_length; + my_bool is_unsigned; + my_bool long_data_used; + my_bool internal_is_null; + void (* store_param_func)(NET * net, struct st_mysql_bind * param); + void (* fetch_result)(struct st_mysql_bind *, unsigned char * * row); + void (* skip_result)(struct st_mysql_bind *, MYSQL_FIELD *, unsigned char * * row); + }; +# 141 "mysql.h" +struct __attribute__((aligned(__alignof__(unsigned long long int)), aligned(__alignof__(void *)))) st_mysql_data + { + my_ulonglong rows; + unsigned int fields; + MYSQL_ROWS * data; + MEM_ROOT alloc; + MYSQL_ROWS * * prev_ptr; + }; +# 93 "mysql.h" +struct __attribute__((aligned(__alignof__(void *)), aligned(__alignof__(unsigned long int)))) st_mysql_field + { + char * name; + char * org_name; + char * table; + char * org_table; + char * db; + char * catalog; + char * def; + unsigned long int length; + unsigned long int max_length; + unsigned int name_length; + unsigned int org_name_length; + unsigned int table_length; + unsigned int org_table_length; + unsigned int db_length; + unsigned int catalog_length; + unsigned int def_length; + unsigned int flags; + unsigned int decimals; + unsigned int charsetnr; + enum enum_field_types type; + }; +# 323 "mysql.h" +struct __attribute__((aligned(__alignof__(void *)), aligned(__alignof__(unsigned long int)))) st_mysql_manager + { + NET net; + char * host; + char * user; + char * passwd; + unsigned int port; + my_bool free_me; + my_bool eof; + int cmd_status; + int last_errno; + char * net_buf; + char * net_buf_pos; + char * net_data_end; + int net_buf_size; + char last_error[256]; + }; +# 648 "mysql.h" +struct __attribute__((aligned(__alignof__(void *)))) st_mysql_methods + { + my_bool (* read_query_result)(MYSQL * mysql); + my_bool (* advanced_command)(MYSQL * mysql, enum enum_server_command, char const * header, unsigned long int, char const * arg, unsigned long int, my_bool, MYSQL_STMT * stmt); + MYSQL_DATA * (* read_rows)(MYSQL * mysql, MYSQL_FIELD * mysql_fields, unsigned int); + MYSQL_RES * (* use_result)(MYSQL * mysql); + void (* fetch_lengths)(unsigned long int * to, MYSQL_ROW, unsigned int); + void (* flush_use_result)(MYSQL * mysql); + MYSQL_FIELD * (* list_fields)(MYSQL * mysql); + my_bool (* read_prepare_result)(MYSQL * mysql, MYSQL_STMT * stmt); + int (* stmt_execute)(MYSQL_STMT * stmt); + int (* read_binary_rows)(MYSQL_STMT * stmt); + int (* unbuffered_fetch)(MYSQL * mysql, char * * row); + void (* free_embedded_thd)(MYSQL * mysql); + char const * (* read_statistics)(MYSQL * mysql); + my_bool (* next_result)(MYSQL * mysql); + int (* read_change_user_result)(MYSQL * mysql, char * buff, char const * passwd); + }; +# 162 "mysql.h" +struct __attribute__((aligned(__alignof__(unsigned long int)), aligned(__alignof__(void *)))) st_mysql_options + { + unsigned int connect_timeout; + unsigned int read_timeout; + unsigned int write_timeout; + unsigned int port; + unsigned int protocol; + unsigned long int client_flag; + char * host; + char * user; + char * password; + char * unix_socket; + char * db; + struct st_dynamic_array * init_commands; + char * my_cnf_file; + char * my_cnf_group; + char * charset_dir; + char * charset_name; + char * ssl_key; + char * ssl_cert; + char * ssl_ca; + char * ssl_capath; + char * ssl_cipher; + char * shared_memory_base_name; + unsigned long int max_allowed_packet; + my_bool use_ssl; + my_bool compress; + my_bool named_pipe; + my_bool rpl_probe; + my_bool rpl_parse; + my_bool no_master_reads; + my_bool separate_thread; + enum mysql_option methods_to_use; + char * client_ip; + my_bool secure_auth; + int (* local_infile_init)(void * *, char const *, void *); + int (* local_infile_read)(void *, char *, unsigned int); + void (* local_infile_end)(void); + int (* local_infile_error)(void *, char *, unsigned int); + void * local_infile_userdata; + }; +# 337 "mysql.h" +struct __attribute__((aligned(__alignof__(void *)))) st_mysql_parameters + { + unsigned long int * p_max_allowed_packet; + unsigned long int * p_net_buffer_length; + }; +# 292 "mysql.h" +struct __attribute__((aligned(__alignof__(unsigned long long int)), aligned(__alignof__(void *)))) st_mysql_res + { + my_ulonglong row_count; + MYSQL_FIELD * fields; + MYSQL_DATA * data; + MYSQL_ROWS * data_cursor; + unsigned long int * lengths; + MYSQL * handle; + MEM_ROOT field_alloc; + unsigned int field_count; + unsigned int current_field; + MYSQL_ROW row; + MYSQL_ROW current_row; + my_bool eof; + my_bool unbuffered_fetch_cancelled; + struct st_mysql_methods const * methods; + }; +# 131 "mysql.h" +struct __attribute__((aligned(__alignof__(void *)), aligned(__alignof__(unsigned long int)))) st_mysql_rows + { + struct st_mysql_rows * next; + MYSQL_ROW data; + unsigned long int length; + }; +# 596 "mysql.h" +struct __attribute__((aligned(__alignof__(void *)), aligned(__alignof__(unsigned long long int)))) st_mysql_stmt + { + MEM_ROOT mem_root; + LIST list; + MYSQL * mysql; + MYSQL_BIND * params; + MYSQL_BIND * bind; + MYSQL_FIELD * fields; + MYSQL_DATA result; + MYSQL_ROWS * data_cursor; + my_ulonglong affected_rows; + my_ulonglong insert_id; + int (* read_row_func)(struct st_mysql_stmt * stmt, unsigned char * * row); + unsigned long int stmt_id; + unsigned int last_errno; + unsigned int param_count; + unsigned int field_count; + enum enum_mysql_stmt_state state; + char last_error[512]; + char sqlstate[(5 + 1)]; + my_bool send_types_to_server; + my_bool bind_param_done; + my_bool bind_result_done; + my_bool unbuffered_fetch_cancelled; + my_bool update_max_length; + }; +# 48 "mysql_time.h" +struct __attribute__((aligned(__alignof__(unsigned long int)))) st_mysql_time + { + unsigned int year; + unsigned int month; + unsigned int day; + unsigned int hour; + unsigned int minute; + unsigned int second; + unsigned long int second_part; + my_bool neg; + enum enum_mysql_timestamp_type time_type; + }; +# 151 "mysql_com.h" +struct __attribute__((aligned(__alignof__(void *)), aligned(__alignof__(unsigned long int)))) st_net + { + Vio * vio; + unsigned char * buff; + unsigned char * buff_end; + unsigned char * write_pos; + unsigned char * read_pos; + my_socket fd; + unsigned long int max_packet; + unsigned long int max_packet_size; + unsigned int pkt_nr; + unsigned int compress_pkt_nr; + unsigned int write_timeout; + unsigned int read_timeout; + unsigned int retry_count; + int fcntl; + my_bool compress; + unsigned long int remain_in_buf; + unsigned long int length; + unsigned long int buf_length; + unsigned long int where_b; + unsigned int * return_status; + unsigned char reading_or_writing; + char save_char; + my_bool no_send_ok; + char last_error[512]; + char sqlstate[(5 + 1)]; + unsigned int last_errno; + unsigned char error; + gptr query_cache_query; + my_bool report_error; + my_bool return_errno; + }; +# 21 "typelib.h" +struct __attribute__((aligned(__alignof__(unsigned int)), aligned(__alignof__(void *)))) st_typelib + { + unsigned int count; + char const * name; + char const * * type_names; + unsigned int * type_lengths; + }; +# 315 "mysql_com.h" +struct __attribute__((aligned(__alignof__(unsigned int)), aligned(__alignof__(void *)))) st_udf_args + { + unsigned int arg_count; + enum Item_result * arg_type; + char * * args; + unsigned long int * lengths; + char * maybe_null; + }; +# 326 "mysql_com.h" +struct __attribute__((aligned(__alignof__(unsigned long int)), aligned(__alignof__(void *)))) st_udf_init + { + my_bool maybe_null; + unsigned int decimals; + unsigned long int max_length; + char * ptr; + my_bool const_item; + }; +# 27 "my_alloc.h" +struct __attribute__((aligned(__alignof__(void *)), aligned(__alignof__(unsigned int)))) st_used_mem + { + struct st_used_mem * next; + unsigned int left; + unsigned int size; + }; +# 313 "mysql_com.h" +enum Item_result + { + STRING_RESULT = 0, + REAL_RESULT = 1, + INT_RESULT = 2, + ROW_RESULT = 3, + }; +# 186 "mysql_com.h" +enum enum_field_types + { + MYSQL_TYPE_DECIMAL = 0, + MYSQL_TYPE_TINY = 1, + MYSQL_TYPE_SHORT = 2, + MYSQL_TYPE_LONG = 3, + MYSQL_TYPE_FLOAT = 4, + MYSQL_TYPE_DOUBLE = 5, + MYSQL_TYPE_NULL = 6, + MYSQL_TYPE_TIMESTAMP = 7, + MYSQL_TYPE_LONGLONG = 8, + MYSQL_TYPE_INT24 = 9, + MYSQL_TYPE_DATE = 10, + MYSQL_TYPE_TIME = 11, + MYSQL_TYPE_DATETIME = 12, + MYSQL_TYPE_YEAR = 13, + MYSQL_TYPE_NEWDATE = 14, + MYSQL_TYPE_ENUM = 247, + MYSQL_TYPE_SET = 248, + MYSQL_TYPE_TINY_BLOB = 249, + MYSQL_TYPE_MEDIUM_BLOB = 250, + MYSQL_TYPE_LONG_BLOB = 251, + MYSQL_TYPE_BLOB = 252, + MYSQL_TYPE_VAR_STRING = 253, + MYSQL_TYPE_STRING = 254, + MYSQL_TYPE_GEOMETRY = 255, + }; +# 269 "mysql_com.h" +enum enum_mysql_set_option + { + MYSQL_OPTION_MULTI_STATEMENTS_ON = 0, + MYSQL_OPTION_MULTI_STATEMENTS_OFF = 1, + }; +# 563 "mysql.h" +enum enum_mysql_stmt_state + { + MYSQL_STMT_INIT_DONE = 1, + MYSQL_STMT_PREPARE_DONE = 2, + MYSQL_STMT_EXECUTE_DONE = 3, + MYSQL_STMT_FETCH_DONE = 4, + }; +# 29 "mysql_time.h" +enum enum_mysql_timestamp_type + { + MYSQL_TIMESTAMP_NONE = -(2), + MYSQL_TIMESTAMP_ERROR = -(1), + MYSQL_TIMESTAMP_DATE = 0, + MYSQL_TIMESTAMP_DATETIME = 1, + MYSQL_TIMESTAMP_TIME = 2, + }; +# 39 "mysql_com.h" +enum enum_server_command + { + COM_SLEEP = 0, + COM_QUIT = 1, + COM_INIT_DB = 2, + COM_QUERY = 3, + COM_FIELD_LIST = 4, + COM_CREATE_DB = 5, + COM_DROP_DB = 6, + COM_REFRESH = 7, + COM_SHUTDOWN = 8, + COM_STATISTICS = 9, + COM_PROCESS_INFO = 10, + COM_CONNECT = 11, + COM_PROCESS_KILL = 12, + COM_DEBUG = 13, + COM_PING = 14, + COM_TIME = 15, + COM_DELAYED_INSERT = 16, + COM_CHANGE_USER = 17, + COM_BINLOG_DUMP = 18, + COM_TABLE_DUMP = 19, + COM_CONNECT_OUT = 20, + COM_REGISTER_SLAVE = 21, + COM_PREPARE = 22, + COM_EXECUTE = 23, + COM_LONG_DATA = 24, + COM_CLOSE_STMT = 25, + COM_RESET_STMT = 26, + COM_SET_OPTION = 27, + COM_END = 28, + }; +# 635 "mysql.h" +enum enum_stmt_attr_type + { + STMT_ATTR_UPDATE_MAX_LENGTH = 0, + }; +# 244 "mysql_com.h" +enum mysql_enum_shutdown_level + { + SHUTDOWN_DEFAULT = 0, + SHUTDOWN_WAIT_CONNECTIONS = (unsigned char)((1 << 0)), + SHUTDOWN_WAIT_TRANSACTIONS = (unsigned char)((1 << 1)), + SHUTDOWN_WAIT_UPDATES = (unsigned char)((1 << 3)), + SHUTDOWN_WAIT_ALL_BUFFERS = ((unsigned char)((1 << 3)) << 1), + SHUTDOWN_WAIT_CRITICAL_BUFFERS = (((unsigned char)((1 << 3)) << 1) + 1), + KILL_CONNECTION = 255, + }; +# 151 "mysql.h" +enum mysql_option + { + MYSQL_OPT_CONNECT_TIMEOUT = 0, + MYSQL_OPT_COMPRESS = 1, + MYSQL_OPT_NAMED_PIPE = 2, + MYSQL_INIT_COMMAND = 3, + MYSQL_READ_DEFAULT_FILE = 4, + MYSQL_READ_DEFAULT_GROUP = 5, + MYSQL_SET_CHARSET_DIR = 6, + MYSQL_SET_CHARSET_NAME = 7, + MYSQL_OPT_LOCAL_INFILE = 8, + MYSQL_OPT_PROTOCOL = 9, + MYSQL_SHARED_MEMORY_BASE_NAME = 10, + MYSQL_OPT_READ_TIMEOUT = 11, + MYSQL_OPT_WRITE_TIMEOUT = 12, + MYSQL_OPT_USE_RESULT = 13, + MYSQL_OPT_USE_REMOTE_CONNECTION = 14, + MYSQL_OPT_USE_EMBEDDED_CONNECTION = 15, + MYSQL_OPT_GUESS_CONNECTION = 16, + MYSQL_SET_CLIENT_IP = 17, + MYSQL_SECURE_AUTH = 18, + }; +# 214 "mysql.h" +enum mysql_protocol_type + { + MYSQL_PROTOCOL_DEFAULT = 0, + MYSQL_PROTOCOL_TCP = 1, + MYSQL_PROTOCOL_SOCKET = 2, + MYSQL_PROTOCOL_PIPE = 3, + MYSQL_PROTOCOL_MEMORY = 4, + }; +# 224 "mysql.h" +enum mysql_rpl_type + { + MYSQL_RPL_MASTER = 0, + MYSQL_RPL_SLAVE = 1, + MYSQL_RPL_ADMIN = 2, + }; +# 209 "mysql.h" +enum mysql_status + { + MYSQL_STATUS_READY = 0, + MYSQL_STATUS_GET_RESULT = 1, + MYSQL_STATUS_USE_RESULT = 2, + }; +# 365 "mysql_com.h" +extern my_bool check_scramble(char const * reply, char const * message, unsigned char const * hash_stage2); +# 358 "mysql_com.h" +extern my_bool check_scramble_323(char const *, char const * message, unsigned long int * salt); +# 353 "mysql_com.h" +extern void create_random_string(char * to, unsigned int, struct rand_struct * rand_st); +# 28 "typelib.h" +extern int find_type(char * x, TYPELIB * typelib, unsigned int); +# 367 "mysql_com.h" +extern void get_salt_from_password(unsigned char * res, char const * password); +# 360 "mysql_com.h" +extern void get_salt_from_password_323(unsigned long int * res, char const * password); +# 372 "mysql_com.h" +extern char * get_tty_password(char * opt_message); +# 30 "typelib.h" +extern char const * get_type(TYPELIB * typelib, unsigned int); +# 355 "mysql_com.h" +extern void hash_password(unsigned long int * to, char const * password, unsigned int); +# 31 "my_list.h" +extern LIST * list_add(LIST * root, LIST * element); +# 33 "my_list.h" +extern LIST * list_cons(void * data, LIST * root); +# 32 "my_list.h" +extern LIST * list_delete(LIST * root, LIST * element); +# 35 "my_list.h" +extern void list_free(LIST * root, unsigned int); +# 36 "my_list.h" +extern unsigned int list_length(LIST *); +# 34 "my_list.h" +extern LIST * list_reverse(LIST * root); +# 37 "my_list.h" +extern int list_walk(LIST *, list_walk_action, gptr); +# 378 "mysql_com.h" +extern int load_defaults(char const * conf_file, char const * * groups, int * argc, char * * * argv); +# 368 "mysql_com.h" +extern void make_password_from_salt(char * to, unsigned char const * hash_stage2); +# 361 "mysql_com.h" +extern void make_password_from_salt_323(char * to, unsigned long int const * salt); +# 363 "mysql_com.h" +extern void make_scrambled_password(char * to, char const * password); +# 356 "mysql_com.h" +extern void make_scrambled_password_323(char * to, char const * password); +# 29 "typelib.h" +extern void make_type(char * to, unsigned int, TYPELIB * typelib); +# 299 "mysql_com.h" +extern int my_connect(my_socket, struct sockaddr const * name, unsigned int, unsigned int); +# 377 "mysql_com.h" +extern my_bool my_init(void); +# 281 "mysql_com.h" +extern my_bool my_net_init(NET * net, Vio * vio); +# 282 "mysql_com.h" +extern void my_net_local_init(NET * net); +# 292 "mysql_com.h" +extern unsigned long int my_net_read(NET * net); +# 287 "mysql_com.h" +extern my_bool my_net_write(NET * net, char const * packet, unsigned long int); +# 352 "mysql_com.h" +extern double my_rnd(struct rand_struct *); +# 381 "mysql_com.h" +extern void my_thread_end(void); +# 380 "mysql_com.h" +extern my_bool my_thread_init(void); +# 539 "mysql.h" +extern void myodbc_remove_escape(MYSQL * mysql, char * name); +# 481 "mysql.h" +extern int mysql_add_slave(MYSQL * mysql, char const * host, unsigned int, char const * user, char const * passwd); +# 393 "mysql.h" +extern my_ulonglong mysql_affected_rows(MYSQL * mysql); +# 720 "mysql.h" +extern my_bool mysql_autocommit(MYSQL * mysql, my_bool); +# 408 "mysql.h" +extern my_bool mysql_change_user(MYSQL * mysql, char const * user, char const * passwd, char const * db); +# 401 "mysql.h" +extern char const * mysql_character_set_name(MYSQL * mysql); +# 723 "mysql.h" +extern void mysql_close(MYSQL * sock); +# 718 "mysql.h" +extern my_bool mysql_commit(MYSQL * mysql); +# 510 "mysql.h" +extern void mysql_data_seek(MYSQL_RES * result, my_ulonglong); +# 528 "mysql.h" +extern void mysql_debug(char const * debug); +# 467 "mysql.h" +extern void mysql_disable_reads_from_master(MYSQL * mysql); +# 461 "mysql.h" +extern void mysql_disable_rpl_parse(MYSQL * mysql); +# 489 "mysql.h" +extern int mysql_dump_debug_info(MYSQL * mysql); +# 541 "mysql.h" +extern my_bool mysql_embedded(void); +# 466 "mysql.h" +extern void mysql_enable_reads_from_master(MYSQL * mysql); +# 460 "mysql.h" +extern void mysql_enable_rpl_parse(MYSQL * mysql); +# 385 "mysql.h" +extern my_bool mysql_eof(MYSQL_RES * res); +# 395 "mysql.h" +extern unsigned int mysql_errno(MYSQL * mysql); +# 373 "mysql_com.h" +extern char const * mysql_errno_to_sqlstate(unsigned int); +# 396 "mysql.h" +extern char const * mysql_error(MYSQL * mysql); +# 521 "mysql.h" +extern unsigned long int mysql_escape_string(char * to, char const * from, unsigned long int); +# 518 "mysql.h" +extern MYSQL_FIELD * mysql_fetch_field(MYSQL_RES * result); +# 386 "mysql.h" +extern MYSQL_FIELD * mysql_fetch_field_direct(MYSQL_RES * res, unsigned int); +# 388 "mysql.h" +extern MYSQL_FIELD * mysql_fetch_fields(MYSQL_RES * res); +# 517 "mysql.h" +extern unsigned long int * mysql_fetch_lengths(MYSQL_RES * result); +# 516 "mysql.h" +extern MYSQL_ROW mysql_fetch_row(MYSQL_RES * result); +# 392 "mysql.h" +extern unsigned int mysql_field_count(MYSQL * mysql); +# 514 "mysql.h" +extern MYSQL_FIELD_OFFSET mysql_field_seek(MYSQL_RES * result, MYSQL_FIELD_OFFSET); +# 390 "mysql.h" +extern MYSQL_FIELD_OFFSET mysql_field_tell(MYSQL_RES * res); +# 509 "mysql.h" +extern void mysql_free_result(MYSQL_RES * result); +# 499 "mysql.h" +extern char const * mysql_get_client_info(void); +# 500 "mysql.h" +extern unsigned long int mysql_get_client_version(void); +# 501 "mysql.h" +extern char const * mysql_get_host_info(MYSQL * mysql); +# 367 "mysql.h" +extern MYSQL_PARAMETERS * mysql_get_parameters(void); +# 503 "mysql.h" +extern unsigned int mysql_get_proto_info(MYSQL * mysql); +# 498 "mysql.h" +extern char const * mysql_get_server_info(MYSQL * mysql); +# 502 "mysql.h" +extern unsigned long int mysql_get_server_version(MYSQL * mysql); +# 523 "mysql.h" +extern unsigned long int mysql_hex_string(char * to, char const * from, unsigned long int); +# 399 "mysql.h" +extern char const * mysql_info(MYSQL * mysql); +# 404 "mysql.h" +extern MYSQL * mysql_init(MYSQL * mysql); +# 394 "mysql.h" +extern my_ulonglong mysql_insert_id(MYSQL * mysql); +# 492 "mysql.h" +extern int mysql_kill(MYSQL * mysql, unsigned long int); +# 504 "mysql.h" +extern MYSQL_RES * mysql_list_dbs(MYSQL * mysql, char const * wild); +# 519 "mysql.h" +extern MYSQL_RES * mysql_list_fields(MYSQL * mysql, char const * table, char const * wild); +# 506 "mysql.h" +extern MYSQL_RES * mysql_list_processes(MYSQL * mysql); +# 505 "mysql.h" +extern MYSQL_RES * mysql_list_tables(MYSQL * mysql, char const * wild); +# 548 "mysql.h" +extern void mysql_manager_close(MYSQL_MANAGER * con); +# 549 "mysql.h" +extern int mysql_manager_command(MYSQL_MANAGER * con, char const * cmd, int); +# 543 "mysql.h" +extern MYSQL_MANAGER * mysql_manager_connect(MYSQL_MANAGER * con, char const * host, char const * user, char const * passwd, unsigned int); +# 551 "mysql.h" +extern int mysql_manager_fetch_line(MYSQL_MANAGER * con, char * res_buf, int); +# 542 "mysql.h" +extern MYSQL_MANAGER * mysql_manager_init(MYSQL_MANAGER * con); +# 427 "mysql.h" +extern my_bool mysql_master_query(MYSQL * mysql, char const * q, unsigned long int); +# 429 "mysql.h" +extern my_bool mysql_master_send_query(MYSQL * mysql, char const * q, unsigned long int); +# 721 "mysql.h" +extern my_bool mysql_more_results(MYSQL * mysql); +# 722 "mysql.h" +extern int mysql_next_result(MYSQL * mysql); +# 384 "mysql.h" +extern unsigned int mysql_num_fields(MYSQL_RES * res); +# 383 "mysql.h" +extern my_ulonglong mysql_num_rows(MYSQL_RES * res); +# 529 "mysql.h" +extern char * mysql_odbc_escape_string(MYSQL * mysql, char * to, unsigned long int, char const * from, unsigned long int, void * param, char * (* extend_buffer)(void *, char * to, unsigned long int * length)); +# 507 "mysql.h" +extern int mysql_options(MYSQL * mysql, enum mysql_option, char const * arg); +# 496 "mysql.h" +extern int mysql_ping(MYSQL * mysql); +# 75 "mysql.h" +extern unsigned int mysql_port; +# 418 "mysql.h" +extern int mysql_query(MYSQL * mysql, char const * q); +# 554 "mysql.h" +extern my_bool mysql_read_query_result(MYSQL * mysql); +# 469 "mysql.h" +extern my_bool mysql_reads_from_master_enabled(MYSQL * mysql); +# 410 "mysql.h" +extern MYSQL * mysql_real_connect(MYSQL * mysql, char const * host, char const * user, char const * passwd, char const * db, unsigned int, char const * unix_socket, unsigned long int); +# 525 "mysql.h" +extern unsigned long int mysql_real_escape_string(MYSQL * mysql, char * to, char const * from, unsigned long int); +# 421 "mysql.h" +extern int mysql_real_query(MYSQL * mysql, char const * q, unsigned long int); +# 490 "mysql.h" +extern int mysql_refresh(MYSQL * mysql, unsigned int); +# 719 "mysql.h" +extern my_bool mysql_rollback(MYSQL * mysql); +# 512 "mysql.h" +extern MYSQL_ROW_OFFSET mysql_row_seek(MYSQL_RES * result, MYSQL_ROW_OFFSET); +# 389 "mysql.h" +extern MYSQL_ROW_OFFSET mysql_row_tell(MYSQL_RES * res); +# 463 "mysql.h" +extern int mysql_rpl_parse_enabled(MYSQL * mysql); +# 474 "mysql.h" +extern my_bool mysql_rpl_probe(MYSQL * mysql); +# 471 "mysql.h" +extern enum mysql_rpl_type mysql_rpl_query_type(char const * q, int); +# 417 "mysql.h" +extern int mysql_select_db(MYSQL * mysql, char const * db); +# 419 "mysql.h" +extern int mysql_send_query(MYSQL * mysql, char const * q, unsigned long int); +# 354 "mysql.h" +extern void mysql_server_end(void); +# 353 "mysql.h" +extern int mysql_server_init(int, char * * argv, char * * groups); +# 402 "mysql.h" +extern int mysql_set_character_set(MYSQL * mysql, char const * csname); +# 452 "mysql.h" +extern void mysql_set_local_infile_default(MYSQL * mysql); +# 441 "mysql.h" +extern void mysql_set_local_infile_handler(MYSQL * mysql, int (* local_infile_init)(void * *, char const *, void *), int (* local_infile_read)(void *, char *, unsigned int), void (* local_infile_end)(void), int (* local_infile_error)(void *, char *, unsigned int), void *); +# 477 "mysql.h" +extern int mysql_set_master(MYSQL * mysql, char const * host, unsigned int, char const * user, char const * passwd); +# 493 "mysql.h" +extern int mysql_set_server_option(MYSQL * mysql, enum enum_mysql_set_option); +# 486 "mysql.h" +extern int mysql_shutdown(MYSQL * mysql, enum mysql_enum_shutdown_level); +# 432 "mysql.h" +extern my_bool mysql_slave_query(MYSQL * mysql, char const * q, unsigned long int); +# 434 "mysql.h" +extern my_bool mysql_slave_send_query(MYSQL * mysql, char const * q, unsigned long int); +# 397 "mysql.h" +extern char const * mysql_sqlstate(MYSQL * mysql); +# 405 "mysql.h" +extern my_bool mysql_ssl_set(MYSQL * mysql, char const * key, char const * cert, char const * ca, char const * capath, char const * cipher); +# 497 "mysql.h" +extern char const * mysql_stat(MYSQL * mysql); +# 714 "mysql.h" +extern my_ulonglong mysql_stmt_affected_rows(MYSQL_STMT * stmt); +# 692 "mysql.h" +extern my_bool mysql_stmt_attr_get(MYSQL_STMT * stmt, enum enum_stmt_attr_type, void * attr); +# 689 "mysql.h" +extern my_bool mysql_stmt_attr_set(MYSQL_STMT * stmt, enum enum_stmt_attr_type, void const * attr); +# 695 "mysql.h" +extern my_bool mysql_stmt_bind_param(MYSQL_STMT * stmt, MYSQL_BIND * bnd); +# 696 "mysql.h" +extern my_bool mysql_stmt_bind_result(MYSQL_STMT * stmt, MYSQL_BIND * bnd); +# 697 "mysql.h" +extern my_bool mysql_stmt_close(MYSQL_STMT * stmt); +# 712 "mysql.h" +extern void mysql_stmt_data_seek(MYSQL_STMT * stmt, my_ulonglong); +# 706 "mysql.h" +extern unsigned int mysql_stmt_errno(MYSQL_STMT * stmt); +# 707 "mysql.h" +extern char const * mysql_stmt_error(MYSQL_STMT * stmt); +# 682 "mysql.h" +extern int mysql_stmt_execute(MYSQL_STMT * stmt); +# 683 "mysql.h" +extern int mysql_stmt_fetch(MYSQL_STMT * stmt); +# 684 "mysql.h" +extern int mysql_stmt_fetch_column(MYSQL_STMT * stmt, MYSQL_BIND * bind, unsigned int, unsigned long int); +# 716 "mysql.h" +extern unsigned int mysql_stmt_field_count(MYSQL_STMT * stmt); +# 699 "mysql.h" +extern my_bool mysql_stmt_free_result(MYSQL_STMT * stmt); +# 679 "mysql.h" +extern MYSQL_STMT * mysql_stmt_init(MYSQL * mysql); +# 715 "mysql.h" +extern my_ulonglong mysql_stmt_insert_id(MYSQL_STMT * stmt); +# 713 "mysql.h" +extern my_ulonglong mysql_stmt_num_rows(MYSQL_STMT * stmt); +# 688 "mysql.h" +extern unsigned long int mysql_stmt_param_count(MYSQL_STMT * stmt); +# 705 "mysql.h" +extern MYSQL_RES * mysql_stmt_param_metadata(MYSQL_STMT * stmt); +# 680 "mysql.h" +extern int mysql_stmt_prepare(MYSQL_STMT * stmt, char const * query, unsigned long int); +# 698 "mysql.h" +extern my_bool mysql_stmt_reset(MYSQL_STMT * stmt); +# 704 "mysql.h" +extern MYSQL_RES * mysql_stmt_result_metadata(MYSQL_STMT * stmt); +# 709 "mysql.h" +extern MYSQL_ROW_OFFSET mysql_stmt_row_seek(MYSQL_STMT * stmt, MYSQL_ROW_OFFSET); +# 711 "mysql.h" +extern MYSQL_ROW_OFFSET mysql_stmt_row_tell(MYSQL_STMT * stmt); +# 700 "mysql.h" +extern my_bool mysql_stmt_send_long_data(MYSQL_STMT * stmt, unsigned int, char const * data, unsigned long int); +# 708 "mysql.h" +extern char const * mysql_stmt_sqlstate(MYSQL_STMT * stmt); +# 687 "mysql.h" +extern int mysql_stmt_store_result(MYSQL_STMT * stmt); +# 423 "mysql.h" +extern MYSQL_RES * mysql_store_result(MYSQL * mysql); +# 376 "mysql.h" +extern void mysql_thread_end(void); +# 400 "mysql.h" +extern unsigned long int mysql_thread_id(MYSQL * mysql); +# 375 "mysql.h" +extern my_bool mysql_thread_init(void); +# 540 "mysql.h" +extern unsigned int mysql_thread_safe(void); +# 76 "mysql.h" +extern char * mysql_unix_port; +# 424 "mysql.h" +extern MYSQL_RES * mysql_use_result(MYSQL * mysql); +# 398 "mysql.h" +extern unsigned int mysql_warning_count(MYSQL * mysql); +# 284 "mysql_com.h" +extern void net_clear(NET * net); +# 283 "mysql_com.h" +extern void net_end(NET * net); +# 286 "mysql_com.h" +extern my_bool net_flush(NET * net); +# 291 "mysql_com.h" +extern int net_real_write(NET * net, char const * packet, unsigned long int); +# 285 "mysql_com.h" +extern my_bool net_realloc(NET * net, unsigned long int); +# 751 "mysql.h" +extern unsigned long int net_safe_read(MYSQL * mysql); +# 288 "mysql_com.h" +extern my_bool net_write_command(NET * net, unsigned char, char const * header, unsigned long int, char const * packet, unsigned long int); +# 350 "mysql_com.h" +extern void randominit(struct rand_struct *, unsigned long int, unsigned long int); +# 364 "mysql_com.h" +extern void scramble(char * to, char const * message, char const * password); +# 357 "mysql_com.h" +extern void scramble_323(char * to, char const * message, char const * password); +# 32 "typelib.h" +extern TYPELIB sql_protocol_typelib; diff --git a/myisam/sort.c b/myisam/sort.c index 154d50d4d39..c5686a8e22c 100644 --- a/myisam/sort.c +++ b/myisam/sort.c @@ -327,7 +327,7 @@ pthread_handler_decl(thr_find_all_keys,arg) if (sort_param->sort_info->got_error) goto err; - if (sort_param->keyinfo->flag && HA_VAR_LENGTH_KEY) + if (sort_param->keyinfo->flag & HA_VAR_LENGTH_KEY) { sort_param->write_keys= write_keys_varlen; sort_param->read_to_buffer= read_to_buffer_varlen; @@ -539,7 +539,7 @@ int thr_write_keys(MI_SORT_PARAM *sort_param) { if (got_error) continue; - if (sinfo->keyinfo->flag && HA_VAR_LENGTH_KEY) + if (sinfo->keyinfo->flag & HA_VAR_LENGTH_KEY) { sinfo->write_keys=write_keys_varlen; sinfo->read_to_buffer=read_to_buffer_varlen; diff --git a/mysql-test/include/analyze_failure_sync_with_master.test b/mysql-test/include/analyze_failure_sync_with_master.test new file mode 100644 index 00000000000..e6fd32d2f46 --- /dev/null +++ b/mysql-test/include/analyze_failure_sync_with_master.test @@ -0,0 +1,15 @@ +# Connect to both master and slave +connect (master,127.0.0.1,root,,test,$MASTER_MYPORT,); +connect (slave,127.0.0.1,root,,test,$SLAVE_MYPORT,); + +vertical_results; + +echo == MASTER ===========================================================; +connection master; +show master status; +show slave status; + +echo == SLAVE ===========================================================; +connection slave; +show master status; +show slave status; diff --git a/mysql-test/include/check-testcase.test b/mysql-test/include/check-testcase.test new file mode 100644 index 00000000000..d67e73470b0 --- /dev/null +++ b/mysql-test/include/check-testcase.test @@ -0,0 +1,49 @@ +# +# This test is executed twice for each test case if mysql-test-run is passed +# the flag --check-testcase. +# Before every testcase it's run with mysqltest in record mode and will +# thus produce an output file +# that can be compared to output from after the tescase. +# In that way it's possible to check that a testcase does not have +# any unwanted side affects. +# + +# +# Dump all global variables +# +show global variables; + +# +# Dump all databases +# +show databases; + +# +# Dump the "test" database, all it's tables and their data +# +--exec $MYSQL_DUMP --skip-comments test + +# +# Dump the "mysql" database and it's tables +# Select data separately to add "order by" +# +--exec $MYSQL_DUMP --skip-comments --no-data mysql +use mysql; +select * from columns_priv; +select * from db order by host, db, user; +select * from func; +select * from help_category; +select * from help_keyword; +select * from help_relation; +select * from help_relation; +select * from host; +select * from tables_priv; +select * from time_zone; +select * from time_zone_leap_second; +select * from time_zone_name; +select * from time_zone_transition; +select * from time_zone_transition_type; +select * from user; + + + diff --git a/mysql-test/include/ctype_like_escape.inc b/mysql-test/include/ctype_like_escape.inc index ac97fbaa1a0..d4abc33c178 100644 --- a/mysql-test/include/ctype_like_escape.inc +++ b/mysql-test/include/ctype_like_escape.inc @@ -11,8 +11,8 @@ insert into t1 values('ab_def'); insert into t1 values('abc_ef'); insert into t1 values('abcd_f'); insert into t1 values('abcde_'); --- should return ab_def +# should return ab_def select c1 as c1u from t1 where c1 like 'ab\_def'; --- should return ab_def +# should return ab_def select c1 as c2h from t1 where c1 like 'ab#_def' escape '#'; drop table t1; diff --git a/mysql-test/include/have_lowercase0.inc b/mysql-test/include/have_lowercase0.inc index f967c18928b..8d3ae02f61e 100644 --- a/mysql-test/include/have_lowercase0.inc +++ b/mysql-test/include/have_lowercase0.inc @@ -1,4 +1,4 @@ --require r/lowercase0.require ---disable_query_log; +--disable_query_log show variables like "lower_case_%"; ---enable_query_log; +--enable_query_log diff --git a/mysql-test/include/have_multi_ndb.inc b/mysql-test/include/have_multi_ndb.inc index ec1a93311fb..844c4d78774 100644 --- a/mysql-test/include/have_multi_ndb.inc +++ b/mysql-test/include/have_multi_ndb.inc @@ -9,8 +9,10 @@ disable_query_log; drop table if exists t1, t2; --enable_warnings flush tables; -@r/have_ndb.require show variables like "have_ndbcluster"; -# @r/server_id.require show variables like "server_id"; +--require r/have_ndb.require +show variables like "have_ndbcluster"; +#--require r/server_id.require +#show variables like "server_id"; enable_query_log; # Check that server2 has NDB support @@ -20,8 +22,10 @@ disable_query_log; drop table if exists t1, t2; --enable_warnings flush tables; -@r/have_ndb.require show variables like "have_ndbcluster"; -# @r/server_id1.require show variables like "server_id"; +--require r/have_ndb.require +show variables like "have_ndbcluster"; +#--require r@r/server_id1.require +#show variables like "server_id"; enable_query_log; # Set the default connection to 'server1' diff --git a/mysql-test/include/master-slave.inc b/mysql-test/include/master-slave.inc index 5ec4b4379f8..9ef5489f346 100644 --- a/mysql-test/include/master-slave.inc +++ b/mysql-test/include/master-slave.inc @@ -8,7 +8,8 @@ connection slave; --disable_warnings stop slave; --enable_warnings -@r/slave-stopped.result show status like 'Slave_running'; +--require r/slave-stopped.result +show status like 'Slave_running'; connection master; --disable_warnings drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; @@ -21,7 +22,8 @@ reset slave; drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; --enable_warnings start slave; -@r/slave-running.result show status like 'Slave_running'; +--require r/slave-running.result +show status like 'Slave_running'; # Set the default connection to 'master' connection master; diff --git a/mysql-test/include/ps_query.inc b/mysql-test/include/ps_query.inc index 9a413bff2f3..a2eac09d121 100644 --- a/mysql-test/include/ps_query.inc +++ b/mysql-test/include/ps_query.inc @@ -52,7 +52,6 @@ execute stmt1; ##### parameter used for keyword like SELECT (must fail) set @arg00='SELECT' ; -# mysqltest gives no output for the next statement, Why ?? --error 1064 @arg00 a from t1 where a=1; --error 1064 diff --git a/mysql-test/include/show_msg.inc b/mysql-test/include/show_msg.inc new file mode 100755 index 00000000000..659dce14686 --- /dev/null +++ b/mysql-test/include/show_msg.inc @@ -0,0 +1,25 @@ +#### include/show_msg.inc +# +# This file writes the value set in @message into the +# a protocol file as part of executing a test sequence +# +# Usage: +# Add the following to any *.test file: +# : +# let $message= <value>; +# --source include/show_msg.inc +# : +# +# Attention: +# - Please do not write any spaces between $message and the "=", because the +# assignment will not work. +# - Be careful with single quotes. They must be escaped like "''" or "\'". +# +# "include/show_msg80.inc" contains a detailed description and examples. + +--disable_query_log +eval SET @utf8_message = CONVERT('$message' using utf8); +select @utf8_message as "" +union +select repeat(CONVERT('-' using utf8),char_length(@utf8_message)); +--enable_query_log diff --git a/mysql-test/include/show_msg80.inc b/mysql-test/include/show_msg80.inc new file mode 100755 index 00000000000..42fb35edbcc --- /dev/null +++ b/mysql-test/include/show_msg80.inc @@ -0,0 +1,118 @@ +#### include/show_msg80.inc +# +# This file writes the value set in @message into the a protocol file as part +# of executing a test sequence with a dash line that is fixed on 80 characters. +# +# This can be used in the case of long messages, multi line messages that +# exceed 80 or if an 80 char line is desired for short messages. +# +# Usage: +# Add the following to any *.test file: +# : +# let $message= <value>; +# --source include/show_msg80.inc +# : +# +# Attention: +# - Please do not write any spaces between $message and the "=", because the +# assignment will not work. +# - Be careful with single quotes within the value. They must be escaped like +# "''" or "\'". +# - Do not keep the value between single quotes. +# +# +# Content of "$message" and protocol output depending on the assignment: +# ---------------------------------------------------------------------- +# +# I is assumed, that the value is not kept between double quotes. +# +# <x> first character after "$message=", +# where the content is not (space or tab) +# <y*> first character after beginning of the line, +# where the content is not (space or tab) +# <z> last char before ";" +# | beginning or end of line +# +# script: let $message= <x><whatever0>| +# | <y1><whatever1>| +# |................| +# | <yn><whatevern><z>; +# content: "<x><whatever0><new line><y1><whatever1><new line> +# ....<new line><yn><whatevern><z>" +# protocol output: |<x><whatever0>| +# |<y1><whatever1>| +# |.....| +# |<yn><whatevern><z>| +# |--- 80 dashes ---| +# +# Attention: +# <x> and <y*> set to characters like "-$#" which are also used +# to start comments, options and the names of mysqltest variables +# lead to syntax errors or mangled messages. +# +# +# Examples of messages: +# --------------------- +# +# Variant1 (ease of use): +# Several lines with indentation kept between double quotes +# script: |let $message= +# |" Testcase 3.1 : Ensure that Pi is not an| +# | integer number.| +# | Third line"; +# protocol: |" Testcase 3.1 : Ensure that Pi is not an| +# | integer number.| +# | Third line"| +# |------ 80 dashes ----| +# +# Please mention that +# - the '"' preserves the indentation. +# - it is easy to write the script lines to get a fine indentation, +# if the value starts at the beginning of a new line +# - the '"' is printed +# - there are the least or no problems with characters like "-#$" +# +# +# Variant 2 (grep the messages from the protocol is easy): +# Several lines with indentation + auxiliary character (".") +# at the (non tab or space) beginning of every message line +# script: |let $message= . Testcase 3.1 : Ensure that Pi is not an| +# | . integer number.| +# | . Third line; +# protocol: |. Testcase 3.1 : Ensure that Pi is not an| +# |. integer number.| +# |. Third line| +# |------ 80 dashes ----| +# Please mention that +# - the auxiliary character preserves the indentation. +# - it is easy to write the script lines to get a fine indentation +# - the auxiliary character is printed +# - it is recommended to use "." as auxiliary character +# - auxiliary characters like "-'$#" cause problems +# +# +# +# Bad variant1: Several lines with lost indentation +# script: |let $message= Here is message line 1 +# | message line 2; +# protocol: |Here is message line 1| +# |message line 2| +# |------ 80 dashes ----| +# Please mention, that the leading spaces of the message lines disappeared. +# +# Bad variant2: Several lines leading to a syntax error, because of "-" +# script: |let $message= - This is a message +# | - with a second and +# | - third line; +# protocol: | - third line;; +# |ERROR 42000: You have an error ... near '- third line' +# + several following errors +# +# + +--disable_query_log +eval SET @utf8_message = CONVERT('$message' using utf8); +select @utf8_message as "" +union +select repeat(CONVERT('-' using utf8),80); +--enable_query_log diff --git a/mysql-test/include/wait_for_query_to_suceed.inc b/mysql-test/include/wait_for_query_to_suceed.inc new file mode 100644 index 00000000000..6ac1144620e --- /dev/null +++ b/mysql-test/include/wait_for_query_to_suceed.inc @@ -0,0 +1,25 @@ +# +# Run a query over and over until it suceeds ot timeout occurs +# + + +let $counter= 100; + +disable_abort_on_error; +disable_query_log; +disable_result_log; +eval $query; +while ($mysql_errno) +{ + eval $query; + sleep 0.1; + dec $counter; + + if (!$counter) + { + die("Waited too long for query to suceed"); + } +} +enable_abort_on_error; +enable_query_log; +enable_result_log; diff --git a/mysql-test/lib/mtr_cases.pl b/mysql-test/lib/mtr_cases.pl index 650fb79155d..d270d72d526 100644 --- a/mysql-test/lib/mtr_cases.pl +++ b/mysql-test/lib/mtr_cases.pl @@ -5,10 +5,13 @@ # same name. use File::Basename; +use IO::File(); use strict; sub collect_test_cases ($); -sub collect_one_test_case ($$$$$$); +sub collect_one_test_case ($$$$$$$); + +sub mtr_options_from_test_file($$); ############################################################################## # @@ -37,81 +40,196 @@ sub collect_test_cases ($) { opendir(TESTDIR, $testdir) or mtr_error("Can't open dir \"$testdir\": $!"); + # ---------------------------------------------------------------------- + # Disable some tests listed in disabled.def + # ---------------------------------------------------------------------- + my %disabled; + if ( open(DISABLED, "$testdir/disabled.def" ) ) + { + while ( <DISABLED> ) + { + chomp; + if ( /^\s*(\S+)\s*:\s*(.*?)\s*$/ ) + { + $disabled{$1}= $2; + } + } + close DISABLED; + } + if ( @::opt_cases ) { - foreach my $tname ( @::opt_cases ) { # Run in specified order, no sort - $tname= basename($tname, ".test"); - my $elem= "$tname.test"; - if ( ! -f "$testdir/$elem") + foreach my $tname ( @::opt_cases ) + { + # Run in specified order, no sort + my $elem= undef; + my $component_id= undef; + + # Get rid of directory part (path). Leave the extension since it is used + # to understand type of the test. + + $tname = basename($tname); + + # Check if the extenstion has been specified. + + if ( mtr_match_extension($tname, "test") ) + { + $elem= $tname; + $tname=~ s/\.test$//; + $component_id= 'mysqld'; + } + elsif ( mtr_match_extension($tname, "imtest") ) + { + $elem= $tname; + $tname =~ s/\.imtest$//; + $component_id= 'im'; + } + + # If target component is known, check that the specified test case + # exists. + # + # Otherwise, try to guess the target component. + + if ( $component_id ) + { + if ( ! -f "$testdir/$elem") + { + mtr_error("Test case $tname ($testdir/$elem) is not found"); + } + } + else { - mtr_error("Test case $tname ($testdir/$elem) is not found"); + my $mysqld_test_exists = -f "$testdir/$tname.test"; + my $im_test_exists = -f "$testdir/$tname.imtest"; + + if ( $mysqld_test_exists and $im_test_exists ) + { + mtr_error("Ambiguous test case name ($tname)"); + } + elsif ( ! $mysqld_test_exists and ! $im_test_exists ) + { + mtr_error("Test case $tname is not found"); + } + elsif ( $mysqld_test_exists ) + { + $elem= "$tname.test"; + $component_id= 'mysqld'; + } + elsif ( $im_test_exists ) + { + $elem= "$tname.imtest"; + $component_id= 'im'; + } } - collect_one_test_case($testdir,$resdir,$tname,$elem,$cases,{}); + + collect_one_test_case($testdir,$resdir,$tname,$elem,$cases,\%disabled, + $component_id); } closedir TESTDIR; } else { - # ---------------------------------------------------------------------- - # Disable some tests listed in disabled.def - # ---------------------------------------------------------------------- - my %disabled; - if ( open(DISABLED, "$testdir/disabled.def" ) ) + foreach my $elem ( sort readdir(TESTDIR) ) { - while ( <DISABLED> ) + my $component_id= undef; + my $tname= undef; + + if ($tname= mtr_match_extension($elem, 'test')) { - chomp; - if ( /^\s*(\S+)\s*:\s*(.*?)\s*$/ ) - { - $disabled{$1}= $2; - } + $component_id = 'mysqld'; + } + elsif ($tname= mtr_match_extension($elem, 'imtest')) + { + $component_id = 'im'; + } + else + { + next; } - close DISABLED; - } - foreach my $elem ( sort readdir(TESTDIR) ) { - my $tname= mtr_match_extension($elem,"test"); - next if ! defined $tname; - next if $::opt_do_test and ! defined mtr_match_prefix($elem,$::opt_do_test); + # Skip tests that does not match the --do-test= filter + next if $::opt_do_test and + ! defined mtr_match_prefix($elem,$::opt_do_test); - collect_one_test_case($testdir,$resdir,$tname,$elem,$cases,\%disabled); + collect_one_test_case($testdir,$resdir,$tname,$elem,$cases,\%disabled, + $component_id); } closedir TESTDIR; } - # To speed things up, we sort first in if the test require a restart - # or not, second in alphanumeric order. - + # Reorder the test cases in an order that will make them faster to run if ( $::opt_reorder ) { - @$cases = sort { - if ( ! $a->{'master_restart'} and ! $b->{'master_restart'} ) - { - return $a->{'name'} cmp $b->{'name'}; - } - if ( $a->{'master_restart'} and $b->{'master_restart'} ) + my %sort_criteria; + + # Make a mapping of test name to a string that represents how that test + # should be sorted among the other tests. Put the most important criterion + # first, then a sub-criterion, then sub-sub-criterion, et c. + foreach my $tinfo (@$cases) + { + my @criteria = (); + + # Look for tests that muct be in run in a defined order + # that is defined by test having the same name except for + # the ending digit + + # Put variables into hash + my $test_name= $tinfo->{'name'}; + my $depend_on_test_name; + if ( $test_name =~ /^([\D]+)([0-9]{1})$/ ) { - my $cmp= mtr_cmp_opts($a->{'master_opt'}, $b->{'master_opt'}); - if ( $cmp == 0 ) - { - return $a->{'name'} cmp $b->{'name'}; - } - else - { - return $cmp; - } + my $base_name= $1; + my $idx= $2; + mtr_verbose("$test_name => $base_name idx=$idx"); + if ( $idx > 1 ) + { + $idx-= 1; + $base_name= "$base_name$idx"; + mtr_verbose("New basename $base_name"); + } + + foreach my $tinfo2 (@$cases) + { + if ( $tinfo2->{'name'} eq $base_name ) + { + mtr_verbose("found dependent test $tinfo2->{'name'}"); + $depend_on_test_name=$base_name; + } + } } - if ( $a->{'master_restart'} ) + if ( defined $depend_on_test_name ) { - return 1; # Is greater + mtr_verbose("Giving $test_name same critera as $depend_on_test_name"); + $sort_criteria{$test_name} = $sort_criteria{$depend_on_test_name}; } else { - return -1; # Is less + # + # Append the criteria for sorting, in order of importance. + # + push(@criteria, "ndb=" . ($tinfo->{'ndb_test'} ? "1" : "0")); + # Group test with equal options together. + # Ending with "~" makes empty sort later than filled + push(@criteria, join("!", sort @{$tinfo->{'master_opt'}}) . "~"); + + $sort_criteria{$test_name} = join(" ", @criteria); + } + } + + @$cases = sort { + $sort_criteria{$a->{'name'}} . $a->{'name'} cmp + $sort_criteria{$b->{'name'}} . $b->{'name'}; } @$cases; + + if ( $::opt_script_debug ) + { + # For debugging the sort-order + foreach my $tinfo (@$cases) + { + print("$sort_criteria{$tinfo->{'name'}} -> \t$tinfo->{'name'}\n"); } - } @$cases; + } } return $cases; @@ -125,13 +243,14 @@ sub collect_test_cases ($) { ############################################################################## -sub collect_one_test_case($$$$$$) { +sub collect_one_test_case($$$$$$$) { my $testdir= shift; my $resdir= shift; my $tname= shift; my $elem= shift; my $cases= shift; my $disabled=shift; + my $component_id= shift; my $path= "$testdir/$elem"; @@ -144,15 +263,17 @@ sub collect_one_test_case($$$$$$) { return; } - # ---------------------------------------------------------------------- - # Skip some tests but include in list, just mark them to skip - # ---------------------------------------------------------------------- my $tinfo= {}; $tinfo->{'name'}= $tname; $tinfo->{'result_file'}= "$resdir/$tname.result"; + $tinfo->{'component_id'} = $component_id; push(@$cases, $tinfo); + # ---------------------------------------------------------------------- + # Skip some tests but include in list, just mark them to skip + # ---------------------------------------------------------------------- + if ( $::opt_skip_test and defined mtr_match_prefix($tname,$::opt_skip_test) ) { $tinfo->{'skip'}= 1; @@ -166,63 +287,38 @@ sub collect_one_test_case($$$$$$) { $tinfo->{'path'}= $path; $tinfo->{'timezone'}= "GMT-3"; # for UNIX_TIMESTAMP tests to work + $tinfo->{'slave_num'}= 0; # Default, no slave if ( defined mtr_match_prefix($tname,"rpl") ) { if ( $::opt_skip_rpl ) { $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No replication tests(--skip-rpl)"; return; } - $tinfo->{'slave_num'}= 1; # Default, use one slave - # FIXME currently we always restart slaves - $tinfo->{'slave_restart'}= 1; + $tinfo->{'slave_num'}= 1; # Default for rpl* tests, use one slave if ( $tname eq 'rpl_failsafe' or $tname eq 'rpl_chain_temp_table' ) { -# $tinfo->{'slave_num'}= 3; # Not 3 ? Check old code, strange + # $tinfo->{'slave_num'}= 3; # Not 3 ? Check old code, strange } } if ( defined mtr_match_prefix($tname,"federated") ) { - $tinfo->{'slave_num'}= 1; # Default, use one slave - - # FIXME currently we always restart slaves - $tinfo->{'slave_restart'}= 1; + # Default, federated uses the first slave as it's federated database + $tinfo->{'slave_num'}= 1; } - # Cluster is needed by test case if testname contains ndb - if ( defined mtr_match_substring($tname,"ndb") ) - { - $tinfo->{'ndb_test'}= 1; - if ( $::opt_skip_ndbcluster ) - { - # Skip all ndb tests - $tinfo->{'skip'}= 1; - return; - } - if ( ! $::opt_with_ndbcluster ) - { - # Ndb is not supported, skip them - $tinfo->{'skip'}= 1; - return; - } - } - else - { - $tinfo->{'ndb_test'}= 0; - } - - # FIXME what about embedded_server + ndbcluster, skip ?! - my $master_opt_file= "$testdir/$tname-master.opt"; my $slave_opt_file= "$testdir/$tname-slave.opt"; my $slave_mi_file= "$testdir/$tname.slave-mi"; my $master_sh= "$testdir/$tname-master.sh"; my $slave_sh= "$testdir/$tname-slave.sh"; my $disabled_file= "$testdir/$tname.disabled"; + my $im_opt_file= "$testdir/$tname-im.opt"; $tinfo->{'master_opt'}= []; $tinfo->{'slave_opt'}= []; @@ -230,57 +326,59 @@ sub collect_one_test_case($$$$$$) { if ( -f $master_opt_file ) { - $tinfo->{'master_restart'}= 1; # We think so for now - MASTER_OPT: - { - my $master_opt= mtr_get_opts_from_file($master_opt_file); + my $master_opt= mtr_get_opts_from_file($master_opt_file); - foreach my $opt ( @$master_opt ) - { - my $value; + foreach my $opt ( @$master_opt ) + { + my $value; - # This is a dirty hack from old mysql-test-run, we use the opt - # file to flag other things as well, it is not a opt list at - # all + # The opt file is used both to send special options to the mysqld + # as well as pass special test case specific options to this + # script - $value= mtr_match_prefix($opt, "--timezone="); - if ( defined $value ) - { - $tinfo->{'timezone'}= $value; - last MASTER_OPT; - } + $value= mtr_match_prefix($opt, "--timezone="); + if ( defined $value ) + { + $tinfo->{'timezone'}= $value; + next; + } - $value= mtr_match_prefix($opt, "--result-file="); - if ( defined $value ) - { - $tinfo->{'result_file'}= "r/$value.result"; - if ( $::opt_result_ext and $::opt_record or - -f "$tinfo->{'result_file'}$::opt_result_ext") - { - $tinfo->{'result_file'}.= $::opt_result_ext; - } - $tinfo->{'master_restart'}= 0; - last MASTER_OPT; - } + $value= mtr_match_prefix($opt, "--result-file="); + if ( defined $value ) + { + # Specifies the file mysqltest should compare + # output against + $tinfo->{'result_file'}= "r/$value.result"; + next; + } - # If we set default time zone, remove the one we have - $value= mtr_match_prefix($opt, "--default-time-zone="); - if ( defined $value ) - { - $tinfo->{'master_opt'}= []; - } + # If we set default time zone, remove the one we have + $value= mtr_match_prefix($opt, "--default-time-zone="); + if ( defined $value ) + { + # Set timezone for this test case to something different + $tinfo->{'timezone'}= "GMT-8"; + # Fallthrough, add the --default-time-zone option + } + # The --restart option forces a restart even if no special + # option is set. If the options are the same as next testcase + # there is no need to restart after the testcase + # has completed + if ( $opt eq "--force-restart" ) + { + $tinfo->{'force_restart'}= 1; + next; } - # Ok, this was a real option list, add it - push(@{$tinfo->{'master_opt'}}, @$master_opt); + # Ok, this was a real option, add it + push(@{$tinfo->{'master_opt'}}, $opt); } } if ( -f $slave_opt_file ) { - $tinfo->{'slave_restart'}= 1; my $slave_opt= mtr_get_opts_from_file($slave_opt_file); foreach my $opt ( @$slave_opt ) @@ -295,7 +393,6 @@ sub collect_one_test_case($$$$$$) { if ( -f $slave_mi_file ) { $tinfo->{'slave_mi'}= mtr_get_opts_from_file($slave_mi_file); - $tinfo->{'slave_restart'}= 1; } if ( -f $master_sh ) @@ -303,11 +400,12 @@ sub collect_one_test_case($$$$$$) { if ( $::glob_win32_perl ) { $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No tests with sh scripts on Windows"; + return; } else { $tinfo->{'master_sh'}= $master_sh; - $tinfo->{'master_restart'}= 1; } } @@ -316,37 +414,214 @@ sub collect_one_test_case($$$$$$) { if ( $::glob_win32_perl ) { $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No tests with sh scripts on Windows"; + return; } else { $tinfo->{'slave_sh'}= $slave_sh; - $tinfo->{'slave_restart'}= 1; } } + if ( -f $im_opt_file ) + { + $tinfo->{'im_opts'} = mtr_get_opts_from_file($im_opt_file); + } + else + { + $tinfo->{'im_opts'} = []; + } + # FIXME why this late? + my $marked_as_disabled= 0; if ( $disabled->{$tname} ) { - $tinfo->{'skip'}= 1; - $tinfo->{'disable'}= 1; # Sub type of 'skip' - $tinfo->{'comment'}= $disabled->{$tname} if $disabled->{$tname}; + $marked_as_disabled= 1; + $tinfo->{'comment'}= $disabled->{$tname}; } if ( -f $disabled_file ) { - $tinfo->{'skip'}= 1; - $tinfo->{'disable'}= 1; # Sub type of 'skip' + $marked_as_disabled= 1; $tinfo->{'comment'}= mtr_fromfile($disabled_file); } - # We can't restart a running server that may be in use + # If test was marked as disabled, either opt_enable_disabled is off and then + # we skip this test, or it is on and then we run this test but warn - if ( $::glob_use_running_server and - ( $tinfo->{'master_restart'} or $tinfo->{'slave_restart'} ) ) + if ( $marked_as_disabled ) { - $tinfo->{'skip'}= 1; + if ( $::opt_enable_disabled ) + { + $tinfo->{'dont_skip_though_disabled'}= 1; + } + else + { + $tinfo->{'skip'}= 1; + $tinfo->{'disable'}= 1; # Sub type of 'skip' + return; + } + } + + if ( $component_id eq 'im' ) + { + if ( $::glob_use_embedded_server ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No IM with embedded server"; + return; + } + elsif ( $::opt_ps_protocol ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No IM with --ps-protocol"; + return; + } + elsif ( $::opt_skip_im ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No IM tests(--skip-im)"; + return; + } + } + else + { + mtr_options_from_test_file($tinfo,"$testdir/${tname}.test"); + + if ( $tinfo->{'big_test'} and ! $::opt_big_test ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Test need 'big-test' option"; + return; + } + + if ( $tinfo->{'ndb_extra'} and ! $::opt_ndb_extra_test ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Test need 'ndb_extra' option"; + return; + } + + if ( $tinfo->{'require_manager'} ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Test need the _old_ manager(to be removed)"; + return; + } + + if ( defined $tinfo->{'binlog_format'} and + ! ( $tinfo->{'binlog_format'} eq $::used_binlog_format ) ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Not running with binlog format '$tinfo->{'binlog_format'}'"; + return; + } + + if ( $tinfo->{'need_debug'} && ! $::debug_compiled_binaries ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Test need debug binaries"; + return; + } + + if ( $tinfo->{'ndb_test'} ) + { + # This is a NDB test + if ( ! $::glob_ndbcluster_supported ) + { + # Ndb is not supported, skip it + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No ndbcluster support"; + return; + } + elsif ( $::opt_skip_ndbcluster ) + { + # All ndb test's should be skipped + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No ndbcluster tests(--skip-ndbcluster)"; + return; + } + } + else + { + # This is not a ndb test + if ( $::opt_with_ndbcluster_only ) + { + # Only the ndb test should be run, all other should be skipped + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Only ndbcluster tests(--with-ndbcluster-only)"; + return; + } + } + + if ( $tinfo->{'innodb_test'} ) + { + # This is a test that need inndob + if ( $::mysqld_variables{'innodb'} eq "FALSE" ) + { + # innodb is not supported, skip it + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No innodb support"; + return; + } + } + } } +# List of tags in the .test files that if found should set +# the specified value in "tinfo" +our @tags= +( + ["include/have_innodb.inc", "innodb_test", 1], + ["include/have_binlog_format_row.inc", "binlog_format", "row"], + ["include/have_binlog_format_statement.inc", "binlog_format", "stmt"], + ["include/have_binlog_format_mixed.inc", "binlog_format", "mixed"], + ["include/big_test.inc", "big_test", 1], + ["include/have_debug.inc", "need_debug", 1], + ["include/have_ndb.inc", "ndb_test", 1], + ["include/have_ndb_extra.inc", "ndb_extra", 1], + ["require_manager", "require_manager", 1], +); + +sub mtr_options_from_test_file($$) { + my $tinfo= shift; + my $file= shift; + #mtr_verbose("$file"); + my $F= IO::File->new($file) or mtr_error("can't open file \"$file\": $!"); + + while ( my $line= <$F> ) + { + # Match this line against tag in "tags" array + foreach my $tag (@tags) + { + if ( index($line, $tag->[0]) >= 0 ) + { + # Tag matched, assign value to "tinfo" + $tinfo->{"$tag->[1]"}= $tag->[2]; + } + } + + # If test sources another file, open it as well + if ( $line =~ /^\-\-([[:space:]]*)source(.*)$/ or + $line =~ /^([[:space:]]*)source(.*);$/ ) + { + my $value= $2; + $value =~ s/^\s+//; # Remove leading space + $value =~ s/[[:space:]]+$//; # Remove ending space + + my $sourced_file= "$::glob_mysql_test_dir/$value"; + if ( -f $sourced_file ) + { + # Only source the file if it exists, we may get + # false positives in the regexes above if someone + # writes "source nnnn;" in a test case(such as mysqltest.test) + mtr_options_from_test_file($tinfo, $sourced_file); + } + } + + } +} + 1; diff --git a/mysql-test/lib/mtr_gcov.pl b/mysql-test/lib/mtr_gcov.pl index 07aac1d2017..71d3d6a2a43 100644 --- a/mysql-test/lib/mtr_gcov.pl +++ b/mysql-test/lib/mtr_gcov.pl @@ -23,12 +23,28 @@ sub gcov_prepare () { -or -name \*.da | xargs rm`; } +# Used by gcov +our @mysqld_src_dirs= + ( + "strings", + "mysys", + "include", + "extra", + "regex", + "isam", + "merge", + "myisam", + "myisammrg", + "heap", + "sql", + ); + sub gcov_collect () { print "Collecting source coverage info...\n"; -f $::opt_gcov_msg and unlink($::opt_gcov_msg); -f $::opt_gcov_err and unlink($::opt_gcov_err); - foreach my $d ( @::mysqld_src_dirs ) + foreach my $d ( @mysqld_src_dirs ) { chdir("$::glob_basedir/$d"); foreach my $f ( (glob("*.h"), glob("*.cc"), glob("*.c")) ) diff --git a/mysql-test/lib/mtr_im.pl b/mysql-test/lib/mtr_im.pl new file mode 100644 index 00000000000..ca17516278e --- /dev/null +++ b/mysql-test/lib/mtr_im.pl @@ -0,0 +1,761 @@ +# -*- cperl -*- + +# This is a library file used by the Perl version of mysql-test-run, +# and is part of the translation of the Bourne shell script with the +# same name. + +use strict; + +# Private IM-related operations. + +sub mtr_im_kill_process ($$$$); +sub mtr_im_load_pids ($); +sub mtr_im_terminate ($); +sub mtr_im_check_alive ($); +sub mtr_im_check_main_alive ($); +sub mtr_im_check_angel_alive ($); +sub mtr_im_check_mysqlds_alive ($); +sub mtr_im_check_mysqld_alive ($); +sub mtr_im_cleanup ($); +sub mtr_im_rm_file ($); +sub mtr_im_errlog ($); +sub mtr_im_kill ($); +sub mtr_im_wait_for_connection ($$$); +sub mtr_im_wait_for_mysqld($$$); + +# Public IM-related operations. + +sub mtr_im_start ($$); +sub mtr_im_stop ($); + +############################################################################## +# +# Private operations. +# +############################################################################## + +sub mtr_im_kill_process ($$$$) { + my $pid_lst= shift; + my $signal= shift; + my $total_retries= shift; + my $timeout= shift; + + my %pids; + + foreach my $pid ( @{$pid_lst} ) + { + $pids{$pid}= 1; + } + + for ( my $cur_attempt= 1; $cur_attempt <= $total_retries; ++$cur_attempt ) + { + foreach my $pid ( keys %pids ) + { + mtr_debug("Sending $signal to $pid..."); + + kill($signal, $pid); + + unless ( kill (0, $pid) ) + { + mtr_debug("Process $pid died."); + delete $pids{$pid}; + } + } + + return if scalar keys %pids == 0; + + mtr_debug("Sleeping $timeout second(s) waiting for processes to die..."); + + sleep($timeout); + } + + mtr_debug("Process(es) " . + join(' ', keys %pids) . + " is still alive after $total_retries " . + "of sending signal $signal."); +} + +########################################################################### + +sub mtr_im_load_pids($) { + my $im= shift; + + mtr_debug("Loading PID files..."); + + # Obtain mysqld-process pids. + + my $instances = $im->{'instances'}; + + for ( my $idx= 0; $idx < 2; ++$idx ) + { + mtr_debug("IM-guarded mysqld[$idx] PID file: '" . + $instances->[$idx]->{'path_pid'} . "'."); + + my $mysqld_pid; + + if ( -r $instances->[$idx]->{'path_pid'} ) + { + $mysqld_pid= mtr_get_pid_from_file($instances->[$idx]->{'path_pid'}); + mtr_debug("IM-guarded mysqld[$idx] PID: $mysqld_pid."); + } + else + { + $mysqld_pid= undef; + mtr_debug("IM-guarded mysqld[$idx]: no PID file."); + } + + $instances->[$idx]->{'pid'}= $mysqld_pid; + } + + # Re-read Instance Manager PIDs from the file, since during tests Instance + # Manager could have been restarted, so its PIDs could have been changed. + + # - IM-main + + mtr_debug("IM-main PID file: '$im->{path_pid}'."); + + if ( -f $im->{'path_pid'} ) + { + $im->{'pid'} = + mtr_get_pid_from_file($im->{'path_pid'}); + + mtr_debug("IM-main PID: $im->{pid}."); + } + else + { + mtr_debug("IM-main: no PID file."); + $im->{'pid'}= undef; + } + + # - IM-angel + + mtr_debug("IM-angel PID file: '$im->{path_angel_pid}'."); + + if ( -f $im->{'path_angel_pid'} ) + { + $im->{'angel_pid'} = + mtr_get_pid_from_file($im->{'path_angel_pid'}); + + mtr_debug("IM-angel PID: $im->{'angel_pid'}."); + } + else + { + mtr_debug("IM-angel: no PID file."); + $im->{'angel_pid'} = undef; + } +} + +########################################################################### + +sub mtr_im_terminate($) { + my $im= shift; + + # Load pids from pid-files. We should do it first of all, because IM deletes + # them on shutdown. + + mtr_im_load_pids($im); + + mtr_debug("Shutting Instance Manager down..."); + + # Ignoring SIGCHLD so that all children could rest in peace. + + start_reap_all(); + + # Send SIGTERM to IM-main. + + if ( defined $im->{'pid'} ) + { + mtr_debug("IM-main pid: $im->{pid}."); + mtr_debug("Stopping IM-main..."); + + mtr_im_kill_process([ $im->{'pid'} ], 'TERM', 10, 1); + } + else + { + mtr_debug("IM-main pid: n/a."); + } + + # If IM-angel was alive, wait for it to die. + + if ( defined $im->{'angel_pid'} ) + { + mtr_debug("IM-angel pid: $im->{'angel_pid'}."); + mtr_debug("Waiting for IM-angel to die..."); + + my $total_attempts= 10; + + for ( my $cur_attempt=1; $cur_attempt <= $total_attempts; ++$cur_attempt ) + { + unless ( kill (0, $im->{'angel_pid'}) ) + { + mtr_debug("IM-angel died."); + last; + } + + sleep(1); + } + } + else + { + mtr_debug("IM-angel pid: n/a."); + } + + stop_reap_all(); + + # Re-load PIDs. + + mtr_im_load_pids($im); +} + +########################################################################### + +sub mtr_im_check_alive($) { + my $im= shift; + + mtr_debug("Checking whether IM-components are alive..."); + + return 1 if mtr_im_check_main_alive($im); + + return 1 if mtr_im_check_angel_alive($im); + + return 1 if mtr_im_check_mysqlds_alive($im); + + return 0; +} + +########################################################################### + +sub mtr_im_check_main_alive($) { + my $im= shift; + + # Check that the process, that we know to be IM's, is dead. + + if ( defined $im->{'pid'} ) + { + if ( kill (0, $im->{'pid'}) ) + { + mtr_debug("IM-main (PID: $im->{pid}) is alive."); + return 1; + } + else + { + mtr_debug("IM-main (PID: $im->{pid}) is dead."); + } + } + else + { + mtr_debug("No PID file for IM-main."); + } + + # Check that IM does not accept client connections. + + if ( mtr_ping_port($im->{'port'}) ) + { + mtr_debug("IM-main (port: $im->{port}) " . + "is accepting connections."); + + mtr_im_errlog("IM-main is accepting connections on port " . + "$im->{port}, but there is no " . + "process information."); + return 1; + } + else + { + mtr_debug("IM-main (port: $im->{port}) " . + "does not accept connections."); + return 0; + } +} + +########################################################################### + +sub mtr_im_check_angel_alive($) { + my $im= shift; + + # Check that the process, that we know to be the Angel, is dead. + + if ( defined $im->{'angel_pid'} ) + { + if ( kill (0, $im->{'angel_pid'}) ) + { + mtr_debug("IM-angel (PID: $im->{angel_pid}) is alive."); + return 1; + } + else + { + mtr_debug("IM-angel (PID: $im->{angel_pid}) is dead."); + return 0; + } + } + else + { + mtr_debug("No PID file for IM-angel."); + return 0; + } +} + +########################################################################### + +sub mtr_im_check_mysqlds_alive($) { + my $im= shift; + + mtr_debug("Checking for IM-guarded mysqld instances..."); + + my $instances = $im->{'instances'}; + + for ( my $idx= 0; $idx < 2; ++$idx ) + { + mtr_debug("Checking mysqld[$idx]..."); + + return 1 + if mtr_im_check_mysqld_alive($instances->[$idx]); + } +} + +########################################################################### + +sub mtr_im_check_mysqld_alive($) { + my $mysqld_instance= shift; + + # Check that the process is dead. + + if ( defined $mysqld_instance->{'pid'} ) + { + if ( kill (0, $mysqld_instance->{'pid'}) ) + { + mtr_debug("Mysqld instance (PID: $mysqld_instance->{pid}) is alive."); + return 1; + } + else + { + mtr_debug("Mysqld instance (PID: $mysqld_instance->{pid}) is dead."); + } + } + else + { + mtr_debug("No PID file for mysqld instance."); + } + + # Check that mysqld does not accept client connections. + + if ( mtr_ping_port($mysqld_instance->{'port'}) ) + { + mtr_debug("Mysqld instance (port: $mysqld_instance->{port}) " . + "is accepting connections."); + + mtr_im_errlog("Mysqld is accepting connections on port " . + "$mysqld_instance->{port}, but there is no " . + "process information."); + return 1; + } + else + { + mtr_debug("Mysqld instance (port: $mysqld_instance->{port}) " . + "does not accept connections."); + return 0; + } +} + +########################################################################### + +sub mtr_im_cleanup($) { + my $im= shift; + + mtr_im_rm_file($im->{'path_pid'}); + mtr_im_rm_file($im->{'path_sock'}); + + mtr_im_rm_file($im->{'path_angel_pid'}); + + for ( my $idx= 0; $idx < 2; ++$idx ) + { + mtr_im_rm_file($im->{'instances'}->[$idx]->{'path_pid'}); + mtr_im_rm_file($im->{'instances'}->[$idx]->{'path_sock'}); + } +} + +########################################################################### + +sub mtr_im_rm_file($) +{ + my $file_path= shift; + + if ( -f $file_path ) + { + mtr_debug("Removing '$file_path'..."); + + unless ( unlink($file_path) ) + { + mtr_warning("Can not remove '$file_path'.") + } + } + else + { + mtr_debug("File '$file_path' does not exist already."); + } +} + +########################################################################### + +sub mtr_im_errlog($) { + my $msg= shift; + + # Complain in error log so that a warning will be shown. + # + # TODO: unless BUG#20761 is fixed, we will print the warning to stdout, so + # that it can be seen on console and does not produce pushbuild error. + + # my $errlog= "$opt_vardir/log/mysql-test-run.pl.err"; + # + # open (ERRLOG, ">>$errlog") || + # mtr_error("Can not open error log ($errlog)"); + # + # my $ts= localtime(); + # print ERRLOG + # "Warning: [$ts] $msg\n"; + # + # close ERRLOG; + + my $ts= localtime(); + print "Warning: [$ts] $msg\n"; +} + +########################################################################### + +sub mtr_im_kill($) { + my $im= shift; + + # Re-load PIDs. That can be useful because some processes could have been + # restarted. + + mtr_im_load_pids($im); + + # Ignoring SIGCHLD so that all children could rest in peace. + + start_reap_all(); + + # Kill IM-angel first of all. + + if ( defined $im->{'angel_pid'} ) + { + mtr_debug("Killing IM-angel (PID: $im->{angel_pid})..."); + mtr_im_kill_process([ $im->{'angel_pid'} ], 'KILL', 10, 1) + } + else + { + mtr_debug("IM-angel is dead."); + } + + # Re-load PIDs again. + + mtr_im_load_pids($im); + + # Kill IM-main. + + if ( defined $im->{'pid'} ) + { + mtr_debug("Killing IM-main (PID: $im->pid})..."); + mtr_im_kill_process([ $im->{'pid'} ], 'KILL', 10, 1); + } + else + { + mtr_debug("IM-main is dead."); + } + + # Re-load PIDs again. + + mtr_im_load_pids($im); + + # Kill guarded mysqld instances. + + my @mysqld_pids; + + mtr_debug("Collecting PIDs of mysqld instances to kill..."); + + for ( my $idx= 0; $idx < 2; ++$idx ) + { + my $pid= $im->{'instances'}->[$idx]->{'pid'}; + + unless ( defined $pid ) + { + next; + } + + mtr_debug(" - IM-guarded mysqld[$idx] PID: $pid."); + + push (@mysqld_pids, $pid); + } + + if ( scalar @mysqld_pids > 0 ) + { + mtr_debug("Killing IM-guarded mysqld instances..."); + mtr_im_kill_process(\@mysqld_pids, 'KILL', 10, 1); + } + + # That's all. + + stop_reap_all(); +} + +############################################################################## + +sub mtr_im_wait_for_connection($$$) { + my $im= shift; + my $total_attempts= shift; + my $connect_timeout= shift; + + mtr_debug("Waiting for IM on port $im->{port} " . + "to start accepting connections..."); + + for ( my $cur_attempt= 1; $cur_attempt <= $total_attempts; ++$cur_attempt ) + { + mtr_debug("Trying to connect to IM ($cur_attempt of $total_attempts)..."); + + if ( mtr_ping_port($im->{'port'}) ) + { + mtr_debug("IM is accepting connections " . + "on port $im->{port}."); + return 1; + } + + mtr_debug("Sleeping $connect_timeout..."); + sleep($connect_timeout); + } + + mtr_debug("IM does not accept connections " . + "on port $im->{port} after " . + ($total_attempts * $connect_timeout) . " seconds."); + + return 0; +} + +############################################################################## + +sub mtr_im_wait_for_mysqld($$$) { + my $mysqld= shift; + my $total_attempts= shift; + my $connect_timeout= shift; + + mtr_debug("Waiting for IM-guarded mysqld on port $mysqld->{port} " . + "to start accepting connections..."); + + for ( my $cur_attempt= 1; $cur_attempt <= $total_attempts; ++$cur_attempt ) + { + mtr_debug("Trying to connect to mysqld " . + "($cur_attempt of $total_attempts)..."); + + if ( mtr_ping_port($mysqld->{'port'}) ) + { + mtr_debug("Mysqld is accepting connections " . + "on port $mysqld->{port}."); + return 1; + } + + mtr_debug("Sleeping $connect_timeout..."); + sleep($connect_timeout); + } + + mtr_debug("Mysqld does not accept connections " . + "on port $mysqld->{port} after " . + ($total_attempts * $connect_timeout) . " seconds."); + + return 0; +} + +############################################################################## +# +# Public operations. +# +############################################################################## + +sub mtr_im_start($$) { + my $im = shift; + my $opts = shift; + + mtr_debug("Starting Instance Manager..."); + + my $args; + mtr_init_args(\$args); + mtr_add_arg($args, "--defaults-file=%s", $im->{'defaults_file'}); + + foreach my $opt ( @{$opts} ) + { + mtr_add_arg($args, $opt); + } + + $im->{'pid'} = + mtr_spawn( + $::exe_im, # path to the executable + $args, # cmd-line args + '', # stdin + $im->{'path_log'}, # stdout + $im->{'path_err'}, # stderr + '', # pid file path (not used) + { append_log_file => 1 } # append log files + ); + + unless ( $im->{'pid'} ) + { + mtr_error('Could not start Instance Manager.') + } + + # Instance Manager can be run in daemon mode. In this case, it creates + # several processes and the parent process, created by mtr_spawn(), exits just + # after start. So, we have to obtain Instance Manager PID from the PID file. + + mtr_debug("Waiting for IM to create PID file (" . + "path: '$im->{path_pid}'; " . + "timeout: $im->{start_timeout})..."); + + unless ( sleep_until_file_created($im->{'path_pid'}, + $im->{'start_timeout'}, + -1) ) # real PID is still unknown + { + mtr_debug("IM has not created PID file in $im->{start_timeout} secs."); + mtr_debug("Aborting test suite..."); + + mtr_kill_leftovers(); + + mtr_report("IM has not created PID file in $im->{start_timeout} secs."); + return 0; + } + + $im->{'pid'}= mtr_get_pid_from_file($im->{'path_pid'}); + + mtr_debug("Instance Manager started. PID: $im->{pid}."); + + # Wait until we can connect to IM. + + my $IM_CONNECT_TIMEOUT= 30; + + unless ( mtr_im_wait_for_connection($im, + $IM_CONNECT_TIMEOUT, 1) ) + { + mtr_debug("Can not connect to Instance Manager " . + "in $IM_CONNECT_TIMEOUT seconds after start."); + mtr_debug("Aborting test suite..."); + + mtr_kill_leftovers(); + + mtr_report("Can not connect to Instance Manager " . + "in $IM_CONNECT_TIMEOUT seconds after start."); + return 0; + } + + # Wait for IM to start guarded instances: + # - wait for PID files; + + mtr_debug("Waiting for guarded mysqlds instances to create PID files..."); + + for ( my $idx= 0; $idx < 2; ++$idx ) + { + my $mysqld= $im->{'instances'}->[$idx]; + + if ( exists $mysqld->{'nonguarded'} ) + { + next; + } + + mtr_debug("Waiting for mysqld[$idx] to create PID file (" . + "path: '$mysqld->{path_pid}'; " . + "timeout: $mysqld->{start_timeout})..."); + + unless ( sleep_until_file_created($mysqld->{'path_pid'}, + $mysqld->{'start_timeout'}, + -1) ) # real PID is still unknown + { + mtr_debug("mysqld[$idx] has not created PID file in " . + "$mysqld->{start_timeout} secs."); + mtr_debug("Aborting test suite..."); + + mtr_kill_leftovers(); + + mtr_report("mysqld[$idx] has not created PID file in " . + "$mysqld->{start_timeout} secs."); + return 0; + } + + mtr_debug("PID file for mysqld[$idx] ($mysqld->{path_pid} created."); + } + + # Wait until we can connect to guarded mysqld-instances + # (in other words -- wait for IM to start guarded instances). + + mtr_debug("Waiting for guarded mysqlds to start accepting connections..."); + + for ( my $idx= 0; $idx < 2; ++$idx ) + { + my $mysqld= $im->{'instances'}->[$idx]; + + if ( exists $mysqld->{'nonguarded'} ) + { + next; + } + + mtr_debug("Waiting for mysqld[$idx] to accept connection..."); + + unless ( mtr_im_wait_for_mysqld($mysqld, 30, 1) ) + { + mtr_debug("Can not connect to mysqld[$idx] " . + "in $IM_CONNECT_TIMEOUT seconds after start."); + mtr_debug("Aborting test suite..."); + + mtr_kill_leftovers(); + + mtr_report("Can not connect to mysqld[$idx] " . + "in $IM_CONNECT_TIMEOUT seconds after start."); + return 0; + } + + mtr_debug("mysqld[$idx] started."); + } + + mtr_debug("Instance Manager and its components are up and running."); + + return 1; +} + +############################################################################## + +sub mtr_im_stop($) { + my $im= shift; + + mtr_debug("Stopping Instance Manager..."); + + # Try graceful shutdown. + + mtr_im_terminate($im); + + # Check that all processes died. + + unless ( mtr_im_check_alive($im) ) + { + mtr_debug("Instance Manager has been stopped successfully."); + mtr_im_cleanup($im); + return 1; + } + + # Instance Manager don't want to die. We should kill it. + + mtr_im_errlog("Instance Manager did not shutdown gracefully."); + + mtr_im_kill($im); + + # Check again that all IM-related processes have been killed. + + my $im_is_alive= mtr_im_check_alive($im); + + mtr_im_cleanup($im); + + if ( $im_is_alive ) + { + mtr_debug("Can not kill Instance Manager or its children."); + return 0; + } + + mtr_debug("Instance Manager has been killed successfully."); + return 1; +} + +########################################################################### + +1; diff --git a/mysql-test/lib/mtr_io.pl b/mysql-test/lib/mtr_io.pl index b3da6d97664..984d834486c 100644 --- a/mysql-test/lib/mtr_io.pl +++ b/mysql-test/lib/mtr_io.pl @@ -11,6 +11,8 @@ sub mtr_get_opts_from_file ($); sub mtr_fromfile ($); sub mtr_tofile ($@); sub mtr_tonewfile($@); +sub mtr_lastlinefromfile($); +sub mtr_appendfile_to_file ($$); ############################################################################## # @@ -19,13 +21,37 @@ sub mtr_tonewfile($@); ############################################################################## sub mtr_get_pid_from_file ($) { - my $file= shift; + my $pid_file_path= shift; + my $TOTAL_ATTEMPTS= 30; + my $timeout= 1; - open(FILE,"<",$file) or mtr_error("can't open file \"$file\": $!"); - my $pid= <FILE>; - chomp($pid); - close FILE; - return $pid; + # We should read from the file until we get correct pid. As it is + # stated in BUG#21884, pid file can be empty at some moment. So, we should + # read it until we get valid data. + + for (my $cur_attempt= 1; $cur_attempt <= $TOTAL_ATTEMPTS; ++$cur_attempt) + { + mtr_debug("Reading pid file '$pid_file_path' " . + "($cur_attempt of $TOTAL_ATTEMPTS)..."); + + open(FILE, '<', $pid_file_path) + or mtr_error("can't open file \"$pid_file_path\": $!"); + + # Read pid number from file + my $pid= <FILE>; + close FILE; + + return $pid if $pid=~ /^(\d+)/; + + mtr_debug("Pid file '$pid_file_path' does not yet contain pid number.\n" . + "Sleeping $timeout second(s) more..."); + + sleep($timeout); + } + + mtr_error("Pid file '$pid_file_path' is corrupted. " . + "Can not retrieve PID in " . + ($timeout * $TOTAL_ATTEMPTS) . " seconds."); } sub mtr_get_opts_from_file ($) { @@ -113,6 +139,20 @@ sub mtr_fromfile ($) { return $text; } +sub mtr_lastlinefromfile ($) { + my $file= shift; + my $text; + + open(FILE,"<",$file) or mtr_error("can't open file \"$file\": $!"); + while (my $line= <FILE>) + { + $text= $line; + } + close FILE; + return $text; +} + + sub mtr_tofile ($@) { my $file= shift; @@ -129,5 +169,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_misc.pl b/mysql-test/lib/mtr_misc.pl index 08c99e90906..5ac89aee62c 100644 --- a/mysql-test/lib/mtr_misc.pl +++ b/mysql-test/lib/mtr_misc.pl @@ -9,10 +9,12 @@ use strict; sub mtr_full_hostname (); sub mtr_short_hostname (); sub mtr_init_args ($); -sub mtr_add_arg ($$); +sub mtr_add_arg ($$@); sub mtr_path_exists(@); sub mtr_script_exists(@); +sub mtr_file_exists(@); sub mtr_exe_exists(@); +sub mtr_exe_maybe_exists(@); sub mtr_copy_dir($$); sub mtr_same_opts($$); sub mtr_cmp_opts($$); @@ -54,7 +56,7 @@ sub mtr_init_args ($) { $$args = []; # Empty list } -sub mtr_add_arg ($$) { +sub mtr_add_arg ($$@) { my $args= shift; my $format= shift; my @fargs = @_; @@ -101,8 +103,17 @@ sub mtr_script_exists (@) { } } -sub mtr_exe_exists (@) { +sub mtr_file_exists (@) { + foreach my $path ( @_ ) + { + return $path if -e $path; + } + return ""; +} + +sub mtr_exe_maybe_exists (@) { my @path= @_; + map {$_.= ".exe"} @path if $::glob_win32; foreach my $path ( @path ) { @@ -115,6 +126,16 @@ sub mtr_exe_exists (@) { return $path if -x $path; } } + return ""; +} + +sub mtr_exe_exists (@) { + my @path= @_; + if (my $path= mtr_exe_maybe_exists(@path)) + { + return $path; + } + # Could not find exe, show error if ( @path == 1 ) { mtr_error("Could not find $path[0]"); @@ -125,19 +146,30 @@ sub mtr_exe_exists (@) { } } + sub mtr_copy_dir($$) { - my $srcdir= shift; - my $dstdir= shift; + my $from_dir= shift; + my $to_dir= shift; - # Create destination directory - mkpath($dstdir); - find(\&mtr_copy_one_file, $dstdir); -} +# mtr_verbose("Copying from $from_dir to $to_dir"); + + mkpath("$to_dir"); + opendir(DIR, "$from_dir") + or mtr_error("Can't find $from_dir$!"); + for(readdir(DIR)) { + next if "$_" eq "." or "$_" eq ".."; + if ( -d "$from_dir/$_" ) + { + mtr_copy_dir("$from_dir/$_", "$to_dir/$_"); + next; + } + copy("$from_dir/$_", "$to_dir/$_"); + } + closedir(DIR); -sub mtr_copy_one_file { - print $File::Find::name, "\n"; } + sub mtr_same_opts ($$) { my $l1= shift; my $l2= shift; diff --git a/mysql-test/lib/mtr_process.pl b/mysql-test/lib/mtr_process.pl index 662b70a4fee..048c336f8a3 100644 --- a/mysql-test/lib/mtr_process.pl +++ b/mysql-test/lib/mtr_process.pl @@ -4,22 +4,25 @@ # 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; -#use POSIX ":sys_wait_h"; use POSIX 'WNOHANG'; sub mtr_run ($$$$$$;$); sub mtr_spawn ($$$$$$;$); -sub mtr_stop_mysqld_servers ($); +sub mtr_check_stop_servers ($); sub mtr_kill_leftovers (); +sub mtr_wait_blocking ($); sub mtr_record_dead_children (); +sub mtr_ndbmgm_start($$); +sub mtr_mysqladmin_start($$$); sub mtr_exit ($); sub sleep_until_file_created ($$$); sub mtr_kill_processes ($); +sub mtr_ping_with_timeout($); +sub mtr_ping_port ($); # static in C sub spawn_impl ($$$$$$$$); @@ -31,7 +34,6 @@ sub spawn_impl ($$$$$$$$); ############################################################################## # This function try to mimic the C version used in "netware/mysql_test_run.c" -# FIXME learn it to handle append mode as well, a "new" flag or a "append" sub mtr_run ($$$$$$;$) { my $path= shift; @@ -112,6 +114,9 @@ sub spawn_impl ($$$$$$$$) { print STDERR "#### ", "-" x 78, "\n"; } + mtr_error("Can't spawn with empty \"path\"") unless defined $path; + + FORK: { my $pid= fork(); @@ -144,17 +149,6 @@ sub spawn_impl ($$$$$$$$) { $SIG{INT}= 'DEFAULT'; # Parent do some stuff, we don't - if ( $::glob_cygwin_shell and $mode eq 'test' ) - { - # Programs started from mysqltest under Cygwin, are to - # execute them within Cygwin. Else simple things in test - # files like - # --system "echo 1 > file" - # will fail. - # FIXME not working :-( -# $ENV{'COMSPEC'}= "$::glob_cygwin_shell -c"; - } - my $log_file_open_mode = '>'; if ($spawn_opts and $spawn_opts->{'append_log_file'}) @@ -164,7 +158,15 @@ sub spawn_impl ($$$$$$$$) { if ( $output ) { - if ( ! open(STDOUT,$log_file_open_mode,$output) ) + if ( $::glob_win32_perl ) + { + # Don't redirect stdout on ActiveState perl since this is + # just another thread in the same process. + # Should be fixed so that the thread that is created with fork + # executes the exe in another process and wait's for it to return. + # In the meanwhile, we get all the output from mysqld's to screen + } + elsif ( ! open(STDOUT,$log_file_open_mode,$output) ) { mtr_child_error("can't redirect STDOUT to \"$output\": $!"); } @@ -216,8 +218,7 @@ sub spawn_parent_impl { { # Simple run of command, we wait for it to return my $ret_pid= waitpid($pid,0); - - if ( $ret_pid <= 0 ) + if ( $ret_pid != $pid ) { mtr_error("$path ($pid) got lost somehow"); } @@ -245,7 +246,6 @@ sub spawn_parent_impl { # Someone terminated, don't know who. Collect # status info first before $? is lost, # but not $exit_value, this is flagged from - # my $timer_name= mtr_timer_timeout($::glob_timers, $ret_pid); if ( $timer_name ) @@ -272,45 +272,22 @@ sub spawn_parent_impl { last; } - # If one of the mysqld processes died, we want to - # mark this, and kill the mysqltest process. - - foreach my $idx (0..1) - { - if ( $::master->[$idx]->{'pid'} eq $ret_pid ) - { - mtr_debug("child $ret_pid was master[$idx], " . - "exit during mysqltest run"); - $::master->[$idx]->{'pid'}= 0; - last; - } - } - - foreach my $idx (0..2) - { - if ( $::slave->[$idx]->{'pid'} eq $ret_pid ) - { - mtr_debug("child $ret_pid was slave[$idx], " . - "exit during mysqltest run"); - $::slave->[$idx]->{'pid'}= 0; - last; - } - } + # One of the child processes died, unless this was expected + # mysqltest should be killed and test aborted - mtr_debug("waitpid() catched exit of unknown child $ret_pid, " . - "exit during mysqltest run"); + check_expected_crash_and_restart($ret_pid); } if ( $ret_pid != $pid ) { # We terminated the waiting because a "mysqld" process died. # Kill the mysqltest process. - + mtr_verbose("Kill mysqltest because another process died"); kill(9,$pid); $ret_pid= waitpid($pid,0); - if ( $ret_pid == -1 ) + if ( $ret_pid != $pid ) { mtr_error("$path ($pid) got lost somehow"); } @@ -351,39 +328,88 @@ sub mtr_process_exit_status { # ############################################################################## -# We just "ping" on the ports, and if we can't do a socket connect -# we assume the server is dead. So we don't *really* know a server -# is dead, we just hope that it after letting the listen port go, -# it is dead enough for us to start a new server. +# Kill all processes(mysqld, ndbd, ndb_mgmd and im) that would conflict with +# this run +# Make sure to remove the PID file, if any. +# kill IM manager first, else it will restart the servers sub mtr_kill_leftovers () { - # First, kill all masters and slaves that would conflict with - # this run. Make sure to remove the PID file, if any. + mtr_report("Killing Possible Leftover Processes"); + mtr_debug("mtr_kill_leftovers(): started."); - my @args; + my @kill_pids; + my %admin_pids; - for ( my $idx; $idx < 2; $idx++ ) + foreach my $srv (@{$::master}, @{$::slave}) { - push(@args,{ - pid => 0, # We don't know the PID - pidfile => $::master->[$idx]->{'path_mypid'}, - sockfile => $::master->[$idx]->{'path_mysock'}, - port => $::master->[$idx]->{'path_myport'}, - }); + mtr_debug(" - mysqld " . + "(pid: $srv->{pid}; " . + "pid file: '$srv->{path_pid}'; " . + "socket: '$srv->{path_sock}'; ". + "port: $srv->{port})"); + + my $pid= mtr_mysqladmin_start($srv, "shutdown", 70); + + # Save the pid of the mysqladmin process + $admin_pids{$pid}= 1; + + push(@kill_pids,{ + pid => $srv->{'pid'}, + pidfile => $srv->{'path_pid'}, + sockfile => $srv->{'path_sock'}, + port => $srv->{'port'}, + }); + $srv->{'pid'}= 0; # Assume we are done with it } - for ( my $idx; $idx < 3; $idx++ ) + if ( ! $::opt_skip_ndbcluster ) { - push(@args,{ - pid => 0, # We don't know the PID - pidfile => $::slave->[$idx]->{'path_mypid'}, - sockfile => $::slave->[$idx]->{'path_mysock'}, - port => $::slave->[$idx]->{'path_myport'}, - }); + # Start shutdown of clusters. + mtr_debug("Shutting down cluster..."); + + foreach my $cluster (@{$::clusters}) + { + mtr_debug(" - cluster " . + "(pid: $cluster->{pid}; " . + "pid file: '$cluster->{path_pid})"); + + my $pid= mtr_ndbmgm_start($cluster, "shutdown"); + + # Save the pid of the ndb_mgm process + $admin_pids{$pid}= 1; + + push(@kill_pids,{ + pid => $cluster->{'pid'}, + pidfile => $cluster->{'path_pid'} + }); + + $cluster->{'pid'}= 0; # Assume we are done with it + + foreach my $ndbd (@{$cluster->{'ndbds'}}) + { + mtr_debug(" - ndbd " . + "(pid: $ndbd->{pid}; " . + "pid file: '$ndbd->{path_pid})"); + + push(@kill_pids,{ + pid => $ndbd->{'pid'}, + pidfile => $ndbd->{'path_pid'}, + }); + $ndbd->{'pid'}= 0; # Assume we are done with it + } + } } - mtr_mysqladmin_shutdown(\@args, 20); + # Wait for all the admin processes to complete + mtr_wait_blocking(\%admin_pids); + + # If we trusted "mysqladmin --shutdown_timeout= ..." we could just + # terminate now, but we don't (FIXME should be debugged). + # So we try again to ping and at least wait the same amount of time + # mysqladmin would for all to die. + + mtr_ping_with_timeout(\@kill_pids); # We now have tried to terminate nice. We have waited for the listen # port to be free, but can't really tell if the mysqld process died @@ -401,6 +427,8 @@ sub mtr_kill_leftovers () { # FIXME $path_run_dir or something my $rundir= "$::opt_vardir/run"; + mtr_debug("Processing PID files in directory '$rundir'..."); + if ( -d $rundir ) { opendir(RUNDIR, $rundir) @@ -410,30 +438,46 @@ sub mtr_kill_leftovers () { while ( my $elem= readdir(RUNDIR) ) { - my $pidfile= "$rundir/$elem"; - - if ( -f $pidfile ) + # Only read pid from files that end with .pid + if ( $elem =~ /.*[.]pid$/) { - my $pid= mtr_get_pid_from_file($pidfile); - # Race, could have been removed between I tested with -f - # and the unlink() below, so I better check again with -f + my $pidfile= "$rundir/$elem"; - if ( ! unlink($pidfile) and -f $pidfile ) - { - mtr_error("can't remove $pidfile"); - } + if ( -f $pidfile ) + { + mtr_debug("Processing PID file: '$pidfile'..."); - if ( $::glob_cygwin_perl or kill(0, $pid) ) - { - push(@pids, $pid); # We know (cygwin guess) it exists - } + my $pid= mtr_get_pid_from_file($pidfile); + + mtr_debug("Got pid: $pid from file '$pidfile'"); + + if ( $::glob_cygwin_perl or kill(0, $pid) ) + { + mtr_debug("There is process with pid $pid -- scheduling for kill."); + push(@pids, $pid); # We know (cygwin guess) it exists + } + else + { + mtr_debug("There is no process with pid $pid -- skipping."); + } + } + } + else + { + mtr_warning("Found non pid file $elem in $rundir"); + next; } } closedir(RUNDIR); if ( @pids ) { + mtr_debug("Killing the following processes with PID files: " . + join(' ', @pids) . "..."); + + start_reap_all(); + if ( $::glob_cygwin_perl ) { # We have no (easy) way of knowing the Cygwin controlling @@ -447,8 +491,9 @@ sub mtr_kill_leftovers () { my $retries= 10; # 10 seconds do { + mtr_debug("Sending SIGKILL to pids: " . join(' ', @pids)); kill(9, @pids); - mtr_debug("Sleep 1 second waiting for processes to die"); + mtr_report("Sleep 1 second waiting for processes to die"); sleep(1) # Wait one second } while ( $retries-- and kill(0, @pids) ); @@ -457,56 +502,74 @@ sub mtr_kill_leftovers () { mtr_warning("can't kill process(es) " . join(" ", @pids)); } } + + stop_reap_all(); } } + else + { + mtr_debug("Directory for PID files ($rundir) does not exist."); + } - # We may have failed everything, bug we now check again if we have + # We may have failed everything, but we now check again if we have # the listen ports free to use, and if they are free, just go for it. - foreach my $srv ( @args ) + mtr_debug("Checking known mysqld servers..."); + + foreach my $srv ( @kill_pids ) { - if ( mtr_ping_mysqld_server($srv->{'port'}, $srv->{'sockfile'}) ) + if ( defined $srv->{'port'} and mtr_ping_port($srv->{'port'}) ) { - mtr_warning("can't kill old mysqld holding port $srv->{'port'}"); + mtr_warning("can't kill old process holding port $srv->{'port'}"); } } -} -############################################################################## -# -# Shut down mysqld servers we have started from this run of this script -# -############################################################################## + mtr_debug("mtr_kill_leftovers(): finished."); +} -# To speed things we kill servers in parallel. The argument is a list -# of 'ports', 'pids', 'pidfiles' and 'socketfiles'. +# Check that all processes in list are killed +# The argument is a list of 'ports', 'pids', 'pidfiles' and 'socketfiles' +# for which shutdown has been started. Make sure they all get killed +# in one way or the other. +# # FIXME On Cygwin, and maybe some other platforms, $srv->{'pid'} and -# $srv->{'pidfile'} will not be the same PID. We need to try to kill +# the pid in $srv->{'pidfile'} will not be the same PID. We need to try to kill # both I think. -sub mtr_stop_mysqld_servers ($) { +sub mtr_check_stop_servers ($) { my $spec= shift; - # ---------------------------------------------------------------------- - # First try nice normal shutdown using 'mysqladmin' - # ---------------------------------------------------------------------- + # Return if no processes are defined + return if ! @$spec; - # Shutdown time must be high as slave may be in reconnect - mtr_mysqladmin_shutdown($spec, 70); + #mtr_report("mtr_check_stop_servers"); + + mtr_ping_with_timeout(\@$spec); # ---------------------------------------------------------------------- # We loop with waitpid() nonblocking to see how many of the ones we - # are to kill, actually got killed by mtr_mysqladmin_shutdown(). - # Note that we don't rely on this, the mysqld server might have stop + # are to kill, actually got killed by mysqladmin or ndb_mgm + # + # Note that we don't rely on this, the mysqld server might have stopped # listening to the port, but still be alive. But it is a start. # ---------------------------------------------------------------------- foreach my $srv ( @$spec ) { - if ( $srv->{'pid'} and (waitpid($srv->{'pid'},&WNOHANG) == $srv->{'pid'}) ) + my $ret_pid; + if ( $srv->{'pid'} ) { - $srv->{'pid'}= 0; + $ret_pid= waitpid($srv->{'pid'},&WNOHANG); + if ($ret_pid == $srv->{'pid'}) + { + mtr_verbose("Caught exit of process $ret_pid"); + $srv->{'pid'}= 0; + } + else + { + # mtr_warning("caught exit of unknown child $ret_pid"); + } } } @@ -540,13 +603,12 @@ sub mtr_stop_mysqld_servers ($) { } # ---------------------------------------------------------------------- - # If the processes where started from this script, and we had no PIDS + # If all the processes in list already have been killed, # then we don't have to do anything. # ---------------------------------------------------------------------- if ( ! keys %mysqld_pids ) { - # cluck "This is how we got here!"; return; } @@ -595,139 +657,288 @@ sub mtr_stop_mysqld_servers ($) { foreach my $file ($srv->{'pidfile'}, $srv->{'sockfile'}) { # Know it is dead so should be no race, careful anyway - if ( -f $file and ! unlink($file) and -f $file ) + if ( defined $file and -f $file and ! unlink($file) and -f $file ) { $errors++; mtr_warning("couldn't delete $file"); } } + $srv->{'pid'}= 0; } } } if ( $errors ) { - # We are in trouble, just die.... - mtr_error("we could not kill or clean up all processes"); + # There where errors killing processes + # do one last attempt to ping the servers + # and if they can't be pinged, assume they are dead + if ( ! mtr_ping_with_timeout( \@$spec ) ) + { + mtr_error("we could not kill or clean up all processes"); + } + else + { + mtr_verbose("All ports were free, continuing"); + } } } # FIXME We just assume they are all dead, for Cygwin we are not # really sure - + } +# Wait for all the process in the list to terminate +sub mtr_wait_blocking($) { + my $admin_pids= shift; -############################################################################## -# -# Shut down mysqld servers using "mysqladmin ... shutdown". -# To speed this up, we start them in parallel and use waitpid() to -# catch their termination. Note that this doesn't say the servers -# are terminated, just that 'mysqladmin' is terminated. -# -# Note that mysqladmin will ask the server about what PID file it uses, -# and mysqladmin will wait for it to be removed before it terminates -# (unless passes timeout). -# -# This function will take at most about 20 seconds, and we still are not -# sure we killed them all. If none is responding to ping, we return 1, -# else we return 0. -# -############################################################################## -sub mtr_mysqladmin_shutdown { - my $spec= shift; + # Return if no processes defined + return if ! %$admin_pids; + + mtr_verbose("mtr_wait_blocking"); + + # Wait for all the started processes to exit + # As mysqladmin is such a simple program, we trust it to terminate itself. + # I.e. we wait blocking, and wait for them all before we go on. + foreach my $pid (keys %{$admin_pids}) + { + my $ret_pid= waitpid($pid,0); + + } +} + +# Start "mysqladmin shutdown" for a specific mysqld +sub mtr_mysqladmin_start($$$) { + my $srv= shift; + my $command= shift; my $adm_shutdown_tmo= shift; - my %mysql_admin_pids; - my @to_kill_specs; + my $args; + mtr_init_args(\$args); - foreach my $srv ( @$spec ) + mtr_add_arg($args, "--no-defaults"); + mtr_add_arg($args, "--user=%s", $::opt_user); + mtr_add_arg($args, "--password="); + mtr_add_arg($args, "--silent"); + if ( -e $srv->{'path_sock'} ) { - if ( mtr_ping_mysqld_server($srv->{'port'}, $srv->{'sockfile'}) ) + mtr_add_arg($args, "--socket=%s", $srv->{'path_sock'}); + } + if ( $srv->{'port'} ) + { + mtr_add_arg($args, "--port=%s", $srv->{'port'}); + } + if ( $srv->{'port'} and ! -e $srv->{'path_sock'} ) + { + mtr_add_arg($args, "--protocol=tcp"); # Needed if no --socket + } + mtr_add_arg($args, "--connect_timeout=5"); + + # Shutdown time must be high as slave may be in reconnect + mtr_add_arg($args, "--shutdown_timeout=$adm_shutdown_tmo"); + mtr_add_arg($args, "$command"); + my $path_mysqladmin_log= "$::opt_vardir/log/mysqladmin.log"; + my $pid= mtr_spawn($::exe_mysqladmin, $args, + "", $path_mysqladmin_log, $path_mysqladmin_log, "", + { append_log_file => 1 }); + mtr_verbose("mtr_mysqladmin_start, pid: $pid"); + return $pid; + +} + +# Start "ndb_mgm shutdown" for a specific cluster, it will +# shutdown all data nodes and leave the ndb_mgmd running +sub mtr_ndbmgm_start($$) { + my $cluster= shift; + my $command= shift; + + my $args; + + mtr_init_args(\$args); + + mtr_add_arg($args, "--no-defaults"); + mtr_add_arg($args, "--core"); + mtr_add_arg($args, "--try-reconnect=1"); + mtr_add_arg($args, "--ndb_connectstring=%s", $cluster->{'connect_string'}); + mtr_add_arg($args, "-e"); + mtr_add_arg($args, "$command"); + + my $pid= mtr_spawn($::exe_ndb_mgm, $args, + "", "/dev/null", "/dev/null", "", + {}); + mtr_verbose("mtr_ndbmgm_start, pid: $pid"); + return $pid; + +} + + +# Ping all servers in list, exit when none of them answers +# or when timeout has passed +sub mtr_ping_with_timeout($) { + my $spec= shift; + my $timeout= 200; # 20 seconds max + my $res= 1; # If we just fall through, we are done + # in the sense that the servers don't + # listen to their ports any longer + + mtr_debug("Waiting for mysqld servers to stop..."); + + TIME: + while ( $timeout-- ) + { + foreach my $srv ( @$spec ) { - push(@to_kill_specs, $srv); + $res= 1; # We are optimistic + if ( $srv->{'pid'} and defined $srv->{'port'} ) + { + if ( mtr_ping_port($srv->{'port'}) ) + { + mtr_verbose("waiting for process $srv->{'pid'} to stop ". + "using port $srv->{'port'}"); + + # Millisceond sleep emulated with select + select(undef, undef, undef, (0.1)); + $res= 0; + next TIME; + } + else + { + # Process was not using port + } + } } + last; # If we got here, we are done } - - foreach my $srv ( @to_kill_specs ) + if ($res) { - # FIXME wrong log..... - # FIXME, stderr..... - # Shutdown time must be high as slave may be in reconnect - my $args; + mtr_debug("mtr_ping_with_timeout(): All mysqld instances are down."); + } + else + { + mtr_report("mtr_ping_with_timeout(): At least one server is alive."); + } - mtr_init_args(\$args); + return $res; +} - mtr_add_arg($args, "--no-defaults"); - mtr_add_arg($args, "--user=%s", $::opt_user); - mtr_add_arg($args, "--password="); - if ( -e $srv->{'sockfile'} ) + +# +# Loop through our list of processes and look for and entry +# with the provided pid +# Set the pid of that process to 0 if found +# +sub mark_process_dead($) +{ + my $ret_pid= shift; + + foreach my $mysqld (@{$::master}, @{$::slave}) + { + if ( $mysqld->{'pid'} eq $ret_pid ) { - mtr_add_arg($args, "--socket=%s", $srv->{'sockfile'}); + mtr_verbose("$mysqld->{'type'} $mysqld->{'idx'} exited, pid: $ret_pid"); + $mysqld->{'pid'}= 0; + return; } - if ( $srv->{'port'} ) + } + + foreach my $cluster (@{$::clusters}) + { + if ( $cluster->{'pid'} eq $ret_pid ) { - mtr_add_arg($args, "--port=%s", $srv->{'port'}); + mtr_verbose("$cluster->{'name'} cluster ndb_mgmd exited, pid: $ret_pid"); + $cluster->{'pid'}= 0; + return; } - if ( $srv->{'port'} and ! -e $srv->{'sockfile'} ) + + foreach my $ndbd (@{$cluster->{'ndbds'}}) { - mtr_add_arg($args, "--protocol=tcp"); # Needed if no --socket + if ( $ndbd->{'pid'} eq $ret_pid ) + { + mtr_verbose("$cluster->{'name'} cluster ndbd exited, pid: $ret_pid"); + $ndbd->{'pid'}= 0; + return; + } } - mtr_add_arg($args, "--connect_timeout=5"); - mtr_add_arg($args, "--shutdown_timeout=$adm_shutdown_tmo"); - mtr_add_arg($args, "shutdown"); - # We don't wait for termination of mysqladmin - my $pid= mtr_spawn($::exe_mysqladmin, $args, - "", $::path_manager_log, $::path_manager_log, "", - { append_log_file => 1 }); - $mysql_admin_pids{$pid}= 1; } + mtr_warning("mark_process_dead couldn't find an entry for pid: $ret_pid"); - # As mysqladmin is such a simple program, we trust it to terminate. - # I.e. we wait blocking, and wait wait for them all before we go on. - while (keys %mysql_admin_pids) +} + +# +# Loop through our list of processes and look for and entry +# with the provided pid, if found check for the file indicating +# expected crash and restart it. +# +sub check_expected_crash_and_restart($) +{ + my $ret_pid= shift; + + foreach my $mysqld (@{$::master}, @{$::slave}) { - foreach my $pid (keys %mysql_admin_pids) + if ( $mysqld->{'pid'} eq $ret_pid ) { - if ( waitpid($pid,0) > 0 ) + mtr_verbose("$mysqld->{'type'} $mysqld->{'idx'} exited, pid: $ret_pid"); + $mysqld->{'pid'}= 0; + + # Check if crash expected and restart if it was + my $expect_file= "$::opt_vardir/tmp/" . "$mysqld->{'type'}" . + "$mysqld->{'idx'}" . ".expect"; + if ( -f $expect_file ) { - delete $mysql_admin_pids{$pid}; + mtr_verbose("Crash was expected, file $expect_file exists"); + mysqld_start($mysqld, $mysqld->{'start_opts'}, + $mysqld->{'start_slave_master_info'}); + unlink($expect_file); } + + return; } } - # If we trusted "mysqladmin --shutdown_timeout= ..." we could just - # terminate now, but we don't (FIXME should be debugged). - # So we try again to ping and at least wait the same amount of time - # mysqladmin would for all to die. - - my $timeout= 20; # 20 seconds max - my $res= 1; # If we just fall through, we are done - # in the sense that the servers don't - # listen to their ports any longer - TIME: - while ( $timeout-- ) + foreach my $cluster (@{$::clusters}) { - foreach my $srv ( @to_kill_specs ) + if ( $cluster->{'pid'} eq $ret_pid ) { - $res= 1; # We are optimistic - if ( mtr_ping_mysqld_server($srv->{'port'}, $srv->{'sockfile'}) ) + mtr_verbose("$cluster->{'name'} cluster ndb_mgmd exited, pid: $ret_pid"); + $cluster->{'pid'}= 0; + + # Check if crash expected and restart if it was + my $expect_file= "$::opt_vardir/tmp/ndb_mgmd_" . "$cluster->{'type'}" . + ".expect"; + if ( -f $expect_file ) { - mtr_debug("Sleep 1 second waiting for processes to stop using port"); - sleep(1); # One second - $res= 0; - next TIME; + mtr_verbose("Crash was expected, file $expect_file exists"); + ndbmgmd_start($cluster); + unlink($expect_file); } + return; } - last; # If we got here, we are done - } - $timeout or mtr_debug("At least one server is still listening to its port"); - - sleep(5) if $::glob_win32; # FIXME next startup fails if no sleep + foreach my $ndbd (@{$cluster->{'ndbds'}}) + { + if ( $ndbd->{'pid'} eq $ret_pid ) + { + mtr_verbose("$cluster->{'name'} cluster ndbd exited, pid: $ret_pid"); + $ndbd->{'pid'}= 0; + + # Check if crash expected and restart if it was + my $expect_file= "$::opt_vardir/tmp/ndbd_" . "$cluster->{'type'}" . + "$ndbd->{'idx'}" . ".expect"; + if ( -f $expect_file ) + { + mtr_verbose("Crash was expected, file $expect_file exists"); + ndbd_start($cluster, $ndbd->{'idx'}, + $ndbd->{'start_extra_args'}); + unlink($expect_file); + } + return; + } + } + } + mtr_warning("check_expected_crash_and_restart couldn't find an entry for pid: $ret_pid"); - return $res; } ############################################################################## @@ -740,32 +951,18 @@ sub mtr_mysqladmin_shutdown { sub mtr_record_dead_children () { + my $process_died= 0; my $ret_pid; - # FIXME the man page says to wait for -1 to terminate, - # but on OS X we get '0' all the time... - while ( ($ret_pid= waitpid(-1,&WNOHANG)) > 0 ) + # Wait without blockinng to see if any processes had died + # -1 or 0 means there are no more procesess to wait for + while ( ($ret_pid= waitpid(-1,&WNOHANG)) != 0 and $ret_pid != -1) { - mtr_debug("waitpid() catched exit of child $ret_pid"); - foreach my $idx (0..1) - { - if ( $::master->[$idx]->{'pid'} eq $ret_pid ) - { - mtr_debug("child $ret_pid was master[$idx]"); - $::master->[$idx]->{'pid'}= 0; - } - } - - foreach my $idx (0..2) - { - if ( $::slave->[$idx]->{'pid'} eq $ret_pid ) - { - mtr_debug("child $ret_pid was slave[$idx]"); - $::slave->[$idx]->{'pid'}= 0; - last; - } - } + mtr_warning("mtr_record_dead_children: $ret_pid"); + mark_process_dead($ret_pid); + $process_died= 1; } + return $process_died; } sub start_reap_all { @@ -777,16 +974,24 @@ sub start_reap_all { # here. If a process terminated before setting $SIG{CHLD} (but after # any attempt to waitpid() it), it will still be a zombie. So we # have to handle any such process here. - while(waitpid(-1, &WNOHANG) > 0) { }; + my $pid; + while(($pid= waitpid(-1, &WNOHANG)) != 0 and $pid != -1) + { + mtr_warning("start_reap_all pid: $pid"); + mark_process_dead($pid); + }; } sub stop_reap_all { $SIG{CHLD}= 'DEFAULT'; } -sub mtr_ping_mysqld_server () { + +sub mtr_ping_port ($) { my $port= shift; + mtr_verbose("mtr_ping_port: $port"); + my $remote= "localhost"; my $iaddr= inet_aton($remote); if ( ! $iaddr ) @@ -799,13 +1004,18 @@ sub mtr_ping_mysqld_server () { { mtr_error("can't create socket: $!"); } + + mtr_debug("Pinging server (port: $port)..."); + if ( connect(SOCK, $paddr) ) { close(SOCK); # FIXME check error? + mtr_verbose("USED"); return 1; } else { + mtr_verbose("FREE"); return 0; } } @@ -822,30 +1032,36 @@ sub sleep_until_file_created ($$$) { my $pidfile= shift; my $timeout= shift; my $pid= shift; + my $sleeptime= 100; # Milliseconds + my $loops= ($timeout * 1000) / $sleeptime; - for ( my $loop= 1; $loop <= $timeout; $loop++ ) + for ( my $loop= 1; $loop <= $loops; $loop++ ) { if ( -r $pidfile ) { return $pid; } - # Check if it died after the fork() was successful - if ( waitpid($pid,&WNOHANG) == $pid ) + # Check if it died after the fork() was successful + if ( $pid != 0 && waitpid($pid,&WNOHANG) == $pid ) { + mtr_warning("Process $pid died"); return 0; } - mtr_debug("Sleep 1 second waiting for creation of $pidfile"); + mtr_debug("Sleep $sleeptime milliseconds waiting for $pidfile"); - if ( $loop % 60 == 0 ) + # Print extra message every 60 seconds + my $seconds= ($loop * $sleeptime) / 1000; + if ( $seconds > 1 and int($seconds) % 60 == 0 ) { - my $left= $timeout - $loop; - mtr_warning("Waited $loop seconds for $pidfile to be created, " . + my $left= $timeout - $seconds; + mtr_warning("Waited $seconds seconds for $pidfile to be created, " . "still waiting for $left seconds..."); } - sleep(1); + # Millisceond sleep emulated with select + select(undef, undef, undef, ($sleeptime/1000)); } return 0; @@ -855,18 +1071,18 @@ sub sleep_until_file_created ($$$) { sub mtr_kill_processes ($) { my $pids = shift; - foreach my $sig (15,9) + mtr_verbose("mtr_kill_processes " . join(" ", @$pids)); + + foreach my $pid (@$pids) { - my $retries= 20; # FIXME 20 seconds, this is silly! - kill($sig, @{$pids}); - while ( $retries-- and kill(0, @{$pids}) ) + foreach my $sig (15, 9) { - mtr_debug("Sleep 1 second waiting for processes to die"); - sleep(1) # Wait one second + last if mtr_im_kill_process([ $pid ], $sig, 10, 1); } } } + ############################################################################## # # When we exit, we kill off all children @@ -876,17 +1092,25 @@ sub mtr_kill_processes ($) { # FIXME something is wrong, we sometimes terminate with "Hangup" written # to tty, and no STDERR output telling us why. -# FIXME for some readon, setting HUP to 'IGNORE' will cause exit() to +# FIXME for some reason, setting HUP to 'IGNORE' will cause exit() to # write out "Hangup", and maybe loose some output. We insert a sleep... sub mtr_exit ($) { my $code= shift; -# cluck("Called mtr_exit()"); mtr_timer_stop_all($::glob_timers); local $SIG{HUP} = 'IGNORE'; - kill('HUP', -$$); - sleep 2; + # ToDo: Signalling -$$ will only work if we are the process group + # leader (in fact on QNX it will signal our session group leader, + # which might be Do-compile or Pushbuild, causing tests to be + # aborted). So we only do it if we are the group leader. We might + # set ourselves as the group leader at startup (with + # POSIX::setpgrp(0,0)), but then care must be needed to always do + # proper child process cleanup. + kill('HUP', -$$) if !$::glob_win32_perl and $$ == getpgrp(); + exit($code); } +########################################################################### + 1; diff --git a/mysql-test/lib/mtr_report.pl b/mysql-test/lib/mtr_report.pl index 515988ee5c7..8d7de9d1a4b 100644 --- a/mysql-test/lib/mtr_report.pl +++ b/mysql-test/lib/mtr_report.pl @@ -10,6 +10,7 @@ sub mtr_report_test_name($); sub mtr_report_test_passed($); sub mtr_report_test_failed($); sub mtr_report_test_skipped($); +sub mtr_report_test_not_skipped_though_disabled($); sub mtr_show_failed_diff ($); sub mtr_report_stats ($); @@ -21,6 +22,7 @@ sub mtr_warning (@); sub mtr_error (@); sub mtr_child_error (@); sub mtr_debug (@); +sub mtr_verbose (@); ############################################################################## @@ -36,6 +38,7 @@ sub mtr_show_failed_diff ($) { my $reject_file= "r/$tname.reject"; my $result_file= "r/$tname.result"; + my $log_file= "r/$tname.log"; my $eval_file= "r/$tname.eval"; if ( $::opt_suite ne "main" ) @@ -43,18 +46,12 @@ sub mtr_show_failed_diff ($) { $reject_file= "$::glob_mysql_test_dir/suite/$::opt_suite/$reject_file"; $result_file= "$::glob_mysql_test_dir/suite/$::opt_suite/$result_file"; $eval_file= "$::glob_mysql_test_dir/suite/$::opt_suite/$eval_file"; + $log_file= "$::glob_mysql_test_dir/suite/$::opt_suite/$log_file"; } if ( -f $eval_file ) - { - $result_file= $eval_file; - } - elsif ( $::opt_result_ext and - ( $::opt_record or -f "$result_file$::opt_result_ext" )) { - # If we have an special externsion for result files we use it if we are - # recording or a result file with that extension exists. - $result_file= "$result_file$::opt_result_ext"; + $result_file= $eval_file; } my $diffopts= $::opt_udiff ? "-u" : "-c"; @@ -70,6 +67,12 @@ sub mtr_show_failed_diff ($) { print "http://www.mysql.com/doc/en/Reporting_mysqltest_bugs.html\n"; print "to find the reason to this problem and how to report this.\n\n"; } + + if ( -f $log_file ) + { + print "Result from queries before failure can be found in $log_file\n"; + # FIXME Maybe a tail -f -n 10 $log_file here + } } sub mtr_report_test_name ($) { @@ -88,7 +91,24 @@ sub mtr_report_test_skipped ($) { } else { - print "[ skipped ]\n"; + print "[ skipped ] $tinfo->{'comment'}\n"; + } +} + +sub mtr_report_tests_not_skipped_though_disabled ($) { + my $tests= shift; + + if ( $::opt_enable_disabled ) + { + my @disabled_tests= grep {$_->{'dont_skip_though_disabled'}} @$tests; + if ( @disabled_tests ) + { + print "\nTest(s) which will be run though they are marked as disabled:\n"; + foreach my $tinfo ( sort {$a->{'name'} cmp $b->{'name'}} @disabled_tests ) + { + printf " %-20s : %s\n", $tinfo->{'name'}, $tinfo->{'comment'}; + } + } } } @@ -99,7 +119,7 @@ sub mtr_report_test_passed ($) { if ( $::opt_timer and -f "$::opt_vardir/log/timer" ) { $timer= mtr_fromfile("$::opt_vardir/log/timer"); - $::glob_tot_real_time += $timer; + $::glob_tot_real_time += ($timer/1000); $timer= sprintf "%12s", $timer; } $tinfo->{'result'}= 'MTR_RES_PASSED'; @@ -110,18 +130,21 @@ sub mtr_report_test_failed ($) { my $tinfo= shift; $tinfo->{'result'}= 'MTR_RES_FAILED'; - if ( $tinfo->{'timeout'} ) + if ( defined $tinfo->{'timeout'} ) { print "[ fail ] timeout\n"; + return; } else { print "[ fail ]\n"; } - # FIXME Instead of this test, and meaningless error message in 'else' - # we should write out into $::path_timefile when the error occurs. - if ( -f $::path_timefile ) + if ( $tinfo->{'comment'} ) + { + print "\nERROR: $tinfo->{'comment'}\n"; + } + elsif ( -f $::path_timefile ) { print "\nErrors are (from $::path_timefile) :\n"; print mtr_fromfile($::path_timefile); # FIXME print_file() instead @@ -144,6 +167,8 @@ sub mtr_report_stats ($) { my $tot_passed= 0; my $tot_failed= 0; my $tot_tests= 0; + my $tot_restarts= 0; + my $found_problems= 0; # Some warnings in the logfiles are errors... foreach my $tinfo (@$tests) { @@ -161,6 +186,10 @@ sub mtr_report_stats ($) { $tot_tests++; $tot_failed++; } + if ( $tinfo->{'restarted'} ) + { + $tot_restarts++; + } } # ---------------------------------------------------------------------- @@ -174,8 +203,9 @@ sub mtr_report_stats ($) { else { my $ratio= $tot_passed * 100 / $tot_tests; - printf "Failed $tot_failed/$tot_tests tests, " . - "%.2f\% were successful.\n\n", $ratio; + print "Failed $tot_failed/$tot_tests tests, "; + printf("%.2f", $ratio); + print "\% were successful.\n\n"; print "The log files in var/log may give you some hint\n", "of what went wrong.\n", @@ -183,45 +213,74 @@ sub mtr_report_stats ($) { "the documentation at\n", "http://www.mysql.com/doc/en/MySQL_test_suite.html\n"; } + print + "The servers were restarted $tot_restarts times\n"; + + if ( $::opt_timer ) + { + print + "Spent $::glob_tot_real_time seconds actually executing testcases\n" + } # ---------------------------------------------------------------------- + # If a debug run, there might be interesting information inside + # the "var/log/*.err" files. We save this info in "var/log/warnings" # ---------------------------------------------------------------------- if ( ! $::glob_use_running_server ) { + # Save and report if there was any fatal warnings/errors in err logs - # Report if there was any fatal warnings/errors in the log files - # - unlink("$::opt_vardir/log/warnings"); - unlink("$::opt_vardir/log/warnings.tmp"); - # Remove some non fatal warnings from the log files - -# FIXME what is going on ????? ;-) -# sed -e 's!Warning: Table:.* on delete!!g' -e 's!Warning: Setting lower_case_table_names=2!!g' -e 's!Warning: One can only use the --user.*root!!g' \ -# var/log/*.err \ -# | sed -e 's!Warning: Table:.* on rename!!g' \ -# > var/log/warnings.tmp; -# -# found_error=0; -# # Find errors -# for i in "^Warning:" "^Error:" "^==.* at 0x" -# do -# if ( $GREP "$i" var/log/warnings.tmp >> var/log/warnings ) -# { -# found_error=1 -# } -# done -# unlink("$::opt_vardir/log/warnings.tmp"); -# if ( $found_error= "1" ) -# { -# print "WARNING: Got errors/warnings while running tests. Please examine\n" -# print "$::opt_vardir/log/warnings for details.\n" -# } -# } + my $warnlog= "$::opt_vardir/log/warnings"; + + unless ( open(WARN, ">$warnlog") ) + { + mtr_warning("can't write to the file \"$warnlog\": $!"); + } + else + { + # We report different types of problems in order + foreach my $pattern ( "^Warning:", "^Error:", "^==.* at 0x", + "InnoDB: Warning", "missing DBUG_RETURN", + "mysqld: Warning", + "Attempting backtrace", "Assertion .* failed" ) + { + foreach my $errlog ( sort glob("$::opt_vardir/log/*.err") ) + { + unless ( open(ERR, $errlog) ) + { + mtr_warning("can't read $errlog"); + next; + } + while ( <ERR> ) + { + # Skip some non fatal warnings from the log files + if ( /Warning:\s+Table:.* on (delete|rename)/ or + /Warning:\s+Setting lower_case_table_names=2/ or + /Warning:\s+One can only use the --user.*root/ or + /InnoDB: Warning: we did not need to do crash recovery/) + { + next; # Skip these lines + } + if ( /$pattern/ ) + { + $found_problems= 1; + print WARN $_; + } + } + } + } + if ( $found_problems ) + { + mtr_warning("Got errors/warnings while running tests, please examine", + "\"$warnlog\" for details."); + } + } } print "\n"; + # Print a list of testcases that failed if ( $tot_failed != 0 ) { my $test_mode= join(" ", @::glob_test_mode) || "default"; @@ -235,6 +294,32 @@ sub mtr_report_stats ($) { } } print "\n"; + + } + + # Print a list of check_testcases that failed(if any) + if ( $::opt_check_testcases ) + { + my @check_testcases= (); + + foreach my $tinfo (@$tests) + { + if ( defined $tinfo->{'check_testcase_failed'} ) + { + push(@check_testcases, $tinfo->{'name'}); + } + } + + if ( @check_testcases ) + { + print "Check of testcase failed for: "; + print join(" ", @check_testcases); + print "\n\n"; + } + } + + if ( $tot_failed != 0 || $found_problems) + { mtr_error("there where failing test cases"); } } @@ -298,5 +383,11 @@ sub mtr_debug (@) { print STDERR "####: ",join(" ", @_),"\n"; } } +sub mtr_verbose (@) { + if ( $::opt_verbose ) + { + print STDERR "> ",join(" ", @_),"\n"; + } +} 1; diff --git a/mysql-test/lib/mtr_stress.pl b/mysql-test/lib/mtr_stress.pl new file mode 100644 index 00000000000..a7d4b68b69d --- /dev/null +++ b/mysql-test/lib/mtr_stress.pl @@ -0,0 +1,178 @@ +# -*- cperl -*- + +# This is a library file used by the Perl version of mysql-test-run, +# and is part of the translation of the Bourne shell script with the +# same name. + +use strict; +use File::Spec; + +# These are not to be prefixed with "mtr_" + +sub run_stress_test (); + +############################################################################## +# +# Run tests in the stress mode +# +############################################################################## + +sub run_stress_test () +{ + + my $args; + my $stress_suitedir; + + mtr_report("Starting stress testing\n"); + + if ( ! $::glob_use_embedded_server ) + { + if ( ! mysqld_start($::master->[0],[],[]) ) + { + mtr_error("Can't start the mysqld server"); + } + } + + my $stress_basedir=File::Spec->catdir($::opt_vardir, "stress"); + + #Clean up stress dir + if ( -d $stress_basedir ) + { + rmtree($stress_basedir); + } + mkpath($stress_basedir); + + if ($::opt_stress_suite ne 'main' && $::opt_stress_suite ne 'default' ) + { + $stress_suitedir=File::Spec->catdir($::glob_mysql_test_dir, "suite", + $::opt_stress_suite); + } + else + { + $stress_suitedir=$::glob_mysql_test_dir; + } + + if ( -d $stress_suitedir ) + { + #$stress_suite_t_dir=File::Spec->catdir($stress_suitedir, "t"); + #$stress_suite_r_dir=File::Spec->catdir($stress_suitedir, "r"); + #FIXME: check dirs above for existence to ensure that test suite + # contains tests and results dirs + } + else + { + mtr_error("Specified test suite $::opt_stress_suite doesn't exist"); + } + + if ( @::opt_cases ) + { + $::opt_stress_test_file=File::Spec->catfile($stress_basedir, "stress_tests.txt"); + open(STRESS_FILE, ">$::opt_stress_test_file"); + print STRESS_FILE join("\n",@::opt_cases),"\n"; + close(STRESS_FILE); + } + elsif ( $::opt_stress_test_file ) + { + $::opt_stress_test_file=File::Spec->catfile($stress_suitedir, + $::opt_stress_test_file); + if ( ! -f $::opt_stress_test_file ) + { + mtr_error("Specified file $::opt_stress_test_file with list of tests does not exist\n", + "Please ensure that file exists and has proper permissions"); + } + } + else + { + $::opt_stress_test_file=File::Spec->catfile($stress_suitedir, + "stress_tests.txt"); + if ( ! -f $::opt_stress_test_file ) + { + mtr_error("Default file $::opt_stress_test_file with list of tests does not exist\n", + "Please use --stress-test-file option to specify custom one or you can\n", + "just specify name of test for testing as last argument in command line"); + + } + } + + if ( $::opt_stress_init_file ) + { + $::opt_stress_init_file=File::Spec->catfile($stress_suitedir, + $::opt_stress_init_file); + if ( ! -f $::opt_stress_init_file ) + { + mtr_error("Specified file $::opt_stress_init_file with list of tests does not exist\n", + "Please ensure that file exists and has proper permissions"); + } + } + else + { + $::opt_stress_init_file=File::Spec->catfile($stress_suitedir, + "stress_init.txt"); + if ( ! -f $::opt_stress_init_file ) + { + $::opt_stress_init_file=''; + } + } + + if ( $::opt_stress_mode ne 'random' && $::opt_stress_mode ne 'seq' ) + { + mtr_error("You specified wrong mode $::opt_stress_mode for stress test\n", + "Correct values are 'random' or 'seq'"); + } + + mtr_init_args(\$args); + + mtr_add_arg($args, "--server-socket=%s", $::master->[0]->{'path_mysock'}); + mtr_add_arg($args, "--server-user=%s", $::opt_user); + mtr_add_arg($args, "--server-database=%s", "test"); + mtr_add_arg($args, "--stress-suite-basedir=%s", $::glob_mysql_test_dir); + mtr_add_arg($args, "--suite=%s", $::opt_stress_suite); + mtr_add_arg($args, "--stress-tests-file=%s", $::opt_stress_test_file); + mtr_add_arg($args, "--stress-basedir=%s", $stress_basedir); + mtr_add_arg($args, "--server-logs-dir=%s", $stress_basedir); + mtr_add_arg($args, "--stress-mode=%s", $::opt_stress_mode); + mtr_add_arg($args, "--mysqltest=%s", $::exe_mysqltest); + mtr_add_arg($args, "--threads=%s", $::opt_stress_threads); + mtr_add_arg($args, "--verbose"); + mtr_add_arg($args, "--cleanup"); + mtr_add_arg($args, "--log-error-details"); + mtr_add_arg($args, "--abort-on-error"); + + if ( $::opt_stress_init_file ) + { + mtr_add_arg($args, "--stress-init-file=%", $::opt_stress_init_file); + } + + if ( !$::opt_stress_loop_count && !$::opt_stress_test_count && + !$::opt_stress_test_duration ) + { + #Limit stress testing with 20 loops in case when any limit parameter + #was specified + $::opt_stress_test_count=20; + } + + if ( $::opt_stress_loop_count ) + { + mtr_add_arg($args, "--loop-count=%s", $::opt_stress_loop_count); + } + + if ( $::opt_stress_test_count ) + { + mtr_add_arg($args, "--test-count=%s", $::opt_stress_test_count); + } + + if ( $::opt_stress_test_duration ) + { + mtr_add_arg($args, "--test-duration=%s", $::opt_stress_test_duration); + } + + #Run stress test + mtr_run("$::glob_mysql_test_dir/mysql-stress-test.pl", $args, "", "", "", ""); + + if ( ! $::glob_use_embedded_server ) + { + stop_masters(); + } +} + +1; diff --git a/mysql-test/lib/mtr_timer.pl b/mysql-test/lib/mtr_timer.pl index a85ab8c6122..06374716c62 100644 --- a/mysql-test/lib/mtr_timer.pl +++ b/mysql-test/lib/mtr_timer.pl @@ -4,23 +4,19 @@ # 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; -#use POSIX ":sys_wait_h"; -use POSIX 'WNOHANG'; - sub mtr_init_timers (); sub mtr_timer_start($$$); sub mtr_timer_stop($$); sub mtr_timer_stop_all($); -sub mtr_timer_waitpid($$$); + ############################################################################## # -# Initiate a structure shared by all timers +# Initiate the structure shared by all timers # ############################################################################## @@ -35,17 +31,19 @@ sub mtr_init_timers () { # Start, stop and poll a timer # # As alarm() isn't portable to Windows, we use separate processes to -# implement timers. That is why there is a mtr_timer_waitpid(), as this -# is where we catch a timeout. +# implement timers. # ############################################################################## sub mtr_timer_start($$$) { my ($timers,$name,$duration)= @_; + mtr_verbose("mtr_timer_start: $name, $duration"); + if ( exists $timers->{'timers'}->{$name} ) { # We have an old running timer, kill it + mtr_verbose("There is an old timer running"); mtr_timer_stop($timers,$name); } @@ -57,7 +55,7 @@ sub mtr_timer_start($$$) { { if ( $! == $!{EAGAIN} ) # See "perldoc Errno" { - mtr_debug("Got EAGAIN from fork(), sleep 1 second and redo"); + mtr_warning("Got EAGAIN from fork(), sleep 1 second and redo"); sleep(1); redo FORK; } @@ -70,6 +68,7 @@ sub mtr_timer_start($$$) { if ( $tpid ) { # Parent, record the information + mtr_verbose("timer parent, record info($name, $tpid, $duration)"); $timers->{'timers'}->{$name}->{'pid'}= $tpid; $timers->{'timers'}->{$name}->{'duration'}= $duration; $timers->{'pids'}->{$tpid}= $name; @@ -85,6 +84,7 @@ sub mtr_timer_start($$$) { $SIG{INT}= 'DEFAULT'; $0= "mtr_timer(timers,$name,$duration)"; + mtr_verbose("timer child $name, sleep $duration"); sleep($duration); exit(0); } @@ -95,9 +95,12 @@ sub mtr_timer_start($$$) { sub mtr_timer_stop ($$) { my ($timers,$name)= @_; + mtr_verbose("mtr_timer_stop: $name"); + if ( exists $timers->{'timers'}->{$name} ) { my $tpid= $timers->{'timers'}->{$name}->{'pid'}; + mtr_verbose("Stopping timer with pid $tpid"); # FIXME as Cygwin reuses pids fast, maybe check that is # the expected process somehow?! @@ -114,7 +117,7 @@ sub mtr_timer_stop ($$) { } else { - mtr_debug("Asked to stop timer \"$name\" not started"); + mtr_error("Asked to stop timer \"$name\" not started"); return 0; } } @@ -136,10 +139,8 @@ sub mtr_timer_timeout ($$) { return "" unless exists $timers->{'pids'}->{$pid}; - # We got a timeout - my $name= $timers->{'pids'}->{$pid}; - mtr_timer_stop($timers, $timers->{'timers'}->{$name}); - return $name; + # We got a timeout, return the name ot the timer + return $timers->{'pids'}->{$pid}; } 1; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index d1e049ff883..de2498a7246 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1,30 +1,24 @@ #!/usr/bin/perl # -*- cperl -*- -# This is a transformation of the "mysql-test-run" Bourne shell script -# to Perl. There are reasons this rewrite is not the prettiest Perl -# you have seen # -# - The original script is huge and for most part uncommented, -# not even a usage description of the flags. +############################################################################## +# +# mysql-test-run.pl +# +# Tool used for executing a suite of .test file # -# - There has been an attempt to write a replacement in C for the -# original Bourne shell script. It was kind of working but lacked -# lot of functionality to really be a replacement. Not to redo -# that mistake and catch all the obscure features of the original -# script, the rewrite in Perl is more close to the original script -# meaning it also share some of the ugly parts as well. +# See the "MySQL Test framework manual" for more information +# http://dev.mysql.com/doc/mysqltest/en/index.html # -# - The original intention was that this script was to be a prototype -# to be the base for a new C version with full functionality. Since -# then it was decided that the Perl version should replace the -# Bourne shell version, but the Perl style still reflects the wish -# to make the Perl to C step easy. +# Please keep the test framework tools identical in all versions! # -# Some coding style from the original intent has been kept +############################################################################## +# +# Coding style directions for this perl script # # - To make this Perl script easy to alter even for those that not -# code Perl that often, the coding style is as close as possible to +# code Perl that often, keeep the coding style as close as possible to # the C/C++ MySQL coding standard. # # - All lists of arguments to send to commands are Perl lists/arrays, @@ -42,15 +36,6 @@ # the information. This separates the "find information" from the # "do the work" and makes the program more easy to maintain. # -# - At the moment, there are tons of "global" variables that control -# this script, even accessed from the files in "lib/*.pl". This -# will change over time, for now global variables are used instead -# of using %opt, %path and %exe hashes, because I want more -# compile time checking, that hashes would not give me. Once this -# script is debugged, hashes will be used and passed as parameters -# to functions, to more closely mimic how it would be coded in C -# using structs. -# # - The rule when it comes to the logic of this program is # # command_line_setup() - is to handle the logic between flags @@ -66,10 +51,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 +61,20 @@ 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; +use warnings; +use diagnostics; + +our $glob_win32_perl= ($^O eq "MSWin32"); # ActiveState Win32 Perl +our $glob_cygwin_perl= ($^O eq "cygwin"); # Cygwin Perl +our $glob_win32= ($glob_win32_perl or $glob_cygwin_perl); +our $glob_netware= ($^O eq "NetWare"); # NetWare require "lib/mtr_cases.pl"; +require "lib/mtr_im.pl"; require "lib/mtr_process.pl"; require "lib/mtr_timer.pl"; require "lib/mtr_io.pl"; @@ -97,25 +84,10 @@ require "lib/mtr_report.pl"; require "lib/mtr_diff.pl"; require "lib/mtr_match.pl"; require "lib/mtr_misc.pl"; +require "lib/mtr_stress.pl"; $Devel::Trace::TRACE= 1; -# Used by gcov -our @mysqld_src_dirs= - ( - "strings", - "mysys", - "include", - "extra", - "regex", - "isam", - "merge", - "myisam", - "myisammrg", - "heap", - "sql", - ); - ############################################################################## # # Default settings @@ -128,11 +100,7 @@ our @mysqld_src_dirs= # structs. We let each struct be a separate hash. # Misc global variables - -our $glob_win32= 0; # OS and native Win32 executables -our $glob_win32_perl= 0; # ActiveState Win32 Perl -our $glob_cygwin_perl= 0; # Cygwin Perl -our $glob_cygwin_shell= undef; +our $mysql_version_id; our $glob_mysql_test_dir= undef; our $glob_mysql_bench_dir= undef; our $glob_hostname= undef; @@ -140,32 +108,32 @@ our $glob_scriptname= undef; our $glob_timers= undef; our $glob_use_running_server= 0; our $glob_use_running_ndbcluster= 0; +our $glob_use_running_ndbcluster_slave= 0; our $glob_use_embedded_server= 0; our @glob_test_mode; our $glob_basedir; -# The total result - our $path_charsetsdir; our $path_client_bindir; our $path_language; our $path_timefile; -our $path_manager_log; # Used by mysqldadmin -our $path_slave_load_tmpdir; # What is this?! +our $path_snapshot; our $path_mysqltest_log; +our $path_current_test_log; our $path_my_basedir; + our $opt_vardir; # A path but set directly on cmd line +our $path_vardir_trace; # unix formatted opt_vardir for trace files our $opt_tmpdir; # A path but set directly on cmd line +our $default_vardir; + our $opt_usage; our $opt_suite; -our $opt_netware; - our $opt_script_debug= 0; # Script debugging, enable with --script-debug - -# Options FIXME not all.... +our $opt_verbose= 0; # Verbose output, enable with --verbose our $exe_master_mysqld; our $exe_mysql; @@ -173,13 +141,21 @@ our $exe_mysqladmin; our $exe_mysqlbinlog; our $exe_mysql_client_test; our $exe_mysqld; -our $exe_mysqldump; # Called from test case -our $exe_mysqlimport; # Called from test case -our $exe_mysqlshow; # Called from test case +our $exe_mysqlcheck; +our $exe_mysqldump; +our $exe_mysqlslap; +our $exe_mysqlimport; +our $exe_mysqlshow; our $exe_mysql_fix_system_tables; our $exe_mysqltest; +our $exe_ndbd; +our $exe_ndb_mgmd; our $exe_slave_mysqld; +our $exe_im; our $exe_my_print_defaults; +our $exe_perror; +our $lib_udf_example; +our $exe_libtool; our $opt_bench= 0; our $opt_small_bench= 0; @@ -187,10 +163,15 @@ our $opt_big_test= 0; # Send --big-test to mysqltest our @opt_extra_mysqld_opt; -our $opt_comment; our $opt_compress; -our $opt_current_test; -our $opt_ddd; +our $opt_ssl; +our $opt_skip_ssl; +our $opt_ssl_supported; +our $opt_ps_protocol; +our $opt_sp_protocol; +our $opt_cursor_protocol; +our $opt_view_protocol; + our $opt_debug; our $opt_do_test; our @opt_cases; # The test cases names in argv @@ -198,53 +179,59 @@ our $opt_embedded_server; our $opt_extern; our $opt_fast; our $opt_force; -our $opt_reorder; +our $opt_reorder= 0; +our $opt_enable_disabled; +our $opt_mem; our $opt_gcov; our $opt_gcov_err; our $opt_gcov_msg; +our $glob_debugger= 0; our $opt_gdb; our $opt_client_gdb; +our $opt_ddd; +our $opt_client_ddd; our $opt_manual_gdb; +our $opt_manual_ddd; +our $opt_manual_debug; +our $opt_debugger; +our $opt_client_debugger; our $opt_gprof; our $opt_gprof_dir; our $opt_gprof_master; our $opt_gprof_slave; -our $opt_local; -our $opt_local_master; - -our $master; # Will be struct in C +our $master; our $slave; +our $clusters; + +our $instance_manager; our $opt_ndbcluster_port; our $opt_ndbconnectstring; - -our $opt_no_manager; # Does nothing now, we never use manager -our $opt_manager_port; # Does nothing now, we never use manager - -our $opt_old_master; +our $opt_ndbcluster_port_slave; +our $opt_ndbconnectstring_slave; our $opt_record; - -our $opt_result_ext; +our $opt_check_testcases; our $opt_skip; our $opt_skip_rpl; -our $opt_skip_im; # --skip-im on command line will just be ignored +our $max_slave_num= 0; +our $use_innodb; our $opt_skip_test; +our $opt_skip_im; our $opt_sleep; -our $opt_ps_protocol; our $opt_sleep_time_after_restart= 1; our $opt_sleep_time_for_delete= 10; our $opt_testcase_timeout; our $opt_suite_timeout; my $default_testcase_timeout= 15; # 15 min max -my $default_suite_timeout= 120; # 2 hours max +my $default_suite_timeout= 180; # 3 hours max our $opt_socket; @@ -256,18 +243,28 @@ our $opt_start_from; our $opt_strace_client; -our $opt_timer; +our $opt_timer= 1; our $opt_user; our $opt_user_test; -our $opt_valgrind; -our $opt_valgrind_mysqld; -our $opt_valgrind_mysqltest; -our $opt_valgrind_all; +our $opt_valgrind= 0; +our $opt_valgrind_mysqld= 0; +our $opt_valgrind_mysqltest= 0; +our $default_valgrind_options= "--show-reachable=yes"; our $opt_valgrind_options; - -our $opt_verbose; +our $opt_valgrind_path; +our $opt_callgrind; + +our $opt_stress= ""; +our $opt_stress_suite= "main"; +our $opt_stress_mode= "random"; +our $opt_stress_threads= 5; +our $opt_stress_test_count= 0; +our $opt_stress_loop_count= 0; +our $opt_stress_test_duration= 0; +our $opt_stress_init_file= ""; +our $opt_stress_test_file= ""; our $opt_wait_for_master; our $opt_wait_for_slave; @@ -277,17 +274,30 @@ our $opt_warnings; our $opt_udiff; -our $opt_skip_ndbcluster; -our $opt_with_ndbcluster; -our $opt_with_ndbcluster_only= 0; # dummy, ignored - -our $opt_with_openssl; +our $opt_skip_ndbcluster= 0; +our $opt_skip_ndbcluster_slave= 0; +our $opt_with_ndbcluster= 0; +our $opt_with_ndbcluster_only= 0; +our $glob_ndbcluster_supported= 0; +our $opt_ndb_extra_test= 0; +our $opt_skip_master_binlog= 0; +our $opt_skip_slave_binlog= 0; our $exe_ndb_mgm; +our $exe_ndb_waiter; our $path_ndb_tools_dir; -our $path_ndb_backup_dir; -our $file_ndb_testrun_log; -our $flag_ndb_status_ok= 1; +our $path_ndb_examples_dir; +our $exe_ndb_example; +our $path_ndb_testrun_log; + +our @data_dir_lst; + +our $used_binlog_format; +our $debug_compiled_binaries; +our $glob_tot_real_time= 0; + +our %mysqld_variables; + ###################################################################### # @@ -298,27 +308,36 @@ our $flag_ndb_status_ok= 1; sub main (); sub initial_setup (); sub command_line_setup (); +sub datadir_setup (); sub executable_setup (); sub environment_setup (); -sub kill_running_server (); -sub kill_and_cleanup (); -sub ndbcluster_support (); -sub ndbcluster_install (); -sub ndbcluster_start (); -sub ndbcluster_stop (); +sub kill_running_servers (); +sub cleanup_stale_files (); +sub check_ssl_support ($); +sub check_running_as_root(); +sub check_ndbcluster_support ($); +sub rm_ndbcluster_tables ($); +sub ndbcluster_start_install ($); +sub ndbcluster_start ($$); +sub ndbcluster_wait_started ($$); +sub mysqld_wait_started($); sub run_benchmarks ($); -sub run_tests (); +sub initialize_servers (); sub mysql_install_db (); sub install_db ($$); +sub copy_install_db ($$); sub run_testcase ($); +sub run_testcase_stop_servers ($$$); +sub run_testcase_start_servers ($); +sub run_testcase_check_skip_test($); sub report_failure_and_restart ($); -sub do_before_start_master ($$); -sub do_before_start_slave ($$); -sub mysqld_start ($$$$); +sub do_before_start_master ($); +sub do_before_start_slave ($); +sub ndbd_start ($$$); +sub ndb_mgmd_start ($); +sub mysqld_start ($$$); sub mysqld_arguments ($$$$$); -sub stop_masters_slaves (); -sub stop_masters (); -sub stop_slaves (); +sub stop_all_servers (); sub run_mysqltest ($); sub usage ($); @@ -334,12 +353,12 @@ sub main () { initial_setup(); command_line_setup(); + + check_ndbcluster_support(\%mysqld_variables); + check_ssl_support(\%mysqld_variables); + check_debug_support(\%mysqld_variables); + executable_setup(); - - if (! $opt_skip_ndbcluster and ! $opt_with_ndbcluster) - { - $opt_with_ndbcluster= ndbcluster_support(); - } environment_setup(); signal_setup(); @@ -354,44 +373,61 @@ sub main () { gprof_prepare(); } - if ( ! $glob_use_running_server ) + if ( $opt_bench ) { - if ( $opt_start_dirty ) - { - kill_running_server(); - } - else + initialize_servers(); + run_benchmarks(shift); # Shift what? Extra arguments?! + } + elsif ( $opt_stress ) + { + initialize_servers(); + run_stress_test() + } + else + { + # Figure out which tests we are going to run + my $tests= collect_test_cases($opt_suite); + + # Turn off NDB and other similar options if no tests use it + my ($need_ndbcluster,$need_im); + foreach my $test (@$tests) { - kill_and_cleanup(); - mysql_install_db(); + next if $test->{skip}; -# mysql_loadstd(); FIXME copying from "std_data" .frm and -# .MGR but there are none?! + $need_ndbcluster||= $test->{ndb_test}; + $need_im||= $test->{component_id} eq 'im'; + + # Count max number of slaves used by a test case + if ( $test->{slave_num} > $max_slave_num) + { + $max_slave_num= $test->{slave_num}; + mtr_error("Too many slaves") if $max_slave_num > 3; + } + $use_innodb||= $test->{'innodb_test'}; } - } - if ( $opt_start_dirty ) - { - if ( ndbcluster_start() ) + # Check if cluster can be skipped + if ( !$need_ndbcluster ) { - mtr_error("Can't start ndbcluster"); + $opt_skip_ndbcluster= 1; + $opt_skip_ndbcluster_slave= 1; } - if ( mysqld_start('master',0,[],[]) ) + + # Check if slave cluster can be skipped + if ($max_slave_num == 0) { - mtr_report("Servers started, exiting"); + $opt_skip_ndbcluster_slave= 1; } - else + + # Check if im can be skipped + if ( ! $need_im ) { - mtr_error("Can't start the mysqld server"); - } - } - elsif ( $opt_bench ) - { - run_benchmarks(shift); # Shift what? Extra arguments?! - } - else - { - run_tests(); + $opt_skip_im= 1; + } + + initialize_servers(); + + run_suite($opt_suite, $tests); } mtr_exit(0); @@ -410,13 +446,8 @@ sub initial_setup () { $glob_scriptname= basename($0); - $glob_win32_perl= ($^O eq "MSWin32"); - $glob_cygwin_perl= ($^O eq "cygwin"); - $glob_win32= ($glob_win32_perl or $glob_cygwin_perl); - # We require that we are in the "mysql-test" directory # to run mysql-test-run - if (! -f $glob_scriptname) { mtr_error("Can't find the location for the mysql-test-run script\n" . @@ -437,21 +468,48 @@ sub initial_setup () { { # Windows programs like 'mysqld' needs Windows paths $glob_mysql_test_dir= `cygpath -m "$glob_mysql_test_dir"`; - my $shell= $ENV{'SHELL'} || "/bin/bash"; - $glob_cygwin_shell= `cygpath -w "$shell"`; # The Windows path c:\... chomp($glob_mysql_test_dir); - chomp($glob_cygwin_shell); } $glob_basedir= dirname($glob_mysql_test_dir); - $glob_mysql_bench_dir= "$glob_basedir/mysql-bench"; # FIXME make configurable - # needs to be same length to test logging (FIXME what???) - $path_slave_load_tmpdir= "../../var/tmp"; + # Expect mysql-bench to be located adjacent to the source tree, by default + $glob_mysql_bench_dir= "$glob_basedir/../mysql-bench" + unless defined $glob_mysql_bench_dir; $path_my_basedir= $opt_source_dist ? $glob_mysql_test_dir : $glob_basedir; $glob_timers= mtr_init_timers(); + + # + # Find the mysqld executable to be able to find the mysqld version + # number as early as possible + # + + # Look for the path where to find the client binaries + $path_client_bindir= mtr_path_exists("$glob_basedir/client_release", + "$glob_basedir/client_debug", + "$glob_basedir/client/release", + "$glob_basedir/client/debug", + "$glob_basedir/client", + "$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/bin/mysqld", + "$glob_basedir/sql/release/mysqld", + "$glob_basedir/sql/debug/mysqld"); + + # Use the mysqld found above to find out what features are available + collect_mysqld_features(); + } @@ -467,9 +525,15 @@ sub command_line_setup () { # These are defaults for things that are set on the command line $opt_suite= "main"; # Special default suite - my $opt_master_myport= 9306; - my $opt_slave_myport= 9308; - $opt_ndbcluster_port= 9350; + my $opt_comment; + + my $opt_master_myport= 9306; + my $opt_slave_myport= 9308; + $opt_ndbcluster_port= 9310; + $opt_ndbcluster_port_slave= 9311; + my $im_port= 9312; + my $im_mysqld1_port= 9313; + my $im_mysqld2_port= 9314; # # To make it easier for different devs to work on the same host, @@ -488,9 +552,13 @@ sub command_line_setup () { if ( $ENV{'MTR_BUILD_THREAD'} ) { # Up to two masters, up to three slaves - $opt_master_myport= $ENV{'MTR_BUILD_THREAD'} * 10 + 10000; # and 1 - $opt_slave_myport= $opt_master_myport + 2; # and 3 4 - $opt_ndbcluster_port= $opt_master_myport + 5; + $opt_master_myport= $ENV{'MTR_BUILD_THREAD'} * 10 + 10000; # and 1 + $opt_slave_myport= $opt_master_myport + 2; # and 3 4 + $opt_ndbcluster_port= $opt_master_myport + 5; + $opt_ndbcluster_port_slave= $opt_master_myport + 6; + $im_port= $opt_master_myport + 7; + $im_mysqld1_port= $opt_master_myport + 8; + $im_mysqld2_port= $opt_master_myport + 9; } if ( $opt_master_myport < 5001 or $opt_master_myport + 10 >= 32767 ) @@ -500,6 +568,11 @@ sub command_line_setup () { "($opt_master_myport - $opt_master_myport + 10)"); } + # This is needed for test log evaluation in "gen-build-status-page" + # in all cases where the calling tool does not log the commands + # directly before it executes them, like "make test-force-pl" in RPM builds. + print "Logging: $0 ", join(" ", @ARGV), "\n"; + # Read the command line # Note: Keep list, and the order, in sync with usage at end of this file @@ -508,42 +581,63 @@ sub command_line_setup () { # Control what engine/variation to run 'embedded-server' => \$opt_embedded_server, 'ps-protocol' => \$opt_ps_protocol, + 'sp-protocol' => \$opt_sp_protocol, + 'view-protocol' => \$opt_view_protocol, + 'cursor-protocol' => \$opt_cursor_protocol, + 'ssl|with-openssl' => \$opt_ssl, + 'skip-ssl' => \$opt_skip_ssl, + 'compress' => \$opt_compress, 'bench' => \$opt_bench, 'small-bench' => \$opt_small_bench, - 'no-manager' => \$opt_no_manager, # Currently not used + 'with-ndbcluster' => \$opt_with_ndbcluster, # Control what test suites or cases to run 'force' => \$opt_force, - 'with-ndbcluster' => \$opt_with_ndbcluster, - 'skip-ndbcluster|skip-ndb' => \$opt_skip_ndbcluster, 'with-ndbcluster-only' => \$opt_with_ndbcluster_only, + 'skip-ndbcluster|skip-ndb' => \$opt_skip_ndbcluster, + 'skip-ndbcluster-slave|skip-ndb-slave' + => \$opt_skip_ndbcluster_slave, + 'ndb-extra-test' => \$opt_ndb_extra_test, + 'skip-master-binlog' => \$opt_skip_master_binlog, + 'skip-slave-binlog' => \$opt_skip_slave_binlog, 'do-test=s' => \$opt_do_test, + 'start-from=s' => \$opt_start_from, 'suite=s' => \$opt_suite, 'skip-rpl' => \$opt_skip_rpl, - 'skip-im' => \$opt_skip_im, + 'skip-im' => \$opt_skip_im, 'skip-test=s' => \$opt_skip_test, + 'big-test' => \$opt_big_test, # Specify ports 'master_port=i' => \$opt_master_myport, 'slave_port=i' => \$opt_slave_myport, - 'ndbcluster_port=i' => \$opt_ndbcluster_port, - 'manager-port=i' => \$opt_manager_port, # Currently not used + 'ndbcluster-port|ndbcluster_port=i' => \$opt_ndbcluster_port, + 'ndbcluster-port-slave=i' => \$opt_ndbcluster_port_slave, + 'im-port=i' => \$im_port, # Instance Manager port. + 'im-mysqld1-port=i' => \$im_mysqld1_port, # Port of mysqld, controlled by IM + 'im-mysqld2-port=i' => \$im_mysqld2_port, # Port of mysqld, controlled by IM # Test case authoring 'record' => \$opt_record, + 'check-testcases' => \$opt_check_testcases, - # ??? + # Extra options used when starting mysqld 'mysqld=s' => \@opt_extra_mysqld_opt, # Run test on running server 'extern' => \$opt_extern, - 'ndbconnectstring=s' => \$opt_ndbconnectstring, + 'ndb-connectstring=s' => \$opt_ndbconnectstring, + 'ndb-connectstring-slave=s' => \$opt_ndbconnectstring_slave, # Debugging 'gdb' => \$opt_gdb, - 'manual-gdb' => \$opt_manual_gdb, 'client-gdb' => \$opt_client_gdb, + 'manual-gdb' => \$opt_manual_gdb, + 'manual-debug' => \$opt_manual_debug, 'ddd' => \$opt_ddd, + 'client-ddd' => \$opt_client_ddd, + 'debugger=s' => \$opt_debugger, + 'client-debugger=s' => \$opt_client_debugger, 'strace-client' => \$opt_strace_client, 'master-binary=s' => \$exe_master_mysqld, 'slave-binary=s' => \$exe_slave_mysqld, @@ -551,48 +645,55 @@ sub command_line_setup () { # Coverage, profiling etc 'gcov' => \$opt_gcov, 'gprof' => \$opt_gprof, - 'valgrind:s' => \$opt_valgrind, - 'valgrind-mysqltest:s' => \$opt_valgrind_mysqltest, - 'valgrind-all:s' => \$opt_valgrind_all, + 'valgrind|valgrind-all' => \$opt_valgrind, + 'valgrind-mysqltest' => \$opt_valgrind_mysqltest, + 'valgrind-mysqld' => \$opt_valgrind_mysqld, 'valgrind-options=s' => \$opt_valgrind_options, + 'valgrind-path=s' => \$opt_valgrind_path, + 'callgrind' => \$opt_callgrind, + + # Stress testing + 'stress' => \$opt_stress, + 'stress-suite=s' => \$opt_stress_suite, + 'stress-threads=i' => \$opt_stress_threads, + 'stress-test-file=s' => \$opt_stress_test_file, + 'stress-init-file=s' => \$opt_stress_init_file, + 'stress-mode=s' => \$opt_stress_mode, + 'stress-loop-count=i' => \$opt_stress_loop_count, + 'stress-test-count=i' => \$opt_stress_test_count, + 'stress-test-duration=i' => \$opt_stress_test_duration, + + # Directories + 'tmpdir=s' => \$opt_tmpdir, + 'vardir=s' => \$opt_vardir, + 'benchdir=s' => \$glob_mysql_bench_dir, + 'mem' => \$opt_mem, # Misc - 'big-test' => \$opt_big_test, 'comment=s' => \$opt_comment, - 'compress' => \$opt_compress, 'debug' => \$opt_debug, 'fast' => \$opt_fast, - 'local' => \$opt_local, - 'local-master' => \$opt_local_master, - 'netware' => \$opt_netware, - 'old-master' => \$opt_old_master, 'reorder' => \$opt_reorder, + 'enable-disabled' => \$opt_enable_disabled, 'script-debug' => \$opt_script_debug, + 'verbose' => \$opt_verbose, 'sleep=i' => \$opt_sleep, 'socket=s' => \$opt_socket, 'start-dirty' => \$opt_start_dirty, 'start-and-exit' => \$opt_start_and_exit, - 'start-from=s' => \$opt_start_from, - 'timer' => \$opt_timer, - 'tmpdir=s' => \$opt_tmpdir, + 'timer!' => \$opt_timer, 'unified-diff|udiff' => \$opt_udiff, 'user-test=s' => \$opt_user_test, 'user=s' => \$opt_user, - 'vardir=s' => \$opt_vardir, - 'verbose' => \$opt_verbose, 'wait-timeout=i' => \$opt_wait_timeout, 'testcase-timeout=i' => \$opt_testcase_timeout, 'suite-timeout=i' => \$opt_suite_timeout, 'warnings|log-warnings' => \$opt_warnings, - 'with-openssl' => \$opt_with_openssl, 'help|h' => \$opt_usage, ) or usage("Can't read options"); - if ( $opt_usage ) - { - usage(""); - } + usage("") if $opt_usage; if ( $opt_comment ) { @@ -602,11 +703,6 @@ sub command_line_setup () { print '#' x 78, "\n\n"; } - if ( $opt_with_ndbcluster_only ) - { - print "# Option '--with-ndbcluster-only' is ignored in this release.\n"; - } - foreach my $arg ( @ARGV ) { if ( $arg =~ /^--skip-/ ) @@ -630,14 +726,78 @@ sub command_line_setup () { } # -------------------------------------------------------------------------- - # Set the "var/" directory, as it is the base for everything else + # Find out type of logging that are being used # -------------------------------------------------------------------------- + # NOTE if the default binlog format is changed, this has to be changed + $used_binlog_format= "stmt"; + if ( $mysql_version_id >= 50100 ) + { + $used_binlog_format= "mixed"; # Default value for binlog format + foreach my $arg ( @opt_extra_mysqld_opt ) + { + if ( $arg =~ /binlog-format=(\S+)/ ) + { + $used_binlog_format= $1; + } + } + mtr_report("Using binlog format '$used_binlog_format'"); + } + + # -------------------------------------------------------------------------- + # Check if we should speed up tests by trying to run on tmpfs + # -------------------------------------------------------------------------- + if ( $opt_mem ) + { + mtr_error("Can't use --mem and --vardir at the same time ") + if $opt_vardir; + mtr_error("Can't use --mem and --tmpdir at the same time ") + if $opt_tmpdir; + + # Use /dev/shm as the preferred location for vardir and + # thus implicitly also tmpdir. Add other locations to list + my @tmpfs_locations= ("/dev/shm"); + # One could maybe use "mount" to find tmpfs location(s) + foreach my $fs (@tmpfs_locations) + { + if ( -d $fs ) + { + mtr_report("Using tmpfs in $fs"); + $opt_mem= "$fs/var"; + $opt_mem .= $ENV{'MTR_BUILD_THREAD'} if $ENV{'MTR_BUILD_THREAD'}; + last; + } + } + } + + # -------------------------------------------------------------------------- + # Set the "var/" directory, as it is the base for everything else + # -------------------------------------------------------------------------- + $default_vardir= "$glob_mysql_test_dir/var"; if ( ! $opt_vardir ) { - $opt_vardir= "$glob_mysql_test_dir/var"; + $opt_vardir= $default_vardir; + } + elsif ( $mysql_version_id < 50000 and + $opt_vardir ne $default_vardir) + { + # Version 4.1 and --vardir was specified + # Only supported as a symlink from var/ + # by setting up $opt_mem that symlink will be created + if ( ! $glob_win32 ) + { + # Only platforms that have native symlinks can use the vardir trick + $opt_mem= $opt_vardir; + mtr_report("Using 4.1 vardir trick"); + } + + $opt_vardir= $default_vardir; } + $path_vardir_trace= $opt_vardir; + # Chop off any "c:", DBUG likes a unix path ex: c:/src/... => /src/... + $path_vardir_trace=~ s/^\w://; + # We make the path absolute, as the server will do a chdir() before usage unless ( $opt_vardir =~ m,^/, or ($glob_win32 and $opt_vardir =~ m,^[a-z]:/,i) ) @@ -647,47 +807,53 @@ sub command_line_setup () { } # -------------------------------------------------------------------------- - # If not set, set these to defaults + # Set tmpdir # -------------------------------------------------------------------------- - $opt_tmpdir= "$opt_vardir/tmp" unless $opt_tmpdir; $opt_tmpdir =~ s,/+$,,; # Remove ending slash if any - # FIXME maybe not needed? - $path_manager_log= "$opt_vardir/log/manager.log" - unless $path_manager_log; - $opt_current_test= "$opt_vardir/log/current_test" - unless $opt_current_test; # -------------------------------------------------------------------------- - # Do sanity checks of command line arguments + # Set socket # -------------------------------------------------------------------------- + if (!$opt_socket) + { + $opt_socket= $mysqld_variables{'socket'}; + } - if ( $opt_extern and $opt_local ) + # -------------------------------------------------------------------------- + # Check im suport + # -------------------------------------------------------------------------- + if ( $mysql_version_id < 50000 ) { - mtr_error("Can't use --extern and --local at the same time"); + # Instance manager is not supported until 5.0 + $opt_skip_im= 1; + } - if ( ! $opt_socket ) - { # FIXME set default before reading options? -# $opt_socket= '@MYSQL_UNIX_ADDR@'; - $opt_socket= "/tmp/mysql.sock"; # FIXME + if ( $glob_win32 ) + { + mtr_report("Disable Instance manager - not supported on Windows"); + $opt_skip_im= 1; } # -------------------------------------------------------------------------- - # Look at the command line options and set script flags + # Record flag # -------------------------------------------------------------------------- - if ( $opt_record and ! @opt_cases ) { mtr_error("Will not run in record mode without a specific test case"); } + # -------------------------------------------------------------------------- + # Embedded server flag + # -------------------------------------------------------------------------- if ( $opt_embedded_server ) { $glob_use_embedded_server= 1; push(@glob_test_mode, "embedded"); $opt_skip_rpl= 1; # We never run replication with embedded - $opt_skip_ndbcluster= 1; + $opt_skip_ndbcluster= 1; # Turn off use of NDB cluster + $opt_skip_ssl= 1; # Turn off use of SSL if ( $opt_extern ) { @@ -695,109 +861,158 @@ sub command_line_setup () { } } + + # -------------------------------------------------------------------------- + # ps protcol flag + # -------------------------------------------------------------------------- if ( $opt_ps_protocol ) { push(@glob_test_mode, "ps-protocol"); } - # FIXME don't understand what this is -# if ( $opt_local_master ) -# { -# $opt_master_myport= 3306; -# } + # -------------------------------------------------------------------------- + # Ndb cluster flags + # -------------------------------------------------------------------------- + if ( $opt_with_ndbcluster and !$opt_bench) + { + mtr_error("Can only use --with-ndbcluster togheter with --bench"); + } + + if ( $opt_ndbconnectstring ) + { + $glob_use_running_ndbcluster= 1; + mtr_error("Can't specify --ndb-connectstring and --skip-ndbcluster") + if $opt_skip_ndbcluster; + mtr_error("Can't specify --ndb-connectstring and --ndbcluster-port") + if $opt_ndbcluster_port; + } + else + { + # Set default connect string + $opt_ndbconnectstring= "host=localhost:$opt_ndbcluster_port"; + } + + if ( $opt_ndbconnectstring_slave ) + { + $glob_use_running_ndbcluster_slave= 1; + mtr_error("Can't specify ndb-connectstring_slave and " . + "--skip-ndbcluster-slave") + if $opt_skip_ndbcluster; + mtr_error("Can't specify --ndb-connectstring-slave and " . + "--ndbcluster-port-slave") + if $opt_ndbcluster_port_slave; + } + else + { + # Set default connect string + $opt_ndbconnectstring_slave= "host=localhost:$opt_ndbcluster_port_slave"; + } + # -------------------------------------------------------------------------- + # Bench flags + # -------------------------------------------------------------------------- if ( $opt_small_bench ) { $opt_bench= 1; } + # -------------------------------------------------------------------------- + # Sleep flag + # -------------------------------------------------------------------------- if ( $opt_sleep ) { $opt_sleep_time_after_restart= $opt_sleep; } + # -------------------------------------------------------------------------- + # Gcov flag + # -------------------------------------------------------------------------- if ( $opt_gcov and ! $opt_source_dist ) { mtr_error("Coverage test needs the source - please use source dist"); } - if ( $opt_gdb ) + # -------------------------------------------------------------------------- + # Check debug related options + # -------------------------------------------------------------------------- + if ( $opt_gdb || $opt_client_gdb || $opt_ddd || $opt_client_ddd || + $opt_manual_gdb || $opt_manual_ddd || $opt_manual_debug || + $opt_debugger || $opt_client_debugger ) { + # Indicate that we are using debugger + $glob_debugger= 1; + # Increase timeouts $opt_wait_timeout= 300; if ( $opt_extern ) { - mtr_error("Can't use --extern with --gdb"); + mtr_error("Can't use --extern when using debugger"); } } - if ( $opt_manual_gdb ) - { - $opt_gdb= 1; - if ( $opt_extern ) - { - mtr_error("Can't use --extern with --manual-gdb"); - } - } + # -------------------------------------------------------------------------- + # Check if special exe was selected for master or slave + # -------------------------------------------------------------------------- + $exe_master_mysqld= $exe_master_mysqld || $exe_mysqld; + $exe_slave_mysqld= $exe_slave_mysqld || $exe_mysqld; - if ( $opt_ddd ) + # -------------------------------------------------------------------------- + # Check valgrind arguments + # -------------------------------------------------------------------------- + if ( $opt_valgrind or $opt_valgrind_path or defined $opt_valgrind_options) { - if ( $opt_extern ) - { - mtr_error("Can't use --extern with --ddd"); - } + mtr_report("Turning on valgrind for all executables"); + $opt_valgrind= 1; + $opt_valgrind_mysqld= 1; + $opt_valgrind_mysqltest= 1; } - - if ( $opt_ndbconnectstring ) + elsif ( $opt_valgrind_mysqld ) { - $glob_use_running_ndbcluster= 1; - $opt_with_ndbcluster= 1; + mtr_report("Turning on valgrind for mysqld(s) only"); + $opt_valgrind= 1; } - else + elsif ( $opt_valgrind_mysqltest ) { - $opt_ndbconnectstring= "host=localhost:$opt_ndbcluster_port"; + mtr_report("Turning on valgrind for mysqltest and mysql_client_test only"); + $opt_valgrind= 1; } - if ( $opt_skip_ndbcluster ) + if ( $opt_callgrind ) { - $opt_with_ndbcluster= 0; - } + mtr_report("Turning on valgrind with callgrind for mysqld(s)"); + $opt_valgrind= 1; + $opt_valgrind_mysqld= 1; - # The ":s" in the argument spec, means we have three different cases - # - # undefined option not set - # "" option set with no argument - # "somestring" option is name/path of valgrind executable - - # Take executable path from any of them, if any - $opt_valgrind_mysqld= $opt_valgrind; - $opt_valgrind= $opt_valgrind_mysqltest if $opt_valgrind_mysqltest; - $opt_valgrind= $opt_valgrind_all if $opt_valgrind_all; + # Set special valgrind options unless options passed on command line + $opt_valgrind_options="--trace-children=yes" + unless defined $opt_valgrind_options; + } - # If valgrind flag not defined, define if other valgrind flags are - unless ( defined $opt_valgrind ) + if ( $opt_valgrind ) { - $opt_valgrind= "" - if defined $opt_valgrind_mysqltest or defined $opt_valgrind_all; + # Set valgrind_options to default unless already defined + $opt_valgrind_options=$default_valgrind_options + unless defined $opt_valgrind_options; + + mtr_report("Running valgrind with options \"$opt_valgrind_options\""); } if ( ! $opt_testcase_timeout ) { $opt_testcase_timeout= $default_testcase_timeout; - $opt_testcase_timeout*= 10 if defined $opt_valgrind; + $opt_testcase_timeout*= 10 if $opt_valgrind; } if ( ! $opt_suite_timeout ) { $opt_suite_timeout= $default_suite_timeout; - $opt_suite_timeout*= 4 if defined $opt_valgrind; + $opt_suite_timeout*= 6 if $opt_valgrind; } - if ( defined $opt_valgrind ) + # Increase times to wait for executables to start if using valgrind + if ( $opt_valgrind ) { $opt_sleep_time_after_restart= 10; $opt_sleep_time_for_delete= 60; - # >=2.1.2 requires the --tool option, some versions write to stdout, some to stderr - # valgrind --help 2>&1 | grep "\-\-tool" > /dev/null && VALGRIND="$VALGRIND --tool=memcheck" } if ( ! $opt_user ) @@ -812,74 +1027,210 @@ sub command_line_setup () { } } + # On QNX, /tmp/dir/master.sock and /tmp/dir//master.sock seem to be + # considered different, so avoid the extra slash (/) in the socket + # paths. + my $sockdir = $opt_tmpdir; + $sockdir =~ s|/+$||; + # Put this into a hash, will be a C struct $master->[0]= { + pid => 0, + type => "master", + idx => 0, path_myddir => "$opt_vardir/master-data", path_myerr => "$opt_vardir/log/master.err", path_mylog => "$opt_vardir/log/master.log", - path_mypid => "$opt_vardir/run/master.pid", - path_mysock => "$opt_tmpdir/master.sock", - path_myport => $opt_master_myport, + path_pid => "$opt_vardir/run/master.pid", + path_sock => "$sockdir/master.sock", + port => $opt_master_myport, start_timeout => 400, # enough time create innodb tables - - ndbcluster => 1, # ndbcluster not started + cluster => 0, # index in clusters list + start_opts => [], }; $master->[1]= { + pid => 0, + type => "master", + idx => 1, path_myddir => "$opt_vardir/master1-data", path_myerr => "$opt_vardir/log/master1.err", path_mylog => "$opt_vardir/log/master1.log", - path_mypid => "$opt_vardir/run/master1.pid", - path_mysock => "$opt_tmpdir/master1.sock", - path_myport => $opt_master_myport + 1, + path_pid => "$opt_vardir/run/master1.pid", + path_sock => "$sockdir/master1.sock", + port => $opt_master_myport + 1, start_timeout => 400, # enough time create innodb tables + cluster => 0, # index in clusters list + start_opts => [], }; $slave->[0]= { + pid => 0, + type => "slave", + idx => 0, path_myddir => "$opt_vardir/slave-data", path_myerr => "$opt_vardir/log/slave.err", path_mylog => "$opt_vardir/log/slave.log", - path_mypid => "$opt_vardir/run/slave.pid", - path_mysock => "$opt_tmpdir/slave.sock", - path_myport => $opt_slave_myport, + path_pid => "$opt_vardir/run/slave.pid", + path_sock => "$sockdir/slave.sock", + port => $opt_slave_myport, start_timeout => 400, + + cluster => 1, # index in clusters list + start_opts => [], }; $slave->[1]= { + pid => 0, + type => "slave", + idx => 1, path_myddir => "$opt_vardir/slave1-data", path_myerr => "$opt_vardir/log/slave1.err", path_mylog => "$opt_vardir/log/slave1.log", - path_mypid => "$opt_vardir/run/slave1.pid", - path_mysock => "$opt_tmpdir/slave1.sock", - path_myport => $opt_slave_myport + 1, + path_pid => "$opt_vardir/run/slave1.pid", + path_sock => "$sockdir/slave1.sock", + port => $opt_slave_myport + 1, start_timeout => 300, + cluster => -1, # index in clusters list + start_opts => [], }; $slave->[2]= { + pid => 0, + type => "slave", + idx => 2, path_myddir => "$opt_vardir/slave2-data", path_myerr => "$opt_vardir/log/slave2.err", path_mylog => "$opt_vardir/log/slave2.log", - path_mypid => "$opt_vardir/run/slave2.pid", - path_mysock => "$opt_tmpdir/slave2.sock", - path_myport => $opt_slave_myport + 2, + path_pid => "$opt_vardir/run/slave2.pid", + path_sock => "$sockdir/slave2.sock", + port => $opt_slave_myport + 2, start_timeout => 300, + cluster => -1, # index in clusters list + start_opts => [], + }; + + $instance_manager= + { + path_err => "$opt_vardir/log/im.err", + path_log => "$opt_vardir/log/im.log", + path_pid => "$opt_vardir/run/im.pid", + path_angel_pid => "$opt_vardir/run/im.angel.pid", + path_sock => "$sockdir/im.sock", + port => $im_port, + start_timeout => $master->[0]->{'start_timeout'}, + admin_login => 'im_admin', + admin_password => 'im_admin_secret', + admin_sha1 => '*598D51AD2DFF7792045D6DF3DDF9AA1AF737B295', + password_file => "$opt_vardir/im.passwd", + defaults_file => "$opt_vardir/im.cnf", + }; + + $instance_manager->{'instances'}->[0]= + { + server_id => 1, + port => $im_mysqld1_port, + path_datadir => "$opt_vardir/im_mysqld_1.data", + path_sock => "$sockdir/mysqld_1.sock", + path_pid => "$opt_vardir/run/mysqld_1.pid", + start_timeout => 400, # enough time create innodb tables + old_log_format => 1 + }; + + $instance_manager->{'instances'}->[1]= + { + server_id => 2, + port => $im_mysqld2_port, + path_datadir => "$opt_vardir/im_mysqld_2.data", + path_sock => "$sockdir/mysqld_2.sock", + path_pid => "$opt_vardir/run/mysqld_2.pid", + nonguarded => 1, + start_timeout => 400, # enough time create innodb tables + old_log_format => 1 }; + my $data_dir= "$opt_vardir/ndbcluster-$opt_ndbcluster_port"; + $clusters->[0]= + { + name => "Master", + nodes => 2, + port => "$opt_ndbcluster_port", + data_dir => "$data_dir", + connect_string => "$opt_ndbconnectstring", + path_pid => "$data_dir/ndb_3.pid", # Nodes + 1 + pid => 0, # pid of ndb_mgmd + installed_ok => 0, + }; + + $data_dir= "$opt_vardir/ndbcluster-$opt_ndbcluster_port_slave"; + $clusters->[1]= + { + name => "Slave", + nodes => 1, + port => "$opt_ndbcluster_port_slave", + data_dir => "$data_dir", + connect_string => "$opt_ndbconnectstring_slave", + path_pid => "$data_dir/ndb_2.pid", # Nodes + 1 + pid => 0, # pid of ndb_mgmd + installed_ok => 0, + }; + + # Init pids of ndbd's + foreach my $cluster ( @{$clusters} ) + { + for ( my $idx= 0; $idx < $cluster->{'nodes'}; $idx++ ) + { + my $nodeid= $idx+1; + $cluster->{'ndbds'}->[$idx]= + { + pid => 0, + nodeid => $nodeid, + path_pid => "$cluster->{'data_dir'}/ndb_${nodeid}.pid", + path_fs => "$cluster->{'data_dir'}/ndb_${nodeid}_fs", + }; + } + } + if ( $opt_extern ) { $glob_use_running_server= 1; $opt_skip_rpl= 1; # We don't run rpl test cases - $master->[0]->{'path_mysock'}= $opt_socket; + $master->[0]->{'path_sock'}= $opt_socket; } $path_timefile= "$opt_vardir/log/mysqltest-time"; $path_mysqltest_log= "$opt_vardir/log/mysqltest.log"; + $path_current_test_log= "$opt_vardir/log/current_test"; + $path_ndb_testrun_log= "$opt_vardir/log/ndb_testrun.log"; + + $path_snapshot= "$opt_tmpdir/snapshot_$opt_master_myport/"; +} + +sub datadir_setup () { + + # Make a list of all data_dirs + @data_dir_lst = ( + $master->[0]->{'path_myddir'}, + $master->[1]->{'path_myddir'}); + + for (my $idx= 0; $idx < $max_slave_num; $idx++) + { + push(@data_dir_lst, $slave->[$idx]->{'path_myddir'}); + } + + unless ($opt_skip_im) + { + foreach my $instance (@{$instance_manager->{'instances'}}) + { + push(@data_dir_lst, $instance->{'path_datadir'}); + } + } } @@ -889,202 +1240,631 @@ sub command_line_setup () { # ############################################################################## -sub executable_setup () { - if ( $opt_source_dist ) +sub collect_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= "$glob_mysql_test_dir/mysqld.spec.$$"; + if ( mtr_run($exe_mysqld, + ["--no-defaults", + "--verbose", + "--help"], + "", "$spec_file", "$spec_file", "") != 0 ) { - if ( $glob_win32 ) - { - $path_client_bindir= mtr_path_exists("$glob_basedir/client_release", - "$glob_basedir/bin"); - $exe_mysqld= mtr_exe_exists ("$path_client_bindir/mysqld-nt", - "$path_client_bindir/mysqld", - "$path_client_bindir/mysqld-debug", - "$path_client_bindir/mysqld-max", - "$path_client_bindir/mysqld-max-nt"); - $path_language= mtr_path_exists("$glob_basedir/share/english/"); - $path_charsetsdir= mtr_path_exists("$glob_basedir/share/charsets"); - $exe_my_print_defaults= - mtr_exe_exists("$path_client_bindir/my_print_defaults"); - } - else - { - $path_client_bindir= mtr_path_exists("$glob_basedir/client"); - $exe_mysqld= mtr_exe_exists ("$glob_basedir/sql/mysqld"); - $path_language= mtr_path_exists("$glob_basedir/sql/share/english/"); - $path_charsetsdir= mtr_path_exists("$glob_basedir/sql/share/charsets"); - $exe_my_print_defaults= - mtr_exe_exists("$glob_basedir/extra/my_print_defaults"); - } + mtr_error("Failed to get version and list of features from %s", + $exe_mysqld); + } - if ( $glob_use_embedded_server ) + 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 ) { - 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"); + # Look for version + my $exe_name= basename($exe_mysqld); + mtr_verbose("exe_name: $exe_name"); + if ( $line =~ /^\S*$exe_name\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"; + mtr_report("MySQL Version $1.$2.$3"); + } } else { - if ( $opt_valgrind_mysqltest ) + if (!$found_variable_list_start) { - # client/mysqltest might be a libtool .sh script, so look for real exe - # to avoid valgrinding bash ;) - $exe_mysqltest= - mtr_exe_exists("$path_client_bindir/.libs/lt-mysqltest", - "$path_client_bindir/.libs/mysqltest", - "$path_client_bindir/mysqltest"); + # Look for start of variables list + if ( $line =~ /[\-]+\s[\-]+/ ) + { + $found_variable_list_start= 1; + } } else { - $exe_mysqltest= mtr_exe_exists("$path_client_bindir/mysqltest"); + # Put variables into hash + if ( $line =~ /^([\S]+)[ \t]+(.*?)\r?$/ ) + { + # print "$1=\"$2\"\n"; + $mysqld_variables{$1}= $2; + } + else + { + # The variable list is ended with a blank line + if ( $line =~ /^[\s]*$/ ) + { + last; + } + else + { + # Send out a warning, we should fix the variables that has no + # space between variable name and it's value + # or should it be fixed width column parsing? It does not + # look like that in function my_print_variables in my_getopt.c + mtr_warning("Could not parse variable list line : $line"); + } + } } - $exe_mysql_client_test= - mtr_exe_exists("$glob_basedir/tests/mysql_client_test", - "/usr/bin/false"); - } - $exe_mysqldump= mtr_exe_exists("$path_client_bindir/mysqldump"); - $exe_mysqlimport= mtr_exe_exists("$path_client_bindir/mysqlimport"); - $exe_mysqlshow= mtr_exe_exists("$path_client_bindir/mysqlshow"); - $exe_mysqlbinlog= mtr_exe_exists("$path_client_bindir/mysqlbinlog"); - $exe_mysqladmin= mtr_exe_exists("$path_client_bindir/mysqladmin"); - $exe_mysql= mtr_exe_exists("$path_client_bindir/mysql"); - $exe_mysql_fix_system_tables= - mtr_script_exists("$glob_basedir/scripts/mysql_fix_privilege_tables"); - $path_ndb_tools_dir= mtr_path_exists("$glob_basedir/ndb/tools"); - $exe_ndb_mgm= "$glob_basedir/ndb/src/mgmclient/ndb_mgm"; + } } - else - { - $path_client_bindir= mtr_path_exists("$glob_basedir/bin"); - $exe_mysqldump= mtr_exe_exists("$path_client_bindir/mysqldump"); - $exe_mysqlimport= mtr_exe_exists("$path_client_bindir/mysqlimport"); - $exe_mysqlshow= mtr_exe_exists("$path_client_bindir/mysqlshow"); - $exe_mysqlbinlog= mtr_exe_exists("$path_client_bindir/mysqlbinlog"); - $exe_mysqladmin= mtr_exe_exists("$path_client_bindir/mysqladmin"); - $exe_mysql= mtr_exe_exists("$path_client_bindir/mysql"); - $exe_mysql_fix_system_tables= - mtr_script_exists("$path_client_bindir/mysql_fix_privilege_tables", - "$glob_basedir/scripts/mysql_fix_privilege_tables"); - $exe_my_print_defaults= - mtr_exe_exists("$path_client_bindir/my_print_defaults"); + 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; - $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 + +sub executable_setup_im () { + + # Look for instance manager binary - mysqlmanager + $exe_im= + mtr_exe_maybe_exists( + "$glob_basedir/server-tools/instance-manager/mysqlmanager", + "$glob_basedir/libexec/mysqlmanager"); + + return ($exe_im eq ""); +} + +sub executable_setup_ndb () { + + # Look for ndb tols and binaries + my $ndb_path= mtr_file_exists("$glob_basedir/ndb", + "$glob_basedir/storage/ndb", + "$glob_basedir/bin"); + + $exe_ndbd= + mtr_exe_maybe_exists("$ndb_path/src/kernel/ndbd", + "$ndb_path/ndbd"); + $exe_ndb_mgm= + mtr_exe_maybe_exists("$ndb_path/src/mgmclient/ndb_mgm", + "$ndb_path/ndb_mgm"); + $exe_ndb_mgmd= + mtr_exe_maybe_exists("$ndb_path/src/mgmsrv/ndb_mgmd", + "$ndb_path/ndb_mgmd"); + $exe_ndb_waiter= + mtr_exe_maybe_exists("$ndb_path/tools/ndb_waiter", + "$ndb_path/ndb_waiter"); + + # May not exist + $path_ndb_tools_dir= mtr_file_exists("$ndb_path/tools", + "$ndb_path"); + # May not exist + $path_ndb_examples_dir= + mtr_file_exists("$ndb_path/ndbapi-examples", + "$ndb_path/examples"); + # May not exist + $exe_ndb_example= + mtr_file_exists("$path_ndb_examples_dir/ndbapi_simple/ndbapi_simple"); + + return ( $exe_ndbd eq "" or + $exe_ndb_mgm eq "" or + $exe_ndb_mgmd eq "" or + $exe_ndb_waiter eq ""); +} + +sub executable_setup () { + + # + # Check if libtool is available in this distribution/clone + # we need it when valgrinding or debugging non installed binary + # Otherwise valgrind will valgrind the libtool wrapper or bash + # and gdb will not find the real executable to debug + # + if ( -x "../libtool") + { + $exe_libtool= "../libtool"; + if ($opt_valgrind or $glob_debugger) { - $exe_mysqld= mtr_exe_exists ("$glob_basedir/libexec/mysqld", - "$glob_basedir/bin/mysqld"); + mtr_report("Using \"$exe_libtool\" when running valgrind or debugger"); } + } + + # Look for language files and charsetsdir, use same share + my $path_share= mtr_path_exists("$glob_basedir/share/mysql", + "$glob_basedir/sql/share", + "$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"); + + # 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"); + if ( $mysql_version_id >= 50100 ) + { + $exe_mysqlslap= mtr_exe_exists("$path_client_bindir/mysqlslap"); + } + + if ( ! $glob_win32 ) + { + # 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 and executable_setup_ndb()) + { + mtr_warning("Could not find all required ndb binaries, " . + "all ndb tests will fail, use --skip-ndbcluster to " . + "skip testing it."); - if ( $glob_use_embedded_server ) + foreach my $cluster (@{$clusters}) { - $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"); + $cluster->{"executable_setup_failed"}= 1; } - else - { + } + + if ( ! $opt_skip_im and executable_setup_im()) + { + mtr_warning("Could not find all required instance manager binaries, " . + "all im tests will fail, use --skip-im to " . + "continue without instance manager"); + $instance_manager->{"executable_setup_failed"}= 1; + } + + # Look for the udf_example library + $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"); + + # Look for mysqltest executable + if ( $glob_use_embedded_server ) + { + $exe_mysqltest= + mtr_exe_exists("$glob_basedir/libmysqld/examples/mysqltest_embedded", + "$path_client_bindir/mysqltest_embedded"); + } + else + { $exe_mysqltest= mtr_exe_exists("$path_client_bindir/mysqltest"); - $exe_mysql_client_test= - mtr_exe_exists("$path_client_bindir/mysql_client_test", - "/usr/bin/false"); # FIXME temporary - } - $path_ndb_tools_dir= "$glob_basedir/bin"; - $exe_ndb_mgm= "$glob_basedir/bin/ndb_mgm"; + } - $exe_master_mysqld= $exe_master_mysqld || $exe_mysqld; - $exe_slave_mysqld= $exe_slave_mysqld || $exe_mysqld; + # Look for mysql_client_test executable which may _not_ exist in + # some versions, test using it should be skipped + if ( $glob_use_embedded_server ) + { + $exe_mysql_client_test= + mtr_exe_maybe_exists( + "$glob_basedir/libmysqld/examples/mysql_client_test_embedded"); + } + else + { + $exe_mysql_client_test= + mtr_exe_maybe_exists("$glob_basedir/tests/mysql_client_test", + "$glob_basedir/tests/release/mysql_client_test", + "$glob_basedir/tests/debug/mysql_client_test", + "$glob_basedir/bin"); + } +} - $path_ndb_backup_dir= - "$opt_vardir/ndbcluster-$opt_ndbcluster_port"; - $file_ndb_testrun_log= "$opt_vardir/log/ndb_testrun.log"; + +sub generate_cmdline_mysqldump ($) { + my($mysqld) = @_; + return + "$exe_mysqldump --no-defaults -uroot " . + "--port=$mysqld->{'port'} " . + "--socket=$mysqld->{'path_sock'} --password="; } ############################################################################## # -# Set environment to be used by childs of this process +# Set environment to be used by childs of this process for +# things that are constant duting the whole lifetime of mysql-test-run.pl # ############################################################################## +sub mysql_client_test_arguments() +{ + my $exe= $exe_mysql_client_test; + + my $args; + mtr_init_args(\$args); + if ( $opt_valgrind_mysqltest ) + { + valgrind_arguments($args, \$exe); + } + + mtr_add_arg($args, "--no-defaults"); + mtr_add_arg($args, "--testcase"); + mtr_add_arg($args, "--user=root"); + mtr_add_arg($args, "--port=$master->[0]->{'port'}"); + mtr_add_arg($args, "--socket=$master->[0]->{'path_sock'}"); + + if ( $mysql_version_id >= 50000 ) + { + mtr_add_arg($args, "--vardir=$opt_vardir") + } + + if ( $opt_debug ) + { + mtr_add_arg($args, + "--debug=d:t:A,$path_vardir_trace/log/mysql_client_test.trace"); + } + + if ( $glob_use_embedded_server ) + { + mtr_add_arg($args, + " -A --language=$path_language"); + mtr_add_arg($args, + " -A --datadir=$slave->[0]->{'path_myddir'}"); + mtr_add_arg($args, + " -A --character-sets-dir=$path_charsetsdir"); + } + + return join(" ", $exe, @$args); +} + # Note that some env is setup in spawn/run, in "mtr_process.pl" sub environment_setup () { + umask(022); + + my @ld_library_paths; + # -------------------------------------------------------------------------- - # We might not use a standard installation directory, like /usr/lib. - # Set LD_LIBRARY_PATH to make sure we find our installed libraries. + # Setup LD_LIBRARY_PATH so the libraries from this distro/clone + # are used in favor of the system installed ones # -------------------------------------------------------------------------- + if ( $opt_source_dist ) + { + push(@ld_library_paths, "$glob_basedir/libmysql/.libs/", + "$glob_basedir/libmysql_r/.libs/"); + } + else + { + push(@ld_library_paths, "$glob_basedir/lib"); + } - unless ( $opt_source_dist ) + # -------------------------------------------------------------------------- + # Add the path where libndbclient can be found + # -------------------------------------------------------------------------- + if ( $glob_ndbcluster_supported ) { - $ENV{'LD_LIBRARY_PATH'}= - "$glob_basedir/lib" . - ($ENV{'LD_LIBRARY_PATH'} ? ":$ENV{'LD_LIBRARY_PATH'}" : ""); - $ENV{'DYLD_LIBRARY_PATH'}= - "$glob_basedir/lib" . - ($ENV{'DYLD_LIBRARY_PATH'} ? ":$ENV{'DYLD_LIBRARY_PATH'}" : ""); + push(@ld_library_paths, "$glob_basedir/storage/ndb/src/.libs"); } # -------------------------------------------------------------------------- + # Add the path where mysqld will find udf_example.so + # -------------------------------------------------------------------------- + if ( $lib_udf_example ) + { + push(@ld_library_paths, dirname($lib_udf_example)); + } + + # -------------------------------------------------------------------------- + # Valgrind need to be run with debug libraries otherwise it's almost + # impossible to add correct supressions, that means if "/usr/lib/debug" + # is available, it should be added to + # LD_LIBRARY_PATH + # -------------------------------------------------------------------------- + my $debug_libraries_path= "/usr/lib/debug"; + if ( $opt_valgrind and -d $debug_libraries_path ) + { + push(@ld_library_paths, $debug_libraries_path); + } + + $ENV{'LD_LIBRARY_PATH'}= join(":", @ld_library_paths, + $ENV{'LD_LIBRARY_PATHS'} ? + split(':', $ENV{'LD_LIBRARY_PATH'}) : ()); + mtr_debug("LD_LIBRARY_PATH: $ENV{'LD_LIBRARY_PATH'}"); + + $ENV{'DYLD_LIBRARY_PATH'}= join(":", @ld_library_paths, + $ENV{'DYLD_LIBRARY_PATH'} ? + split(':', $ENV{'DYLD_LIBRARY_PATH'}) : ()); + mtr_debug("DYLD_LIBRARY_PATH: $ENV{'DYLD_LIBRARY_PATH'}"); + + + # -------------------------------------------------------------------------- # Also command lines in .opt files may contain env vars # -------------------------------------------------------------------------- + $ENV{'CHARSETSDIR'}= $path_charsetsdir; $ENV{'UMASK'}= "0660"; # The octal *string* $ENV{'UMASK_DIR'}= "0770"; # The octal *string* $ENV{'LC_COLLATE'}= "C"; $ENV{'USE_RUNNING_SERVER'}= $glob_use_running_server; $ENV{'MYSQL_TEST_DIR'}= $glob_mysql_test_dir; - $ENV{'MYSQL_TEST_WINDIR'}= $glob_mysql_test_dir; $ENV{'MYSQLTEST_VARDIR'}= $opt_vardir; - $ENV{'MASTER_WINMYSOCK'}= $master->[0]->{'path_mysock'}; - $ENV{'MASTER_MYSOCK'}= $master->[0]->{'path_mysock'}; - $ENV{'MASTER_MYSOCK1'}= $master->[1]->{'path_mysock'}; - $ENV{'MASTER_MYPORT'}= $master->[0]->{'path_myport'}; - $ENV{'MASTER_MYPORT1'}= $master->[1]->{'path_myport'}; - $ENV{'SLAVE_MYPORT'}= $slave->[0]->{'path_myport'}; - $ENV{'SLAVE_MYPORT1'}= $slave->[1]->{'path_myport'}; - $ENV{'SLAVE_MYPORT2'}= $slave->[2]->{'path_myport'}; -# $ENV{'MYSQL_TCP_PORT'}= '@MYSQL_TCP_PORT@'; # FIXME - $ENV{'MYSQL_TCP_PORT'}= 3306; - - $ENV{'NDBCLUSTER_PORT'}= $opt_ndbcluster_port; + $ENV{'MYSQL_TMP_DIR'}= $opt_tmpdir; + $ENV{'MASTER_MYSOCK'}= $master->[0]->{'path_sock'}; + $ENV{'MASTER_MYSOCK1'}= $master->[1]->{'path_sock'}; + $ENV{'MASTER_MYPORT'}= $master->[0]->{'port'}; + $ENV{'MASTER_MYPORT1'}= $master->[1]->{'port'}; + $ENV{'SLAVE_MYSOCK'}= $slave->[0]->{'path_sock'}; + $ENV{'SLAVE_MYPORT'}= $slave->[0]->{'port'}; + $ENV{'SLAVE_MYPORT1'}= $slave->[1]->{'port'}; + $ENV{'SLAVE_MYPORT2'}= $slave->[2]->{'port'}; + $ENV{'MYSQL_TCP_PORT'}= $mysqld_variables{'port'}; - if ( $glob_cygwin_perl ) + $ENV{MTR_BUILD_THREAD}= 0 unless $ENV{MTR_BUILD_THREAD}; # Set if not set + + # ---------------------------------------------------- + # Setup env for NDB + # ---------------------------------------------------- + if ( ! $opt_skip_ndbcluster ) { - foreach my $key ('MYSQL_TEST_WINDIR','MASTER_MYSOCK') + $ENV{'NDB_MGM'}= $exe_ndb_mgm; + + $ENV{'NDBCLUSTER_PORT'}= $opt_ndbcluster_port; + $ENV{'NDBCLUSTER_PORT_SLAVE'}= $opt_ndbcluster_port_slave; + + $ENV{'NDB_EXTRA_TEST'}= $opt_ndb_extra_test; + + $ENV{'NDB_BACKUP_DIR'}= $clusters->[0]->{'data_dir'}; + $ENV{'NDB_DATA_DIR'}= $clusters->[0]->{'data_dir'}; + $ENV{'NDB_TOOLS_DIR'}= $path_ndb_tools_dir; + $ENV{'NDB_TOOLS_OUTPUT'}= $path_ndb_testrun_log; + $ENV{'NDB_CONNECTSTRING'}= $opt_ndbconnectstring; + + if ( $mysql_version_id >= 50000 ) { - $ENV{$key}= `cygpath -w $ENV{$key}`; - $ENV{$key} =~ s,\\,\\\\,g; - chomp($ENV{$key}); + $ENV{'NDB_EXAMPLES_DIR'}= $path_ndb_examples_dir; + $ENV{'MY_NDB_EXAMPLES_BINARY'}= $exe_ndb_example; } + $ENV{'NDB_EXAMPLES_OUTPUT'}= $path_ndb_testrun_log; } - $ENV{MTR_BUILD_THREAD}= 0 unless $ENV{MTR_BUILD_THREAD}; # Set if not set + # ---------------------------------------------------- + # Setup env for IM + # ---------------------------------------------------- + if ( ! $opt_skip_im ) + { + $ENV{'IM_EXE'}= $exe_im; + $ENV{'IM_PATH_PID'}= $instance_manager->{path_pid}; + $ENV{'IM_PATH_ANGEL_PID'}= $instance_manager->{path_angel_pid}; + $ENV{'IM_PORT'}= $instance_manager->{port}; + $ENV{'IM_DEFAULTS_PATH'}= $instance_manager->{defaults_file}; + $ENV{'IM_PASSWORD_PATH'}= $instance_manager->{password_file}; + + $ENV{'IM_MYSQLD1_SOCK'}= + $instance_manager->{instances}->[0]->{path_sock}; + $ENV{'IM_MYSQLD1_PORT'}= + $instance_manager->{instances}->[0]->{port}; + $ENV{'IM_MYSQLD1_PATH_PID'}= + $instance_manager->{instances}->[0]->{path_pid}; + $ENV{'IM_MYSQLD2_SOCK'}= + $instance_manager->{instances}->[1]->{path_sock}; + $ENV{'IM_MYSQLD2_PORT'}= + $instance_manager->{instances}->[1]->{port}; + $ENV{'IM_MYSQLD2_PATH_PID'}= + $instance_manager->{instances}->[1]->{path_pid}; + } + + # ---------------------------------------------------- + # Setup env so childs can execute mysqlcheck + # ---------------------------------------------------- + my $cmdline_mysqlcheck= + "$exe_mysqlcheck --no-defaults -uroot " . + "--port=$master->[0]->{'port'} " . + "--socket=$master->[0]->{'path_sock'} --password="; + + if ( $opt_debug ) + { + $cmdline_mysqlcheck .= + " --debug=d:t:A,$path_vardir_trace/log/mysqlcheck.trace"; + } + $ENV{'MYSQL_CHECK'}= $cmdline_mysqlcheck; + + # ---------------------------------------------------- + # Setup env to childs can execute myqldump + # ---------------------------------------------------- + my $cmdline_mysqldump= generate_cmdline_mysqldump($master->[0]); + my $cmdline_mysqldumpslave= generate_cmdline_mysqldump($slave->[0]); + + if ( $opt_debug ) + { + $cmdline_mysqldump .= + " --debug=d:t:A,$path_vardir_trace/log/mysqldump-master.trace"; + $cmdline_mysqldumpslave .= + " --debug=d:t:A,$path_vardir_trace/log/mysqldump-slave.trace"; + } + $ENV{'MYSQL_DUMP'}= $cmdline_mysqldump; + $ENV{'MYSQL_DUMP_SLAVE'}= $cmdline_mysqldumpslave; + + + # ---------------------------------------------------- + # Setup env so childs can execute mysqlslap + # ---------------------------------------------------- + if ( $exe_mysqlslap ) + { + my $cmdline_mysqlslap= + "$exe_mysqlslap -uroot " . + "--port=$master->[0]->{'port'} " . + "--socket=$master->[0]->{'path_sock'} --password= " . + "--lock-directory=$opt_tmpdir"; + + if ( $opt_debug ) + { + $cmdline_mysqlslap .= + " --debug=d:t:A,$path_vardir_trace/log/mysqlslap.trace"; + } + $ENV{'MYSQL_SLAP'}= $cmdline_mysqlslap; + } + + # ---------------------------------------------------- + # Setup env so childs can execute mysqlimport + # ---------------------------------------------------- + my $cmdline_mysqlimport= + "$exe_mysqlimport -uroot " . + "--port=$master->[0]->{'port'} " . + "--socket=$master->[0]->{'path_sock'} --password="; + + if ( $opt_debug ) + { + $cmdline_mysqlimport .= + " --debug=d:t:A,$path_vardir_trace/log/mysqlimport.trace"; + } + $ENV{'MYSQL_IMPORT'}= $cmdline_mysqlimport; + + + # ---------------------------------------------------- + # Setup env so childs can execute mysqlshow + # ---------------------------------------------------- + my $cmdline_mysqlshow= + "$exe_mysqlshow -uroot " . + "--port=$master->[0]->{'port'} " . + "--socket=$master->[0]->{'path_sock'} --password="; + + if ( $opt_debug ) + { + $cmdline_mysqlshow .= + " --debug=d:t:A,$path_vardir_trace/log/mysqlshow.trace"; + } + $ENV{'MYSQL_SHOW'}= $cmdline_mysqlshow; + + # ---------------------------------------------------- + # Setup env so childs can execute mysqlbinlog + # ---------------------------------------------------- + my $cmdline_mysqlbinlog= + "$exe_mysqlbinlog" . + " --no-defaults --local-load=$opt_tmpdir"; + if ( $mysql_version_id >= 50000 ) + { + $cmdline_mysqlbinlog .=" --character-sets-dir=$path_charsetsdir"; + } + + if ( $opt_debug ) + { + $cmdline_mysqlbinlog .= + " --debug=d:t:A,$path_vardir_trace/log/mysqlbinlog.trace"; + } + $ENV{'MYSQL_BINLOG'}= $cmdline_mysqlbinlog; + + # ---------------------------------------------------- + # Setup env so childs can execute mysql + # ---------------------------------------------------- + my $cmdline_mysql= + "$exe_mysql --no-defaults --host=localhost --user=root --password= " . + "--port=$master->[0]->{'port'} " . + "--socket=$master->[0]->{'path_sock'} ". + "--character-sets-dir=$path_charsetsdir"; + + $ENV{'MYSQL'}= $cmdline_mysql; + + # ---------------------------------------------------- + # Setup env so childs can execute mysql_client_test + # ---------------------------------------------------- + $ENV{'MYSQL_CLIENT_TEST'}= mysql_client_test_arguments(); + + # ---------------------------------------------------- + # Setup env so childs can execute mysql_fix_system_tables + # ---------------------------------------------------- + if ( ! $glob_win32 ) + { + my $cmdline_mysql_fix_system_tables= + "$exe_mysql_fix_system_tables --no-defaults --host=localhost " . + "--user=root --password= " . + "--basedir=$glob_basedir --bindir=$path_client_bindir --verbose " . + "--port=$master->[0]->{'port'} " . + "--socket=$master->[0]->{'path_sock'}"; + $ENV{'MYSQL_FIX_SYSTEM_TABLES'}= $cmdline_mysql_fix_system_tables; + } + + # ---------------------------------------------------- + # Setup env so childs can execute my_print_defaults + # ---------------------------------------------------- + $ENV{'MYSQL_MY_PRINT_DEFAULTS'}= $exe_my_print_defaults; + + + # ---------------------------------------------------- + # Setup env so childs can execute perror + # ---------------------------------------------------- + $ENV{'MY_PERROR'}= $exe_perror; + + # ---------------------------------------------------- + # Add the path where mysqld will find udf_example.so + # ---------------------------------------------------- + $ENV{'UDF_EXAMPLE_LIB'}= + ($lib_udf_example ? basename($lib_udf_example) : ""); + $ENV{'LD_LIBRARY_PATH'}= + ($lib_udf_example ? dirname($lib_udf_example) : "") . + ($ENV{'LD_LIBRARY_PATH'} ? ":$ENV{'LD_LIBRARY_PATH'}" : ""); + + + # ---------------------------------------------------- # We are nice and report a bit about our settings - print "Using MTR_BUILD_THREAD = $ENV{MTR_BUILD_THREAD}\n"; - print "Using MASTER_MYPORT = $ENV{MASTER_MYPORT}\n"; - print "Using MASTER_MYPORT1 = $ENV{MASTER_MYPORT1}\n"; - print "Using SLAVE_MYPORT = $ENV{SLAVE_MYPORT}\n"; - print "Using SLAVE_MYPORT1 = $ENV{SLAVE_MYPORT1}\n"; - print "Using SLAVE_MYPORT2 = $ENV{SLAVE_MYPORT2}\n"; - print "Using NDBCLUSTER_PORT = $ENV{NDBCLUSTER_PORT}\n"; + # ---------------------------------------------------- + if (!$opt_extern) + { + print "Using MTR_BUILD_THREAD = $ENV{MTR_BUILD_THREAD}\n"; + print "Using MASTER_MYPORT = $ENV{MASTER_MYPORT}\n"; + print "Using MASTER_MYPORT1 = $ENV{MASTER_MYPORT1}\n"; + print "Using SLAVE_MYPORT = $ENV{SLAVE_MYPORT}\n"; + print "Using SLAVE_MYPORT1 = $ENV{SLAVE_MYPORT1}\n"; + print "Using SLAVE_MYPORT2 = $ENV{SLAVE_MYPORT2}\n"; + if ( ! $opt_skip_ndbcluster ) + { + print "Using NDBCLUSTER_PORT = $ENV{NDBCLUSTER_PORT}\n"; + if ( ! $opt_skip_ndbcluster_slave ) + { + print "Using NDBCLUSTER_PORT_SLAVE = $ENV{NDBCLUSTER_PORT_SLAVE}\n"; + } + } + if ( ! $opt_skip_im ) + { + print "Using IM_PORT = $ENV{IM_PORT}\n"; + print "Using IM_MYSQLD1_PORT = $ENV{IM_MYSQLD1_PORT}\n"; + print "Using IM_MYSQLD2_PORT = $ENV{IM_MYSQLD2_PORT}\n"; + } + } + + # Create an environment variable to make it possible + # to detect that valgrind is being used from test cases + $ENV{'VALGRIND_TEST'}= $opt_valgrind; + } @@ -1099,10 +1879,11 @@ sub signal_setup () { $SIG{INT}= \&handle_int_signal; } + sub handle_int_signal () { $SIG{INT}= 'DEFAULT'; # If we get a ^C again, we die... mtr_warning("got INT signal, cleaning up....."); - stop_masters_slaves(); + stop_all_servers(); mtr_error("We die from ^C signal from user"); } @@ -1113,53 +1894,73 @@ sub handle_int_signal () { # ############################################################################## -sub kill_running_server () { +sub kill_running_servers () { if ( $opt_fast or $glob_use_embedded_server ) { # FIXME is embedded server really using PID files?! - unlink($master->[0]->{'path_mypid'}); - unlink($master->[1]->{'path_mypid'}); - unlink($slave->[0]->{'path_mypid'}); - unlink($slave->[1]->{'path_mypid'}); - unlink($slave->[2]->{'path_mypid'}); + unlink($master->[0]->{'path_pid'}); + unlink($master->[1]->{'path_pid'}); + unlink($slave->[0]->{'path_pid'}); + unlink($slave->[1]->{'path_pid'}); + unlink($slave->[2]->{'path_pid'}); } else { # Ensure that no old mysqld test servers are running # This is different from terminating processes we have - # started from ths run of the script, this is terminating + # started from this run of the script, this is terminating # leftovers from previous runs. - mtr_report("Killing Possible Leftover Processes"); - mkpath("$opt_vardir/log"); # Needed for mysqladmin log + if ( ! -d $opt_vardir ) + { + if ( -l $opt_vardir and ! -d readlink($opt_vardir) ) + { + mtr_report("Removing $opt_vardir symlink without destination"); + unlink($opt_vardir); + } + # The "var" dir does not exist already + # the processes that mtr_kill_leftovers start will write + # their log files to var/log so it should be created + mkpath("$opt_vardir/log"); + } mtr_kill_leftovers(); - - ndbcluster_stop(); - $master->[0]->{'ndbcluster'}= 1; - } + } } -sub kill_and_cleanup () { +sub cleanup_stale_files () { - kill_running_server (); + my $created_by_mem_file= "$glob_mysql_test_dir/var/created_by_mem"; mtr_report("Removing Stale Files"); - if ( $opt_vardir eq "$glob_mysql_test_dir/var" ) + if ( $opt_vardir eq $default_vardir ) { # # Running with "var" in mysql-test dir # - if ( -l "$glob_mysql_test_dir/var" ) + if ( -l $opt_vardir) { - # Some users creates a soft link in mysql-test/var to another area - # - allow it - mtr_report("WARNING: Using the 'mysql-test/var' symlink"); - rmtree("$opt_vardir/log"); - rmtree("$opt_vardir/ndbcluster-$opt_ndbcluster_port"); - rmtree("$opt_vardir/run"); - rmtree("$opt_vardir/tmp"); + # var is a symlink + if (-f $created_by_mem_file) + { + # Remove the directory which the link points at + rmtree(readlink($opt_vardir)); + # Remove the entire "var" dir + rmtree("$opt_vardir/"); + # Remove the "var" symlink + unlink($opt_vardir); + } + else + { + # Some users creates a soft link in mysql-test/var to another area + # - allow it + mtr_report("WARNING: Using the 'mysql-test/var' symlink"); + rmtree("$opt_vardir/log"); + rmtree("$opt_vardir/ndbcluster-$opt_ndbcluster_port"); + rmtree("$opt_vardir/run"); + rmtree("$opt_vardir/tmp"); + } } else { @@ -1175,27 +1976,29 @@ sub kill_and_cleanup () { # Remove the var/ dir in mysql-test dir if any # this could be an old symlink that shouldn't be there - rmtree("$glob_mysql_test_dir/var"); + rmtree($default_vardir); # Remove the "var" dir rmtree("$opt_vardir/"); } + if ( $opt_mem ) + { + # Runinng with var as a link to some "memory" location, normally tmpfs + rmtree($opt_mem); + mkpath($opt_mem); + mtr_report("Creating symlink from $opt_vardir to $opt_mem"); + symlink($opt_mem, $opt_vardir); + # Put a small file to recognize this dir was created by --mem + mtr_tofile($created_by_mem_file, $opt_mem); + } + mkpath("$opt_vardir/log"); mkpath("$opt_vardir/run"); mkpath("$opt_vardir/tmp"); mkpath($opt_tmpdir) if $opt_tmpdir ne "$opt_vardir/tmp"; - # FIXME do we really need to create these all, or are they - # created for us when tables are created? - - my @data_dir_lst = ( - $master->[0]->{'path_myddir'}, - $master->[1]->{'path_myddir'}, - $slave->[0]->{'path_myddir'}, - $slave->[1]->{'path_myddir'}, - $slave->[2]->{'path_myddir'}); - + # Remove old and create new data dirs foreach my $data_dir (@data_dir_lst) { rmtree("$data_dir"); @@ -1214,101 +2017,372 @@ sub kill_and_cleanup () { mkpath("$opt_vardir/std_data_ln"); opendir(DIR, "$glob_mysql_test_dir/std_data") or mtr_error("Can't find the std_data directory: $!"); - for my $elem ( readdir(DIR) ) { - next if -d "$glob_mysql_test_dir/std_data/$elem"; - copy("$glob_mysql_test_dir/std_data/$elem", "$opt_vardir/std_data_ln/$elem"); + for(readdir(DIR)) { + next if -d "$glob_mysql_test_dir/std_data/$_"; + copy("$glob_mysql_test_dir/std_data/$_", "$opt_vardir/std_data_ln/$_"); } closedir(DIR); } } +sub check_running_as_root () { + # Check if running as root + # i.e a file can be read regardless what mode we set it to + my $test_file= "$opt_vardir/test_running_as_root.txt"; + mtr_tofile($test_file, "MySQL"); + chmod(oct("0000"), $test_file); + + my $result=""; + if (open(FILE,"<",$test_file)) + { + $result= join('', <FILE>); + close FILE; + } + + chmod(oct("0755"), $test_file); + unlink($test_file); + + $ENV{'MYSQL_TEST_ROOT'}= "NO"; + if ($result eq "MySQL") + { + mtr_warning("running this script as _root_ will cause some " . + "tests to be skipped"); + $ENV{'MYSQL_TEST_ROOT'}= "YES"; + } +} + + +sub check_ssl_support ($) { + my $mysqld_variables= shift; + + if ($opt_skip_ssl || $opt_extern) + { + mtr_report("Skipping SSL"); + $opt_ssl_supported= 0; + $opt_ssl= 0; + return; + } + + if ( ! $mysqld_variables->{'ssl'} ) + { + if ( $opt_ssl) + { + mtr_error("Couldn't find support for SSL"); + return; + } + mtr_report("Skipping SSL, mysqld not compiled with SSL"); + $opt_ssl_supported= 0; + $opt_ssl= 0; + return; + } + mtr_report("Setting mysqld to support SSL connections"); + $opt_ssl_supported= 1; +} + + +sub check_debug_support ($) { + my $mysqld_variables= shift; + + if ( ! $mysqld_variables->{'debug'} ) + { + #mtr_report("Binaries are not debug compiled"); + $debug_compiled_binaries= 0; + + if ( $opt_debug ) + { + mtr_error("Can't use --debug, binaries does not support it"); + } + return; + } + mtr_report("Binaries are debug compiled"); + $debug_compiled_binaries= 1; +} + ############################################################################## # # Start the ndb cluster # ############################################################################## -sub ndbcluster_support () { +sub check_ndbcluster_support ($) { + my $mysqld_variables= shift; - # check ndbcluster support by testing using a switch - # that is only available in that case - if ( mtr_run($exe_mysqld, - ["--no-defaults", - "--ndb-use-exact-count", - "--help"], - "", "/dev/null", "/dev/null", "") != 0 ) + if ($opt_skip_ndbcluster) { - mtr_report("No ndbcluster support"); - return 0; + mtr_report("Skipping ndbcluster"); + $opt_skip_ndbcluster_slave= 1; + return; + } + + if ( ! $mysqld_variables->{'ndb-connectstring'} ) + { + mtr_report("Skipping ndbcluster, mysqld not compiled with ndbcluster"); + $opt_skip_ndbcluster= 1; + $opt_skip_ndbcluster_slave= 1; + return; + } + $glob_ndbcluster_supported= 1; + mtr_report("Using ndbcluster when necessary, mysqld supports it"); + + if ( $mysql_version_id < 50100 ) + { + # Slave cluster is not supported until 5.1 + $opt_skip_ndbcluster_slave= 1; + } - mtr_report("Has ndbcluster support"); - return 1; + + return; } -# FIXME why is there a different start below?! -sub ndbcluster_install () { +sub ndbcluster_start_install ($) { + my $cluster= shift; - if ( ! $opt_with_ndbcluster or $glob_use_running_ndbcluster ) + mtr_report("Installing $cluster->{'name'} Cluster"); + + mkdir($cluster->{'data_dir'}); + + # Create a config file from template + my $ndb_no_ord=512; + my $ndb_no_attr=2048; + my $ndb_con_op=105000; + my $ndb_dmem="80M"; + my $ndb_imem="24M"; + my $ndb_pbmem="32M"; + my $nodes= $cluster->{'nodes'}; + my $ndb_host= "localhost"; + my $ndb_diskless= 0; + + if (!$opt_bench) { - return 0; + # Use a smaller configuration + if ( $mysql_version_id < 50100 ) + { + # 4.1 and 5.0 is using a "larger" --small configuration + $ndb_no_ord=128; + $ndb_con_op=10000; + $ndb_dmem="40M"; + $ndb_imem="12M"; + } + else + { + $ndb_no_ord=32; + $ndb_con_op=5000; + $ndb_dmem="20M"; + $ndb_imem="1M"; + $ndb_pbmem="4M"; + } } - mtr_report("Install ndbcluster"); - my $ndbcluster_opts= $opt_bench ? "" : "--small"; - my $ndbcluster_port_base= $opt_ndbcluster_port + 2; - if ( mtr_run("$glob_mysql_test_dir/ndb/ndbcluster", - ["--port=$opt_ndbcluster_port", - "--port-base=$ndbcluster_port_base", - "--data-dir=$opt_vardir", - $ndbcluster_opts, - "--initial"], - "", "", "", "") ) - { - mtr_error("Error ndbcluster_install"); - return 1; + + my $config_file_template= "ndb/ndb_config_${nodes}_node.ini"; + my $config_file= "$cluster->{'data_dir'}/config.ini"; + + open(IN, $config_file_template) + or mtr_error("Can't open $config_file_template: $!"); + open(OUT, ">", $config_file) + or mtr_error("Can't write to $config_file: $!"); + while (<IN>) + { + chomp; + + s/CHOOSE_MaxNoOfAttributes/$ndb_no_attr/; + s/CHOOSE_MaxNoOfOrderedIndexes/$ndb_no_ord/; + s/CHOOSE_MaxNoOfConcurrentOperations/$ndb_con_op/; + s/CHOOSE_DataMemory/$ndb_dmem/; + s/CHOOSE_IndexMemory/$ndb_imem/; + s/CHOOSE_Diskless/$ndb_diskless/; + s/CHOOSE_HOSTNAME_.*/$ndb_host/; + s/CHOOSE_FILESYSTEM/$cluster->{'data_dir'}/; + s/CHOOSE_PORT_MGM/$cluster->{'port'}/; + 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"; } + close OUT; + close IN; - ndbcluster_stop(); - $master->[0]->{'ndbcluster'}= 1; + + # Start cluster with "--initial" + + ndbcluster_start($cluster, "--initial"); return 0; } -sub ndbcluster_start () { - if ( ! $opt_with_ndbcluster or $glob_use_running_ndbcluster ) +sub ndbcluster_wait_started($$){ + my $cluster= shift; + my $ndb_waiter_extra_opt= shift; + my $path_waiter_log= "$cluster->{'data_dir'}/ndb_waiter.log"; + my $args; + + mtr_init_args(\$args); + + mtr_add_arg($args, "--no-defaults"); + mtr_add_arg($args, "--core"); + mtr_add_arg($args, "--ndb-connectstring=%s", $cluster->{'connect_string'}); + mtr_add_arg($args, "--timeout=60"); + + if ($ndb_waiter_extra_opt) + { + mtr_add_arg($args, "$ndb_waiter_extra_opt"); + } + + # Start the ndb_waiter which will connect to the ndb_mgmd + # and poll it for state of the ndbd's, will return when + # all nodes in the cluster is started + my $res= mtr_run($exe_ndb_waiter, $args, + "", $path_waiter_log, $path_waiter_log, ""); + mtr_verbose("ndbcluster_wait_started, returns: $res") if $res; + return $res; +} + + + +sub mysqld_wait_started($){ + my $mysqld= shift; + + my $res= sleep_until_file_created($mysqld->{'path_pid'}, + $mysqld->{'start_timeout'}, + $mysqld->{'pid'}); + return $res == 0; +} + + +sub ndb_mgmd_wait_started($) { + my ($cluster)= @_; + + my $retries= 100; + while (ndbcluster_wait_started($cluster, "--no-contact") and + $retries) + { + # Millisceond sleep emulated with select + select(undef, undef, undef, (0.1)); + + $retries--; + } + + return $retries == 0; + +} + +sub ndb_mgmd_start ($) { + my $cluster= shift; + + my $args; # Arg vector + my $pid= -1; + + mtr_init_args(\$args); + mtr_add_arg($args, "--no-defaults"); + mtr_add_arg($args, "--core"); + mtr_add_arg($args, "--nodaemon"); + mtr_add_arg($args, "--config-file=%s", "$cluster->{'data_dir'}/config.ini"); + + + my $path_ndb_mgmd_log= "$cluster->{'data_dir'}/\l$cluster->{'name'}_ndb_mgmd.log"; + $pid= mtr_spawn($exe_ndb_mgmd, $args, "", + $path_ndb_mgmd_log, + $path_ndb_mgmd_log, + "", + { append_log_file => 1 }); + + # FIXME Should not be needed + # Unfortunately the cluster nodes will fail to start + # if ndb_mgmd has not started properly + if (ndb_mgmd_wait_started($cluster)) + { + mtr_error("Failed to wait for start of ndb_mgmd"); + } + + # Remember pid of ndb_mgmd + $cluster->{'pid'}= $pid; + + mtr_verbose("ndb_mgmd_start, pid: $pid"); + + return $pid; +} + + +sub ndbd_start ($$$) { + my $cluster= shift; + my $idx= shift; + my $extra_args= shift; + + my $args; # Arg vector + my $pid= -1; + + mtr_init_args(\$args); + mtr_add_arg($args, "--no-defaults"); + mtr_add_arg($args, "--core"); + mtr_add_arg($args, "--ndb-connectstring=%s", "$cluster->{'connect_string'}"); + 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"); + + my $nodeid= $cluster->{'ndbds'}->[$idx]->{'nodeid'}; + my $path_ndbd_log= "$cluster->{'data_dir'}/ndb_${nodeid}.log"; + $pid= mtr_spawn($exe_ndbd, $args, "", + $path_ndbd_log, + $path_ndbd_log, + "", + { append_log_file => 1 }); + + # Add pid to list of pids for this cluster + $cluster->{'ndbds'}->[$idx]->{'pid'}= $pid; + + # Rememeber options used when starting + $cluster->{'ndbds'}->[$idx]->{'start_extra_args'}= $extra_args; + $cluster->{'ndbds'}->[$idx]->{'idx'}= $idx; + + mtr_verbose("ndbd_start, pid: $pid"); + + return $pid; +} + + +sub ndbcluster_start ($$) { + my $cluster= shift; + my $extra_args= shift; + + mtr_verbose("ndbcluster_start '$cluster->{'name'}'"); + + if ( $glob_use_running_ndbcluster ) { return 0; } - # FIXME, we want to _append_ output to file $file_ndb_testrun_log instead of /dev/null - if ( mtr_run("$glob_mysql_test_dir/ndb/ndbcluster", - ["--port=$opt_ndbcluster_port", - "--data-dir=$opt_vardir"], - "", "/dev/null", "", "") ) + + if ( $cluster->{'pid'} ) + { + mtr_error("Cluster '$cluster->{'name'}' already started"); + } + + ndb_mgmd_start($cluster); + + for ( my $idx= 0; $idx < $cluster->{'nodes'}; $idx++ ) { - mtr_error("Error ndbcluster_start"); - return 1; + ndbd_start($cluster, $idx, $extra_args); } return 0; } -sub ndbcluster_stop () { - if ( ! $opt_with_ndbcluster or $glob_use_running_ndbcluster ) +sub rm_ndbcluster_tables ($) { + my $dir= shift; + foreach my $bin ( glob("$dir/cluster/apply_status*"), + glob("$dir/cluster/schema*") ) { - return; + unlink($bin); } - my $ndbcluster_port_base= $opt_ndbcluster_port + 2; - # FIXME, we want to _append_ output to file $file_ndb_testrun_log instead of /dev/null - mtr_run("$glob_mysql_test_dir/ndb/ndbcluster", - ["--port=$opt_ndbcluster_port", - "--data-dir=$opt_vardir", - "--stop"], - "", "/dev/null", "", ""); - - return; } @@ -1323,9 +2397,9 @@ sub run_benchmarks ($) { my $args; - if ( ! $glob_use_embedded_server and ! $opt_local_master ) + if ( ! $glob_use_embedded_server ) { - $master->[0]->{'pid'}= mysqld_start('master',0,[],[]); + mysqld_start($master->[0],[],[]); if ( ! $master->[0]->{'pid'} ) { mtr_error("Can't start the mysqld server"); @@ -1334,7 +2408,7 @@ sub run_benchmarks ($) { mtr_init_args(\$args); - mtr_add_arg($args, "--socket=%s", $master->[0]->{'path_mysock'}); + mtr_add_arg($args, "--socket=%s", $master->[0]->{'path_sock'}); mtr_add_arg($args, "--user=%s", $opt_user); if ( $opt_small_bench ) @@ -1348,10 +2422,8 @@ sub run_benchmarks ($) { mtr_add_arg($args, "--create-options=TYPE=ndb"); } - my $benchdir= "$glob_basedir/sql-bench"; - chdir($benchdir); # FIXME check error - - # FIXME write shorter.... + chdir($glob_mysql_bench_dir) + or mtr_error("Couldn't chdir to '$glob_mysql_bench_dir': $!"); if ( ! $benchmark ) { @@ -1384,29 +2456,26 @@ sub run_benchmarks ($) { # ############################################################################## -# FIXME how to specify several suites to run? Comma separated list? - -sub run_tests () { - run_suite($opt_suite); -} - sub run_suite () { - my $suite= shift; + my ($suite, $tests)= @_; mtr_print_thick_line(); - mtr_report("Finding Tests in the '$suite' suite"); - mtr_timer_start($glob_timers,"suite", 60 * $opt_suite_timeout); - my $tests= collect_test_cases($suite); - mtr_report("Starting Tests in the '$suite' suite"); + mtr_report_tests_not_skipped_though_disabled($tests); + mtr_print_header(); foreach my $tinfo ( @$tests ) { + if (run_testcase_check_skip_test($tinfo)) + { + next; + } + mtr_timer_start($glob_timers,"testcase", 60 * $opt_testcase_timeout); run_testcase($tinfo); mtr_timer_stop($glob_timers,"testcase"); @@ -1414,10 +2483,11 @@ sub run_suite () { mtr_print_line(); - if ( ! $opt_gdb and ! $glob_use_running_server and - ! $opt_ddd and ! $glob_use_embedded_server ) + if ( ! $glob_debugger and + ! $glob_use_running_server and + ! $glob_use_embedded_server ) { - stop_masters_slaves(); + stop_all_servers(); } if ( $opt_gcov ) @@ -1441,26 +2511,115 @@ sub run_suite () { # ############################################################################## +sub initialize_servers () { + + datadir_setup(); + + if ( ! $glob_use_running_server ) + { + kill_running_servers(); + + if ( ! $opt_start_dirty ) + { + cleanup_stale_files(); + mysql_install_db(); + if ( $opt_force ) + { + save_installed_db(); + } + } + check_running_as_root(); + } +} + sub mysql_install_db () { - # FIXME not exactly true I think, needs improvements install_db('master', $master->[0]->{'path_myddir'}); - install_db('master', $master->[1]->{'path_myddir'}); - install_db('slave', $slave->[0]->{'path_myddir'}); - install_db('slave', $slave->[1]->{'path_myddir'}); - install_db('slave', $slave->[2]->{'path_myddir'}); - if ( ndbcluster_install() ) + # FIXME check if testcase really is using second master + copy_install_db('master', $master->[1]->{'path_myddir'}); + + # Install the number of slave databses needed + for (my $idx= 0; $idx < $max_slave_num; $idx++) + { + copy_install_db("slave".($idx+1), $slave->[$idx]->{'path_myddir'}); + } + + if ( ! $opt_skip_im ) + { + im_prepare_env($instance_manager); + } + + my $cluster_started_ok= 1; # Assume it can be started + + if ($opt_skip_ndbcluster || $glob_use_running_ndbcluster || + $clusters->[0]->{executable_setup_failed}) { - # failed to install, disable usage but flag that its no ok - $opt_with_ndbcluster= 0; - $flag_ndb_status_ok= 0; + # Don't install master cluster + } + elsif (ndbcluster_start_install($clusters->[0])) + { + mtr_warning("Failed to start install of $clusters->[0]->{name}"); + $cluster_started_ok= 0; + } + + if ($max_slave_num == 0 || + $opt_skip_ndbcluster_slave || $glob_use_running_ndbcluster_slave || + $clusters->[1]->{executable_setup_failed}) + { + # Don't install slave cluster + } + elsif (ndbcluster_start_install($clusters->[1])) + { + mtr_warning("Failed to start install of $clusters->[1]->{name}"); + $cluster_started_ok= 0; + } + + foreach my $cluster (@{$clusters}) + { + + next if !$cluster->{'pid'}; + + $cluster->{'installed_ok'}= 1; # Assume install suceeds + + if (ndbcluster_wait_started($cluster, "")) + { + # failed to install, disable usage and flag that its no ok + mtr_report("ndbcluster_install of $cluster->{'name'} failed"); + $cluster->{"installed_ok"}= 0; + + $cluster_started_ok= 0; + } + } + + if ( ! $cluster_started_ok ) + { + if ( $opt_force) + { + # Continue without cluster + } + else + { + mtr_error("To continue, re-run with '--force'."); + } } return 0; } +sub copy_install_db ($$) { + my $type= shift; + my $data_dir= shift; + + mtr_report("Installing \u$type Database"); + + # Just copy the installed db from first master + mtr_copy_dir($master->[0]->{'path_myddir'}, $data_dir); + +} + + sub install_db ($$) { my $type= shift; my $data_dir= shift; @@ -1469,7 +2628,7 @@ sub install_db ($$) { my $init_db_sql_tmp= "/tmp/init_db.sql$$"; my $args; - mtr_report("Installing \u$type Databases"); + mtr_report("Installing \u$type Database"); open(IN, $init_db_sql) or mtr_error("Can't open $init_db_sql: $!"); @@ -1505,16 +2664,30 @@ sub install_db ($$) { mtr_add_arg($args, "--datadir=%s", $data_dir); mtr_add_arg($args, "--skip-innodb"); mtr_add_arg($args, "--skip-ndbcluster"); - mtr_add_arg($args, "--skip-bdb"); + mtr_add_arg($args, "--tmpdir=."); + mtr_add_arg($args, "--core-file"); + + if ( $opt_debug ) + { + mtr_add_arg($args, "--debug=d:t:i:A,%s/log/bootstrap_%s.trace", + $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); } + # Log bootstrap command + my $path_bootstrap_log= "$opt_vardir/log/bootstrap.log"; + mtr_tofile($path_bootstrap_log, + "$exe_mysqld " . join(" ", @$args) . "\n"); + if ( mtr_run($exe_mysqld, $args, $init_db_sql_tmp, - $path_manager_log, $path_manager_log, "") != 0 ) + $path_bootstrap_log, $path_bootstrap_log, + "", { append_log_file => 1 }) != 0 ) + { unlink($init_db_sql_tmp); mtr_error("Error executing mysqld --bootstrap\n" . @@ -1524,31 +2697,127 @@ sub install_db ($$) { } -############################################################################## -# -# Run a single test case -# -############################################################################## +sub im_prepare_env($) { + my $instance_manager = shift; -# When we get here, we have already filtered out test cases that doesn't -# apply to the current setup, for example if we use a running server, test -# cases that restart the server are dropped. So this function should mostly -# be about doing things, not a lot of logic. + im_create_passwd_file($instance_manager); + im_prepare_data_dir($instance_manager); +} -# We don't start and kill the servers for each testcase. But some -# testcases needs a restart, because they specify options to start -# mysqld with. After that testcase, we need to restart again, to set -# back the normal options. -sub run_testcase ($) { - my $tinfo= shift; +sub im_create_passwd_file($) { + my $instance_manager = shift; - my $tname= $tinfo->{'name'}; + my $pwd_file_path = $instance_manager->{'password_file'}; - mtr_tonewfile($opt_current_test,"$tname\n"); # Always tell where we are + mtr_report("Creating IM password file ($pwd_file_path)"); + + open(OUT, ">", $pwd_file_path) + or mtr_error("Can't write to $pwd_file_path: $!"); + + print OUT $instance_manager->{'admin_login'}, ":", + $instance_manager->{'admin_sha1'}, "\n"; + + close(OUT); +} + + +sub im_create_defaults_file($) { + my $instance_manager = shift; + + my $defaults_file = $instance_manager->{'defaults_file'}; + + open(OUT, ">", $defaults_file) + or mtr_error("Can't write to $defaults_file: $!"); + + print OUT <<EOF +[mysql] + +[manager] +pid-file = $instance_manager->{path_pid} +angel-pid-file = $instance_manager->{path_angel_pid} +socket = $instance_manager->{path_sock} +port = $instance_manager->{port} +password-file = $instance_manager->{password_file} +default-mysqld-path = $exe_mysqld + +EOF +; + + foreach my $instance (@{$instance_manager->{'instances'}}) + { + my $server_id = $instance->{'server_id'}; + + print OUT <<EOF +[mysqld$server_id] +socket = $instance->{path_sock} +pid-file = $instance->{path_pid} +port = $instance->{port} +datadir = $instance->{path_datadir} +log = $instance->{path_datadir}/mysqld$server_id.log +log-error = $instance->{path_datadir}/mysqld$server_id.err.log +log-slow-queries = $instance->{path_datadir}/mysqld$server_id.slow.log +language = $path_language +character-sets-dir = $path_charsetsdir +basedir = $path_my_basedir +server_id = $server_id +skip-stack-trace +skip-innodb +skip-ndbcluster +EOF +; + if ( $mysql_version_id < 50100 ) + { + print OUT "skip-bdb\n"; + } + print OUT "nonguarded\n" if $instance->{'nonguarded'}; + if ( $mysql_version_id >= 50100 ) + { + print OUT "log-output=FILE\n" if $instance->{'old_log_format'}; + } + print OUT "\n"; + } + + close(OUT); +} + + +sub im_prepare_data_dir($) { + my $instance_manager = shift; + + foreach my $instance (@{$instance_manager->{'instances'}}) + { + copy_install_db( + 'im_mysqld_' . $instance->{'server_id'}, + $instance->{'path_datadir'}); + } +} + + + +# +# Restore snapshot of the installed slave databases +# if the snapshot exists +# +sub restore_slave_databases ($) { + my ($num_slaves)= @_; + + if ( -d $path_snapshot) + { + for (my $idx= 0; $idx < $num_slaves; $idx++) + { + my $data_dir= $slave->[$idx]->{'path_myddir'}; + my $name= basename($data_dir); + rmtree($data_dir); + mtr_copy_dir("$path_snapshot/$name", $data_dir); + } + } +} - # output current test to ndbcluster log file to enable diagnostics - mtr_tofile($file_ndb_testrun_log,"CURRENT TEST $tname\n"); + +sub run_testcase_check_skip_test($) +{ + my ($tinfo)= @_; # ---------------------------------------------------------------------- # If marked to skip, just print out and return. @@ -1561,213 +2830,260 @@ sub run_testcase ($) { { mtr_report_test_name($tinfo); mtr_report_test_skipped($tinfo); - return; + return 1; } - # ---------------------------------------------------------------------- - # If not using a running servers we may need to stop and restart. - # We restart in the case we have initiation scripts, server options - # etc to run. But we also restart again after the test first restart - # and test is run, to get back to normal server settings. - # - # To make the code a bit more clean, we actually only stop servers - # here, and mark this to be done. Then a generic "start" part will - # start up the needed servers again. - # ---------------------------------------------------------------------- - - if ( ! $glob_use_running_server and ! $glob_use_embedded_server ) + if ($tinfo->{'ndb_test'}) { - # We try to find out if we are to restart the server - my $do_restart= 0; # Assumes we don't have to - - if ( $tinfo->{'master_sh'} ) + foreach my $cluster (@{$clusters}) { - $do_restart= 1; # Always restart if script to run - } - elsif ( $tinfo->{'ndb_test'} and $master->[0]->{'ndbcluster'} == 1 ) - { - $do_restart= 1; # Restart with cluster - # print "Restarting because cluster need to be enabled\n"; - } - elsif ($tinfo->{'ndb_test'} == 0 and $master->[0]->{'ndbcluster'} == 0) - { - $do_restart= 1; # Restart without cluster - # print "Restarting because cluster need to be disabled\n"; - } - elsif ( $master->[0]->{'running_master_is_special'} and - $master->[0]->{'running_master_is_special'}->{'timezone'} eq - $tinfo->{'timezone'} and - mtr_same_opts($master->[0]->{'running_master_is_special'}->{'master_opt'}, - $tinfo->{'master_opt'}) ) - { - # If running master was started with special settings, but - # the current test requuires the same ones, we *don't* restart. - $do_restart= 0; - } - elsif ( $tinfo->{'master_restart'} or - $master->[0]->{'running_master_is_special'} ) - { - $do_restart= 1; + last if ($opt_skip_ndbcluster_slave and + $cluster->{'name'} eq 'Slave'); + + # If test needs this cluster, check binaries was found ok + if ( $cluster->{'executable_setup_failed'} ) + { + mtr_report_test_name($tinfo); + $tinfo->{comment}= + "Failed to find cluster binaries"; + mtr_report_test_failed($tinfo); + return 1; + } + + # If test needs this cluster, check it was installed ok + if ( !$cluster->{'installed_ok'} ) + { + mtr_report_test_name($tinfo); + $tinfo->{comment}= + "Cluster $cluster->{'name'} was not installed ok"; + mtr_report_test_failed($tinfo); + return 1; + } + } + } - if ( $do_restart ) + if ( $tinfo->{'component_id'} eq 'im' ) + { + # If test needs im, check binaries was found ok + if ( $instance_manager->{'executable_setup_failed'} ) { - stop_masters(); - delete $master->[0]->{'running_master_is_special'}; # Forget history + mtr_report_test_name($tinfo); + $tinfo->{comment}= + "Failed to find MySQL manager binaries"; + mtr_report_test_failed($tinfo); + return 1; } + } - # ---------------------------------------------------------------------- - # Always terminate all slaves, if any. Else we may have useless - # reconnection attempts and error messages in case the slave and - # master servers restart. - # ---------------------------------------------------------------------- + return 0; +} - stop_slaves(); - } - # ---------------------------------------------------------------------- - # Prepare to start masters. Even if we use embedded, we want to run - # the preparation. - # ---------------------------------------------------------------------- +sub do_before_run_mysqltest($) +{ + my $tinfo= shift; + my $tname= $tinfo->{'name'}; - $ENV{'TZ'}= $tinfo->{'timezone'}; + # Remove old files produced by mysqltest + my $result_dir= "r"; + if ( $opt_suite ne "main" ) + { + $result_dir= "suite/$opt_suite/r"; + } + unlink("$result_dir/$tname.reject"); + unlink("$result_dir/$tname.progress"); + unlink("$result_dir/$tname.log"); + unlink("$result_dir/$tname.warnings"); + + mtr_tonewfile($path_current_test_log,"$tname\n"); # Always tell where we are - mtr_report_test_name($tinfo); + # output current test to ndbcluster log file to enable diagnostics + mtr_tofile($path_ndb_testrun_log,"CURRENT TEST $tname\n"); mtr_tofile($master->[0]->{'path_myerr'},"CURRENT_TEST: $tname\n"); + if ( $master->[1]->{'pid'} ) + { + mtr_tofile($master->[1]->{'path_myerr'},"CURRENT_TEST: $tname\n"); + } -# FIXME test cases that depend on each other, prevent this from -# being at this location. -# do_before_start_master($tname,$tinfo->{'master_sh'}); + if ( $mysql_version_id < 50000 ) + { + # Set envirnoment variable NDB_STATUS_OK to 1 + # if script decided to run mysqltest cluster _is_ installed ok + $ENV{'NDB_STATUS_OK'} = "1"; + } + elsif ( $mysql_version_id < 50100 ) + { + # Set envirnoment variable NDB_STATUS_OK to YES + # if script decided to run mysqltest cluster _is_ installed ok + $ENV{'NDB_STATUS_OK'} = "YES"; + } +} - # ---------------------------------------------------------------------- - # If any mysqld servers running died, we have to know - # ---------------------------------------------------------------------- +sub do_after_run_mysqltest($) +{ + my $tinfo= shift; + my $tname= $tinfo->{'name'}; - mtr_record_dead_children(); + mtr_tofile($path_mysqltest_log,"CURRENT TEST $tname\n"); - # ---------------------------------------------------------------------- - # Start masters - # ---------------------------------------------------------------------- + # Save info from this testcase run to mysqltest.log + mtr_appendfile_to_file($path_timefile, $path_mysqltest_log) + if -f $path_timefile; - if ( ! $glob_use_running_server and ! $glob_use_embedded_server ) - { - # FIXME give the args to the embedded server?! - # FIXME what does $opt_local_master mean?! - # FIXME split up start and check that started so that can do - # starts in parallel, masters and slaves at the same time. + # Remove the file that mysqltest writes info to + unlink($path_timefile); - if ( ! $opt_local_master ) +} + + +sub find_testcase_skipped_reason($) +{ + my ($tinfo)= @_; + + # Open mysqltest.log + my $F= IO::File->new($path_timefile) or + mtr_error("can't open file \"$path_timefile\": $!"); + my $reason; + + while ( my $line= <$F> ) + { + # Look for "reason: <reason fo skiping test>" + if ( $line =~ /reason: (.*)/ ) { - if ( $master->[0]->{'ndbcluster'} && $tinfo->{'ndb_test'}) - { - $master->[0]->{'ndbcluster'}= ndbcluster_start(); - if ( $master->[0]->{'ndbcluster'} ) - { - report_failure_and_restart($tinfo); - return; - } - } - if ( ! $master->[0]->{'pid'} ) - { - # FIXME not correct location for do_before_start_master() - do_before_start_master($tname,$tinfo->{'master_sh'}); + $reason= $1; + } + } - # Save skip_ndbcluster - my $save_opt_skip_ndbcluster= $opt_skip_ndbcluster; - if (!$tinfo->{'ndb_test'}) - { - # Modify skip_ndbcluster so cluster is skipped for this - # and subsequent testcases(until we find one that does not cluster) - $opt_skip_ndbcluster= 1; - } + if ( ! $reason ) + { + mtr_warning("Could not find reason for skipping test in $path_timefile"); + $reason= "Detected by testcase(reason unknown) "; + } + $tinfo->{'comment'}= $reason; +} - $master->[0]->{'pid'}= - mysqld_start('master',0,$tinfo->{'master_opt'},[]); - # Restore skip_ndbcluster - $opt_skip_ndbcluster= $save_opt_skip_ndbcluster; +sub analyze_testcase_failure_sync_with_master($) +{ + my ($tinfo)= @_; - if ( ! $master->[0]->{'pid'} ) - { - report_failure_and_restart($tinfo); - return; - } - } - if ( $opt_with_ndbcluster and ! $master->[1]->{'pid'} ) - { - # Test needs cluster, start an extra mysqld connected to cluster - $master->[1]->{'pid'}= - mysqld_start('master',1,$tinfo->{'master_opt'},[]); - if ( ! $master->[1]->{'pid'} ) - { - report_failure_and_restart($tinfo); - return; - } - } + my $args; + mtr_init_args(\$args); + + mtr_add_arg($args, "--no-defaults"); + mtr_add_arg($args, "--silent"); + mtr_add_arg($args, "-v"); + mtr_add_arg($args, "--skip-safemalloc"); + mtr_add_arg($args, "--tmpdir=%s", $opt_tmpdir); + + mtr_add_arg($args, "--socket=%s", $master->[0]->{'path_sock'}); + mtr_add_arg($args, "--port=%d", $master->[0]->{'port'}); + mtr_add_arg($args, "--database=test"); + mtr_add_arg($args, "--user=%s", $opt_user); + mtr_add_arg($args, "--password="); - if ( $tinfo->{'master_restart'} ) + # Run the test file and append output to log file + mtr_run_test($exe_mysqltest,$args, + "include/analyze_failure_sync_with_master.test", + "$path_timefile", "$path_timefile","", + { append_log_file => 1 }); + +} + +sub analyze_testcase_failure($) +{ + my ($tinfo)= @_; + + # Open mysqltest.log + my $F= IO::File->new($path_timefile) or + mtr_error("can't open file \"$path_timefile\": $!"); + + while ( my $line= <$F> ) + { + # Look for "mysqltest: At line nnn: <error> + if ( $line =~ /mysqltest: At line [0-9]*: (.*)/ ) + { + my $error= $1; + # Look for "could not sync with master" + if ( $error =~ /could not sync with master/ ) { - # Save this test case information, so next can examine it - $master->[0]->{'running_master_is_special'}= $tinfo; + analyze_testcase_failure_sync_with_master($tinfo); } } + } +} + +############################################################################## +# +# Run a single test case +# +############################################################################## - # ---------------------------------------------------------------------- - # Start slaves - if needed - # ---------------------------------------------------------------------- +# When we get here, we have already filtered out test cases that doesn't +# apply to the current setup, for example if we use a running server, test +# cases that restart the server are dropped. So this function should mostly +# be about doing things, not a lot of logic. - if ( $tinfo->{'slave_num'} ) - { - mtr_tofile($slave->[0]->{'path_myerr'},"CURRENT_TEST: $tname\n"); +# We don't start and kill the servers for each testcase. But some +# testcases needs a restart, because they specify options to start +# mysqld with. After that testcase, we need to restart again, to set +# back the normal options. + +sub run_testcase ($) { + my $tinfo= shift; - do_before_start_slave($tname,$tinfo->{'slave_sh'}); + # ------------------------------------------------------- + # Init variables that can change between each test case + # ------------------------------------------------------- - for ( my $idx= 0; $idx < $tinfo->{'slave_num'}; $idx++ ) - { - if ( ! $slave->[$idx]->{'pid'} ) - { - $slave->[$idx]->{'pid'}= - mysqld_start('slave',$idx, - $tinfo->{'slave_opt'}, $tinfo->{'slave_mi'}); - if ( ! $slave->[$idx]->{'pid'} ) - { - report_failure_and_restart($tinfo); - return; - } - } - } + $ENV{'TZ'}= $tinfo->{'timezone'}; + mtr_verbose("Starting server with timezone: $tinfo->{'timezone'}"); + + my $master_restart= run_testcase_need_master_restart($tinfo); + my $slave_restart= run_testcase_need_slave_restart($tinfo); + + if ($master_restart or $slave_restart) + { + # Can't restart a running server that may be in use + if ( $glob_use_running_server ) + { + mtr_report_test_name($tinfo); + $tinfo->{comment}= "Can't restart a running server"; + mtr_report_test_skipped($tinfo); + return; + } + + run_testcase_stop_servers($tinfo, $master_restart, $slave_restart); + } + my $died= mtr_record_dead_children(); + if ($died or $master_restart or $slave_restart) + { + if (run_testcase_start_servers($tinfo)) + { + mtr_report_test_name($tinfo); + report_failure_and_restart($tinfo); + return 1; } } # ---------------------------------------------------------------------- - # If --start-and-exit given, stop here to let user manually run tests + # If --start-and-exit or --start-dirty given, stop here to let user manually + # run tests # ---------------------------------------------------------------------- - - if ( $opt_start_and_exit ) + if ( $opt_start_and_exit or $opt_start_dirty ) { + mtr_timer_stop_all($glob_timers); mtr_report("\nServers started, exiting"); exit(0); } - # ---------------------------------------------------------------------- - # Run the test case - # ---------------------------------------------------------------------- - { - # remove the old reject file - if ( $opt_suite eq "main" ) - { - unlink("r/$tname.reject"); - } - else - { - unlink("suite/$opt_suite/r/$tname.reject"); - } - unlink($path_timefile); + do_before_run_mysqltest($tinfo); my $res= run_mysqltest($tinfo); - + mtr_report_test_name($tinfo); if ( $res == 0 ) { mtr_report_test_passed($tinfo); @@ -1775,6 +3091,9 @@ sub run_testcase ($) { elsif ( $res == 62 ) { # Testcase itself tell us to skip this one + + # Try to get reason from mysqltest.log + find_testcase_skipped_reason($tinfo); mtr_report_test_skipped($tinfo); } elsif ( $res == 63 ) @@ -1782,47 +3101,138 @@ sub run_testcase ($) { $tinfo->{'timeout'}= 1; # Mark as timeout report_failure_and_restart($tinfo); } - else + elsif ( $res == 1 ) { - # Test case failed, if in control mysqltest returns 1 - if ( $res != 1 ) + if ( $opt_force ) { - mtr_tofile($path_timefile, - "mysqltest returned unexpected code $res, " . - "it has probably crashed"); + analyze_testcase_failure($tinfo); } + # Test case failure reported by mysqltest + report_failure_and_restart($tinfo); + } + else + { + # mysqltest failed, probably crashed + $tinfo->{comment}= + "mysqltest returned unexpected code $res, it has probably crashed"; report_failure_and_restart($tinfo); } + + do_after_run_mysqltest($tinfo); + } + + # ---------------------------------------------------------------------- + # Stop Instance Manager if we are processing an IM-test case. + # ---------------------------------------------------------------------- + if ( $tinfo->{'component_id'} eq 'im' and + !mtr_im_stop($instance_manager, $tinfo->{'name'}) ) + { + mtr_error("Failed to stop Instance Manager.") + } +} + + +# +# Save a snapshot of the installed test db(s) +# I.e take a snapshot of the var/ dir +# +sub save_installed_db () { + + mtr_report("Saving snapshot of installed databases"); + rmtree($path_snapshot); + + foreach my $data_dir (@data_dir_lst) + { + my $name= basename($data_dir); + mtr_copy_dir("$data_dir", "$path_snapshot/$name"); + } +} + + +# +# Save any interesting files in the data_dir +# before the data dir is removed. +# +sub save_files_before_restore($$) { + my $test_name= shift; + my $data_dir= shift; + my $save_name= "$opt_vardir/log/$test_name"; + + # Look for core files + foreach my $core_file ( glob("$data_dir/core*") ) + { + my $core_name= basename($core_file); + mtr_report("Saving $core_name"); + mkdir($save_name) if ! -d $save_name; + rename("$core_file", "$save_name/$core_name"); } } +# +# Restore snapshot of the installed test db(s) +# if the snapshot exists +# +sub restore_installed_db ($) { + my $test_name= shift; + + if ( -d $path_snapshot) + { + mtr_report("Restoring snapshot of databases"); + + foreach my $data_dir (@data_dir_lst) + { + my $name= basename($data_dir); + save_files_before_restore($test_name, $data_dir); + rmtree("$data_dir"); + mtr_copy_dir("$path_snapshot/$name", "$data_dir"); + } + + # Remove the ndb_*_fs dirs for all ndbd nodes + # forcing a clean start of ndb + foreach my $cluster (@{$clusters}) + { + foreach my $ndbd (@{$cluster->{'ndbds'}}) + { + rmtree("$ndbd->{'path_fs'}" ); + } + } + } + else + { + # No snapshot existed + mtr_error("No snapshot existed"); + } +} + sub report_failure_and_restart ($) { my $tinfo= shift; mtr_report_test_failed($tinfo); mtr_show_failed_diff($tinfo->{'name'}); print "\n"; - if ( ! $opt_force ) + if ( $opt_force ) { - my $test_mode= join(" ", @::glob_test_mode) || "default"; - print "Aborting: $tinfo->{'name'} failed in $test_mode mode. "; - print "To continue, re-run with '--force'.\n"; - if ( ! $opt_gdb and ! $glob_use_running_server and - ! $opt_ddd and ! $glob_use_embedded_server ) - { - stop_masters_slaves(); - } - mtr_exit(1); + # Stop all servers that are known to be running + stop_all_servers(); + + # Restore the snapshot of the installed test db + restore_installed_db($tinfo->{'name'}); + print "Resuming Tests\n\n"; + return; } - # FIXME always terminate on failure?! - if ( ! $opt_gdb and ! $glob_use_running_server and - ! $opt_ddd and ! $glob_use_embedded_server ) + my $test_mode= join(" ", @::glob_test_mode) || "default"; + print "Aborting: $tinfo->{'name'} failed in $test_mode mode. "; + print "To continue, re-run with '--force'.\n"; + if ( ! $glob_debugger and + ! $glob_use_running_server and + ! $glob_use_embedded_server ) { - stop_masters_slaves(); + stop_all_servers(); } - print "Resuming Tests\n\n"; + mtr_exit(1); + } @@ -1832,25 +3242,18 @@ sub report_failure_and_restart ($) { # ############################################################################## -# The embedded server needs the cleanup so we do some of the start work -# but stop before actually running mysqld or anything. -sub do_before_start_master ($$) { - my $tname= shift; - my $init_script= shift; +sub do_before_start_master ($) { + my ($tinfo)= @_; + + my $tname= $tinfo->{'name'}; + my $init_script= $tinfo->{'master_sh'}; # FIXME what about second master..... - # Remove stale binary logs except for 2 tests which need them FIXME here???? - if ( $tname ne "rpl_crash_binlog_ib_1b" and - $tname ne "rpl_crash_binlog_ib_2b" and - $tname ne "rpl_crash_binlog_ib_3b") + foreach my $bin ( glob("$opt_vardir/log/master*-bin*") ) { - # FIXME we really want separate dir for binlogs - foreach my $bin ( glob("$opt_vardir/log/master*-bin*") ) - { - unlink($bin); - } + unlink($bin); } # FIXME only remove the ones that are tied to this master @@ -1867,33 +3270,26 @@ sub do_before_start_master ($$) { if ( $ret != 0 ) { # FIXME rewrite those scripts to return 0 if successful -# mtr_warning("$init_script exited with code $ret"); + # mtr_warning("$init_script exited with code $ret"); } } - # for gcov FIXME needed? If so we need more absolute paths -# chdir($glob_basedir); } -sub do_before_start_slave ($$) { - my $tname= shift; - my $init_script= shift; - # Remove stale binary logs and old master.info files - # except for too tests which need them - if ( $tname ne "rpl_crash_binlog_ib_1b" and - $tname ne "rpl_crash_binlog_ib_2b" and - $tname ne "rpl_crash_binlog_ib_3b" ) +sub do_before_start_slave ($) { + my ($tinfo)= @_; + + my $tname= $tinfo->{'name'}; + my $init_script= $tinfo->{'master_sh'}; + + foreach my $bin ( glob("$opt_vardir/log/slave*-bin*") ) { - # FIXME we really want separate dir for binlogs - foreach my $bin ( glob("$opt_vardir/log/slave*-bin*") ) - { - unlink($bin); - } - # FIXME really master?! - unlink("$slave->[0]->{'path_myddir'}/master.info"); - unlink("$slave->[0]->{'path_myddir'}/relay-log.info"); + unlink($bin); } + unlink("$slave->[0]->{'path_myddir'}/master.info"); + unlink("$slave->[0]->{'path_myddir'}/relay-log.info"); + # Run slave initialization shell script if one exists if ( $init_script ) { @@ -1901,7 +3297,7 @@ sub do_before_start_slave ($$) { if ( $ret != 0 ) { # FIXME rewrite those scripts to return 0 if successful -# mtr_warning("$init_script exited with code $ret"); + # mtr_warning("$init_script exited with code $ret"); } } @@ -1911,9 +3307,10 @@ sub do_before_start_slave ($$) { } } + sub mysqld_arguments ($$$$$) { my $args= shift; - my $type= shift; # master/slave/bootstrap + my $type= shift; my $idx= shift; my $extra_opt= shift; my $slave_master_info= shift; @@ -1921,7 +3318,7 @@ sub mysqld_arguments ($$$$$) { my $sidx= ""; # Index as string, 0 is empty string if ( $idx > 0 ) { - $sidx= sprintf("%d", $idx); # sprintf not needed in Perl for this + $sidx= "$idx"; } my $prefix= ""; # If mysqltest server arg @@ -1931,21 +3328,25 @@ sub mysqld_arguments ($$$$$) { $prefix= "--server-arg="; } else { # We can't pass embedded server --no-defaults - mtr_add_arg($args, "%s--no-defaults", $prefix); + mtr_add_arg($args, "--no-defaults"); } mtr_add_arg($args, "%s--console", $prefix); mtr_add_arg($args, "%s--basedir=%s", $prefix, $path_my_basedir); mtr_add_arg($args, "%s--character-sets-dir=%s", $prefix, $path_charsetsdir); - mtr_add_arg($args, "%s--core", $prefix); + + if ( $mysql_version_id >= 50000 ) + { + mtr_add_arg($args, "%s--log-bin-trust-function-creators", $prefix); + } + mtr_add_arg($args, "%s--default-character-set=latin1", $prefix); mtr_add_arg($args, "%s--language=%s", $prefix, $path_language); mtr_add_arg($args, "%s--tmpdir=$opt_tmpdir", $prefix); - if ( defined $opt_valgrind_mysqld ) + if ( $opt_valgrind_mysqld ) { mtr_add_arg($args, "%s--skip-safemalloc", $prefix); - mtr_add_arg($args, "%s--skip-bdb", $prefix); } my $pidfile; @@ -1954,29 +3355,44 @@ sub mysqld_arguments ($$$$$) { { my $id= $idx > 0 ? $idx + 101 : 1; - mtr_add_arg($args, "%s--log-bin=%s/log/master-bin%s", $prefix, - $opt_vardir, $sidx); + if (! $opt_skip_master_binlog) + { + mtr_add_arg($args, "%s--log-bin=%s/log/master-bin%s", $prefix, + $opt_vardir, $sidx); + } mtr_add_arg($args, "%s--pid-file=%s", $prefix, - $master->[$idx]->{'path_mypid'}); + $master->[$idx]->{'path_pid'}); mtr_add_arg($args, "%s--port=%d", $prefix, - $master->[$idx]->{'path_myport'}); + $master->[$idx]->{'port'}); mtr_add_arg($args, "%s--server-id=%d", $prefix, $id); mtr_add_arg($args, "%s--socket=%s", $prefix, - $master->[$idx]->{'path_mysock'}); - mtr_add_arg($args, "%s--innodb_data_file_path=ibdata1:50M", $prefix); + $master->[$idx]->{'path_sock'}); + mtr_add_arg($args, "%s--innodb_data_file_path=ibdata1:10M:autoextend", $prefix); mtr_add_arg($args, "%s--local-infile", $prefix); mtr_add_arg($args, "%s--datadir=%s", $prefix, $master->[$idx]->{'path_myddir'}); - if ( $idx > 0 ) + if ( $idx > 0 or !$use_innodb) { mtr_add_arg($args, "%s--skip-innodb", $prefix); } - if ( $opt_skip_ndbcluster ) + my $cluster= $clusters->[$master->[$idx]->{'cluster'}]; + if ( $opt_skip_ndbcluster || + !$cluster->{'pid'}) { mtr_add_arg($args, "%s--skip-ndbcluster", $prefix); } + else + { + mtr_add_arg($args, "%s--ndbcluster", $prefix); + mtr_add_arg($args, "%s--ndb-connectstring=%s", $prefix, + $cluster->{'connect_string'}); + if ( $mysql_version_id >= 50100 ) + { + mtr_add_arg($args, "%s--ndb-extra-logging", $prefix); + } + } } if ( $type eq 'slave' ) @@ -1986,25 +3402,26 @@ sub mysqld_arguments ($$$$$) { mtr_add_arg($args, "%s--datadir=%s", $prefix, $slave->[$idx]->{'path_myddir'}); - # FIXME slave get this option twice?! - mtr_add_arg($args, "%s--exit-info=256", $prefix); mtr_add_arg($args, "%s--init-rpl-role=slave", $prefix); - mtr_add_arg($args, "%s--log-bin=%s/log/slave%s-bin", $prefix, - $opt_vardir, $sidx); # FIXME use own dir for binlogs - mtr_add_arg($args, "%s--log-slave-updates", $prefix); - # FIXME option duplicated for slave + if (! $opt_skip_slave_binlog) + { + mtr_add_arg($args, "%s--log-bin=%s/log/slave%s-bin", $prefix, + $opt_vardir, $sidx); # FIXME use own dir for binlogs + mtr_add_arg($args, "%s--log-slave-updates", $prefix); + } + mtr_add_arg($args, "%s--log=%s", $prefix, $slave->[$idx]->{'path_mylog'}); mtr_add_arg($args, "%s--master-retry-count=10", $prefix); mtr_add_arg($args, "%s--pid-file=%s", $prefix, - $slave->[$idx]->{'path_mypid'}); + $slave->[$idx]->{'path_pid'}); mtr_add_arg($args, "%s--port=%d", $prefix, - $slave->[$idx]->{'path_myport'}); + $slave->[$idx]->{'port'}); mtr_add_arg($args, "%s--relay-log=%s/log/slave%s-relay-bin", $prefix, $opt_vardir, $sidx); mtr_add_arg($args, "%s--report-host=127.0.0.1", $prefix); mtr_add_arg($args, "%s--report-port=%d", $prefix, - $slave->[$idx]->{'path_myport'}); + $slave->[$idx]->{'port'}); mtr_add_arg($args, "%s--report-user=root", $prefix); mtr_add_arg($args, "%s--skip-innodb", $prefix); mtr_add_arg($args, "%s--skip-ndbcluster", $prefix); @@ -2013,10 +3430,11 @@ sub mysqld_arguments ($$$$$) { # Directory where slaves find the dumps generated by "load data" # on the server. The path need to have constant length otherwise # test results will vary, thus a relative path is used. + my $slave_load_path= "../tmp"; mtr_add_arg($args, "%s--slave-load-tmpdir=%s", $prefix, - $path_slave_load_tmpdir); + $slave_load_path); mtr_add_arg($args, "%s--socket=%s", $prefix, - $slave->[$idx]->{'path_mysock'}); + $slave->[$idx]->{'path_sock'}); mtr_add_arg($args, "%s--set-variable=slave_net_timeout=10", $prefix); if ( @$slave_master_info ) @@ -2033,10 +3451,27 @@ sub mysqld_arguments ($$$$$) { mtr_add_arg($args, "%s--master-host=127.0.0.1", $prefix); mtr_add_arg($args, "%s--master-password=", $prefix); mtr_add_arg($args, "%s--master-port=%d", $prefix, - $master->[0]->{'path_myport'}); # First master + $master->[0]->{'port'}); # First master mtr_add_arg($args, "%s--server-id=%d", $prefix, $slave_server_id); mtr_add_arg($args, "%s--rpl-recovery-rank=%d", $prefix, $slave_rpl_rank); } + + if ( $opt_skip_ndbcluster_slave || + $slave->[$idx]->{'cluster'} == -1 || + !$clusters->[$slave->[$idx]->{'cluster'}]->{'pid'} ) + { + mtr_add_arg($args, "%s--skip-ndbcluster", $prefix); + } + else + { + mtr_add_arg($args, "%s--ndbcluster", $prefix); + mtr_add_arg($args, "%s--ndb-connectstring=%s", $prefix, + $clusters->[$slave->[$idx]->{'cluster'}]->{'connect_string'}); + if ( $mysql_version_id >= 50100 ) + { + mtr_add_arg($args, "%s--ndb-extra-logging", $prefix); + } + } } # end slave if ( $opt_debug ) @@ -2044,28 +3479,21 @@ sub mysqld_arguments ($$$$$) { if ( $type eq 'master' ) { mtr_add_arg($args, "%s--debug=d:t:i:A,%s/log/master%s.trace", - $prefix, $opt_vardir, $sidx); + $prefix, $path_vardir_trace, $sidx); } if ( $type eq 'slave' ) { mtr_add_arg($args, "%s--debug=d:t:i:A,%s/log/slave%s.trace", - $prefix, $opt_vardir, $sidx); + $prefix, $path_vardir_trace, $sidx); } } - if ( $opt_with_ndbcluster && !$opt_skip_ndbcluster && $type eq 'master') - { - mtr_add_arg($args, "%s--ndbcluster", $prefix); - mtr_add_arg($args, "%s--ndb-connectstring=%s", $prefix, - $opt_ndbconnectstring); - } - # FIXME always set nowdays??? SMALL_SERVER mtr_add_arg($args, "%s--key_buffer_size=1M", $prefix); mtr_add_arg($args, "%s--sort_buffer=256K", $prefix); mtr_add_arg($args, "%s--max_heap_table_size=1M", $prefix); - if ( $opt_with_openssl ) + if ( $opt_ssl_supported ) { mtr_add_arg($args, "%s--ssl-ca=%s/std_data/cacert.pem", $prefix, $glob_mysql_test_dir); @@ -2080,7 +3508,8 @@ sub mysqld_arguments ($$$$$) { mtr_add_arg($args, "%s--log-warnings", $prefix); } - if ( $opt_gdb or $opt_client_gdb or $opt_manual_gdb or $opt_ddd) + # Indicate to "mysqld" it will be debugged in debugger + if ( $glob_debugger ) { mtr_add_arg($args, "%s--gdb", $prefix); } @@ -2093,27 +3522,22 @@ sub mysqld_arguments ($$$$$) { mtr_add_arg($args, "%s--user=root", $prefix); } - if ( $type eq 'master' ) + my $found_skip_core= 0; + foreach my $arg ( @opt_extra_mysqld_opt, @$extra_opt ) { - - if ( ! $opt_old_master ) + # Allow --skip-core-file to be set in master.opt file + if ($arg eq "--skip-core-file") { - mtr_add_arg($args, "%s--rpl-recovery-rank=1", $prefix); - mtr_add_arg($args, "%s--init-rpl-role=master", $prefix); + $found_skip_core= 1; } - - # FIXME strange,..... - # FIXME MYSQL_MYPORT is not set anythere?! - if ( $opt_local_master ) + else { - mtr_add_arg($args, "%s--host=127.0.0.1", $prefix); - mtr_add_arg($args, "%s--port=%s", $prefix, $ENV{'MYSQL_MYPORT'}); + mtr_add_arg($args, "%s%s", $prefix, $arg); } } - - foreach my $arg ( @opt_extra_mysqld_opt, @$extra_opt ) + if ( !$found_skip_core ) { - mtr_add_arg($args, "%s%s", $prefix, $arg); + mtr_add_arg($args, "%s%s", $prefix, "--core-file"); } if ( $opt_bench ) @@ -2123,7 +3547,6 @@ sub mysqld_arguments ($$$$$) { } elsif ( $type eq 'master' ) { - mtr_add_arg($args, "%s--exit-info=256", $prefix); mtr_add_arg($args, "%s--open-files-limit=1024", $prefix); mtr_add_arg($args, "%s--log=%s", $prefix, $master->[0]->{'path_mylog'}); } @@ -2131,14 +3554,6 @@ sub mysqld_arguments ($$$$$) { return $args; } -# FIXME -# if ( $type eq 'master' and $glob_use_embedded_server ) -# { -# # Add a -A to each argument to pass it to embedded server -# my @mysqltest_opt= map {("-A",$_)} @args; -# $opt_extra_mysqltest_opt= \@mysqltest_opt; -# return; -# } ############################################################################## # @@ -2146,15 +3561,21 @@ sub mysqld_arguments ($$$$$) { # ############################################################################## -sub mysqld_start ($$$$) { - my $type= shift; # master/slave/bootstrap - my $idx= shift; +sub mysqld_start ($$$) { + my $mysqld= shift; my $extra_opt= shift; my $slave_master_info= shift; my $args; # Arg vector my $exe; - my $pid; + my $pid= -1; + my $wait_for_pid_file= 1; + + my $type= $mysqld->{'type'}; + my $idx= $mysqld->{'idx'}; + + mtr_error("Internal error: mysqld should never be started for embedded") + if $glob_use_embedded_server; if ( $type eq 'master' ) { @@ -2166,214 +3587,660 @@ sub mysqld_start ($$$$) { } else { - $exe= $exe_mysqld; + mtr_error("Unknown 'type' \"$type\" passed to mysqld_start"); } mtr_init_args(\$args); - if ( defined $opt_valgrind_mysqld ) + if ( $opt_valgrind_mysqld ) { valgrind_arguments($args, \$exe); } mysqld_arguments($args,$type,$idx,$extra_opt,$slave_master_info); - if ( $type eq 'master' ) + if ( $opt_gdb || $opt_manual_gdb) + { + gdb_arguments(\$args, \$exe, "$type"."_$idx"); + } + elsif ( $opt_ddd || $opt_manual_ddd ) + { + ddd_arguments(\$args, \$exe, "$type"."_$idx"); + } + elsif ( $opt_debugger ) + { + debugger_arguments(\$args, \$exe, "$type"."_$idx"); + } + elsif ( $opt_manual_debug ) { - if ( $pid= mtr_spawn($exe, $args, "", - $master->[$idx]->{'path_myerr'}, - $master->[$idx]->{'path_myerr'}, - "", - { append_log_file => 1 }) ) + print "\nStart $type in your debugger\n" . + "dir: $glob_mysql_test_dir\n" . + "exe: $exe\n" . + "args: " . join(" ", @$args) . "\n\n" . + "Waiting ....\n"; + + # Indicate the exe should not be started + $exe= undef; + } + else + { + # Default to not wait until pid file has been created + $wait_for_pid_file= 0; + } + + if ( defined $exe ) + { + $pid= mtr_spawn($exe, $args, "", + $mysqld->{'path_myerr'}, + $mysqld->{'path_myerr'}, + "", + { append_log_file => 1 }); + } + + + if ( $wait_for_pid_file && !sleep_until_file_created($mysqld->{'path_pid'}, + $mysqld->{'start_timeout'}, + $pid)) + { + + mtr_error("Failed to start mysqld $mysqld->{'type'}"); + } + + + # Remember pid of the started process + $mysqld->{'pid'}= $pid; + + # Remember options used when starting + $mysqld->{'start_opts'}= $extra_opt; + $mysqld->{'start_slave_master_info'}= $slave_master_info; + + mtr_verbose("mysqld pid: $pid"); + return $pid; +} + + +sub stop_all_servers () { + + print "Stopping All Servers\n"; + + if ( ! $opt_skip_im ) + { + print "Shutting-down Instance Manager\n"; + unless (mtr_im_stop($instance_manager, "stop_all_servers")) { - return sleep_until_file_created($master->[$idx]->{'path_mypid'}, - $master->[$idx]->{'start_timeout'}, $pid); + mtr_error("Failed to stop Instance Manager.") } } - if ( $type eq 'slave' ) + my %admin_pids; # hash of admin processes that requests shutdown + my @kill_pids; # list of processes to shutdown/kill + my $pid; + + # Start shutdown of all started masters + foreach my $mysqld (@{$master}, @{$slave}) { - if ( $pid= mtr_spawn($exe, $args, "", - $slave->[$idx]->{'path_myerr'}, - $slave->[$idx]->{'path_myerr'}, - "", - { append_log_file => 1 }) ) + if ( $mysqld->{'pid'} ) { - return sleep_until_file_created($slave->[$idx]->{'path_mypid'}, - $master->[$idx]->{'start_timeout'}, $pid); + $pid= mtr_mysqladmin_start($mysqld, "shutdown", 70); + $admin_pids{$pid}= 1; + + push(@kill_pids,{ + pid => $mysqld->{'pid'}, + pidfile => $mysqld->{'path_pid'}, + sockfile => $mysqld->{'path_sock'}, + port => $mysqld->{'port'}, + }); + + $mysqld->{'pid'}= 0; # Assume we are done with it } } - return 0; -} + # Start shutdown of clusters + foreach my $cluster (@{$clusters}) + { + if ( $cluster->{'pid'} ) + { + $pid= mtr_ndbmgm_start($cluster, "shutdown"); + $admin_pids{$pid}= 1; -sub stop_masters_slaves () { + push(@kill_pids,{ + pid => $cluster->{'pid'}, + pidfile => $cluster->{'path_pid'} + }); - print "Ending Tests\n"; - print "Shutting-down MySQL daemon\n\n"; - stop_masters(); - print "Master(s) shutdown finished\n"; - stop_slaves(); - print "Slave(s) shutdown finished\n"; -} + $cluster->{'pid'}= 0; # Assume we are done with it + + foreach my $ndbd (@{$cluster->{'ndbds'}}) + { + if ( $ndbd->{'pid'} ) + { + push(@kill_pids,{ + pid => $ndbd->{'pid'}, + pidfile => $ndbd->{'path_pid'}, + }); + $ndbd->{'pid'}= 0; + } + } + } + } -sub stop_masters () { + # Wait blocking until all shutdown processes has completed + mtr_wait_blocking(\%admin_pids); - my @args; + # Make sure that process has shutdown else try to kill them + mtr_check_stop_servers(\@kill_pids); - for ( my $idx; $idx < 2; $idx++ ) + foreach my $mysqld (@{$master}, @{$slave}) { - # FIXME if we hit ^C before fully started, this test will prevent - # the mysqld process from being killed - if ( $master->[$idx]->{'pid'} ) - { - push(@args,{ - pid => $master->[$idx]->{'pid'}, - pidfile => $master->[$idx]->{'path_mypid'}, - sockfile => $master->[$idx]->{'path_mysock'}, - port => $master->[$idx]->{'path_myport'}, - }); - $master->[$idx]->{'pid'}= 0; # Assume we are done with it - } + rm_ndbcluster_tables($mysqld->{'path_myddir'}); } +} + + +sub run_testcase_need_master_restart($) +{ + my ($tinfo)= @_; + + # We try to find out if we are to restart the master(s) + my $do_restart= 0; # Assumes we don't have to - if ( ! $master->[0]->{'ndbcluster'} ) + if ( $glob_use_embedded_server ) + { + mtr_verbose("Never start or restart for embedded server"); + return $do_restart; + } + elsif ( $tinfo->{'master_sh'} ) + { + $do_restart= 1; # Always restart if script to run + mtr_verbose("Restart master: Always restart if script to run"); + } + if ( $tinfo->{'force_restart'} ) { - ndbcluster_stop(); - $master->[0]->{'ndbcluster'}= 1; + $do_restart= 1; # Always restart if --force-restart in -opt file + mtr_verbose("Restart master: Restart forced with --force-restart"); + } + elsif ( ! $opt_skip_ndbcluster and + !$tinfo->{'ndb_test'} and + $clusters->[0]->{'pid'} != 0 ) + { + $do_restart= 1; # Restart without cluster + mtr_verbose("Restart master: Test does not need cluster"); + } + elsif ( ! $opt_skip_ndbcluster and + $tinfo->{'ndb_test'} and + $clusters->[0]->{'pid'} == 0 ) + { + $do_restart= 1; # Restart with cluster + mtr_verbose("Restart master: Test need cluster"); + } + elsif( $tinfo->{'component_id'} eq 'im' ) + { + $do_restart= 1; + mtr_verbose("Restart master: Always restart for im tests"); + } + elsif ( $master->[0]->{'running_master_options'} and + $master->[0]->{'running_master_options'}->{'timezone'} ne + $tinfo->{'timezone'}) + { + $do_restart= 1; + mtr_verbose("Restart master: Different timezone"); + } + # Check that running master was started with same options + # as the current test requires + elsif (! mtr_same_opts($master->[0]->{'start_opts'}, + $tinfo->{'master_opt'}) ) + { + $do_restart= 1; + mtr_verbose("Restart master: running with different options '" . + join(" ", @{$tinfo->{'master_opt'}}) . "' != '" . + join(" ", @{$master->[0]->{'start_opts'}}) . "'" ); + } + elsif( ! $master->[0]->{'pid'} ) + { + $do_restart= 1; + mtr_verbose("Restart master: master is not started"); } - mtr_stop_mysqld_servers(\@args); + return $do_restart; } -sub stop_slaves () { - my $force= shift; +sub run_testcase_need_slave_restart($) +{ + my ($tinfo)= @_; - my @args; + # We try to find out if we are to restart the slaves + my $do_slave_restart= 0; # Assumes we don't have to - for ( my $idx; $idx < 3; $idx++ ) + if ( $glob_use_embedded_server ) + { + mtr_verbose("Never start or restart for embedded server"); + return $do_slave_restart; + } + elsif ( $max_slave_num == 0) + { + mtr_verbose("Skip slave restart: No testcase use slaves"); + } + else { - if ( $slave->[$idx]->{'pid'} ) + + # Check if any slave is currently started + my $any_slave_started= 0; + foreach my $mysqld (@{$slave}) + { + if ( $mysqld->{'pid'} ) + { + $any_slave_started= 1; + last; + } + } + + if ($any_slave_started) + { + mtr_verbose("Restart slave: Slave is started, always restart"); + $do_slave_restart= 1; + } + elsif ( $tinfo->{'slave_num'} ) { - push(@args,{ - pid => $slave->[$idx]->{'pid'}, - pidfile => $slave->[$idx]->{'path_mypid'}, - sockfile => $slave->[$idx]->{'path_mysock'}, - port => $slave->[$idx]->{'path_myport'}, - }); - $slave->[$idx]->{'pid'}= 0; # Assume we are done with it + mtr_verbose("Restart slave: Test need slave"); + $do_slave_restart= 1; } } - mtr_stop_mysqld_servers(\@args); + return $do_slave_restart; + } +# ---------------------------------------------------------------------- +# If not using a running servers we may need to stop and restart. +# We restart in the case we have initiation scripts, server options +# etc to run. But we also restart again after the test first restart +# and test is run, to get back to normal server settings. +# +# To make the code a bit more clean, we actually only stop servers +# here, and mark this to be done. Then a generic "start" part will +# start up the needed servers again. +# ---------------------------------------------------------------------- -sub run_mysqltest ($) { - my $tinfo= shift; +sub run_testcase_stop_servers($$$) { + my ($tinfo, $do_restart, $do_slave_restart)= @_; + my $pid; + my %admin_pids; # hash of admin processes that requests shutdown + my @kill_pids; # list of processes to shutdown/kill - my $cmdline_mysqldump= "$exe_mysqldump --no-defaults -uroot " . - "--port=$master->[0]->{'path_myport'} " . - "--socket=$master->[0]->{'path_mysock'} --password="; - if ( $opt_debug ) + # Remember if we restarted for this test case (count restarts) + $tinfo->{'restarted'}= $do_restart; + + if ( $do_restart ) { - $cmdline_mysqldump .= - " --debug=d:t:A,$opt_vardir/log/mysqldump.trace"; + delete $master->[0]->{'running_master_options'}; # Forget history + + # Start shutdown of all started masters + foreach my $mysqld (@{$master}) + { + if ( $mysqld->{'pid'} ) + { + $pid= mtr_mysqladmin_start($mysqld, "shutdown", 70); + + $admin_pids{$pid}= 1; + + push(@kill_pids,{ + pid => $mysqld->{'pid'}, + pidfile => $mysqld->{'path_pid'}, + sockfile => $mysqld->{'path_sock'}, + port => $mysqld->{'port'}, + }); + + $mysqld->{'pid'}= 0; # Assume we are done with it + } + } + + # Start shutdown of master cluster + my $cluster= $clusters->[0]; + if ( $cluster->{'pid'} ) + { + $pid= mtr_ndbmgm_start($cluster, "shutdown"); + $admin_pids{$pid}= 1; + + push(@kill_pids,{ + pid => $cluster->{'pid'}, + pidfile => $cluster->{'path_pid'} + }); + + $cluster->{'pid'}= 0; # Assume we are done with it + + foreach my $ndbd (@{$cluster->{'ndbds'}}) + { + push(@kill_pids,{ + pid => $ndbd->{'pid'}, + pidfile => $ndbd->{'path_pid'}, + }); + $ndbd->{'pid'}= 0; # Assume we are done with it + } + } } - my $cmdline_mysqlimport= "$exe_mysqlimport -uroot " . - "--port=$master->[0]->{'path_myport'} " . - "--socket=$master->[0]->{'path_mysock'} --password="; - if ( $opt_debug ) + + if ( $do_restart || $do_slave_restart ) { - $cmdline_mysqlimport .= - " --debug=d:t:A,$opt_vardir/log/mysqlimport.trace"; + + delete $slave->[0]->{'running_slave_options'}; # Forget history + + # Start shutdown of all started slaves + foreach my $mysqld (@{$slave}) + { + if ( $mysqld->{'pid'} ) + { + $pid= mtr_mysqladmin_start($mysqld, "shutdown", 70); + + $admin_pids{$pid}= 1; + + push(@kill_pids,{ + pid => $mysqld->{'pid'}, + pidfile => $mysqld->{'path_pid'}, + sockfile => $mysqld->{'path_sock'}, + port => $mysqld->{'port'}, + }); + + + $mysqld->{'pid'}= 0; # Assume we are done with it + } + } + + # Start shutdown of slave cluster + my $cluster= $clusters->[1]; + if ( $cluster->{'pid'} ) + { + $pid= mtr_ndbmgm_start($cluster, "shutdown"); + + $admin_pids{$pid}= 1; + + push(@kill_pids,{ + pid => $cluster->{'pid'}, + pidfile => $cluster->{'path_pid'} + }); + + $cluster->{'pid'}= 0; # Assume we are done with it + + foreach my $ndbd (@{$cluster->{'ndbds'}} ) + { + push(@kill_pids,{ + pid => $ndbd->{'pid'}, + pidfile => $ndbd->{'path_pid'}, + }); + $ndbd->{'pid'}= 0; # Assume we are done with it + } + } } - my $cmdline_mysqlshow= "$exe_mysqlshow -uroot " . - "--port=$master->[0]->{'path_myport'} " . - "--socket=$master->[0]->{'path_mysock'} --password="; - if ( $opt_debug ) + # ---------------------------------------------------------------------- + # Shutdown has now been started and lists for the shutdown processes + # and the processes to be killed has been created + # ---------------------------------------------------------------------- + + # Wait blocking until all shutdown processes has completed + mtr_wait_blocking(\%admin_pids); + + + # Make sure that process has shutdown else try to kill them + mtr_check_stop_servers(\@kill_pids); + + foreach my $mysqld (@{$master}, @{$slave}) { - $cmdline_mysqlshow .= - " --debug=d:t:A,$opt_vardir/log/mysqlshow.trace"; + if ( ! $mysqld->{'pid'} ) + { + # Remove ndbcluster tables if server is stopped + rm_ndbcluster_tables($mysqld->{'path_myddir'}); + } } +} - my $cmdline_mysqlbinlog= - "$exe_mysqlbinlog --no-defaults --local-load=$opt_tmpdir"; - if ( $opt_debug ) +# +# run_testcase_start_servers +# +# Start the servers needed by this test case +# +# RETURN +# 0 OK +# 1 Start failed +# + +sub run_testcase_start_servers($) { + my $tinfo= shift; + my $tname= $tinfo->{'name'}; + + if ( $tinfo->{'component_id'} eq 'mysqld' ) { - $cmdline_mysqlbinlog .= - " --debug=d:t:A,$opt_vardir/log/mysqlbinlog.trace"; + if ( ! $opt_skip_ndbcluster and + !$clusters->[0]->{'pid'} and + $tinfo->{'ndb_test'} ) + { + # Test need cluster, cluster is not started, start it + ndbcluster_start($clusters->[0], ""); + } + + if ( !$master->[0]->{'pid'} ) + { + # Master mysqld is not started + do_before_start_master($tinfo); + + mysqld_start($master->[0],$tinfo->{'master_opt'},[]); + + } + + if ( $clusters->[0]->{'pid'} and ! $master->[1]->{'pid'} ) + { + # Test needs cluster, start an extra mysqld connected to cluster + + if ( $mysql_version_id >= 50100 ) + { + # 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'})) + { + + $tinfo->{'comment'}= "Failed to create 'cluster/apply_status' table"; + return 1; + } + } + mtr_tofile($master->[1]->{'path_myerr'},"CURRENT_TEST: $tname\n"); + + mysqld_start($master->[1],$tinfo->{'master_opt'},[]); + } + + # Save this test case information, so next can examine it + $master->[0]->{'running_master_options'}= $tinfo; } + elsif ( ! $opt_skip_im and $tinfo->{'component_id'} eq 'im' ) + { + # We have to create defaults file every time, in order to ensure that it + # will be the same for each test. The problem is that test can change the + # file (by SET/UNSET commands), so w/o recreating the file, execution of + # one test can affect the other. - my $cmdline_mysql= - "$exe_mysql --host=localhost --user=root --password= " . - "--port=$master->[0]->{'path_myport'} " . - "--socket=$master->[0]->{'path_mysock'}"; + im_create_defaults_file($instance_manager); - my $cmdline_mysql_client_test= - "$exe_mysql_client_test --no-defaults --testcase --user=root --silent " . - "--port=$master->[0]->{'path_myport'} " . - "--socket=$master->[0]->{'path_mysock'}"; + if ( ! mtr_im_start($instance_manager, $tinfo->{im_opts}) ) + { + $tinfo->{'comment'}= "Failed to start Instance Manager. "; + return 1; + } + } - if ( $glob_use_embedded_server ) + # ---------------------------------------------------------------------- + # Start slaves - if needed + # ---------------------------------------------------------------------- + if ( $tinfo->{'slave_num'} ) + { + mtr_tofile($slave->[0]->{'path_myerr'},"CURRENT_TEST: $tname\n"); + + restore_slave_databases($tinfo->{'slave_num'}); + + do_before_start_slave($tinfo); + + if ( ! $opt_skip_ndbcluster_slave and + !$clusters->[1]->{'pid'} and + $tinfo->{'ndb_test'} ) + { + # Test need slave cluster, cluster is not started, start it + ndbcluster_start($clusters->[1], ""); + } + + for ( my $idx= 0; $idx < $tinfo->{'slave_num'}; $idx++ ) + { + if ( ! $slave->[$idx]->{'pid'} ) + { + mysqld_start($slave->[$idx],$tinfo->{'slave_opt'}, + $tinfo->{'slave_mi'}); + + } + } + + # Save this test case information, so next can examine it + $slave->[0]->{'running_slave_options'}= $tinfo; + } + + # Wait for clusters to start + foreach my $cluster (@{$clusters}) { - $cmdline_mysql_client_test.= - " -A --language=$path_language" . - " -A --datadir=$slave->[0]->{'path_myddir'}" . - " -A --character-sets-dir=$path_charsetsdir"; + + next if !$cluster->{'pid'}; + + if (ndbcluster_wait_started($cluster, "")) + { + # failed to start + $tinfo->{'comment'}= "Start of $cluster->{'name'} cluster failed"; + return 1; + } } - my $cmdline_mysql_fix_system_tables= - "$exe_mysql_fix_system_tables --no-defaults --host=localhost --user=root --password= " . - "--basedir=$glob_basedir --bindir=$path_client_bindir --verbose " . - "--port=$master->[0]->{'path_myport'} " . - "--socket=$master->[0]->{'path_mysock'}"; + # Wait for mysqld's to start + foreach my $mysqld (@{$master},@{$slave}) + { + next if !$mysqld->{'pid'}; + if (mysqld_wait_started($mysqld)) + { + # failed to start + $tinfo->{'comment'}= + "Failed to start $mysqld->{'type'} mysqld $mysqld->{'idx'}"; + return 1; + } + } + return 0; +} - # FIXME really needing a PATH??? - # $ENV{'PATH'}= "/bin:/usr/bin:/usr/local/bin:/usr/bsd:/usr/X11R6/bin:/usr/openwin/bin:/usr/bin/X11:$ENV{'PATH'}"; +# +# Run include/check-testcase.test +# Before a testcase, run in record mode, save result file to var +# After testcase, run and compare with the recorded file, they should be equal! +# +# RETURN VALUE +# 0 OK +# 1 Check failed +# +sub run_check_testcase ($$) { - $ENV{'MYSQL'}= $cmdline_mysql; - $ENV{'MYSQL_DUMP'}= $cmdline_mysqldump; - $ENV{'MYSQL_IMPORT'}= $cmdline_mysqlimport; - $ENV{'MYSQL_SHOW'}= $cmdline_mysqlshow; - $ENV{'MYSQL_BINLOG'}= $cmdline_mysqlbinlog; - $ENV{'MYSQL_FIX_SYSTEM_TABLES'}= $cmdline_mysql_fix_system_tables; - $ENV{'MYSQL_CLIENT_TEST'}= $cmdline_mysql_client_test; - $ENV{'CHARSETSDIR'}= $path_charsetsdir; - $ENV{'MYSQL_MY_PRINT_DEFAULTS'}= $exe_my_print_defaults; + my $mode= shift; + my $mysqld= shift; - $ENV{'NDB_STATUS_OK'}= $flag_ndb_status_ok; - $ENV{'NDB_MGM'}= $exe_ndb_mgm; - $ENV{'NDB_BACKUP_DIR'}= $path_ndb_backup_dir; - $ENV{'NDB_TOOLS_DIR'}= $path_ndb_tools_dir; - $ENV{'NDB_TOOLS_OUTPUT'}= $file_ndb_testrun_log; - $ENV{'NDB_CONNECTSTRING'}= $opt_ndbconnectstring; + my $name= "check-" . $mysqld->{'type'} . $mysqld->{'idx'}; - my $exe= $exe_mysqltest; my $args; - mtr_init_args(\$args); mtr_add_arg($args, "--no-defaults"); - mtr_add_arg($args, "--socket=%s", $master->[0]->{'path_mysock'}); + mtr_add_arg($args, "--silent"); + mtr_add_arg($args, "-v"); + mtr_add_arg($args, "--skip-safemalloc"); + mtr_add_arg($args, "--tmpdir=%s", $opt_tmpdir); + + mtr_add_arg($args, "--socket=%s", $mysqld->{'path_sock'}); + mtr_add_arg($args, "--port=%d", $mysqld->{'port'}); mtr_add_arg($args, "--database=test"); mtr_add_arg($args, "--user=%s", $opt_user); mtr_add_arg($args, "--password="); + + mtr_add_arg($args, "-R"); + mtr_add_arg($args, "$opt_vardir/tmp/$name.result"); + + if ( $mode eq "before" ) + { + mtr_add_arg($args, "--record"); + } + + my $res = mtr_run_test($exe_mysqltest,$args, + "include/check-testcase.test", "", "", ""); + + if ( $res == 1 and $mode eq "after") + { + mtr_run("diff",["-u", + "$opt_vardir/tmp/$name.result", + "$opt_vardir/tmp/$name.reject"], + "", "", "", ""); + } + elsif ( $res ) + { + mtr_error("Could not execute 'check-testcase' $mode testcase"); + } + return $res; +} + + + +sub run_mysqltest ($) { + my ($tinfo)= @_; + my $exe= $exe_mysqltest; + my $args; + + mtr_init_args(\$args); + + mtr_add_arg($args, "--no-defaults"); mtr_add_arg($args, "--silent"); mtr_add_arg($args, "-v"); mtr_add_arg($args, "--skip-safemalloc"); mtr_add_arg($args, "--tmpdir=%s", $opt_tmpdir); - mtr_add_arg($args, "--port=%d", $master->[0]->{'path_myport'}); + + if ($tinfo->{'component_id'} eq 'im') + { + mtr_add_arg($args, "--socket=%s", $instance_manager->{'path_sock'}); + mtr_add_arg($args, "--port=%d", $instance_manager->{'port'}); + mtr_add_arg($args, "--user=%s", $instance_manager->{'admin_login'}); + mtr_add_arg($args, "--password=%s", $instance_manager->{'admin_password'}); + } + else # component_id == mysqld + { + mtr_add_arg($args, "--socket=%s", $master->[0]->{'path_sock'}); + mtr_add_arg($args, "--port=%d", $master->[0]->{'port'}); + mtr_add_arg($args, "--database=test"); + mtr_add_arg($args, "--user=%s", $opt_user); + mtr_add_arg($args, "--password="); + } if ( $opt_ps_protocol ) { mtr_add_arg($args, "--ps-protocol"); } + if ( $opt_sp_protocol ) + { + mtr_add_arg($args, "--sp-protocol"); + } + + if ( $opt_view_protocol ) + { + mtr_add_arg($args, "--view-protocol"); + } + + if ( $opt_cursor_protocol ) + { + mtr_add_arg($args, "--cursor-protocol"); + } + if ( $opt_strace_client ) { $exe= "strace"; # FIXME there are ktrace, .... @@ -2404,17 +4271,30 @@ sub run_mysqltest ($) { if ( $opt_debug ) { - mtr_add_arg($args, "--debug=d:t:A,%s/log/mysqltest.trace", $opt_vardir); + mtr_add_arg($args, "--debug=d:t:A,%s/log/mysqltest.trace", + $path_vardir_trace); } - if ( $opt_with_openssl ) + if ( $opt_ssl_supported ) { mtr_add_arg($args, "--ssl-ca=%s/std_data/cacert.pem", - $glob_mysql_test_dir); + $glob_mysql_test_dir); mtr_add_arg($args, "--ssl-cert=%s/std_data/client-cert.pem", - $glob_mysql_test_dir); + $glob_mysql_test_dir); mtr_add_arg($args, "--ssl-key=%s/std_data/client-key.pem", - $glob_mysql_test_dir); + $glob_mysql_test_dir); + } + + if ( $opt_ssl ) + { + # Turn on SSL for _all_ test cases if option --ssl was used + mtr_add_arg($args, "--ssl", + $glob_mysql_test_dir); + } + elsif ( $opt_ssl_supported ) + { + mtr_add_arg($args, "--skip-ssl", + $glob_mysql_test_dir); } # ---------------------------------------------------------------------- @@ -2435,7 +4315,7 @@ sub run_mysqltest ($) { # Add arguments that should not go into the MYSQL_TEST env var # ---------------------------------------------------------------------- - if ( defined $opt_valgrind_mysqltest ) + if ( $opt_valgrind_mysqltest ) { # Prefix the Valgrind options to the argument list. # We do this here, since we do not want to Valgrind the nested invocations @@ -2446,7 +4326,10 @@ sub run_mysqltest ($) { mtr_add_arg($args, "%s", $_) for @args_saved; } - mtr_add_arg($args, "-R"); + mtr_add_arg($args, "--test-file"); + mtr_add_arg($args, $tinfo->{'path'}); + + mtr_add_arg($args, "--result-file"); mtr_add_arg($args, $tinfo->{'result_file'}); if ( $opt_record ) @@ -2454,36 +4337,252 @@ sub run_mysqltest ($) { mtr_add_arg($args, "--record"); } - return mtr_run_test($exe,$args,$tinfo->{'path'},"",$path_timefile,""); + if ( $opt_client_gdb ) + { + gdb_arguments(\$args, \$exe, "client"); + } + elsif ( $opt_client_ddd ) + { + ddd_arguments(\$args, \$exe, "client"); + } + elsif ( $opt_client_debugger ) + { + debugger_arguments(\$args, \$exe, "client"); + } + + if ( $opt_check_testcases ) + { + foreach my $mysqld (@{$master}, @{$slave}) + { + if ($mysqld->{'pid'}) + { + run_check_testcase("before", $mysqld); + } + } + } + + my $res = mtr_run_test($exe,$args,"","",$path_timefile,""); + + if ( $opt_check_testcases ) + { + foreach my $mysqld (@{$master}, @{$slave}) + { + if ($mysqld->{'pid'}) + { + if (run_check_testcase("after", $mysqld)) + { + # Check failed, mark the test case with that info + $tinfo->{'check_testcase_failed'}= 1; + } + } + } + } + + return $res; + } -sub valgrind_arguments { +# +# Modify the exe and args so that program is run in gdb in xterm +# +sub gdb_arguments { + my $args= shift; + my $exe= shift; + my $type= shift; + + # Write $args to gdb init file + my $str= join(" ", @$$args); + my $gdb_init_file= "$opt_tmpdir/gdbinit.$type"; + + # Remove the old gdbinit file + unlink($gdb_init_file); + + if ( $type eq "client" ) + { + # write init file for client + mtr_tofile($gdb_init_file, + "set args $str\n" . + "break main\n"); + } + else + { + # write init file for mysqld + mtr_tofile($gdb_init_file, + "set args $str\n" . + "break mysql_parse\n" . + "commands 1\n" . + "disable 1\n" . + "end\n" . + "run"); + } + + if ( $opt_manual_gdb ) + { + print "\nTo start gdb for $type, type in another window:\n"; + print "cd $glob_mysql_test_dir;\n"; + print "gdb -x $gdb_init_file $$exe\n"; + + # Indicate the exe should not be started + $$exe= undef; + return; + } + + $$args= []; + mtr_add_arg($$args, "-title"); + mtr_add_arg($$args, "$type"); + mtr_add_arg($$args, "-e"); + + if ( $exe_libtool ) + { + mtr_add_arg($$args, $exe_libtool); + mtr_add_arg($$args, "--mode=execute"); + } + + mtr_add_arg($$args, "gdb"); + mtr_add_arg($$args, "-x"); + mtr_add_arg($$args, "$gdb_init_file"); + mtr_add_arg($$args, "$$exe"); + + $$exe= "xterm"; +} + + +# +# Modify the exe and args so that program is run in ddd +# +sub ddd_arguments { my $args= shift; my $exe= shift; + my $type= shift; + + # Write $args to ddd init file + my $str= join(" ", @$$args); + my $gdb_init_file= "$opt_tmpdir/gdbinit.$type"; - mtr_add_arg($args, "--tool=memcheck"); # From >= 2.1.2 needs this option - mtr_add_arg($args, "--alignment=8"); - mtr_add_arg($args, "--leak-check=yes"); - mtr_add_arg($args, "--num-callers=16"); - mtr_add_arg($args, "--suppressions=%s/valgrind.supp", $glob_mysql_test_dir) - if -f "$glob_mysql_test_dir/valgrind.supp"; + # Remove the old gdbinit file + unlink($gdb_init_file); + + if ( $type eq "client" ) + { + # write init file for client + mtr_tofile($gdb_init_file, + "set args $str\n" . + "break main\n"); + } + else + { + # write init file for mysqld + mtr_tofile($gdb_init_file, + "file $$exe\n" . + "set args $str\n" . + "break mysql_parse\n" . + "commands 1\n" . + "disable 1\n" . + "end"); + } - if ( defined $opt_valgrind_all ) + if ( $opt_manual_ddd ) { - mtr_add_arg($args, "-v"); - mtr_add_arg($args, "--show-reachable=yes"); + print "\nTo start ddd for $type, type in another window:\n"; + print "cd $glob_mysql_test_dir;\n"; + print "ddd -x $gdb_init_file $$exe\n"; + + # Indicate the exe should not be started + $$exe= undef; + return; } - if ( $opt_valgrind_options ) + my $save_exe= $$exe; + $$args= []; + if ( $exe_libtool ) { - # FIXME split earlier and put into @glob_valgrind_* - mtr_add_arg($args, '%s', $_) for (split(' ', $opt_valgrind_options)); + $$exe= $exe_libtool; + mtr_add_arg($$args, "--mode=execute"); + mtr_add_arg($$args, "ddd"); } + else + { + $$exe= "ddd"; + } + mtr_add_arg($$args, "--command=$gdb_init_file"); + mtr_add_arg($$args, "$save_exe"); +} + + +# +# Modify the exe and args so that program is run in the selected debugger +# +sub debugger_arguments { + my $args= shift; + my $exe= shift; + my $debugger= $opt_debugger || $opt_client_debugger; + + # FIXME Need to change the below "eq"'s to + # "case unsensitive string contains" + if ( $debugger eq "vcexpress" or $debugger eq "vc") + { + # vc[express] /debugexe exe arg1 .. argn + + # Add /debugexe and name of the exe before args + unshift(@$$args, "/debugexe"); + unshift(@$$args, "$$exe"); + + } + elsif ( $debugger eq "windbg" ) + { + # windbg exe arg1 .. argn + + # Add name of the exe before args + unshift(@$$args, "$$exe"); + + } + else + { + mtr_error("Unknown argument \"$debugger\" passed to --debugger"); + } + + # Set exe to debuggername + $$exe= $debugger; +} + + +# +# Modify the exe and args so that program is run in valgrind +# +sub valgrind_arguments { + my $args= shift; + my $exe= shift; + + if ( $opt_callgrind) + { + mtr_add_arg($args, "--tool=callgrind"); + mtr_add_arg($args, "--base=$opt_vardir/log"); + } + else + { + mtr_add_arg($args, "--tool=memcheck"); # From >= 2.1.2 needs this option + mtr_add_arg($args, "--alignment=8"); + mtr_add_arg($args, "--leak-check=yes"); + mtr_add_arg($args, "--num-callers=16"); + mtr_add_arg($args, "--suppressions=%s/valgrind.supp", $glob_mysql_test_dir) + if -f "$glob_mysql_test_dir/valgrind.supp"; + } + + # Add valgrind options, can be overriden by user + mtr_add_arg($args, '%s', $_) for (split(' ', $opt_valgrind_options)); mtr_add_arg($args, $$exe); - $$exe= $opt_valgrind || "valgrind"; + $$exe= $opt_valgrind_path || "valgrind"; + + if ($exe_libtool) + { + # Add "libtool --mode-execute" before the test to execute + # if running in valgrind(to avoid valgrinding bash) + unshift(@$args, "--mode=execute", $$exe); + $$exe= $exe_libtool; + } } @@ -2503,38 +4602,63 @@ 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 embedded-server Use the embedded server, i.e. no mysqld daemons ps-protocol Use the binary protocol between client and server - bench Run the benchmark suite FIXME - small-bench FIXME - no-manager Use the istanse manager (currently disabled) + cursor-protocol Use the cursor protocol between client and server + (implies --ps-protocol) + view-protocol Create a view to execute all non updating queries + sp-protocol Create a stored procedure to execute all queries + compress Use the compressed protocol between client and server + ssl Use ssl protocol between client and server + skip-ssl Dont start server with support for ssl connections + bench Run the benchmark suite + small-bench Run the benchmarks with --small-tests --small-tables + with-ndbcluster Use cluster as default table type for benchmark + +Options to control directories to use + benchdir=DIR The directory where the benchmark suite is stored + (default: ../../mysql-bench) + tmpdir=DIR The directory where temporary files are stored + (default: ./var/tmp). + vardir=DIR The directory where files generated from the test run + is stored (default: ./var). Specifying a ramdisk or + tmpfs will speed up tests. + mem=DIR Run testsuite in "memory" using tmpfs if + available(default: /dev/shm) + Options to control what test suites or cases to run force Continue to run the suite after failure - with-ndbcluster Use cluster, and enable test cases that requres it + with-ndbcluster-only Run only tests that include "ndb" in the filename + skip-ndb[cluster] Skip all tests that need cluster + skip-ndb[cluster]-slave Skip all tests that need a slave cluster + ndb-extra Run extra tests from ndb directory do-test=PREFIX Run test cases which name are prefixed with PREFIX start-from=PREFIX Run test cases starting from test prefixed with PREFIX suite=NAME Run the test suite named NAME. The default is "main" skip-rpl Skip the replication test cases. + skip-im Don't start IM, and skip the IM test cases skip-test=PREFIX Skip test cases which name are prefixed with PREFIX + big-test Pass "--big-test" to mysqltest which will set the + environment variable BIG_TEST, which can be checked + from test cases. Options that specify ports master_port=PORT Specify the port number used by the first master slave_port=PORT Specify the port number used by the first slave - ndbcluster_port=PORT Specify the port number used by cluster - manager-port=PORT Specify the port number used by manager (currently not used) + ndbcluster-port=PORT Specify the port number used by cluster + ndbcluster-port-slave=PORT Specify the port number used by slave cluster Options for test case authoring record TESTNAME (Re)genereate the result file for TESTNAME + check-testcases Check testcases for sideeffects Options that pass on options @@ -2543,65 +4667,73 @@ Options that pass on options Options to run test on running server extern Use running server for tests FIXME DANGEROUS - ndbconnectstring=STR Use running cluster, and connect using STR + ndb-connectstring=STR Use running cluster, and connect using STR + ndb-connectstring-slave=STR Use running slave cluster, and connect using STR user=USER User for connect to server Options for debugging the product - gdb FIXME - manual-gdb FIXME - client-gdb FIXME - ddd FIXME - strace-client FIXME + client-ddd Start mysqltest client in ddd + client-debugger=NAME Start mysqltest in the selected debugger + client-gdb Start mysqltest client in gdb + ddd Start mysqld in ddd + debug Dump trace output for all servers and client programs + debugger=NAME Start mysqld in the selected debugger + gdb Start the mysqld(s) in gdb + manual-debug Let user manually start mysqld in debugger, before + running test(s) + manual-gdb Let user manually start mysqld in gdb, before running + test(s) master-binary=PATH Specify the master "mysqld" to use slave-binary=PATH Specify the slave "mysqld" to use + strace-client Create strace output for mysqltest client Options for coverage, profiling etc gcov FIXME gprof FIXME - valgrind[=EXE] Run the "mysqltest" executable as well as the "mysqld" - server using valgrind, optionally specifying the - executable path/name - valgrind-mysqltest[=EXE] In addition, run the "mysqltest" executable with valgrind - valgrind-all[=EXE] Adds verbose flag, and --show-reachable to valgrind - valgrind-options=ARGS Extra options to give valgrind + valgrind Run the "mysqltest" and "mysqld" executables using + valgrind with options($default_valgrind_options) + valgrind-all Synonym for --valgrind + valgrind-mysqltest Run the "mysqltest" and "mysql_client_test" executable + with valgrind + valgrind-mysqld Run the "mysqld" executable with valgrind + valgrind-options=ARGS Options to give valgrind, replaces default options + valgrind-path=[EXE] Path to the valgrind executable + callgrind Instruct valgrind to use callgrind Misc options - verbose Verbose output from this script - script-debug Debug this script itself comment=STR Write STR to the output - compress Use the compressed protocol between client and server - timer Show test case execution time - start-and-exit Only initiate and start the "mysqld" servers, use the startup - settings for the specified test case if any - start-dirty Only start the "mysqld" servers without initiation - fast Don't try to cleanup from earlier runs - reorder Reorder tests to get less server restarts + notimer Don't show test case execution time + script-debug Debug this script itself + verbose More verbose output + start-and-exit Only initialize and start the servers, using the + startup settings for the specified test case (if any) + start-dirty Only start the servers (without initialization) for + the specified test case (if any) + fast Don't try to clean up from earlier runs + reorder Reorder tests to get fewer server restarts help Get this help text unified-diff | udiff When presenting differences, use unified diff - testcase-timeout=MINUTES Max test case run time (default 5) - suite-timeout=MINUTES Max test suite run time (default 120) + testcase-timeout=MINUTES Max test case run time (default $default_testcase_timeout) + suite-timeout=MINUTES Max test suite run time (default $default_suite_timeout) -Options not yet described, or that I want to look into more +Deprecated options + with-openssl Deprecated option for ssl - big-test - debug + +Options not yet described, or that I want to look into more local - local-master netware - old-master sleep=SECONDS socket=PATH - tmpdir=DIR user-test=s wait-timeout=SECONDS warnings log-warnings - with-openssl HERE mtr_exit(1); diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh index 15c7470a74c..141a725db2e 100644 --- a/mysql-test/mysql-test-run.sh +++ b/mysql-test/mysql-test-run.sh @@ -188,7 +188,7 @@ SYST=0 REALT=0 FAST_START="" MYSQL_TMP_DIR=$MYSQL_TEST_DIR/var/tmp -SLAVE_LOAD_TMPDIR=../../var/tmp #needs to be same length to test logging +SLAVE_LOAD_TMPDIR=../tmp #needs to be same length to test logging RES_SPACE=" " MYSQLD_SRC_DIRS="strings mysys include extra regex isam merge myisam \ myisammrg heap sql" diff --git a/mysql-test/r/check.result b/mysql-test/r/check.result index ecaa13642bd..26dbc9e345c 100644 --- a/mysql-test/r/check.result +++ b/mysql-test/r/check.result @@ -1,6 +1,6 @@ drop table if exists t1; create table t1(n int not null, key(n), key(n), key(n), key(n)); - check table t1 extended; +check table t1 extended; insert into t1 values (200000); Table Op Msg_type Msg_text test.t1 check status OK diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result index 68c86b80e60..b9c1e3e5542 100644 --- a/mysql-test/r/connect.result +++ b/mysql-test/r/connect.result @@ -18,6 +18,10 @@ time_zone_transition_type user show tables; Tables_in_test +connect(localhost,root,z,test2,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'root'@'localhost' (using password: YES) +connect(localhost,root,z,test,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'root'@'localhost' (using password: YES) grant ALL on *.* to test@localhost identified by "gambling"; grant ALL on *.* to test@127.0.0.1 identified by "gambling"; show tables; @@ -39,6 +43,14 @@ time_zone_transition_type user show tables; Tables_in_test +connect(localhost,test,,test2,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'test'@'localhost' (using password: NO) +connect(localhost,test,,"",MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'test'@'localhost' (using password: NO) +connect(localhost,test,zorro,test2,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'test'@'localhost' (using password: YES) +connect(localhost,test,zorro,test,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'test'@'localhost' (using password: YES) update mysql.user set password=old_password("gambling2") where user=_binary"test"; flush privileges; set password=""; @@ -64,6 +76,14 @@ time_zone_transition_type user show tables; Tables_in_test +connect(localhost,test,,test2,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'test'@'localhost' (using password: NO) +connect(localhost,test,,test,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'test'@'localhost' (using password: NO) +connect(localhost,test,zorro,test2,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'test'@'localhost' (using password: YES) +connect(localhost,test,zorro,test,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'test'@'localhost' (using password: YES) delete from mysql.user where user=_binary"test"; flush privileges; create table t1 (id integer not null auto_increment primary key); diff --git a/mysql-test/r/csv.result b/mysql-test/r/csv.result index 2e3d11ad461..7c7bcc3440c 100644 --- a/mysql-test/r/csv.result +++ b/mysql-test/r/csv.result @@ -4913,8 +4913,7 @@ bonfire Colombo nondecreasing DROP TABLE t1; -ALTER TABLE t2 RENAME t1 -#; +ALTER TABLE t2 RENAME t1; DROP TABLE t1; CREATE TABLE t1 ( Period smallint(4) unsigned zerofill DEFAULT '0000' NOT NULL, diff --git a/mysql-test/r/delete.result b/mysql-test/r/delete.result index cb632fcd6c8..7a6af8fd905 100644 --- a/mysql-test/r/delete.result +++ b/mysql-test/r/delete.result @@ -176,3 +176,14 @@ create table t1 (a int); delete `4.t1` from t1 as `4.t1` where `4.t1`.a = 5; delete FROM `4.t1` USING t1 as `4.t1` where `4.t1`.a = 5; drop table t1; +create table t1(a date not null); +insert into t1 values (0); +select * from t1 where a is null; +a +0000-00-00 +delete from t1 where a is null; +select count(*) from t1; +count(*) +0 +drop table t1; +End of 4.1 tests diff --git a/mysql-test/r/flush.result b/mysql-test/r/flush.result index 384bdc1214b..83b8b769f67 100644 --- a/mysql-test/r/flush.result +++ b/mysql-test/r/flush.result @@ -9,13 +9,13 @@ n flush tables with read lock; drop table t2; ERROR HY000: Table 't2' was locked with a READ lock and can't be updated - drop table t2; +drop table t2; unlock tables; create database mysqltest; create table mysqltest.t1(n int); insert into mysqltest.t1 values (23); flush tables with read lock; - drop database mysqltest; +drop database mysqltest; select * from mysqltest.t1; n 23 diff --git a/mysql-test/r/flush_block_commit.result b/mysql-test/r/flush_block_commit.result index 2e9f1920937..d5b10868358 100644 --- a/mysql-test/r/flush_block_commit.result +++ b/mysql-test/r/flush_block_commit.result @@ -5,7 +5,7 @@ insert into t1 values(1); flush tables with read lock; select * from t1; a - commit; +commit; select * from t1; a unlock tables; @@ -14,8 +14,8 @@ select * from t1 for update; a 1 begin; - select * from t1 for update; - flush tables with read lock; +select * from t1 for update; +flush tables with read lock; commit; a 1 diff --git a/mysql-test/r/func_compress.result b/mysql-test/r/func_compress.result index 8d6fa9927ce..ed8d66e9327 100644 --- a/mysql-test/r/func_compress.result +++ b/mysql-test/r/func_compress.result @@ -79,3 +79,13 @@ uncompress(a) uncompressed_length(a) NULL NULL a 1 drop table t1; +create table t1(a blob); +insert into t1 values ('0'), (NULL), ('0'); +select compress(a), compress(a) from t1; +select compress(a) is null from t1; +compress(a) is null +0 +1 +0 +drop table t1; +End of 4.1 tests diff --git a/mysql-test/r/func_misc.result b/mysql-test/r/func_misc.result index adf2035173f..246713489b4 100644 --- a/mysql-test/r/func_misc.result +++ b/mysql-test/r/func_misc.result @@ -69,7 +69,7 @@ FROM t1 WHERE conn = 'default'; IS_USED_LOCK('bug16501') = connection_id 1 - SELECT GET_LOCK('bug16501',600); +SELECT GET_LOCK('bug16501',600); SELECT IS_USED_LOCK('bug16501') = CONNECTION_ID(); IS_USED_LOCK('bug16501') = CONNECTION_ID() 1 diff --git a/mysql-test/r/func_sapdb.result b/mysql-test/r/func_sapdb.result index b18885e218a..447cadb9898 100644 --- a/mysql-test/r/func_sapdb.result +++ b/mysql-test/r/func_sapdb.result @@ -97,7 +97,9 @@ subtime("02:01:01.999999", "01:01:01.999999") 01:00:00.000000 select timediff("1997-01-01 23:59:59.000001","1995-12-31 23:59:59.000002"); timediff("1997-01-01 23:59:59.000001","1995-12-31 23:59:59.000002") -8807:59:59.999999 +838:59:59 +Warnings: +Warning 1292 Truncated incorrect time value: '8807:59:59.999999' select timediff("1997-12-31 23:59:59.000001","1997-12-30 01:01:01.000002"); timediff("1997-12-31 23:59:59.000001","1997-12-30 01:01:01.000002") 46:58:57.999999 @@ -208,13 +210,16 @@ NULL NULL SELECT TIMEDIFF(t1,t4) As ttt, TIMEDIFF(t2, t3) As qqq from test; ttt qqq -744:00:00 NULL -26305:01:02 22:58:58 --26305:01:02 -22:58:58 +838:59:59 22:58:58 +-838:59:59 -22:58:58 NULL 26:02:02 00:00:00 -26:02:02 NULL NULL NULL NULL 00:00:00 -24:00:00 +Warnings: +Warning 1292 Truncated incorrect time value: '26305:01:02' +Warning 1292 Truncated incorrect time value: '-26305:01:02' drop table t1, test; select addtime("-01:01:01.01", "-23:59:59.1") as a; a @@ -224,7 +229,9 @@ a 10000 select microsecond(19971231235959.01) as a; a -10000 +0 +Warnings: +Warning 1292 Truncated incorrect time value: '19971231235959.01' select date_add("1997-12-31",INTERVAL "10.09" SECOND_MICROSECOND) as a; a 1997-12-31 00:00:10.090000 diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index 6f41855f609..5b2deeada55 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -330,7 +330,9 @@ extract(DAY_MINUTE FROM "02 10:11:12") 21011 select extract(DAY_SECOND FROM "225 10:11:12"); extract(DAY_SECOND FROM "225 10:11:12") -225101112 +8385959 +Warnings: +Warning 1292 Truncated incorrect time value: '225 10:11:12' select extract(HOUR FROM "1999-01-02 10:11:12"); extract(HOUR FROM "1999-01-02 10:11:12") 10 @@ -694,6 +696,93 @@ t1 CREATE TABLE `t1` ( `from_unixtime(1) + 0` double(23,6) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; +SELECT SEC_TO_TIME(3300000); +SEC_TO_TIME(3300000) +838:59:59 +Warnings: +Warning 1292 Truncated incorrect time value: '3300000' +SELECT SEC_TO_TIME(3300000)+0; +SEC_TO_TIME(3300000)+0 +8385959.000000 +Warnings: +Warning 1292 Truncated incorrect time value: '3300000' +SELECT SEC_TO_TIME(3600 * 4294967296); +SEC_TO_TIME(3600 * 4294967296) +838:59:59 +Warnings: +Warning 1292 Truncated incorrect time value: '15461882265600' +SELECT TIME_TO_SEC('916:40:00'); +TIME_TO_SEC('916:40:00') +3020399 +Warnings: +Warning 1292 Truncated incorrect time value: '916:40:00' +SELECT ADDTIME('500:00:00', '416:40:00'); +ADDTIME('500:00:00', '416:40:00') +838:59:59 +Warnings: +Warning 1292 Truncated incorrect time value: '916:40:00' +SELECT ADDTIME('916:40:00', '416:40:00'); +ADDTIME('916:40:00', '416:40:00') +838:59:59 +Warnings: +Warning 1292 Truncated incorrect time value: '916:40:00' +Warning 1292 Truncated incorrect time value: '1255:39:59' +SELECT SUBTIME('916:40:00', '416:40:00'); +SUBTIME('916:40:00', '416:40:00') +422:19:59 +Warnings: +Warning 1292 Truncated incorrect time value: '916:40:00' +SELECT SUBTIME('-916:40:00', '416:40:00'); +SUBTIME('-916:40:00', '416:40:00') +-838:59:59 +Warnings: +Warning 1292 Truncated incorrect time value: '-916:40:00' +Warning 1292 Truncated incorrect time value: '-1255:39:59' +SELECT MAKETIME(916,0,0); +MAKETIME(916,0,0) +838:59:59 +Warnings: +Warning 1292 Truncated incorrect time value: '916:00:00' +SELECT MAKETIME(4294967296, 0, 0); +MAKETIME(4294967296, 0, 0) +838:59:59 +Warnings: +Warning 1292 Truncated incorrect time value: '4294967296:00:00' +SELECT MAKETIME(-4294967296, 0, 0); +MAKETIME(-4294967296, 0, 0) +-838:59:59 +Warnings: +Warning 1292 Truncated incorrect time value: '-4294967296:00:00' +SELECT MAKETIME(0, 4294967296, 0); +MAKETIME(0, 4294967296, 0) +NULL +SELECT MAKETIME(0, 0, 4294967296); +MAKETIME(0, 0, 4294967296) +NULL +SELECT MAKETIME(CAST(-1 AS UNSIGNED), 0, 0); +MAKETIME(CAST(-1 AS UNSIGNED), 0, 0) +838:59:59 +Warnings: +Warning 1292 Truncated incorrect time value: '18446744073709551615:00:00' +SELECT EXTRACT(HOUR FROM '100000:02:03'); +EXTRACT(HOUR FROM '100000:02:03') +838 +Warnings: +Warning 1292 Truncated incorrect time value: '100000:02:03' +CREATE TABLE t1(f1 TIME); +INSERT INTO t1 VALUES('916:00:00 a'); +Warnings: +Warning 1265 Data truncated for column 'f1' at row 1 +Warning 1264 Data truncated; out of range for column 'f1' at row 1 +SELECT * FROM t1; +f1 +838:59:59 +DROP TABLE t1; +SELECT SEC_TO_TIME(CAST(-1 AS UNSIGNED)); +SEC_TO_TIME(CAST(-1 AS UNSIGNED)) +838:59:59 +Warnings: +Warning 1292 Truncated incorrect time value: '18446744073709551615' SET NAMES latin1; SET character_set_results = NULL; SHOW VARIABLES LIKE 'character_set_results'; diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result index c047dc033c6..36d4e51119d 100644 --- a/mysql-test/r/grant2.result +++ b/mysql-test/r/grant2.result @@ -123,12 +123,12 @@ drop database mysqltest_1; set password = password("changed"); ERROR 42000: Access denied for user ''@'localhost' to database 'mysql' lock table mysql.user write; - flush privileges; - grant all on *.* to 'mysqltest_1'@'localhost'; +flush privileges; +grant all on *.* to 'mysqltest_1'@'localhost'; unlock tables; lock table mysql.user write; - set password for 'mysqltest_1'@'localhost' = password(''); - revoke all on *.* from 'mysqltest_1'@'localhost'; +set password for 'mysqltest_1'@'localhost' = password(''); +revoke all on *.* from 'mysqltest_1'@'localhost'; unlock tables; drop user 'mysqltest_1'@'localhost'; insert into mysql.user (user, host) values diff --git a/mysql-test/r/handler.result b/mysql-test/r/handler.result index 9b0c6dbc263..e88d359e0ef 100644 --- a/mysql-test/r/handler.result +++ b/mysql-test/r/handler.result @@ -452,7 +452,7 @@ handler t1 read first; c1 1 send the below to another connection, do not wait for the result - optimize table t1; +optimize table t1; proceed with the normal connection handler t1 read next; c1 diff --git a/mysql-test/r/init_file.result b/mysql-test/r/init_file.result new file mode 100644 index 00000000000..5404a7b2064 --- /dev/null +++ b/mysql-test/r/init_file.result @@ -0,0 +1,2 @@ +ok +End of 4.1 tests diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index 68995565752..2cd6f7826ca 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -119,4 +119,13 @@ SELECT * FROM t1, t2; i i 1 1 DROP TABLE t2, t1; -End of 4.1 tests. +flush status; +create table t1 (c1 int) engine=innodb; +handler t1 open; +handler t1 read first; +c1 +show /*!50002 GLOBAL */ status like 'Handler_rollback'; +Variable_name Value +Handler_rollback 0 +drop table t1; +End of 4.1 tests diff --git a/mysql-test/r/kill.result b/mysql-test/r/kill.result index eff59b102de..2a88f1ce3a8 100644 --- a/mysql-test/r/kill.result +++ b/mysql-test/r/kill.result @@ -24,7 +24,7 @@ create table t2 (id int unsigned not null); insert into t2 select id from t1; create table t3 (kill_id int); insert into t3 values(connection_id()); - select id from t1 where id in (select distinct id from t2); +select id from t1 where id in (select distinct id from t2); select ((@id := kill_id) - kill_id) from t3; ((@id := kill_id) - kill_id) 0 diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index 9eedbf50064..59b683b1917 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -2,8 +2,8 @@ drop table if exists t1,t2; create table t1(n int); insert into t1 values (1); lock tables t1 write; - update low_priority t1 set n = 4; - select n from t1; +update low_priority t1 set n = 4; +select n from t1; unlock tables; n 4 @@ -11,8 +11,8 @@ drop table t1; create table t1(n int); insert into t1 values (1); lock tables t1 read; - update low_priority t1 set n = 4; - select n from t1; +update low_priority t1 set n = 4; +select n from t1; unlock tables; n 1 @@ -23,7 +23,7 @@ insert into t1 values(1,1); insert into t1 values(2,2); insert into t2 values(1,2); lock table t1 read; - update t1,t2 set c=a where b=d; +update t1,t2 set c=a where b=d; select c from t2; c 2 @@ -32,7 +32,7 @@ drop table t2; create table t1 (a int); create table t2 (a int); lock table t1 write, t2 write; - insert t1 select * from t2; +insert t1 select * from t2; drop table t2; ERROR 42S02: Table 'test.t2' doesn't exist drop table t1; diff --git a/mysql-test/r/mix_innodb_myisam_binlog.result b/mysql-test/r/mix_innodb_myisam_binlog.result index 8cf99e8d623..f8de0336e63 100644 --- a/mysql-test/r/mix_innodb_myisam_binlog.result +++ b/mysql-test/r/mix_innodb_myisam_binlog.result @@ -193,7 +193,7 @@ select (@before:=unix_timestamp())*0; (@before:=unix_timestamp())*0 0 begin; - select * from t1 for update; +select * from t1 for update; insert into t2 values (20); ERROR HY000: Lock wait timeout exceeded; try restarting transaction select (@after:=unix_timestamp())*0; diff --git a/mysql-test/r/mysql_client.result b/mysql-test/r/mysql_client.result index 87d09428ff6..5375deb250d 100644 --- a/mysql-test/r/mysql_client.result +++ b/mysql-test/r/mysql_client.result @@ -2,3 +2,17 @@ 1 ERROR 1064 (42000) at line 3: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1 ERROR at line 1: USE must be followed by a database name +create table t17583 (a int); +insert into t17583 (a) values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +insert into t17583 select a from t17583; +insert into t17583 select a from t17583; +insert into t17583 select a from t17583; +insert into t17583 select a from t17583; +insert into t17583 select a from t17583; +insert into t17583 select a from t17583; +insert into t17583 select a from t17583; +select count(*) from t17583; +count(*) +1280 +drop table t17583; +End of 4.1 tests. diff --git a/mysql-test/r/mysql_client_test.result b/mysql-test/r/mysql_client_test.result new file mode 100644 index 00000000000..9766475a418 --- /dev/null +++ b/mysql-test/r/mysql_client_test.result @@ -0,0 +1 @@ +ok diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result index 091a3c0547d..a63863977b0 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -16,7 +16,7 @@ otto mysqltest: At line 1: query 'select otto from (select 1 as otto) as t1' succeeded - should have failed with sqlstate 42S22... select friedrich from (select 1 as otto) as t1; ERROR 42S22: Unknown column 'friedrich' in 'field list' -mysqltest: At line 1: query 'select friedrich from (select 1 as otto) as t1' failed with wrong sqlstate 42S22 instead of 00000... +mysqltest: At line 1: query 'select friedrich from (select 1 as otto) as t1' failed with wrong sqlstate 42S22: 'Unknown column 'friedrich' in 'field list'', instead of 00000... select otto from (select 1 as otto) as t1; otto 1 @@ -133,8 +133,7 @@ ERROR 42S02: Table 'test.t1' doesn't exist select 1146 as "after_!errno_masked_error" ; after_!errno_masked_error 1146 -mysqltest: At line 1: query 'select 3 from t1' failed with wrong errno 1146 instead of 1000... -mysqltest: At line 1: query 'select 3 from t1' failed with wrong errno 1146 instead of 1000... +mysqltest: At line 1: query 'select 3 from t1' failed with wrong errno 1146: 'Table 'test.t1' doesn't exist', instead of 1000... garbage ; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'garbage' at line 1 select 1064 as "after_--enable_abort_on_error" ; @@ -142,8 +141,7 @@ after_--enable_abort_on_error 1064 select 3 from t1 ; ERROR 42S02: Table 'test.t1' doesn't exist -mysqltest: At line 1: query 'select 3 from t1' failed with wrong errno 1146 instead of 1064... -mysqltest: At line 1: query 'select 3 from t1' failed: 1146: Table 'test.t1' doesn't exist +mysqltest: At line 1: query 'select 3 from t1' failed with wrong errno 1146: 'Table 'test.t1' doesn't exist', instead of 1064... hello hello ;;;;;;;; @@ -151,7 +149,41 @@ hello mysqltest: At line 1: End of line junk detected: "6" mysqltest: At line 1: End of line junk detected: "6" mysqltest: At line 1: Missing delimiter +mysqltest: At line 1: End of line junk detected: "sleep 7 +# Another comment +" +mysqltest: At line 1: Missing delimiter +mysqltest: At line 1: Missing delimiter +mysqltest: At line 1: End of line junk detected: "disconnect default + +# +# comment +# comment2 + +# comment 3 +--disable_query_log +" +mysqltest: At line 1: End of line junk detected: "disconnect default # comment +# comment part2 + +# comment 3 +--disable_query_log +" mysqltest: At line 1: Extra delimiter ";" found +mysqltest: At line 1: Extra delimiter ";" found +mysqltest: At line 1: Missing argument(s) to 'error' +mysqltest: At line 1: Missing argument(s) to 'error' +mysqltest: At line 1: The sqlstate definition must start with an uppercase S +mysqltest: At line 1: The error name definition must start with an uppercase E +mysqltest: At line 1: Invalid argument to error: '9eeeee' - the errno may only consist of digits[0-9] +mysqltest: At line 1: Invalid argument to error: '1sssss' - the errno may only consist of digits[0-9] +mysqltest: At line 1: The sqlstate must be exactly 5 chars long +mysqltest: At line 1: The sqlstate may only consist of digits[0-9] and _uppercase_ letters +mysqltest: At line 1: The sqlstate must be exactly 5 chars long +mysqltest: At line 1: Not available in this version of mysqltest +mysqltest: At line 1: Invalid argument to error: '999e9' - the errno may only consist of digits[0-9] +mysqltest: At line 1: Invalid argument to error: '9b' - the errno may only consist of digits[0-9] +mysqltest: At line 1: Too many errorcodes specified MySQL "MySQL" MySQL: The world''s most popular open source database @@ -166,8 +198,8 @@ source database - most popular open - source database - MySQL: The world''s --- most popular open --- source database +-- most popular +-- open source database # MySQL: The --world''s # most popular @@ -193,14 +225,20 @@ source database # source database -- MySQL: The -- world''s most --- popular open --- source database +-- popular +-- open source database # MySQL: The - world''s most -- popular open # source database -'$message' -"$message" +'# MySQL: The +- world''s most +-- popular open +# source database' +"# MySQL: The +- world''s most +-- popular open +# source database" hej hej hej @@ -209,21 +247,32 @@ hej a long variable content a long variable content -a long $where variable content +a long a long variable content variable content +a long \$where variable content +banana = banana +Not a banana: ba\$cat\$cat mysqltest: At line 1: Missing arguments to let mysqltest: At line 1: Missing variable name in let -mysqltest: At line 1: Variable name in hi=hi does not start with '$' mysqltest: At line 1: Missing assignment operator in let mysqltest: At line 1: Missing assignment operator in let -mysqltest: At line 1: Missing arguments to let +mysqltest: At line 1: Missing assignment operator in let +mysqltest: At line 1: Missing variable name in let mysqltest: At line 1: Missing variable name in let -mysqltest: At line 1: Variable name in =hi does not start with '$' mysqltest: At line 1: Missing assignment operator in let -mysqltest: At line 1: Missing file name in source +# Execute: --echo # <whatever> success: $success +# <whatever> success: 1 +# Execute: echo # <whatever> success: $success ; +# <whatever> success: 1 +# The next two variants work fine and expand the content of $success +# Execute: --echo $success +1 +# Execute: echo $success ; +1 +mysqltest: At line 1: Missing required argument 'filename' to command 'source' mysqltest: At line 1: Could not open file ./non_existingFile -mysqltest: In included file "./var/tmp/recursive.sql": At line 1: Source directives are nesting too deep -mysqltest: In included file "./var/tmp/error.sql": At line 1: query 'garbage ' failed: 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'garbage' at line 1 +mysqltest: In included file "MYSQLTEST_VARDIR/tmp/recursive.sql": At line 1: Source directives are nesting too deep +mysqltest: In included file "MYSQLTEST_VARDIR/tmp/error.sql": At line 1: query 'garbage ' failed: 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'garbage' at line 1 2 = outer loop variable after while here is the sourced script @@ -288,8 +337,8 @@ mysqltest: At line 1: Invalid argument to real_sleep "abc" 101 hej 1 -mysqltest: At line 1: Missing arguments to inc -mysqltest: At line 1: First argument to inc must be a variable (start with $) +mysqltest: At line 1: Missing argument to inc +mysqltest: At line 1: The argument to inc must be a variable (start with $) mysqltest: At line 1: End of line junk detected: "1000" 4 4 @@ -298,27 +347,31 @@ mysqltest: At line 1: End of line junk detected: "1000" 99 hej -1 -mysqltest: At line 1: Missing arguments to dec -mysqltest: At line 1: First argument to dec must be a variable (start with $) +mysqltest: At line 1: Missing argument to dec +mysqltest: At line 1: The argument to dec must be a variable (start with $) mysqltest: At line 1: End of line junk detected: "1000" mysqltest: At line 1: Missing arguments to system, nothing to do! mysqltest: At line 1: Missing arguments to system, nothing to do! mysqltest: At line 1: system command 'false' failed +system command 'NonExistsinfComamdn 2> /dev/null' failed test test2 test3 test4 +Counter is greater than 0, (counter=10) +Counter is not 0, (counter=0) 1 -mysqltest: In included file "./include/mysqltest_while.inc": At line 64: Nesting too deeply +Testing while with not +mysqltest: In included file "MYSQLTEST_VARDIR/tmp/mysqltest_while.inc": At line 64: Nesting too deeply mysqltest: At line 1: missing '(' in while mysqltest: At line 1: missing ')' in while mysqltest: At line 1: Missing '{' after while. Found "dec $i" mysqltest: At line 1: Stray '}' - end of block before beginning mysqltest: At line 1: Stray 'end' command - end of block before beginning -mysqltest: At line 1: query '' failed: 1065: Query was empty +mysqltest: At line 1: query '{' failed: 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '{' at line 1 mysqltest: At line 1: Missing '{' after while. Found "echo hej" mysqltest: At line 3: Missing end of block -mysqltest: At line 1: Missing newline between while and '{' +mysqltest: At line 3: Missing end of block mysqltest: At line 1: missing '(' in if mysqltest: At line 1: Stray 'end' command - end of block before beginning select "b" bs col1, "c" bs col2; @@ -331,6 +384,7 @@ mysqltest: At line 1: Wrong number of arguments to replace_result in 'replace_re mysqltest: At line 1: Wrong number of arguments to replace_result in 'replace_result a;' mysqltest: At line 1: Wrong number of arguments to replace_result in 'replace_result a' mysqltest: At line 1: Wrong number of arguments to replace_result in 'replace_result a ' +OK mysqltest: At line 1: Wrong number of arguments to replace_result in 'replace_result a b c' mysqltest: At line 1: Wrong number of arguments to replace_result in 'replace_result a b c ' select "a" as col1, "c" as col2; @@ -347,6 +401,18 @@ mysqltest: At line 1: Wrong column number to replace_column in 'replace_column 1 mysqltest: At line 1: Invalid integer argument "10!" mysqltest: At line 1: End of line junk detected: "!" mysqltest: At line 1: Invalid integer argument "a" +mysqltest: At line 1: Missing required argument 'connection name' to command 'connect' +mysqltest: At line 1: Missing required argument 'connection name' to command 'connect' +mysqltest: At line 1: Missing required argument 'host' to command 'connect' +mysqltest: At line 1: Missing required argument 'host' to command 'connect' +mysqltest: At line 1: query 'connect con2,localhost,root,,illegal_db' failed: 1049: Unknown database 'illegal_db' +mysqltest: At line 1: Illegal argument for port: 'illegal_port' +mysqltest: At line 1: Illegal option to connect: SMTP +OK +mysqltest: In included file "MYSQLTEST_VARDIR/tmp/mysqltest.sql": At line 7: Connection limit exhausted, you can have max 128 connections +mysqltest: In included file "MYSQLTEST_VARDIR/tmp/mysqltest.sql": At line 3: connection 'test_con1' not found in connection pool +mysqltest: In included file "MYSQLTEST_VARDIR/tmp/mysqltest.sql": At line 2: Connection test_con1 already exists +connect(localhost,root,,test,MASTER_PORT,MASTER_SOCKET); Output from mysqltest-x.inc Output from mysqltest-x.inc Output from mysqltest-x.inc @@ -358,3 +424,100 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp SELECT 1 as a; a 1 +select 1 as `a'b`, 2 as `a"b`; +a'b a"b +1 2 +select 'aaa\\','aa''a',"aa""a"; +aaa\ aa'a aa"a +aaa\ aa'a aa"a + +Here comes a message +-------------------- + +root@localhost +-------------- + +"Here comes a very very long message that + - is longer then 80 characters and + - consists of several lines" +-------------------------------------------------------------------------------- + +. Here comes a very very long message that +. - is longer then 80 characters and +. - consists of several lines +-------------------------------------------------------------------------------- +this will be executed +this will be executed +mysqltest: Result length mismatch +mysqltest: The test didn't produce any output +Failing multi statement query +mysqltest: At line 3: query 'create table t1 (a int primary key); +insert into t1 values (1); +select 'select-me'; +insertz 'error query'' failed: 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'insertz 'error query'' at line 1 +drop table t1; +mysqltest: At line 3: query 'create table t1 (a int primary key); +insert into t1 values (1); +select 'select-me'; +insertz 'error query'' failed: 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'insertz 'error query'' at line 1 +drop table t1; +Multi statement using expected error +create table t1 (a int primary key); +insert into t1 values (1); +select 'select-me'; +insertz error query|||| +select-me +select-me +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'insertz error query' at line 1 +drop table t1; +drop table t1; +sleep; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'sleep' at line 1 +sleep; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'sleep' at line 1 +; +ERROR 42000: Query was empty +select "b" as col1, "c" as col2; +col1 col2 +b c +select "b" as col1, "b" as col2, "c" as col3; +col1 col2 col3 +b b c +seled "b" bs col1, "d" bs col2; +col1 col2 +b d +select "raspberry and strawberry","blackberry","tomato"; +raspberry and strawberry blackberry tomato +raspberry and strawberry blackberry tomato +mysqltest: At line 1: Error parsing replace_regex "a" +mysqltest: At line 1: Error parsing replace_regex "a;" +mysqltest: At line 1: Error parsing replace_regex "a" +mysqltest: At line 1: Error parsing replace_regex "a " +mysqltest: At line 1: Error parsing replace_regex "a b" +mysqltest: At line 1: Error parsing replace_regex "/a b c" +mysqltest: At line 1: Error parsing replace_regex "/a /b c " +create table t1 (a int, b int); +insert into t1 values (1,3); +insert into t1 values (2,4); +select * from t1; +a D +1 1 +1 4 +drop table t1; +mysqltest: At line 1: Missing required argument 'filename' to command 'remove_file' +mysqltest: At line 1: Missing required argument 'filename' to command 'write_file' +mysqltest: At line 1: End of file encountered before 'EOF' delimiter was found +mysqltest: At line 1: End of line junk detected: "write_file filename "; +" +mysqltest: At line 1: Missing required argument 'filename' to command 'file_exists' +mysqltest: At line 1: Missing required argument 'from_file' to command 'copy_file' +mysqltest: At line 1: Missing required argument 'to_file' to command 'copy_file' +hello +hello +hello +mysqltest: At line 1: Max delimiter length(16) exceeded +hello +hello +mysqltest: At line 1: test of die +Some output +End of tests diff --git a/mysql-test/r/ps_2myisam.result b/mysql-test/r/ps_2myisam.result index 4b655cfb854..771245c3713 100644 --- a/mysql-test/r/ps_2myisam.result +++ b/mysql-test/r/ps_2myisam.result @@ -85,6 +85,8 @@ c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c 1 1 1 1 1 1 1 1 1 1 1.0000 1.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 1 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext one monday 9 9 9 9 9 9 9 9 9 9 9.0000 9.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 0 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext two tuesday set @arg00='SELECT' ; +@arg00 a from t1 where a=1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@arg00 a from t1 where a=1' at line 1 prepare stmt1 from ' ? a from t1 where a=1 '; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? a from t1 where a=1' at line 1 set @arg00=1 ; diff --git a/mysql-test/r/ps_3innodb.result b/mysql-test/r/ps_3innodb.result index 4d2a62887d6..8435bb348f4 100644 --- a/mysql-test/r/ps_3innodb.result +++ b/mysql-test/r/ps_3innodb.result @@ -85,6 +85,8 @@ c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c 1 1 1 1 1 1 1 1 1 1 1.0000 1.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 1 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext one monday 9 9 9 9 9 9 9 9 9 9 9.0000 9.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 0 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext two tuesday set @arg00='SELECT' ; +@arg00 a from t1 where a=1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@arg00 a from t1 where a=1' at line 1 prepare stmt1 from ' ? a from t1 where a=1 '; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? a from t1 where a=1' at line 1 set @arg00=1 ; diff --git a/mysql-test/r/ps_4heap.result b/mysql-test/r/ps_4heap.result index a4919b664c4..d6876825aa9 100644 --- a/mysql-test/r/ps_4heap.result +++ b/mysql-test/r/ps_4heap.result @@ -86,6 +86,8 @@ c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c 1 1 1 1 1 1 1 1 1 1 1.0000 1.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 1 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext one monday 9 9 9 9 9 9 9 9 9 9 9.0000 9.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 0 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext two tuesday set @arg00='SELECT' ; +@arg00 a from t1 where a=1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@arg00 a from t1 where a=1' at line 1 prepare stmt1 from ' ? a from t1 where a=1 '; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? a from t1 where a=1' at line 1 set @arg00=1 ; diff --git a/mysql-test/r/ps_5merge.result b/mysql-test/r/ps_5merge.result index f98cc1b3cdf..f2d459c6769 100644 --- a/mysql-test/r/ps_5merge.result +++ b/mysql-test/r/ps_5merge.result @@ -128,6 +128,8 @@ c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c 1 1 1 1 1 1 1 1 1 1 1.0000 1.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 1 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext one monday 9 9 9 9 9 9 9 9 9 9 9.0000 9.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 0 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext two tuesday set @arg00='SELECT' ; +@arg00 a from t1 where a=1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@arg00 a from t1 where a=1' at line 1 prepare stmt1 from ' ? a from t1 where a=1 '; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? a from t1 where a=1' at line 1 set @arg00=1 ; @@ -3142,6 +3144,8 @@ c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c 1 1 1 1 1 1 1 1 1 1 1.0000 1.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 1 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext one monday 9 9 9 9 9 9 9 9 9 9 9.0000 9.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 0 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext two tuesday set @arg00='SELECT' ; +@arg00 a from t1 where a=1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@arg00 a from t1 where a=1' at line 1 prepare stmt1 from ' ? a from t1 where a=1 '; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? a from t1 where a=1' at line 1 set @arg00=1 ; diff --git a/mysql-test/r/ps_6bdb.result b/mysql-test/r/ps_6bdb.result index acd7f45de95..3e16923c46f 100644 --- a/mysql-test/r/ps_6bdb.result +++ b/mysql-test/r/ps_6bdb.result @@ -85,6 +85,8 @@ c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c 1 1 1 1 1 1 1 1 1 1 1.0000 1.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 1 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext one monday 9 9 9 9 9 9 9 9 9 9 9.0000 9.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 0 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext two tuesday set @arg00='SELECT' ; +@arg00 a from t1 where a=1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@arg00 a from t1 where a=1' at line 1 prepare stmt1 from ' ? a from t1 where a=1 '; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? a from t1 where a=1' at line 1 set @arg00=1 ; diff --git a/mysql-test/r/ps_7ndb.result b/mysql-test/r/ps_7ndb.result index 27a1ea0925d..baceb1e4a9e 100644 --- a/mysql-test/r/ps_7ndb.result +++ b/mysql-test/r/ps_7ndb.result @@ -85,6 +85,8 @@ c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19 c20 c21 c22 c 1 1 1 1 1 1 1 1 1 1 1.0000 1.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 1 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext one monday 9 9 9 9 9 9 9 9 9 9 9.0000 9.0000 2004-02-29 2004-02-29 11:11:11 2004-02-29 11:11:11 11:11:11 2004 1 0 a 123456789a 123456789a123456789b123456789c tinyblob tinytext blob text mediumblob mediumtext longblob longtext two tuesday set @arg00='SELECT' ; +@arg00 a from t1 where a=1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@arg00 a from t1 where a=1' at line 1 prepare stmt1 from ' ? a from t1 where a=1 '; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? a from t1 where a=1' at line 1 set @arg00=1 ; diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index b436519d967..f25d94f8066 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -649,3 +649,13 @@ OR ((pk4 =1) AND (((pk1 IN ( 7, 2, 1 ))) OR (pk1 =522)) AND ((pk2 IN ( 0, 2635)) pk1 pk2 pk3 pk4 filler 2621 2635 1000015 0 filler drop table t1, t2; +create table t1(a char(2), key(a(1))); +insert into t1 values ('x'), ('xx'); +explain select a from t1 where a > 'x'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a a 2 NULL 2 Using where +select a from t1 where a > 'x'; +a +xx +drop table t1; +End of 4.1 tests diff --git a/mysql-test/r/rename.result b/mysql-test/r/rename.result index 76c0f4422fe..964babb1228 100644 --- a/mysql-test/r/rename.result +++ b/mysql-test/r/rename.result @@ -43,7 +43,7 @@ Note 1051 Unknown table 't4' CREATE TABLE t1 (a int); CREATE TABLE t3 (a int); FLUSH TABLES WITH READ LOCK; - RENAME TABLE t1 TO t2, t3 to t4; +RENAME TABLE t1 TO t2, t3 to t4; show tables; Tables_in_test t1 diff --git a/mysql-test/r/rpl000001.result b/mysql-test/r/rpl000001.result index eef986d8f8c..1bce3b1b235 100644 --- a/mysql-test/r/rpl000001.result +++ b/mysql-test/r/rpl000001.result @@ -59,7 +59,7 @@ create table t2(id int); insert into t2 values(connection_id()); create temporary table t3(n int); insert into t3 select get_lock('crash_lock%20C', 1) from t2; - update t1 set n = n + get_lock('crash_lock%20C', 2); +update t1 set n = n + get_lock('crash_lock%20C', 2); select (@id := id) - id from t2; (@id := id) - id 0 diff --git a/mysql-test/r/rpl000015.result b/mysql-test/r/rpl000015.result index 8cbbe3ab0e8..b3e6ef71e45 100644 --- a/mysql-test/r/rpl000015.result +++ b/mysql-test/r/rpl000015.result @@ -8,16 +8,16 @@ Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File change master to master_host='127.0.0.1'; show slave status; Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 test MASTER_PORT 7 4 slave-relay-bin.000001 4 No No 0 0 0 4 None 0 No # +# 127.0.0.1 test MASTER_PORT 7 4 # # No No 0 0 0 # None 0 No # change master to master_host='127.0.0.1',master_user='root', master_password='',master_port=MASTER_PORT; show slave status; Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_PORT 7 4 slave-relay-bin.000001 4 No No 0 0 0 4 None 0 No # +# 127.0.0.1 root MASTER_PORT 7 4 # # No No 0 0 0 # None 0 No # start slave; show slave status; Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_PORT 7 master-bin.000001 79 slave-relay-bin.000001 123 master-bin.000001 Yes Yes 0 0 79 123 None 0 No # +# 127.0.0.1 root MASTER_PORT 7 master-bin.000001 79 # # master-bin.000001 Yes Yes 0 0 79 # None 0 No # drop table if exists t1; create table t1 (n int); insert into t1 values (10),(45),(90); diff --git a/mysql-test/r/rpl000018.result b/mysql-test/r/rpl000018.result deleted file mode 100644 index b71f6492b97..00000000000 --- a/mysql-test/r/rpl000018.result +++ /dev/null @@ -1,14 +0,0 @@ -reset master; -reset slave; -start slave; -show binary logs; -Log_name -master-bin.000001 -master-bin.000002 -drop table if exists t1; -create table t1(n int); -insert into t1 values (3351); -select * from t1; -n -3351 -drop table t1; diff --git a/mysql-test/r/rpl_chain_temp_table.result b/mysql-test/r/rpl_chain_temp_table.result deleted file mode 100644 index 5ece80565c7..00000000000 --- a/mysql-test/r/rpl_chain_temp_table.result +++ /dev/null @@ -1,30 +0,0 @@ -slave stop; -drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; -reset master; -reset slave; -drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; -slave start; -reset master; -change master to master_host='127.0.0.1',master_port=9307, master_user='root'; -start slave; -create temporary table t1 (a int); -create temporary table t1 (a int); -show status like 'slave_open_temp_tables'; -Variable_name Value -Slave_open_temp_tables 2 -create temporary table t1 (a int); -create temporary table t1 (a int); -show status like 'slave_open_temp_tables'; -Variable_name Value -Slave_open_temp_tables 4 -stop slave; -insert into t1 values(1); -create table t2 as select * from t1; -start slave; -show status like 'slave_open_temp_tables'; -Variable_name Value -Slave_open_temp_tables 4 -select * from t2; -a -1 -drop table t2; diff --git a/mysql-test/r/rpl_change_master.result b/mysql-test/r/rpl_change_master.result index a6342d47b49..16e14f5da2e 100644 --- a/mysql-test/r/rpl_change_master.result +++ b/mysql-test/r/rpl_change_master.result @@ -20,7 +20,7 @@ Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File change master to master_user='root'; show slave status; Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 214 slave-relay-bin.000001 4 master-bin.000001 No No 0 0 214 4 None 0 No # +# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 214 # # master-bin.000001 No No 0 0 214 # None 0 No # select release_lock("a"); release_lock("a") 1 diff --git a/mysql-test/r/rpl_charset.result b/mysql-test/r/rpl_charset.result index 292cfb19175..0ebd97d0b6a 100644 --- a/mysql-test/r/rpl_charset.result +++ b/mysql-test/r/rpl_charset.result @@ -147,10 +147,10 @@ master-bin.000001 3577 Intvar 1 3577 INSERT_ID=4 master-bin.000001 3605 Query 1 3605 use `mysqltest2`; insert into t1 (b) values(LEAST("Müller","Muffler")) master-bin.000001 3698 Query 1 3698 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64 master-bin.000001 3839 Intvar 1 3839 INSERT_ID=74 -master-bin.000001 3867 Create_file 1 3867 db=mysqltest2;table=t1;file_id=1;block_len=581 +master-bin.000001 3867 Create_file 1 3867 db=mysqltest2;table=t1;file_id=x;block_len=581 master-bin.000001 4540 Query 1 4540 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64 master-bin.000001 4681 Intvar 1 4681 INSERT_ID=5 -master-bin.000001 4709 Exec_load 1 4709 ;file_id=1 +master-bin.000001 4709 Exec_load 1 4709 ;file_id=x master-bin.000001 4732 Query 1 4732 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64 master-bin.000001 4873 Query 1 4873 use `mysqltest2`; truncate table t1 master-bin.000001 4931 Query 1 4931 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64 diff --git a/mysql-test/r/rpl_error_ignored_table.result b/mysql-test/r/rpl_error_ignored_table.result index 4a562dbfc70..740d139b4c8 100644 --- a/mysql-test/r/rpl_error_ignored_table.result +++ b/mysql-test/r/rpl_error_ignored_table.result @@ -9,7 +9,7 @@ insert into t1 values (1),(1); ERROR 23000: Duplicate entry '1' for key 1 show slave status; Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 213 slave-relay-bin.000002 257 master-bin.000001 Yes Yes test.t3,test.t1,test.t2 0 0 213 257 None 0 No # +# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 213 # # master-bin.000001 Yes Yes test.t3,test.t1,test.t2 0 0 213 # None 0 No # show tables like 't1'; Tables_in_test (t1) drop table t1; @@ -20,7 +20,7 @@ create table t2 (a int primary key); insert into t2 values(1); create table t3 (id int); insert into t3 values(connection_id()); - update t2 set a = a + 1 + get_lock('crash_lock%20C', 10); +update t2 set a = a + 1 + get_lock('crash_lock%20C', 10); select (@id := id) - id from t3; (@id := id) - id 0 diff --git a/mysql-test/r/rpl_failsafe.result b/mysql-test/r/rpl_failsafe.result deleted file mode 100644 index 956555f9318..00000000000 --- a/mysql-test/r/rpl_failsafe.result +++ /dev/null @@ -1,34 +0,0 @@ -stop slave; -drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; -reset master; -reset slave; -drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; -start slave; -show variables like 'rpl_recovery_rank'; -Variable_name Value -rpl_recovery_rank 1 -show status like 'Rpl_status'; -Variable_name Value -Rpl_status AUTH_MASTER -create table t1(n int); -drop table t1; -show variables like 'rpl_recovery_rank'; -Variable_name Value -rpl_recovery_rank 2 -show status like 'Rpl_status'; -Variable_name Value -Rpl_status ACTIVE_SLAVE -start slave; -show variables like 'rpl_recovery_rank'; -Variable_name Value -rpl_recovery_rank 3 -show status like 'Rpl_status'; -Variable_name Value -Rpl_status ACTIVE_SLAVE -start slave; -show variables like 'rpl_recovery_rank'; -Variable_name Value -rpl_recovery_rank 4 -show status like 'Rpl_status'; -Variable_name Value -Rpl_status ACTIVE_SLAVE diff --git a/mysql-test/r/rpl_heap.result b/mysql-test/r/rpl_heap.result deleted file mode 100644 index 1facbcb7676..00000000000 --- a/mysql-test/r/rpl_heap.result +++ /dev/null @@ -1,29 +0,0 @@ -reset master; -drop table if exists t1; -create table t1 type=HEAP select 10 as a; -insert into t1 values(11); -show binlog events from 79; -Log_name Pos Event_type Server_id Orig_log_pos Info -master-bin.001 79 Query 1 79 use `test`; create table t1 type=HEAP select 10 as a -master-bin.001 154 Query 1 154 use `test`; insert into t1 values(11) -reset slave; -start slave; -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` bigint(2) NOT NULL default '0' -) TYPE=HEAP -select * from t1; -a -10 -11 -select * from t1; -a -select * from t1 limit 10; -a -show binlog events in 'master-bin.002' from 79; -Log_name Pos Event_type Server_id Orig_log_pos Info -master-bin.002 79 Query 1 79 use `test`; DELETE FROM `test`.`t1` -select * from t1; -a -drop table t1; diff --git a/mysql-test/r/rpl_loaddata.result b/mysql-test/r/rpl_loaddata.result index 65fc9d1b415..446b7961578 100644 --- a/mysql-test/r/rpl_loaddata.result +++ b/mysql-test/r/rpl_loaddata.result @@ -22,7 +22,7 @@ day id category name 2003-03-22 2416 a bbbbb show master status; File Position Binlog_Do_DB Binlog_Ignore_DB -slave-bin.000001 964 +slave-bin.000001 950 drop table t1; drop table t2; drop table t3; @@ -33,7 +33,7 @@ set global sql_slave_skip_counter=1; start slave; show slave status; Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1311 slave-relay-bin.000002 1355 master-bin.000001 Yes Yes 0 0 1311 1355 None 0 No # +# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1311 # # master-bin.000001 Yes Yes 0 0 1311 # None 0 No # set sql_log_bin=0; delete from t1; set sql_log_bin=1; @@ -43,7 +43,7 @@ change master to master_user='test'; change master to master_user='root'; show slave status; Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1419 slave-relay-bin.000001 4 master-bin.000001 No No 0 0 1419 4 None 0 No # +# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1419 # # master-bin.000001 No No 0 0 1419 # None 0 No # set global sql_slave_skip_counter=1; start slave; set sql_log_bin=0; @@ -54,7 +54,7 @@ stop slave; reset slave; show slave status; Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_PORT 1 4 slave-relay-bin.000001 4 No No 0 0 0 4 None 0 No # +# 127.0.0.1 root MASTER_PORT 1 4 # # No No 0 0 0 # None 0 No # reset master; create table t2 (day date,id int(9),category enum('a','b','c'),name varchar(60), unique(day)); diff --git a/mysql-test/r/rpl_loaddatalocal.result b/mysql-test/r/rpl_loaddatalocal.result index b49ea842485..b2ca868a094 100644 --- a/mysql-test/r/rpl_loaddatalocal.result +++ b/mysql-test/r/rpl_loaddatalocal.result @@ -5,9 +5,9 @@ reset slave; drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; start slave; create table t1(a int); -select * into outfile '../../var/master-data/rpl_loaddatalocal.select_outfile' from t1; +select * into outfile 'MYSQLTEST_VARDIR/master-data/rpl_loaddatalocal.select_outfile' from t1; truncate table t1; -load data local infile './var/master-data/rpl_loaddatalocal.select_outfile' into table t1; +load data local infile 'MYSQLTEST_VARDIR/master-data/rpl_loaddatalocal.select_outfile' into table t1; select a,count(*) from t1 group by a; a count(*) 1 10000 diff --git a/mysql-test/r/rpl_log.result b/mysql-test/r/rpl_log.result index 3833800bfeb..5a351ea693c 100644 --- a/mysql-test/r/rpl_log.result +++ b/mysql-test/r/rpl_log.result @@ -73,7 +73,7 @@ master-bin.000002 276 start slave; show binary logs; Log_name File_size -slave-bin.000001 1285 +slave-bin.000001 1278 slave-bin.000002 170 show binlog events in 'slave-bin.000001' from 4; Log_name Pos Event_type Server_id Orig_log_pos Info @@ -84,11 +84,11 @@ slave-bin.000001 200 Query 1 200 use `test`; insert into t1 values (NULL) slave-bin.000001 263 Query 1 263 use `test`; drop table t1 slave-bin.000001 311 Query 1 311 use `test`; create table t1 (word char(20) not null) slave-bin.000001 386 Create_file 1 386 db=test;table=t1;file_id=1;block_len=581 -slave-bin.000001 1065 Exec_load 1 1065 ;file_id=1 -slave-bin.000001 1088 Query 1 1088 use `test`; drop table t1 -slave-bin.000001 1136 Query 1 1136 use `test`; create table t5 (a int) -slave-bin.000001 1194 Query 1 1194 use `test`; drop table t5 -slave-bin.000001 1242 Rotate 2 1242 slave-bin.000002;pos=4 +slave-bin.000001 1058 Exec_load 1 1058 ;file_id=1 +slave-bin.000001 1081 Query 1 1081 use `test`; drop table t1 +slave-bin.000001 1129 Query 1 1129 use `test`; create table t5 (a int) +slave-bin.000001 1187 Query 1 1187 use `test`; drop table t5 +slave-bin.000001 1235 Rotate 2 1235 slave-bin.000002;pos=4 show binlog events in 'slave-bin.000002' from 4; Log_name Pos Event_type Server_id Orig_log_pos Info slave-bin.000002 4 Query 1 4 use `test`; create table t1 (n int) @@ -96,7 +96,7 @@ slave-bin.000002 62 Query 1 62 use `test`; insert into t1 values (1) slave-bin.000002 122 Query 1 122 use `test`; drop table t1 show slave status; Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_PORT 1 master-bin.000002 276 slave-relay-bin.000003 214 master-bin.000002 Yes Yes 0 0 276 214 None 0 No # +# 127.0.0.1 root MASTER_PORT 1 master-bin.000002 276 # # master-bin.000002 Yes Yes 0 0 276 # None 0 No # show binlog events in 'slave-bin.000005' from 4; ERROR HY000: Error when executing command SHOW BINLOG EVENTS: Could not find target log create table t1(a int auto_increment primary key, b int); diff --git a/mysql-test/r/rpl_master_pos_wait.result b/mysql-test/r/rpl_master_pos_wait.result index e92d1ffa361..84f2b0bd91e 100644 --- a/mysql-test/r/rpl_master_pos_wait.result +++ b/mysql-test/r/rpl_master_pos_wait.result @@ -12,7 +12,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: Note 1003 select sql_no_cache master_pos_wait(_latin1'master-bin.999999',0,2) AS `master_pos_wait('master-bin.999999',0,2)` - select master_pos_wait('master-bin.999999',0); +select master_pos_wait('master-bin.999999',0); stop slave sql_thread; master_pos_wait('master-bin.999999',0) NULL diff --git a/mysql-test/r/rpl_max_relay_size.result b/mysql-test/r/rpl_max_relay_size.result index c0a7a0a5b00..eee655adea0 100644 --- a/mysql-test/r/rpl_max_relay_size.result +++ b/mysql-test/r/rpl_max_relay_size.result @@ -28,8 +28,8 @@ Master_Port MASTER_PORT Connect_Retry 1 Master_Log_File master-bin.000001 Read_Master_Log_Pos 50477 -Relay_Log_File slave-relay-bin.000014 -Relay_Log_Pos 1221 +Relay_Log_File # +Relay_Log_Pos # Relay_Master_Log_File master-bin.000001 Slave_IO_Running Yes Slave_SQL_Running Yes @@ -43,7 +43,7 @@ Last_Errno 0 Last_Error Skip_Counter 0 Exec_Master_Log_Pos 50477 -Relay_Log_Space 1221 +Relay_Log_Space # Until_Condition None Until_Log_File Until_Log_Pos 0 @@ -71,8 +71,8 @@ Master_Port MASTER_PORT Connect_Retry 1 Master_Log_File master-bin.000001 Read_Master_Log_Pos 50477 -Relay_Log_File slave-relay-bin.000004 -Relay_Log_Pos 9457 +Relay_Log_File # +Relay_Log_Pos # Relay_Master_Log_File master-bin.000001 Slave_IO_Running Yes Slave_SQL_Running Yes @@ -86,7 +86,7 @@ Last_Errno 0 Last_Error Skip_Counter 0 Exec_Master_Log_Pos 50477 -Relay_Log_Space 9457 +Relay_Log_Space # Until_Condition None Until_Log_File Until_Log_Pos 0 @@ -114,8 +114,8 @@ Master_Port MASTER_PORT Connect_Retry 1 Master_Log_File master-bin.000001 Read_Master_Log_Pos 50477 -Relay_Log_File slave-relay-bin.000008 -Relay_Log_Pos 1283 +Relay_Log_File # +Relay_Log_Pos # Relay_Master_Log_File master-bin.000001 Slave_IO_Running Yes Slave_SQL_Running Yes @@ -129,7 +129,7 @@ Last_Errno 0 Last_Error Skip_Counter 0 Exec_Master_Log_Pos 50477 -Relay_Log_Space 1283 +Relay_Log_Space # Until_Condition None Until_Log_File Until_Log_Pos 0 @@ -154,8 +154,8 @@ Master_Port MASTER_PORT Connect_Retry 1 Master_Log_File Read_Master_Log_Pos 4 -Relay_Log_File slave-relay-bin.000001 -Relay_Log_Pos 4 +Relay_Log_File # +Relay_Log_Pos # Relay_Master_Log_File Slave_IO_Running No Slave_SQL_Running No @@ -169,7 +169,7 @@ Last_Errno 0 Last_Error Skip_Counter 0 Exec_Master_Log_Pos 0 -Relay_Log_Space 4 +Relay_Log_Space # Until_Condition None Until_Log_File Until_Log_Pos 0 @@ -195,8 +195,8 @@ Master_Port MASTER_PORT Connect_Retry 1 Master_Log_File master-bin.000001 Read_Master_Log_Pos 50535 -Relay_Log_File slave-relay-bin.000009 -Relay_Log_Pos 62 +Relay_Log_File # +Relay_Log_Pos # Relay_Master_Log_File master-bin.000001 Slave_IO_Running Yes Slave_SQL_Running Yes @@ -210,7 +210,7 @@ Last_Errno 0 Last_Error Skip_Counter 0 Exec_Master_Log_Pos 50535 -Relay_Log_Space 62 +Relay_Log_Space # Until_Condition None Until_Log_File Until_Log_Pos 0 @@ -234,8 +234,8 @@ Master_Port MASTER_PORT Connect_Retry 1 Master_Log_File master-bin.000001 Read_Master_Log_Pos 50583 -Relay_Log_File slave-relay-bin.000010 -Relay_Log_Pos 52 +Relay_Log_File # +Relay_Log_Pos # Relay_Master_Log_File master-bin.000001 Slave_IO_Running Yes Slave_SQL_Running Yes @@ -249,7 +249,7 @@ Last_Errno 0 Last_Error Skip_Counter 0 Exec_Master_Log_Pos 50583 -Relay_Log_Space 52 +Relay_Log_Space # Until_Condition None Until_Log_File Until_Log_Pos 0 diff --git a/mysql-test/r/rpl_replicate_do.result b/mysql-test/r/rpl_replicate_do.result index 45ef6cb9b7b..b9294c9c767 100644 --- a/mysql-test/r/rpl_replicate_do.result +++ b/mysql-test/r/rpl_replicate_do.result @@ -28,7 +28,7 @@ ERROR 42S02: Table 'test.t11' doesn't exist drop table if exists t1,t2,t11; show slave status; Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1340 slave-relay-bin.000002 1384 master-bin.000001 Yes Yes test.t1 0 0 1340 1384 None 0 No # +# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1340 # # master-bin.000001 Yes Yes test.t1 0 0 1340 # None 0 No # create table t1 (ts timestamp); set one_shot time_zone='met'; insert into t1 values('2005-08-12 00:00:00'); diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index ad847b5f156..e16a605434e 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -2697,8 +2697,7 @@ select (1,2,3) = (select * from t1); ERROR 21000: Operand should contain 3 column(s) select (select * from t1) = (1,2,3); ERROR 21000: Operand should contain 2 column(s) -drop table t1 -#; +drop table t1; CREATE TABLE `t1` ( `itemid` bigint(20) unsigned NOT NULL auto_increment, `sessionid` bigint(20) unsigned default NULL, diff --git a/mysql-test/r/synchronization.result b/mysql-test/r/synchronization.result index ad9443c86da..8b243fe29b2 100644 --- a/mysql-test/r/synchronization.result +++ b/mysql-test/r/synchronization.result @@ -1,5 +1,5 @@ CREATE TABLE t1 (x1 int); - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -7,7 +7,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -15,7 +15,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -23,7 +23,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -31,7 +31,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -39,7 +39,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -47,7 +47,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -55,7 +55,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -63,7 +63,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -71,7 +71,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -79,7 +79,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -87,7 +87,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -95,7 +95,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -103,7 +103,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -111,7 +111,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -119,7 +119,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -127,7 +127,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -135,7 +135,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -143,7 +143,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x1 x2 int; +ALTER TABLE t1 CHANGE x1 x2 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table @@ -151,7 +151,7 @@ t2 CREATE TABLE `t2` ( `xx` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t2; - ALTER TABLE t1 CHANGE x2 x1 int; +ALTER TABLE t1 CHANGE x2 x1 int; CREATE TABLE t2 LIKE t1; SHOW CREATE TABLE t2; Table Create Table diff --git a/mysql-test/r/type_blob.result b/mysql-test/r/type_blob.result index 6b4b54fbb66..977377c0bc6 100644 --- a/mysql-test/r/type_blob.result +++ b/mysql-test/r/type_blob.result @@ -24,8 +24,7 @@ t3 CREATE TABLE `t3` ( `a` mediumtext, `b` mediumblob ) ENGINE=MyISAM DEFAULT CHARSET=latin1 -drop table t1,t2,t3 -#; +drop table t1,t2,t3; CREATE TABLE t1 (a char(257) default "hello"); ERROR 42000: Column length too big for column 'a' (max = 255); use BLOB or TEXT instead CREATE TABLE t2 (a blob default "hello"); diff --git a/mysql-test/t/bdb-alter-table-2-master.opt b/mysql-test/t/bdb-alter-table-2-master.opt index 15ad73c500f..c2dca33445b 100644 --- a/mysql-test/t/bdb-alter-table-2-master.opt +++ b/mysql-test/t/bdb-alter-table-2-master.opt @@ -1,2 +1,2 @@ ---skip-external-locking +--force-restart diff --git a/mysql-test/t/connect.test b/mysql-test/t/connect.test index ff15d74e5ac..89e34f0c8e0 100644 --- a/mysql-test/t/connect.test +++ b/mysql-test/t/connect.test @@ -1,7 +1,6 @@ # This test is to check various cases of connections -# with right and wrong password, with and without database -# Unfortunately the check is incomplete as we can't handle errors on connect -# Also we can't connect without database +# with right and wrong password, with and without database +# Unfortunately the check is incomplete as we can't connect without database # This test makes no sense with the embedded server --source include/not_embedded.inc @@ -10,69 +9,73 @@ drop table if exists t1,t2; --enable_warnings + #connect (con1,localhost,root,,""); #show tables; connect (con1,localhost,root,,mysql); show tables; -connect (con1,localhost,root,,test); +connect (con2,localhost,root,,test); show tables; -# Re enable this one day if error handling on connect will take place - -#connect (con1,localhost,root,z,test2); -#--error 1045 -#connect (con1,localhost,root,z,); -#--error 1045 +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,root,z,test2); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,root,z,); grant ALL on *.* to test@localhost identified by "gambling"; grant ALL on *.* to test@127.0.0.1 identified by "gambling"; # Now check this user with different databases - #connect (con1,localhost,test,gambling,""); #show tables; -connect (con1,localhost,test,gambling,mysql); +connect (con3,localhost,test,gambling,mysql); show tables; -connect (con1,localhost,test,gambling,test); +connect (con4,localhost,test,gambling,test); show tables; -# Re enable this one day if error handling on connect will take place - -#connect (con1,localhost,test,,test2); -#--error 1045 -#connect (con1,localhost,test,,""); -#--error 1045 -#connect (con1,localhost,test,zorro,test2); -#--error 1045 -#connect (con1,localhost,test,zorro,); -#--error 1045 +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,test,,test2); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,test,,""); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,test,zorro,test2); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,test,zorro,); # check if old password version also works update mysql.user set password=old_password("gambling2") where user=_binary"test"; flush privileges; -#connect (con1,localhost,test,gambling2,""); -#show tables; -connect (con1,localhost,test,gambling2,mysql); +connect (con10,localhost,test,gambling2,); +connect (con5,localhost,test,gambling2,mysql); +connection con5; set password=""; --error 1105 set password='gambling3'; set password=old_password('gambling3'); show tables; -connect (con1,localhost,test,gambling3,test); +connect (con6,localhost,test,gambling3,test); show tables; -# Re enable this one day if error handling on connect will take place - -#connect (con1,localhost,test,,test2); -#--error 1045 -#connect (con1,localhost,test,,); -#--error 1045 -#connect (con1,localhost,test,zorro,test2); -#--error 1045 -#connect (con1,localhost,test,zorro,); -#--error 1045 +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,test,,test2); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,test,,); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,test,zorro,test2); +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (fail_con,localhost,test,zorro,); # remove user 'test' so that other tests which may use 'test' @@ -84,13 +87,13 @@ flush privileges; # # Bug#12517: Clear user variables and replication events before # closing temp tables in thread cleanup. -connect (con2,localhost,root,,test); -connection con2; +connect (con7,localhost,root,,test); +connection con7; create table t1 (id integer not null auto_increment primary key); create temporary table t2(id integer not null auto_increment primary key); set @id := 1; delete from t1 where id like @id; -disconnect con2; +disconnect con7; --sleep 5 connection default; drop table t1; diff --git a/mysql-test/t/csv.test b/mysql-test/t/csv.test index 5b693335a43..e78c2ccc578 100644 --- a/mysql-test/t/csv.test +++ b/mysql-test/t/csv.test @@ -1295,7 +1295,7 @@ SELECT fld3 FROM t2; # DROP TABLE t1; -ALTER TABLE t2 RENAME t1 +ALTER TABLE t2 RENAME t1; # # Drop and recreate diff --git a/mysql-test/t/delete.test b/mysql-test/t/delete.test index d4eb01cab23..2036b59d810 100644 --- a/mysql-test/t/delete.test +++ b/mysql-test/t/delete.test @@ -163,4 +163,15 @@ delete `4.t1` from t1 as `4.t1` where `4.t1`.a = 5; delete FROM `4.t1` USING t1 as `4.t1` where `4.t1`.a = 5; drop table t1; -# End of 4.1 tests +# +# Bug #8143: deleting '0000-00-00' values using IS NULL +# + +create table t1(a date not null); +insert into t1 values (0); +select * from t1 where a is null; +delete from t1 where a is null; +select count(*) from t1; +drop table t1; + +--echo End of 4.1 tests diff --git a/mysql-test/t/func_compress.test b/mysql-test/t/func_compress.test index 0f3c3cab307..223a5540f38 100644 --- a/mysql-test/t/func_compress.test +++ b/mysql-test/t/func_compress.test @@ -54,4 +54,16 @@ insert into t1 values(NULL), (compress('a')); select uncompress(a), uncompressed_length(a) from t1; drop table t1; -# End of 4.1 tests +# +# Bug #23254: problem with compress(NULL) +# + +create table t1(a blob); +insert into t1 values ('0'), (NULL), ('0'); +--disable_result_log +select compress(a), compress(a) from t1; +--enable_result_log +select compress(a) is null from t1; +drop table t1; + +--echo End of 4.1 tests diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index 714379d0fb3..066a059483c 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -369,6 +369,47 @@ show create table t1; drop table t1; # +# Bug #11655: Wrong time is returning from nested selects - maximum time exists +# +# check if SEC_TO_TIME() handles out-of-range values correctly +SELECT SEC_TO_TIME(3300000); +SELECT SEC_TO_TIME(3300000)+0; +SELECT SEC_TO_TIME(3600 * 4294967296); + +# check if TIME_TO_SEC() handles out-of-range values correctly +SELECT TIME_TO_SEC('916:40:00'); + +# check if ADDTIME() handles out-of-range values correctly +SELECT ADDTIME('500:00:00', '416:40:00'); +SELECT ADDTIME('916:40:00', '416:40:00'); + +# check if SUBTIME() handles out-of-range values correctly +SELECT SUBTIME('916:40:00', '416:40:00'); +SELECT SUBTIME('-916:40:00', '416:40:00'); + +# check if MAKETIME() handles out-of-range values correctly +SELECT MAKETIME(916,0,0); +SELECT MAKETIME(4294967296, 0, 0); +SELECT MAKETIME(-4294967296, 0, 0); +SELECT MAKETIME(0, 4294967296, 0); +SELECT MAKETIME(0, 0, 4294967296); +SELECT MAKETIME(CAST(-1 AS UNSIGNED), 0, 0); + +# check if EXTRACT() handles out-of-range values correctly +SELECT EXTRACT(HOUR FROM '100000:02:03'); + +# check if we get proper warnings if both input string truncation +# and out-of-range value occur +CREATE TABLE t1(f1 TIME); +INSERT INTO t1 VALUES('916:00:00 a'); +SELECT * FROM t1; +DROP TABLE t1; + +# +# Bug #20927: sec_to_time treats big unsigned as signed +# +# check if SEC_TO_TIME() handles BIGINT UNSIGNED values correctly +SELECT SEC_TO_TIME(CAST(-1 AS UNSIGNED)); # 21913: DATE_FORMAT() Crashes mysql server if I use it through # mysql-connector-j driver. # diff --git a/mysql-test/t/init_file.test b/mysql-test/t/init_file.test index de6aca455bd..bbe0c4ff884 100644 --- a/mysql-test/t/init_file.test +++ b/mysql-test/t/init_file.test @@ -4,6 +4,7 @@ # # See mysql-test/std_data/init_file.dat and # mysql-test/t/init_file-master.opt for the actual test -# +# -# End of 4.1 tests +--echo ok +--echo End of 4.1 tests diff --git a/mysql-test/t/innodb_mysql.test b/mysql-test/t/innodb_mysql.test index 9593982826b..c5a5e997775 100644 --- a/mysql-test/t/innodb_mysql.test +++ b/mysql-test/t/innodb_mysql.test @@ -145,4 +145,20 @@ SELECT * FROM t1, t2; DROP TABLE t2, t1; ---echo End of 4.1 tests. +# +# Bug #22728 - Handler_rollback value is growing +# +flush status; +create table t1 (c1 int) engine=innodb; +connect (con1,localhost,root,,); +connect (con2,localhost,root,,); +connection con2; +handler t1 open; +handler t1 read first; +disconnect con2; +connection con1; +show /*!50002 GLOBAL */ status like 'Handler_rollback'; +connection default; +drop table t1; +disconnect con1; +--echo End of 4.1 tests diff --git a/mysql-test/t/lowercase_table3.test b/mysql-test/t/lowercase_table3.test index 549bd7d8045..bf48710d4a6 100644 --- a/mysql-test/t/lowercase_table3.test +++ b/mysql-test/t/lowercase_table3.test @@ -5,12 +5,8 @@ # --source include/have_innodb.inc ---require r/lowercase0.require -disable_query_log; -show variables like "lower_case_%"; ---require r/true.require -select convert(@@version_compile_os using latin1) NOT IN ("NT","WIN2000","Win95/Win98","XP") as "TRUE"; -enable_query_log; +--source include/have_lowercase0.inc +--source include/not_windows.inc --disable_warnings DROP TABLE IF EXISTS t1,T1; diff --git a/mysql-test/t/mysql_client.test b/mysql-test/t/mysql_client.test index b382357dacf..2a7f4a935bb 100644 --- a/mysql-test/t/mysql_client.test +++ b/mysql-test/t/mysql_client.test @@ -33,3 +33,22 @@ # --exec echo 'help' | $MYSQL > $MYSQLTEST_VARDIR/tmp/bug20328.tmp --exec echo 'help ' | $MYSQL > $MYSQLTEST_VARDIR/tmp/bug20328.tmp + +# +# Bug#17583: mysql drops connection when stdout is not writable +# +create table t17583 (a int); +insert into t17583 (a) values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +insert into t17583 select a from t17583; +insert into t17583 select a from t17583; +insert into t17583 select a from t17583; +insert into t17583 select a from t17583; +insert into t17583 select a from t17583; +insert into t17583 select a from t17583; +insert into t17583 select a from t17583; +# Close to the minimal data needed to exercise bug. +select count(*) from t17583; +--exec echo "select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; " |$MYSQL test >&- +drop table t17583; + +--echo End of 4.1 tests. diff --git a/mysql-test/t/mysql_client_test.test b/mysql-test/t/mysql_client_test.test index 1225bf73009..2677e470289 100644 --- a/mysql-test/t/mysql_client_test.test +++ b/mysql-test/t/mysql_client_test.test @@ -8,8 +8,8 @@ # server or run mysql-test-run --debug mysql_client_test and check # var/log/mysql_client_test.trace ---disable_result_log ---exec echo $MYSQL_CLIENT_TEST --getopt-ll-test=25600M ---exec $MYSQL_CLIENT_TEST --getopt-ll-test=25600M +--exec echo "$MYSQL_CLIENT_TEST --getopt-ll-test=25600M" > $MYSQLTEST_VARDIR/log/mysql_client_test.log 2>&1 +--exec $MYSQL_CLIENT_TEST --getopt-ll-test=25600M >> $MYSQLTEST_VARDIR/log/mysql_client_test.log 2>&1 # End of 4.1 tests +echo ok; diff --git a/mysql-test/t/mysql_protocols.test b/mysql-test/t/mysql_protocols.test index 0253c2b5d17..5eba780420c 100644 --- a/mysql-test/t/mysql_protocols.test +++ b/mysql-test/t/mysql_protocols.test @@ -1,5 +1,7 @@ # Embedded server doesn't support external clients --source include/not_embedded.inc +# Windows does not have SOCKET, but will try to create a PIPE as well as MEMORY +--source include/not_windows.inc # test for Bug #4998 "--protocol doesn't reject bad values" diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test index a15a143e9f4..3c20b38722f 100644 --- a/mysql-test/t/mysqltest.test +++ b/mysql-test/t/mysqltest.test @@ -1,3 +1,4 @@ +# This test should work in embedded server after mysqltest is fixed -- source include/not_embedded.inc # ============================================================================ @@ -26,7 +27,7 @@ # ---------------------------------------------------------------------------- # $mysql_errno contains the return code of the last command -# send to the server. +# sent to the server. # ---------------------------------------------------------------------------- # get $mysql_errno before the first statement # $mysql_errno should be -1 @@ -49,7 +50,7 @@ select otto from (select 1 as otto) as t1; # ---------------------------------------------------------------------------- # Negative case(statement): -# The dervied table t1 does not contain a column named 'friedrich' . +# The derived table t1 does not contain a column named 'friedrich' . # --> ERROR 42S22: Unknown column 'friedrich' in 'field list and # --> 1054: Unknown column 'friedrich' in 'field list' # ---------------------------------------------------------------------------- @@ -67,7 +68,7 @@ select friedrich from (select 1 as otto) as t1; # The following unmasked unsuccessful statement must give # 1. mysqltest gives a 'failed' # 2. does not produce a r/<test case>.reject file !!! -# PLEASE uncomment it and check it's effect +# PLEASE uncomment it and check its effect #select friedrich from (select 1 as otto) as t1; @@ -113,7 +114,7 @@ select friedrich from (select 1 as otto) as t1; # test cases for $mysql_errno # # $mysql_errno is a builtin variable of mysqltest and contains the return code -# of the last command send to the server. +# of the last command sent to the server. # # The following test cases often initialize $mysql_errno to 1064 by # a command with wrong syntax. @@ -216,7 +217,7 @@ garbage ; execute stmt; eval select $mysql_errno as "after_successful_execute" ; -# failing execute (table dropped) +# failing execute (table has been dropped) drop table t1; --error 1064 garbage ; @@ -248,8 +249,8 @@ eval select $mysql_errno as "after_failing_deallocate" ; # ---------------------------------------------------------------------------- # test cases for "--disable_abort_on_error" # -# "--disable_abort_on_error" switches the abort of mysqltest -# after "unmasked" failing statements off. +# "--disable_abort_on_error" switches off the abort of mysqltest +# after "unmasked" failing statements. # # The default is "--enable_abort_on_error". # @@ -257,13 +258,13 @@ eval select $mysql_errno as "after_failing_deallocate" ; # --error <error number> and --error <error number> # in the line before the failing statement. # -# There are some additional test case for $mysql_errno +# There are some additional test cases for $mysql_errno # because "--disable_abort_on_error" enables a new situation. # Example: "unmasked" statement fails + analysis of $mysql_errno # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Switch the abort on error off and check the effect on $mysql_errno +# Switch off the abort on error and check the effect on $mysql_errno # ---------------------------------------------------------------------------- --error 1064 garbage ; @@ -316,7 +317,6 @@ select 3 from t1 ; # #select 3 from t1 ; -# End of 4.1 tests --error 1 --exec echo "disable_abort_on_error; enable_abort_on_error; error 1064; select 3 from t1; select 3 from t1;" | $MYSQL_TEST 2>&1 @@ -344,7 +344,7 @@ select 3 from t1 ; # ---------------------------------------------------------------------------- # Test detect end of line "junk" -# Most likely causes by a missing delimiter +# Most likely caused by a missing delimiter # ---------------------------------------------------------------------------- # Too many parameters to function @@ -359,23 +359,157 @@ select 3 from t1 ; # Missing delimiter # The comment will be "sucked into" the sleep command since # delimiter is missing until after "show status" ---system echo "sleep 4" > var/log/mysqltest.sql ---system echo "# A comment" >> var/log/mysqltest.sql ---system echo "show status;" >> var/log/mysqltest.sql +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql +sleep 4 +# A comment +show status; +EOF --error 1 ---exec $MYSQL_TEST < var/log/mysqltest.sql 2>&1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 + +# +# Missing delimiter until eof +# The comment will be "sucked into" the sleep command since +# delimiter is missing +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql +sleep 7 +# Another comment +EOF +--error 1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 + +# +# Missing delimiter until "disable_query_log" +# +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql +disconnect default + +# +# comment +# comment 3 +disable_query_log; +EOF +--error 1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 + +# +# Missing delimiter until "disable_query_log" +# +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql +disconnect default + +# +# comment + +# comment 3 +disable_query_log; +EOF +--error 1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 + +# +# Missing delimiter until eof +# +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql +disconnect default + +# +# comment +# comment2 + +# comment 3 +--disable_query_log +EOF +--error 1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 + +# +# Missing delimiter until eof +# +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql +disconnect default # comment +# comment part2 + +# comment 3 +--disable_query_log +EOF +--error 1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 # # Extra delimiter # --error 1 --exec echo "--sleep 4;" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "--disable_query_log;" | $MYSQL_TEST 2>&1 # Allow trailing # comment --sleep 1 # Wait for insert delayed to be executed. --sleep 1 # Wait for insert delayed to be executed. +# ---------------------------------------------------------------------------- +# Test error +# ---------------------------------------------------------------------------- + +# Missing argument +--error 1 +--exec echo "error;" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "--error" | $MYSQL_TEST 2>&1 + +# First char must be uppercase 'S' or 'E' or [0-9] +--error 1 +--exec echo "--error s99999" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "--error e99999" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "--error 9eeeee" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "--error 1sssss" | $MYSQL_TEST 2>&1 + +# First char 'S' but too long +--error 1 +--exec echo "--error S999999" | $MYSQL_TEST 2>&1 + +# First char 'S' but lowercase char found +--error 1 +--exec echo "--error S99a99" | $MYSQL_TEST 2>&1 + +# First char 'S' but too short +--error 1 +--exec echo "--error S9999" | $MYSQL_TEST 2>&1 + +# First char 'E' but not found in error array +--error 1 +--exec echo "--error E9999" | $MYSQL_TEST 2>&1 + +# First char [0-9] but contains chars +--error 1 +--exec echo "--error 999e9" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "--error 9b" | $MYSQL_TEST 2>&1 + +# Multiple errorcodes separated by ',' +--error 1,1,1,1 +#--error 9,ER_PARSE_ERROR +#--error ER_PARSE_ERROR +#--error 9,ER_PARSE_ERROR,9,ER_PARSE_ERROR +#--error 9, ER_PARSE_ERROR, 9, ER_PARSE_ERROR +#--error 9,S00000,9,ER_PARSE_ERROR +#--error 9,S00000,9,ER_PARSE_ERROR,ER_PARSE_ERROR,ER_PARSE_ERROR,9,10,11,12 +--error 9,S00000,9 +--error 9,S00000,9,9,10,11,12 +--error 9 ,10 +--error 9 , 10 +--error 9 , 10 +--error 9 , 10 + +# Too many errorcodes specified +--error 1 +--exec echo "--error 1,2,3,4,5,6,7,8,9,10,11" | $MYSQL_TEST 2>&1 + # ---------------------------------------------------------------------------- # Test echo command @@ -399,8 +533,8 @@ echo - MySQL: The world''s - source database; echo - MySQL: The world''s --- most popular open --- source database; +-- most popular +-- open source database; echo # MySQL: The --world''s @@ -419,7 +553,7 @@ echo ; # Illegal use of echo --error 1 ---exec echo "echo $;" | $MYSQL_TEST 2>&1 +--exec echo "echo \$;" | $MYSQL_TEST 2>&1 # ---------------------------------------------------------------------------- @@ -457,8 +591,8 @@ echo $message; let $message= -- MySQL: The -- world''s most --- popular open --- source database; +-- popular +-- open source database; echo $message; let $message= # MySQL: The @@ -495,9 +629,21 @@ echo $where2; let $where3=a long $where variable content; echo $where3; +let $where3=a long \\\$where variable content; +echo $where3; + let $novar1= $novar2; echo $novar1; +let $cat=na; +let $cat=ba$cat$cat; +echo banana = $cat; + +# ba\$cat\$cat should have been sufficient. +# ba\\\$cat\\\$cat -> ba\$cat\$cat -> ba$cat$cat -> banana +# Magnus' upcoming patch will fix the missing second interpretation. +let $cat=ba\\\$cat\\\$cat; +echo Not a banana: $cat; # Test illegal uses of let @@ -506,22 +652,19 @@ echo $novar1; --exec echo "let ;" | $MYSQL_TEST 2>&1 --error 1 ---exec echo "let $=hi;" | $MYSQL_TEST 2>&1 +--exec echo "let \$=hi;" | $MYSQL_TEST 2>&1 --error 1 ---exec echo "let hi=hi;" | $MYSQL_TEST 2>&1 +--exec echo "let \$1 hi;" | $MYSQL_TEST 2>&1 --error 1 ---exec echo "let $1 hi;" | $MYSQL_TEST 2>&1 +--exec echo "let \$m hi;" | $MYSQL_TEST 2>&1 --error 1 ---exec echo "let $m hi;" | $MYSQL_TEST 2>&1 +--exec echo "let \$hi;" | $MYSQL_TEST 2>&1 --error 1 ---exec echo "let $hi;" | $MYSQL_TEST 2>&1 - ---error 1 ---exec echo "let $ hi;" | $MYSQL_TEST 2>&1 +--exec echo "let \$ hi;" | $MYSQL_TEST 2>&1 --error 1 --exec echo "let =hi;" | $MYSQL_TEST 2>&1 @@ -529,6 +672,55 @@ echo $novar1; --error 1 --exec echo "let hi;" | $MYSQL_TEST 2>&1 +# More advanced test for bug#17280 +let $success= 1; +--echo # Execute: --echo # <whatever> success: \$success +--echo # <whatever> success: $success +--echo # Execute: echo # <whatever> success: \$success ; +echo # <whatever> success: $success ; + +--echo # The next two variants work fine and expand the content of \$success +--echo # Execute: --echo \$success +--echo $success +--echo # Execute: echo \$success ; +echo $success ; + +# ---------------------------------------------------------------------------- +# Test to assign let from query +# let $<var_name>=`<query>`; +# ---------------------------------------------------------------------------- +--disable_parsing +echo var1; +let $var1= `select "hi" as "Col", 1 as "Column1", "hi there" as Col3`; +echo $var1; +echo $var1_Col; +echo $var1_Column1; +echo $var1_Col3; + +echo var2; +let $var2= `select 2 as "Column num 2"`; +echo $var2; +echo $var2_Column num 2; +echo $var2_Column; + +echo var2 again; +let $var2= `select 2 as "Column num 2"`; +echo $var2; +echo $var2_Column num 2; +echo $var2_Column_num_2; +echo $var2_Column; + +echo var3 two columns with same name; +let $var3= `select 1 as "Col", 2 as "Col", 3 as "var3"`; +echo $var3; +echo $var3_Col; +echo $var3_Col; +echo $var3_var3; + +#echo failing query in let; +#--error 1 +#--exec echo "let $var2= `failing query;`" | $MYSQL_TEST 2>&1 +--enable_parsing # ---------------------------------------------------------------------------- # Test source command # ---------------------------------------------------------------------------- @@ -538,29 +730,36 @@ echo $novar1; --error 1 --exec echo "source ;" | $MYSQL_TEST 2>&1 +# Fix win paths +--replace_result \\ / +# Source a nonexisting file --error 1 --exec echo "source non_existingFile;" | $MYSQL_TEST 2>&1 # Too many source ---exec echo "source var/tmp/recursive.sql;" > var/tmp/recursive.sql +--exec echo "source $MYSQLTEST_VARDIR/tmp/recursive.sql;" > $MYSQLTEST_VARDIR/tmp/recursive.sql +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --error 1 ---exec echo "source var/tmp/recursive.sql;" | $MYSQL_TEST 2>&1 +--exec echo "source $MYSQLTEST_VARDIR/tmp/recursive.sql;" | $MYSQL_TEST 2>&1 # Source a file with error ---exec echo "garbage ;" > var/tmp/error.sql +--exec echo "garbage ;" > $MYSQLTEST_VARDIR/tmp/error.sql +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --error 1 ---exec echo "source var/tmp/error.sql;" | $MYSQL_TEST 2>&1 +--exec echo "source $MYSQLTEST_VARDIR/tmp/error.sql;" | $MYSQL_TEST 2>&1 # Test execution of source in a while loop ---exec echo "echo here is the sourced script;" > var/tmp/sourced.sql +--write_file $MYSQLTEST_VARDIR/tmp/sourced.inc +echo here is the sourced script; +EOF --disable_query_log let $outer= 2; # Number of outer loops while ($outer) { eval SELECT '$outer = outer loop variable after while' AS ""; - --source var/tmp/sourced.sql + --source $MYSQLTEST_VARDIR/tmp/sourced.inc eval SELECT '$outer = outer loop variable before dec' AS ""; dec $outer; @@ -581,7 +780,6 @@ while ($outer) # Test execution of source in a while loop ---exec echo "--source var/tmp/sourced.sql" > var/tmp/sourced1.sql --disable_abort_on_error # Sourcing of a file within while loop, sourced file will # source other file @@ -589,11 +787,12 @@ let $num= 9; while ($num) { SELECT 'In loop' AS ""; - --source var/tmp/sourced1.sql + --source $MYSQLTEST_VARDIR/tmp/sourced.inc dec $num; } --enable_abort_on_error --enable_query_log +--remove_file $MYSQLTEST_VARDIR/tmp/sourced.inc # ---------------------------------------------------------------------------- # Test sleep command @@ -683,7 +882,7 @@ system echo "hej" > /dev/null; --exec echo "system false;" | $MYSQL_TEST 2>&1 --disable_abort_on_error -system NonExistsinfComamdn; +system NonExistsinfComamdn 2> /dev/null; --enable_abort_on_error @@ -700,6 +899,30 @@ echo test3stop --delimiter ; echo test4; + +# ---------------------------------------------------------------------------- +# Test if +# ---------------------------------------------------------------------------- + +let $counter=10; +if ($counter) +{ + echo Counter is greater than 0, (counter=10); +} +if (!$counter) +{ + echo Counter is not 0, (counter=10); +} +let $counter=0; +if ($counter) +{ + echo Counter is greater than 0, (counter=0); +} +if (!$counter) +{ + echo Counter is not 0, (counter=0); +} + # ---------------------------------------------------------------------------- # Test while, { and } # ---------------------------------------------------------------------------- @@ -713,11 +936,158 @@ while ($i) # One liner #let $i=1;while ($i){echo $i;dec $i;} - +let $i=0; +while (!$i) +{ + echo Testing while with not; + inc $i; +} # Exceed max nesting level +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest_while.inc +let $1 = 10; +while ($1) +{ +while ($1) +{ +while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + while ($1) +{ + echo $1; + dec $1; +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +} +EOF +# Fix win path +--replace_result \\ / $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --error 1 ---exec echo "source include/mysqltest_while.inc;" | $MYSQL_TEST 2>&1 +--exec echo "source $MYSQLTEST_VARDIR/tmp/mysqltest_while.inc;" | $MYSQL_TEST 2>&1 +--remove_file $MYSQLTEST_VARDIR/tmp/mysqltest_while.inc --error 1 --exec echo "while \$i;" | $MYSQL_TEST 2>&1 --error 1 @@ -731,20 +1101,20 @@ while ($i) --error 1 --exec echo "{;" | $MYSQL_TEST 2>&1 ---system echo "while (0)" > var/log/mysqltest.sql ---system echo "echo hej;" >> var/log/mysqltest.sql +--system echo "while (0)" > $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "echo hej;" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql --error 1 ---exec $MYSQL_TEST < var/log/mysqltest.sql 2>&1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 ---system echo "while (0)" > var/log/mysqltest.sql ---system echo "{echo hej;" >> var/log/mysqltest.sql +--system echo "while (0)" > $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "{echo hej;" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql --error 1 ---exec $MYSQL_TEST < var/log/mysqltest.sql 2>&1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 ---system echo "while (0){" > var/log/mysqltest.sql ---system echo "echo hej;" >> var/log/mysqltest.sql +--system echo "while (0){" > $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "echo hej;" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql --error 1 ---exec $MYSQL_TEST < var/log/mysqltest.sql 2>&1 +--exec $MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/mysqltest.sql 2>&1 # ---------------------------------------------------------------------------- # Test error messages returned from comments starting with a command @@ -772,7 +1142,7 @@ select "a" as col1, "c" as col2; --exec echo "replace_result a;" | $MYSQL_TEST 2>&1 --error 1 --exec echo "replace_result a ;" | $MYSQL_TEST 2>&1 ---exec echo "replace_result a b;" | $MYSQL_TEST 2>&1 +--exec echo "replace_result a b; echo OK;" | $MYSQL_TEST 2>&1 --error 1 --exec echo "--replace_result a b c" | $MYSQL_TEST 2>&1 --error 1 @@ -809,6 +1179,71 @@ select "a" as col1, "c" as col2; --error 1 --exec echo "save_master_pos; sync_with_master a;" | $MYSQL_TEST 2>&1 +# ---------------------------------------------------------------------------- +# Test connect +# ---------------------------------------------------------------------------- + +--error 1 +--exec echo "connect;" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "connect ();" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "connect (con2);" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "connect (con2,);" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "connect (con2,localhost,root,,illegal_db);" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "connect (con1,localhost,root,,,illegal_port,);" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "connect (con1,localhost,root,,,,,SMTP POP);" | $MYSQL_TEST 2>&1 + +# Repeat connect/disconnect +--write_file $MYSQLTEST_VARDIR/tmp/mysqltest.sql +let $i=100; +while ($i) +{ + connect (test_con1,localhost,root,,); + disconnect test_con1; + dec $i; +} +EOF +--exec echo "source $MYSQLTEST_VARDIR/tmp/mysqltest.sql; echo OK;" | $MYSQL_TEST 2>&1 + +# Repeat connect/disconnect, exceed max number of connections +--system echo "let \$i=200;" > $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "while (\$i)" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "{" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo " connect (test_con1,localhost,root,,); " >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo " disconnect test_con1; " >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo " dec \$i; " >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "}" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--error 1 +--exec echo "source $MYSQLTEST_VARDIR/tmp/mysqltest.sql;" | $MYSQL_TEST 2>&1 + +# Select disconnected connection +--system echo "connect (test_con1,localhost,root,,);" > $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "disconnect test_con1; " >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "connection test_con1;" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--error 1 +--exec echo "source $MYSQLTEST_VARDIR/tmp/mysqltest.sql;" | $MYSQL_TEST 2>&1 + +# Connection name already used +--system echo "connect (test_con1,localhost,root,,);" > $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--system echo "connect (test_con1,localhost,root,,);" >> $MYSQLTEST_VARDIR/tmp/mysqltest.sql +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--error 1 +--exec echo "source $MYSQLTEST_VARDIR/tmp/mysqltest.sql;" | $MYSQL_TEST 2>&1 + +# connect when "disable_abort_on_error" caused "connection not found" +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--disable_abort_on_error +connect (con1,localhost,root,,); +connection default; +connection con1; +--enable_abort_on_error # ---------------------------------------------------------------------------- # Test mysqltest arguments @@ -818,6 +1253,8 @@ select "a" as col1, "c" as col2; --exec $MYSQL_TEST < $MYSQL_TEST_DIR/include/mysqltest-x.inc --exec $MYSQL_TEST -x $MYSQL_TEST_DIR/include/mysqltest-x.inc --exec $MYSQL_TEST --test_file=$MYSQL_TEST_DIR/include/mysqltest-x.inc +# Fix Win paths +--replace_result \\ / --error 1 --exec $MYSQL_TEST -x non_existing_file.inc 2>&1 @@ -841,3 +1278,289 @@ while ($num) SELECT 1 as a; + +# +# Bug #10251: Identifiers containing quotes not handled correctly +# +select 1 as `a'b`, 2 as `a"b`; + +# Test escaping of quotes +select 'aaa\\','aa''a',"aa""a"; + +# +# Check of include/show_msg.inc and include/show_msg80.inc +# + +# The message contains in most cases a string with the default character set +let $message= Here comes a message; +--source include/show_msg.inc + +# The message could also contain a string with character set utf8 +let $message= `SELECT USER()`; +--source include/show_msg.inc + +# The message contains more then 80 characters on multiple lines +# and is kept between double quotes. +let $message= +"Here comes a very very long message that + - is longer then 80 characters and + - consists of several lines"; +--source include/show_msg80.inc + +# The message contains more then 80 characters on multiple lines +# and uses the auxiliary character "." at the beginning of the message lines. +let $message= . Here comes a very very long message that + . - is longer then 80 characters and + . - consists of several lines; +--source include/show_msg80.inc + +# +# Test --enable_parsing / disable_parsing +# +--disable_query_log +--disable_parsing +# The following will not enable query logging +--enable_query_log +select "this will not be executed"; +--enable_parsing +select "this will be executed"; +--enable_query_log + +# +# Test zero length result file. Should not pass +# +--exec touch $MYSQLTEST_VARDIR/tmp/zero_length_file.result +--exec echo "echo ok;" > $MYSQLTEST_VARDIR/tmp/query.sql +--error 1 +--exec $MYSQL_TEST -x $MYSQLTEST_VARDIR/tmp/query.sql -R $MYSQLTEST_VARDIR/tmp/zero_length_file.result 2>&1 +# +# Test that a test file that does not generate any output fails. +# +--exec echo "let \$i= 1;" > $MYSQLTEST_VARDIR/tmp/query.sql +--error 1 +--exec $MYSQL_TEST -x $MYSQLTEST_VARDIR/tmp/query.sql 2>&1 + +# +# Test that mysqltest fails when there are no queries executed +# but a result file exists +# NOTE! This will never happen as long as it's not allowed to have +# test files that produce no output +#--exec echo "something" > $MYSQLTEST_VARDIR/tmp/result_file.result +#--exec echo "let \$i= 1;" > $MYSQLTEST_VARDIR/tmp/query.sql +#--error 1 +#--exec $MYSQL_TEST -x $MYSQLTEST_VARDIR/tmp/query.sql -R $MYSQLTEST_VARDIR/tmp/result_file.result 2>&1 + +# +# Bug #11731 mysqltest in multi-statement queries ignores errors in +# non-1st queries +# + +echo Failing multi statement query; +# PS does not support multi statement +--exec echo "--disable_ps_protocol" > $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "delimiter ||||;" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "create table t1 (a int primary key);" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "insert into t1 values (1);" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "select 'select-me';" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "insertz 'error query'||||" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "delimiter ;||||" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql + +--error 1 +--exec $MYSQL_TEST -x $MYSQLTEST_VARDIR/tmp/bug11731.sql 2>&1 +drop table t1; + +--error 1 +--exec $MYSQL_TEST --record -x $MYSQLTEST_VARDIR/tmp/bug11731.sql -R $MYSQLTEST_VARDIR/tmp/bug11731.out 2>&1 +# The .out file should be non existent +--exec test ! -s $MYSQLTEST_VARDIR/tmp/bug11731.out +drop table t1; + + +echo Multi statement using expected error; +# PS does not support multi statement +--exec echo "--disable_ps_protocol" > $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "delimiter ||||;" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "--error 1064" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "create table t1 (a int primary key);" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "insert into t1 values (1);" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "select 'select-me';" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "insertz "error query"||||" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql +--exec echo "delimiter ;||||" >> $MYSQLTEST_VARDIR/tmp/bug11731.sql + +# These two should work since the error is expected +--exec $MYSQL_TEST -x $MYSQLTEST_VARDIR/tmp/bug11731.sql 2>&1 +drop table t1; + +--exec $MYSQL_TEST --record -x $MYSQLTEST_VARDIR/tmp/bug11731.sql -R $MYSQLTEST_VARDIR/tmp/bug11731.out 2>&1 +# The .out file should exist +--exec test -s $MYSQLTEST_VARDIR/tmp/bug11731.out +drop table t1; + +# +# Bug#19890 mysqltest: "query" command is broken +# + +# It should be possible to use the command "query" to force mysqltest to +# send the command to the server although it's a builtin mysqltest command. +--error 1064 +query sleep; + +--error 1064 +--query sleep + +# Just an empty query command +--error 1065 +query ; + +# test for replace_regex +--replace_regex /at/b/ +select "at" as col1, "c" as col2; + +--replace_regex /at/b/i +select "at" as col1, "AT" as col2, "c" as col3; + +--replace_regex /a/b/ /ct/d/ +select "a" as col1, "ct" as col2; + +--replace_regex /(strawberry)/raspberry and \1/ /blueberry/blackberry/ /potato/tomato/; +select "strawberry","blueberry","potato"; + +--error 1 +--exec echo "--replace_regex a" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "--replace_regex a;" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "replace_regex a;" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "replace_regex a ;" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "replace_regex a b; echo OK;" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "--replace_regex /a b c" | $MYSQL_TEST 2>&1 +--error 1 +--exec echo "replace_regex /a /b c ;" | $MYSQL_TEST 2>&1 + +# REQUIREMENT +# replace_regex should replace substitutions from left to right in output + +create table t1 (a int, b int); +insert into t1 values (1,3); +insert into t1 values (2,4); +--replace_regex /A/C/ /B/D/i /3/2/ /2/1/ +select * from t1; +drop table t1; + +# ---------------------------------------------------------------------------- +# test for remove_file +# ---------------------------------------------------------------------------- + +--error 1 +--exec echo "remove_file ;" | $MYSQL_TEST 2>&1 + +--error 1 +remove_file non_existing_file; + +# ---------------------------------------------------------------------------- +# test for write_file +# ---------------------------------------------------------------------------- +--error 1 +--exec echo "write_file ;" | $MYSQL_TEST 2>&1 + +--error 1 +--exec echo "write_file filename ;" | $MYSQL_TEST 2>&1 + +--error 1 +--exec echo "write_file filename \";" | $MYSQL_TEST 2>&1 + +write_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +Content for test_file1 +EOF +file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +remove_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; + +write_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp END_DELIMITER; +Content for test_file1 contains EOF +END_DELIMITER +file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +remove_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; + +# ---------------------------------------------------------------------------- +# test for file_exist +# ---------------------------------------------------------------------------- +--error 1 +--exec echo "file_exists ;" | $MYSQL_TEST 2>&1 + +--error 0,1 +remove_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +--error 1 +file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +write_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +Content for test_file1 +EOF +file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +remove_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp; +--error 1 +file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp; + + +# ---------------------------------------------------------------------------- +# test for copy_file +# ---------------------------------------------------------------------------- +--write_file $MYSQLTEST_VARDIR/tmp/file1.tmp +file1 +EOF + +copy_file $MYSQLTEST_VARDIR/tmp/file1.tmp $MYSQLTEST_VARDIR/tmp/file2.tmp; +file_exists $MYSQLTEST_VARDIR/tmp/file2.tmp; +remove_file $MYSQLTEST_VARDIR/tmp/file1.tmp; +remove_file $MYSQLTEST_VARDIR/tmp/file2.tmp; + +--error 1 +--exec echo "copy_file ;" | $MYSQL_TEST 2>&1 + +--error 1 +--exec echo "copy_file from_file;" | $MYSQL_TEST 2>&1 + +# ---------------------------------------------------------------------------- +# test for perl +# ---------------------------------------------------------------------------- +--perl +print "hello\n"; +EOF + +--perl EOF +print "hello\n"; +EOF + +--perl DELIMITER +print "hello\n"; +DELIMITER + +--error 1 +--exec echo "perl TOO_LONG_DELIMITER ;" | $MYSQL_TEST 2>&1 + +perl; +print "hello\n"; +EOF + +perl; + # Print "hello" + print "hello\n"; +EOF + +# ---------------------------------------------------------------------------- +# test for die +# ---------------------------------------------------------------------------- + +--error 1 +--exec echo "die test of die;" | $MYSQL_TEST 2>&1 + + +# ---------------------------------------------------------------------------- +# test for exit +# ---------------------------------------------------------------------------- + +--exec echo "echo Some output; exit; echo Not this;" | $MYSQL_TEST 2>&1 + + +--echo End of tests diff --git a/mysql-test/t/not_embedded_server-master.opt b/mysql-test/t/not_embedded_server-master.opt index 35fcc5f30c6..cef79bc8585 100644 --- a/mysql-test/t/not_embedded_server-master.opt +++ b/mysql-test/t/not_embedded_server-master.opt @@ -1 +1 @@ ---loose-to-force-a-restart +--force-restart diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 76fcb49d598..5b488ae4393 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -351,14 +351,14 @@ create table t1 (a int, b int); insert into t1 (a, b) values (1,1), (1,2), (2,1), (2,2); prepare stmt from "explain select * from t1 where t1.a=2 and t1.a=t1.b and t1.b > 1 + ?"; ---replace_column 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - set @v=5; -execute stmt using @v; --replace_column 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - -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 6f15ba8f673..ec4964d116a 100644 --- a/mysql-test/t/ps_1general.test +++ b/mysql-test/t/ps_1general.test @@ -305,8 +305,8 @@ prepare stmt4 from ' show table status from test like ''t9%'' '; --replace_result 2147483647 4294967295 # Bug#4288 execute stmt4; ---replace_column 2 # prepare stmt4 from ' show status like ''Threads_running'' '; +--replace_column 2 # execute stmt4; prepare stmt4 from ' show variables like ''sql_mode'' '; execute stmt4; diff --git a/mysql-test/t/ps_grant.test b/mysql-test/t/ps_grant.test index 4cb056db358..afd543caacc 100644 --- a/mysql-test/t/ps_grant.test +++ b/mysql-test/t/ps_grant.test @@ -35,7 +35,7 @@ use mysqltest; --source include/ps_create.inc --source include/ps_renew.inc --enable_query_log -eval use $DB; +use test; grant usage on mysqltest.* to second_user@localhost identified by 'looser' ; grant select on mysqltest.t9 to second_user@localhost diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index 9280911df2c..245178d7d4a 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -510,4 +510,14 @@ OR ((pk4 =1) AND (((pk1 IN ( 7, 2, 1 ))) OR (pk1 =522)) AND ((pk2 IN ( 0, 2635)) ) AND (pk3 >=1000000); drop table t1, t2; -# End of 4.1 tests +# +# Bug #20732: Partial index and long sjis search with '>' fails sometimes +# + +create table t1(a char(2), key(a(1))); +insert into t1 values ('x'), ('xx'); +explain select a from t1 where a > 'x'; +select a from t1 where a > 'x'; +drop table t1; + +--echo End of 4.1 tests diff --git a/mysql-test/t/rename.test b/mysql-test/t/rename.test index 5caecef176e..b0fb60c0ee4 100644 --- a/mysql-test/t/rename.test +++ b/mysql-test/t/rename.test @@ -43,8 +43,8 @@ select * from t3; drop table if exists t1,t2,t3,t4; # -# Test-case for Bug #2397 RENAME TABLES is not blocked by -# FLUSH TABLES WITH READ LOCK +# Bug #2397 RENAME TABLES is not blocked by +# FLUSH TABLES WITH READ LOCK # connect (con1,localhost,root,,); @@ -58,10 +58,14 @@ FLUSH TABLES WITH READ LOCK; connection con1; send RENAME TABLE t1 TO t2, t3 to t4; connection con2; -sleep 1; show tables; UNLOCK TABLES; -sleep 1; + +# Wait for the the tables to be renamed +# i.e the query below succeds +let $query= select * from t2, t4; +source include/wait_for_query_to_suceed.inc; + show tables; drop table t2, t4; diff --git a/mysql-test/t/rpl000015.test b/mysql-test/t/rpl000015.test index a9520676e1e..4e329fc87ea 100644 --- a/mysql-test/t/rpl000015.test +++ b/mysql-test/t/rpl000015.test @@ -7,24 +7,24 @@ save_master_pos; connection slave; reset slave; --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # show slave status; change master to master_host='127.0.0.1'; # The following needs to be cleaned up when change master is fixed --replace_result $MASTER_MYPORT MASTER_PORT $MYSQL_TCP_PORT MASTER_PORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # show slave status; --replace_result $MASTER_MYPORT MASTER_PORT eval change master to master_host='127.0.0.1',master_user='root', master_password='',master_port=$MASTER_MYPORT; --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # show slave status; start slave; sync_with_master; --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # show slave status; connection master; --disable_warnings diff --git a/mysql-test/t/rpl000018.test b/mysql-test/t/rpl000018.test deleted file mode 100644 index aee052ffd28..00000000000 --- a/mysql-test/t/rpl000018.test +++ /dev/null @@ -1,29 +0,0 @@ -# -# Running test with abort-slave-event-count=1 -# This will force slave to reconnect after every event -# - -require_manager; -connect (master,localhost,root,,test,0,master.sock); -connect (slave,localhost,root,,test,0,slave.sock); -connection master; -reset master; -server_stop master; -server_start master; -connection slave; -reset slave; -start slave; -connection master; -show binary logs; ---disable_warnings -drop table if exists t1; ---enable_warnings -create table t1(n int); -insert into t1 values (3351); -sync_slave_with_master; -select * from t1; -connection master; -drop table t1; -sync_slave_with_master; - -# End of 4.1 tests diff --git a/mysql-test/t/rpl_chain_temp_table.test b/mysql-test/t/rpl_chain_temp_table.test deleted file mode 100644 index 96e228a17a1..00000000000 --- a/mysql-test/t/rpl_chain_temp_table.test +++ /dev/null @@ -1,101 +0,0 @@ - # This test makes some assumptions about values of thread ids, which should be -# true if the servers have been restarted for this test. So we want to -# stop/restart servers. Note that if assumptions are wrong, the test will not -# fail; it will just fail to test the error-prone scenario. -# Using the manager is the only way to have more than one slave server. -# So you must run this test with --manager. - -require_manager; -server_stop master; -server_start master; -server_stop slave; -server_start slave; -# no need for slave_sec (no assumptions on thread ids for this server). - -source include/master-slave.inc; -connect (slave_sec,localhost,root,,test,0,slave.sock-1); -connection master; -save_master_pos; -connection slave; -sync_with_master; -reset master; -save_master_pos; -connection slave_sec; -eval change master to master_host='127.0.0.1',master_port=$SLAVE_MYPORT, master_user='root'; -start slave; -sync_with_master; - -# :P now we have a chain ready-to-test. - -connection master; -create temporary table t1 (a int); -save_master_pos; -connection slave; -sync_with_master; -connection master1; -create temporary table t1 (a int); -save_master_pos; -connection slave; -sync_with_master; -save_master_pos; - -# First test: - -connection slave_sec; -# Before BUG#1686 ("If 2 master threads with same-name temp table, slave makes -# bad binlog") was fixed, sync_with_master failed -sync_with_master; -show status like 'slave_open_temp_tables'; - -# 'master' and 'master1' usually have thread id 2-3 or 3-4. -# 'slave' and 'slave1' usually have thread id 2-3. -connection slave; -create temporary table t1 (a int); -connection slave1; -create temporary table t1 (a int); -# So it's likely that in the binlog of slave we get -# server_id=of_master thread_id=3 create temp... -# server_id=of_slave thread_id=3 create temp... -# which would confuse slave-sec unless slave-sec uses server id to distinguish -# between temp tables (here thread id is obviously not enough to distinguish). - -save_master_pos; - -# Second test: - -connection slave_sec; -# If we did not use the server id to distinguish between temp tables, -# sync_with_master would fail -sync_with_master; -show status like 'slave_open_temp_tables'; - -# Third test (BUG#1240 "slave of slave breaks when STOP SLAVE was issud on -# parent slave and temp tables"). -stop slave; -connection slave; -insert into t1 values(1); -create table t2 as select * from t1; -save_master_pos; -connection slave_sec; -start slave; -sync_with_master; -show status like 'slave_open_temp_tables'; -select * from t2; - -# clean up -connection slave; -drop table t2; -save_master_pos; -connection slave_sec; -sync_with_master; - -# On purpose, we don't delete the temporary tables explicitely. -# So temp tables remain on slave (remember they are not deleted when the slave -# SQL thread terminates). If you run this test with -# --valgrind --valgrind-options=--show-reachable=yes -# you will see if they get cleaned up at slave's shutdown (that is, if the -# memory they use is freed (it should) by mysqld before it terminates). -# If they wouldn't be cleaned up, you would see some "still reachable" blocks in -# Valgrind. - -# End of 4.1 tests diff --git a/mysql-test/t/rpl_change_master.test b/mysql-test/t/rpl_change_master.test index befc469e7b5..e7ae798b1a7 100644 --- a/mysql-test/t/rpl_change_master.test +++ b/mysql-test/t/rpl_change_master.test @@ -12,11 +12,12 @@ connection slave; stop slave; select * from t1; --replace_result $MASTER_MYPORT MASTER_MYPORT +--replace_column 1 # 8 # 9 # 23 # 33 # --replace_column 1 # 33 # show slave status; change master to master_user='root'; --replace_result $MASTER_MYPORT MASTER_MYPORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # show slave status; # Will restart from after the values(2), which is bug select release_lock("a"); diff --git a/mysql-test/t/rpl_charset.test b/mysql-test/t/rpl_charset.test index cf079048fc3..a6f4c2ba9be 100644 --- a/mysql-test/t/rpl_charset.test +++ b/mysql-test/t/rpl_charset.test @@ -106,6 +106,8 @@ select * from mysqltest2.t1 order by a; connection master; drop database mysqltest2; drop database mysqltest3; +# file_id: xx can vary depending on previous tests +--replace_regex /file_id=[0-9]/file_id=x/ show binlog events from 79; sync_slave_with_master; diff --git a/mysql-test/t/rpl_drop_db.test b/mysql-test/t/rpl_drop_db.test index 61354198c83..98afc6e3d02 100644 --- a/mysql-test/t/rpl_drop_db.test +++ b/mysql-test/t/rpl_drop_db.test @@ -13,6 +13,7 @@ insert into mysqltest1.t1 values (1); select * from mysqltest1.t1 into outfile 'mysqltest1/f1.txt'; create table mysqltest1.t2 (n int); create table mysqltest1.t3 (n int); +--replace_result \\ / --error 1010 drop database mysqltest1; use mysqltest1; @@ -29,6 +30,7 @@ while ($1) } --enable_query_log +--replace_result \\ / --error 1010 drop database mysqltest1; use mysqltest1; diff --git a/mysql-test/t/rpl_dual_pos_advance-master.opt b/mysql-test/t/rpl_dual_pos_advance-master.opt index 35fcc5f30c6..cef79bc8585 100644 --- a/mysql-test/t/rpl_dual_pos_advance-master.opt +++ b/mysql-test/t/rpl_dual_pos_advance-master.opt @@ -1 +1 @@ ---loose-to-force-a-restart +--force-restart diff --git a/mysql-test/t/rpl_empty_master_crash-master.opt b/mysql-test/t/rpl_empty_master_crash-master.opt new file mode 100644 index 00000000000..cef79bc8585 --- /dev/null +++ b/mysql-test/t/rpl_empty_master_crash-master.opt @@ -0,0 +1 @@ +--force-restart diff --git a/mysql-test/t/rpl_error_ignored_table.test b/mysql-test/t/rpl_error_ignored_table.test index 96900697d5b..8076d835ac0 100644 --- a/mysql-test/t/rpl_error_ignored_table.test +++ b/mysql-test/t/rpl_error_ignored_table.test @@ -15,7 +15,7 @@ sync_with_master; # The port number is different when doing the release build with # Do-compile, hence we have to replace the port number here accordingly --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # show slave status; # check that the table has been ignored, because otherwise the test is nonsense show tables like 't1'; diff --git a/mysql-test/t/rpl_failsafe.test b/mysql-test/t/rpl_failsafe.test deleted file mode 100644 index 4336d897fc0..00000000000 --- a/mysql-test/t/rpl_failsafe.test +++ /dev/null @@ -1,24 +0,0 @@ -require_manager; -source include/master-slave.inc; -connect (slave_sec,localhost,root,,test,0,slave.sock-1); -connect (slave_ter,localhost,root,,test,0,slave.sock-2); -connection master; -show variables like 'rpl_recovery_rank'; -show status like 'Rpl_status'; -create table t1(n int); -drop table t1; -sync_slave_with_master; -show variables like 'rpl_recovery_rank'; -show status like 'Rpl_status'; -connection slave_sec; -start slave; -sync_with_master; -show variables like 'rpl_recovery_rank'; -show status like 'Rpl_status'; -connection slave_ter; -start slave; -sync_with_master; -show variables like 'rpl_recovery_rank'; -show status like 'Rpl_status'; - -# End of 4.1 tests diff --git a/mysql-test/t/rpl_flush_tables.test b/mysql-test/t/rpl_flush_tables.test index 378fa479f09..aea8d7a5353 100644 --- a/mysql-test/t/rpl_flush_tables.test +++ b/mysql-test/t/rpl_flush_tables.test @@ -8,6 +8,8 @@ # merge table getting renamed. --source include/not_windows.inc +let $SERVER_VERSION=`select version()`; + create table t1 (a int); insert into t1 values (10); create table t2 (a int); diff --git a/mysql-test/t/rpl_heap.test b/mysql-test/t/rpl_heap.test deleted file mode 100644 index 66dac1d7926..00000000000 --- a/mysql-test/t/rpl_heap.test +++ /dev/null @@ -1,51 +0,0 @@ -# You must run this test with --manager. - -require_manager; - -# Don't know why, but using TCP/IP connections makes this test fail -# with "Lost connection to MySQL server during query" when we -# issue a query after the server restart. -# Maybe this is something awkward in mysqltest or in the manager? -# So we use sockets. -connect (master,localhost,root,,test,0,master.sock); -connect (slave,localhost,root,,test,0,slave.sock); - -connection master; -reset master; -drop table if exists t1; -# we use CREATE SELECT to verify that DELETE does not get into binlog -# before CREATE SELECT -create table t1 type=HEAP select 10 as a; -insert into t1 values(11); -save_master_pos; -show binlog events from 79; -connection slave; -reset slave; -start slave; -sync_with_master; -show create table t1; -select * from t1; # should be one row - -server_stop master; -server_start master; - -connection master; -select * from t1; -# to check that DELETE is not written twice -# (the LIMIT is to not use the query cache) -select * from t1 limit 10; -save_master_pos; -show binlog events in 'master-bin.002' from 79; - -connection slave; -sync_with_master; -select * from t1; # should be empty - -# clean up -connection master; -drop table t1; -save_master_pos; -connection slave; -sync_with_master; - -# End of 4.1 tests diff --git a/mysql-test/t/rpl_loaddata.test b/mysql-test/t/rpl_loaddata.test index 21515080ca2..65855dd3ceb 100644 --- a/mysql-test/t/rpl_loaddata.test +++ b/mysql-test/t/rpl_loaddata.test @@ -72,7 +72,7 @@ set global sql_slave_skip_counter=1; start slave; sync_with_master; --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # show slave status; # Trigger error again to test CHANGE MASTER @@ -92,7 +92,7 @@ stop slave; change master to master_user='test'; change master to master_user='root'; --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # show slave status; # Trigger error again to test RESET SLAVE @@ -114,7 +114,7 @@ wait_for_slave_to_stop; stop slave; reset slave; --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # show slave status; # Finally, see if logging is done ok on master for a failing LOAD DATA INFILE diff --git a/mysql-test/t/rpl_loaddatalocal.test b/mysql-test/t/rpl_loaddatalocal.test index 2ca142c3b64..fd91aba856a 100644 --- a/mysql-test/t/rpl_loaddatalocal.test +++ b/mysql-test/t/rpl_loaddatalocal.test @@ -20,11 +20,13 @@ while ($1) } set SQL_LOG_BIN=1; enable_query_log; -select * into outfile '../../var/master-data/rpl_loaddatalocal.select_outfile' from t1; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval select * into outfile '$MYSQLTEST_VARDIR/master-data/rpl_loaddatalocal.select_outfile' from t1; #This will generate a 20KB file, now test LOAD DATA LOCAL truncate table t1; -load data local infile './var/master-data/rpl_loaddatalocal.select_outfile' into table t1; -system rm ./var/master-data/rpl_loaddatalocal.select_outfile ; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval load data local infile '$MYSQLTEST_VARDIR/master-data/rpl_loaddatalocal.select_outfile' into table t1; +--remove_file $MYSQLTEST_VARDIR/master-data/rpl_loaddatalocal.select_outfile save_master_pos; connection slave; sync_with_master; diff --git a/mysql-test/t/rpl_log-master.opt b/mysql-test/t/rpl_log-master.opt index e0d075c3fbd..cef79bc8585 100644 --- a/mysql-test/t/rpl_log-master.opt +++ b/mysql-test/t/rpl_log-master.opt @@ -1 +1 @@ ---skip-external-locking +--force-restart diff --git a/mysql-test/t/rpl_log.test b/mysql-test/t/rpl_log.test index 08aa3b28850..40fc36cad4a 100644 --- a/mysql-test/t/rpl_log.test +++ b/mysql-test/t/rpl_log.test @@ -94,7 +94,7 @@ show binlog events in 'slave-bin.000001' from 4; --replace_result $MASTER_MYPORT MASTER_PORT $VERSION VERSION show binlog events in 'slave-bin.000002' from 4; --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # show slave status; # Need to recode the following diff --git a/mysql-test/t/rpl_max_relay_size.test b/mysql-test/t/rpl_max_relay_size.test index 63d7ef35413..003f60df28f 100644 --- a/mysql-test/t/rpl_max_relay_size.test +++ b/mysql-test/t/rpl_max_relay_size.test @@ -38,7 +38,7 @@ select @@global.max_relay_log_size; start slave; sync_with_master; --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # --vertical_results show slave status; @@ -53,7 +53,7 @@ select @@global.max_relay_log_size; start slave; sync_with_master; --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # --vertical_results show slave status; @@ -68,7 +68,7 @@ select @@global.max_relay_log_size; start slave; sync_with_master; --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # --vertical_results show slave status; @@ -82,7 +82,7 @@ reset slave; # (to make sure it does not crash). flush logs; --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # --vertical_results show slave status; @@ -103,7 +103,7 @@ save_master_pos; connection slave; sync_with_master; --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # --vertical_results show slave status; @@ -118,7 +118,7 @@ save_master_pos; connection slave; sync_with_master; --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # --vertical_results show slave status; diff --git a/mysql-test/t/rpl_replicate_do.test b/mysql-test/t/rpl_replicate_do.test index 9bbaa9f0076..b8559af2394 100644 --- a/mysql-test/t/rpl_replicate_do.test +++ b/mysql-test/t/rpl_replicate_do.test @@ -33,7 +33,7 @@ connection slave; sync_with_master; # show slave status, just to see of it prints replicate-do-table --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 33 # +--replace_column 1 # 8 # 9 # 23 # 33 # show slave status; # diff --git a/mysql-test/t/rpl_slave_status.test b/mysql-test/t/rpl_slave_status.test index 67d3816f443..cb12198bae2 100644 --- a/mysql-test/t/rpl_slave_status.test +++ b/mysql-test/t/rpl_slave_status.test @@ -9,7 +9,7 @@ start slave; connection master; --disable_warnings drop table if exists t1; ---enable_warning +--enable_warnings create table t1 (n int); insert into t1 values (1); save_master_pos; diff --git a/mysql-test/t/rpl_trunc_temp.test b/mysql-test/t/rpl_trunc_temp.test index be570a6f80c..b4ea3c318da 100644 --- a/mysql-test/t/rpl_trunc_temp.test +++ b/mysql-test/t/rpl_trunc_temp.test @@ -29,7 +29,4 @@ connection slave; show status like 'Slave_open_temp_tables'; -connection master; - - # End of 4.1 tests diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 6defa8b16a5..f3877b301d6 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -1721,7 +1721,7 @@ select (select a from t1) = (1,2); select (1,2,3) = (select * from t1); -- error 1241 select (select * from t1) = (1,2,3); -drop table t1 +drop table t1; # # Item_int_with_ref check (BUG#10020) diff --git a/mysql-test/t/system_mysql_db_fix.test b/mysql-test/t/system_mysql_db_fix.test index dc3047a8564..5e8822569b7 100644 --- a/mysql-test/t/system_mysql_db_fix.test +++ b/mysql-test/t/system_mysql_db_fix.test @@ -27,7 +27,7 @@ CREATE TABLE db ( KEY User (User) ) type=ISAM; ---enable-warnings +--enable_warnings INSERT INTO db VALUES ('%','test', '','Y','Y','Y','Y','Y','Y'); INSERT INTO db VALUES ('%','test\_%','','Y','Y','Y','Y','Y','Y'); @@ -45,7 +45,7 @@ CREATE TABLE host ( PRIMARY KEY Host (Host,Db) ) type=ISAM; ---enable-warnings +--enable_warnings --disable_warnings CREATE TABLE user ( @@ -64,7 +64,7 @@ CREATE TABLE user ( PRIMARY KEY Host (Host,User) ) type=ISAM; ---enable-warnings +--enable_warnings INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y'); INSERT INTO user VALUES ('localhost','', '','N','N','N','N','N','N','N','N','N'); diff --git a/mysql-test/t/type_blob.test b/mysql-test/t/type_blob.test index 2ac55da7442..2c3f4e5efce 100644 --- a/mysql-test/t/type_blob.test +++ b/mysql-test/t/type_blob.test @@ -23,7 +23,7 @@ CREATE TABLE t2 (a char(257), b varbinary(70000), c varchar(70000000)); show columns from t2; create table t3 (a long, b long byte); show create TABLE t3; -drop table t1,t2,t3 +drop table t1,t2,t3; # # Check errors with blob diff --git a/mysys/charset.c b/mysys/charset.c index 665f2efecd8..6f2d4d3c347 100644 --- a/mysys/charset.c +++ b/mysys/charset.c @@ -312,7 +312,7 @@ static my_bool my_read_charset_file(const char *filename, myf myflags) { char *buf; int fd; - uint len; + uint len, tmp_len; MY_STAT stat_info; if (!my_stat(filename, &stat_info, MYF(myflags)) || @@ -321,12 +321,11 @@ static my_bool my_read_charset_file(const char *filename, myf myflags) return TRUE; if ((fd=my_open(filename,O_RDONLY,myflags)) < 0) - { - my_free(buf,myflags); - return TRUE; - } - len=read(fd,buf,len); + goto error; + tmp_len=my_read(fd, buf, len, myflags); my_close(fd,myflags); + if (tmp_len != len) + goto error; if (my_parse_charset_xml(buf,len,add_collation)) { @@ -340,6 +339,10 @@ static my_bool my_read_charset_file(const char *filename, myf myflags) my_free(buf, myflags); return FALSE; + +error: + my_free(buf, myflags); + return TRUE; } diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c index 58d4756b702..109d9fabda3 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -439,11 +439,24 @@ int _my_b_read(register IO_CACHE *info, byte *Buffer, uint Count) /* pos_in_file always point on where info->buffer was read */ pos_in_file=info->pos_in_file+(uint) (info->read_end - info->buffer); + + /* + Whenever a function which operates on IO_CACHE flushes/writes + some part of the IO_CACHE to disk it will set the property + "seek_not_done" to indicate this to other functions operating + on the IO_CACHE. + */ if (info->seek_not_done) - { /* File touched, do seek */ - VOID(my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0))); + { + if (my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0)) + == MY_FILEPOS_ERROR) + { + info->error= -1; + DBUG_RETURN(1); + } info->seek_not_done=0; } + diff_length=(uint) (pos_in_file & (IO_SIZE-1)); if (Count >= (uint) (IO_SIZE+(IO_SIZE-diff_length))) { /* Fill first intern buffer */ @@ -944,8 +957,22 @@ int _my_b_read_r(register IO_CACHE *cache, byte *Buffer, uint Count) len= 0; else { - if (cache->seek_not_done) /* File touched, do seek */ - VOID(my_seek(cache->file, pos_in_file, MY_SEEK_SET, MYF(0))); + /* + Whenever a function which operates on IO_CACHE flushes/writes + some part of the IO_CACHE to disk it will set the property + "seek_not_done" to indicate this to other functions operating + on the IO_CACHE. + */ + if (info->seek_not_done) + { + if (my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0)) + == MY_FILEPOS_ERROR) + { + info->error= -1; + unlock_io_cache(info); + DBUG_RETURN(1); + } + } len= (int) my_read(cache->file, cache->buffer, length, cache->myflags); } DBUG_PRINT("io_cache_share", ("read %d bytes", len)); @@ -1047,11 +1074,16 @@ static void copy_to_read_buffer(IO_CACHE *write_cache, /* - Do sequential read from the SEQ_READ_APPEND cache - we do this in three stages: + Do sequential read from the SEQ_READ_APPEND cache. + + We do this in three stages: - first read from info->buffer - then if there are still data to read, try the file descriptor - afterwards, if there are still data to read, try append buffer + + RETURNS + 0 Success + 1 Failed to read */ int _my_b_seq_read(register IO_CACHE *info, byte *Buffer, uint Count) @@ -1079,7 +1111,13 @@ int _my_b_seq_read(register IO_CACHE *info, byte *Buffer, uint Count) With read-append cache we must always do a seek before we read, because the write could have moved the file pointer astray */ - VOID(my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0))); + if (my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0)) + == MY_FILEPOS_ERROR) + { + info->error= -1; + unlock_append_buffer(info); + return (1); + } info->seek_not_done=0; diff_length=(uint) (pos_in_file & (IO_SIZE-1)); @@ -1195,6 +1233,21 @@ read_append_buffer: #ifdef HAVE_AIOWAIT +/* + Read from the IO_CACHE into a buffer and feed asynchronously + from disk when needed. + + SYNOPSIS + _my_b_async_read() + info IO_CACHE pointer + Buffer Buffer to retrieve count bytes from file + Count Number of bytes to read into Buffer + + RETURN VALUE + -1 An error has occurred; my_errno is set. + 0 Success + 1 An error has occurred; IO_CACHE to error state. +*/ int _my_b_async_read(register IO_CACHE *info, byte *Buffer, uint Count) { uint length,read_length,diff_length,left_length,use_length,org_Count; @@ -1285,13 +1338,20 @@ int _my_b_async_read(register IO_CACHE *info, byte *Buffer, uint Count) info->error=(int) (read_length+left_length); return 1; } - VOID(my_seek(info->file,next_pos_in_file,MY_SEEK_SET,MYF(0))); + + if (my_seek(info->file,next_pos_in_file,MY_SEEK_SET,MYF(0)) + == MY_FILEPOS_ERROR) + { + info->error= -1; + return (1); + } + read_length=IO_SIZE*2- (uint) (next_pos_in_file & (IO_SIZE-1)); if (Count < read_length) { /* Small block, read to cache */ if ((read_length=my_read(info->file,info->request_pos, read_length, info->myflags)) == (uint) -1) - return info->error= -1; + return info->error= -1; use_length=min(Count,read_length); memcpy(Buffer,info->request_pos,(size_t) use_length); info->read_pos=info->request_pos+Count; @@ -1378,7 +1438,15 @@ int _my_b_get(IO_CACHE *info) return (int) (uchar) buff; } - /* Returns != 0 if error on write */ +/* + Write a byte buffer to IO_CACHE and flush to disk + if IO_CACHE is full. + + RETURN VALUE + 1 On error on write + 0 On success + -1 On error; my_errno contains error code. +*/ int _my_b_write(register IO_CACHE *info, const byte *Buffer, uint Count) { @@ -1402,8 +1470,18 @@ int _my_b_write(register IO_CACHE *info, const byte *Buffer, uint Count) { /* Fill first intern buffer */ length=Count & (uint) ~(IO_SIZE-1); if (info->seek_not_done) - { /* File touched, do seek */ - VOID(my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0))); + { + /* + Whenever a function which operates on IO_CACHE flushes/writes + some part of the IO_CACHE to disk it will set the property + "seek_not_done" to indicate this to other functions operating + on the IO_CACHE. + */ + if (my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0))) + { + info->error= -1; + return (1); + } info->seek_not_done=0; } if (my_write(info->file,Buffer,(uint) length,info->myflags | MY_NABP)) diff --git a/mysys/my_chsize.c b/mysys/my_chsize.c index efc19927183..149a5c81e08 100644 --- a/mysys/my_chsize.c +++ b/mysys/my_chsize.c @@ -90,7 +90,11 @@ int my_chsize(File fd, my_off_t newlength, int filler, myf MyFlags) Fill space between requested length and true length with 'filler' We should never come here on any modern machine */ - VOID(my_seek(fd, newlength, MY_SEEK_SET, MYF(MY_WME+MY_FAE))); + if (my_seek(fd, newlength, MY_SEEK_SET, MYF(MY_WME+MY_FAE)) + == MY_FILEPOS_ERROR) + { + goto err; + } swap_variables(my_off_t, newlength, oldsize); } #endif diff --git a/mysys/my_lock.c b/mysys/my_lock.c index 8f915d6003a..b8307f366c0 100644 --- a/mysys/my_lock.c +++ b/mysys/my_lock.c @@ -35,7 +35,14 @@ #include <nks/fsio.h> #endif - /* Lock a part of a file */ +/* + Lock a part of a file + + RETURN VALUE + 0 Success + -1 An error has occured and 'my_errno' is set + to indicate the actual error code. +*/ int my_lock(File fd, int locktype, my_off_t start, my_off_t length, myf MyFlags) @@ -104,10 +111,22 @@ int my_lock(File fd, int locktype, my_off_t start, my_off_t length, #elif defined(HAVE_LOCKING) /* Windows */ { - my_bool error; + my_bool error= FALSE; pthread_mutex_lock(&my_file_info[fd].mutex); - if (MyFlags & MY_SEEK_NOT_DONE) - VOID(my_seek(fd,start,MY_SEEK_SET,MYF(MyFlags & ~MY_SEEK_NOT_DONE))); + if (MyFlags & MY_SEEK_NOT_DONE) + { + if( my_seek(fd,start,MY_SEEK_SET,MYF(MyFlags & ~MY_SEEK_NOT_DONE)) + == MY_FILEPOS_ERROR ) + { + /* + If my_seek fails my_errno will already contain an error code; + just unlock and return error code. + */ + DBUG_PRINT("error",("my_errno: %d (%d)",my_errno,errno)); + pthread_mutex_unlock(&my_file_info[fd].mutex); + DBUG_RETURN(-1); + } + } error= locking(fd,locktype,(ulong) length) && errno != EINVAL; pthread_mutex_unlock(&my_file_info[fd].mutex); if (!error) @@ -145,7 +164,17 @@ int my_lock(File fd, int locktype, my_off_t start, my_off_t length, } #else if (MyFlags & MY_SEEK_NOT_DONE) - VOID(my_seek(fd,start,MY_SEEK_SET,MYF(MyFlags & ~MY_SEEK_NOT_DONE))); + { + if (my_seek(fd,start,MY_SEEK_SET,MYF(MyFlags & ~MY_SEEK_NOT_DONE)) + == MY_FILEPOS_ERROR) + { + /* + If an error has occured in my_seek then we will already + have an error code in my_errno; Just return error code. + */ + DBUG_RETURN(-1); + } + } if (lockf(fd,locktype,length) != -1) DBUG_RETURN(0); #endif /* HAVE_FCNTL */ diff --git a/mysys/my_lread.c b/mysys/my_lread.c index 601d772b844..a96febe4474 100644 --- a/mysys/my_lread.c +++ b/mysys/my_lread.c @@ -30,6 +30,8 @@ uint32 my_lread(int Filedes, byte *Buffer, uint32 Count, myf MyFlags) DBUG_PRINT("my",("Fd: %d Buffer: %ld Count: %ld MyFlags: %d", Filedes, Buffer, Count, MyFlags)); + DBUG_PRINT("error", ("Deprecated my_lread() function should not be used.")); + /* Temp hack to get count to int32 while read wants int */ if ((readbytes = (uint32) read(Filedes, Buffer, (uint) Count)) != Count) { diff --git a/mysys/my_lwrite.c b/mysys/my_lwrite.c index 3b9afdbd71f..02c56a69ba4 100644 --- a/mysys/my_lwrite.c +++ b/mysys/my_lwrite.c @@ -26,6 +26,8 @@ uint32 my_lwrite(int Filedes, const byte *Buffer, uint32 Count, myf MyFlags) DBUG_PRINT("my",("Fd: %d Buffer: 0x%lx Count: %ld MyFlags: %d", Filedes, Buffer, Count, MyFlags)); + DBUG_PRINT("error", ("Deprecated my_lwrite() function should not be used.")); + /* Temp hack to get count to int32 while write wants int */ if ((writenbytes = (uint32) write(Filedes, Buffer, (uint) Count)) != Count) { diff --git a/mysys/my_pread.c b/mysys/my_pread.c index 3d02e368720..f378d548731 100644 --- a/mysys/my_pread.c +++ b/mysys/my_pread.c @@ -52,8 +52,12 @@ uint my_pread(File Filedes, byte *Buffer, uint Count, my_off_t offset, DBUG_PRINT("warning",("Read only %ld bytes off %ld from %d, errno: %d", readbytes,Count,Filedes,my_errno)); #ifdef THREAD - if (readbytes == 0 && errno == EINTR) - continue; /* Interrupted */ + if ((readbytes == 0 || (int) readbytes == -1) && errno == EINTR) + { + DBUG_PRINT("debug", ("my_pread() was interrupted and returned %d", + (int) readbytes)); + continue; /* Interrupted */ + } #endif if (MyFlags & (MY_WME | MY_FAE | MY_FNABP)) { @@ -124,8 +128,8 @@ uint my_pwrite(int Filedes, const byte *Buffer, uint Count, my_off_t offset, VOID(sleep(MY_WAIT_FOR_USER_TO_FIX_PANIC)); continue; } - if ((writenbytes == 0 && my_errno == EINTR) || - (writenbytes > 0 && (uint) writenbytes != (uint) -1)) + if ((writenbytes > 0 && (uint) writenbytes != (uint) -1) || + my_errno == EINTR) continue; /* Retry */ #endif if (MyFlags & (MY_NABP | MY_FNABP)) diff --git a/mysys/my_quick.c b/mysys/my_quick.c index 44ed3fc0b2c..ffc8160c371 100644 --- a/mysys/my_quick.c +++ b/mysys/my_quick.c @@ -26,6 +26,14 @@ uint my_quick_read(File Filedes,byte *Buffer,uint Count,myf MyFlags) if ((readbytes = (uint) read(Filedes, Buffer, Count)) != Count) { +#ifndef DBUG_OFF + if ((readbytes == 0 || (int) readbytes == -1) && errno == EINTR) + { + DBUG_PRINT("error", ("my_quick_read() was interrupted and returned %d" + ". This function does not retry the read!", + (int) readbytes)); + } +#endif my_errno=errno; return readbytes; } @@ -35,8 +43,24 @@ uint my_quick_read(File Filedes,byte *Buffer,uint Count,myf MyFlags) uint my_quick_write(File Filedes,const byte *Buffer,uint Count) { - if ((uint) write(Filedes,Buffer,Count) != Count) +#ifndef DBUG_OFF + uint writtenbytes; +#endif + + if (( +#ifndef DBUG_OFF + writtenbytes = +#endif + (uint) write(Filedes,Buffer,Count)) != Count) { +#ifndef DBUG_OFF + if ((writtenbytes == 0 || (int) writtenbytes == -1) && errno == EINTR) + { + DBUG_PRINT("error", ("my_quick_write() was interrupted and returned %d" + ". This function does not retry the write!", + (int) writtenbytes)); + } +#endif my_errno=errno; return (uint) -1; } diff --git a/mysys/my_read.c b/mysys/my_read.c index 2e23f2175f8..8b88e483fef 100644 --- a/mysys/my_read.c +++ b/mysys/my_read.c @@ -51,10 +51,11 @@ uint my_read(File Filedes, byte *Buffer, uint Count, myf MyFlags) DBUG_PRINT("warning",("Read only %ld bytes off %ld from %d, errno: %d", readbytes, Count, Filedes, my_errno)); #ifdef THREAD - if ((int) readbytes <= 0 && errno == EINTR) - { - DBUG_PRINT("debug", ("my_read() was interrupted and returned %d", (int) readbytes)); - continue; /* Interrupted */ + if ((readbytes == 0 || (int) readbytes == -1) && errno == EINTR) + { + DBUG_PRINT("debug", ("my_read() was interrupted and returned %d", + (int) readbytes)); + continue; /* Interrupted */ } #endif if (MyFlags & (MY_WME | MY_FAE | MY_FNABP)) diff --git a/mysys/my_seek.c b/mysys/my_seek.c index 6af65d70fd0..a9ae68cd5f0 100644 --- a/mysys/my_seek.c +++ b/mysys/my_seek.c @@ -16,8 +16,30 @@ #include "mysys_priv.h" - /* Seek to position in file */ - /*ARGSUSED*/ +/* + Seek to a position in a file. + + ARGUMENTS + File fd The file descriptor + my_off_t pos The expected position (absolute or relative) + int whence A direction parameter and one of + {SEEK_SET, SEEK_CUR, SEEK_END} + myf MyFlags Not used. + + DESCRIPTION + The my_seek function is a wrapper around the system call lseek and + repositions the offset of the file descriptor fd to the argument + offset according to the directive whence as follows: + SEEK_SET The offset is set to offset bytes. + SEEK_CUR The offset is set to its current location plus offset bytes + SEEK_END The offset is set to the size of the file plus offset bytes + + RETURN VALUE + my_off_t newpos The new position in the file. + MY_FILEPOS_ERROR An error was encountered while performing + the seek. my_errno is set to indicate the + actual error. +*/ my_off_t my_seek(File fd, my_off_t pos, int whence, myf MyFlags __attribute__((unused))) diff --git a/mysys/my_write.c b/mysys/my_write.c index 4e8369480b3..ae8cb4ab02b 100644 --- a/mysys/my_write.c +++ b/mysys/my_write.c @@ -57,18 +57,24 @@ uint my_write(int Filedes, const byte *Buffer, uint Count, myf MyFlags) VOID(sleep(MY_WAIT_FOR_USER_TO_FIX_PANIC)); continue; } - if (!writenbytes) + + if ((writenbytes == 0 || (int) writenbytes == -1)) { - /* We may come here on an interrupt or if the file quote is exeeded */ if (my_errno == EINTR) - continue; - if (!errors++) /* Retry once */ { - errno=EFBIG; /* Assume this is the error */ - continue; + DBUG_PRINT("debug", ("my_write() was interrupted and returned %d", + (int) writenbytes)); + continue; /* Interrupted */ + } + + if (!writenbytes && !errors++) /* Retry once */ + { + /* We may come here if the file quota is exeeded */ + errno=EFBIG; /* Assume this is the error */ + continue; } } - else if ((uint) writenbytes != (uint) -1) + else continue; /* Retry */ #endif if (MyFlags & (MY_NABP | MY_FNABP)) diff --git a/scripts/make_binary_distribution.sh b/scripts/make_binary_distribution.sh index b2eb9f3b727..d05c4427627 100644 --- a/scripts/make_binary_distribution.sh +++ b/scripts/make_binary_distribution.sh @@ -225,6 +225,7 @@ $CP mysql-test/lib/*.sql $BASE/mysql-test/lib $CP mysql-test/t/*.def $BASE/mysql-test/t $CP mysql-test/include/*.inc $BASE/mysql-test/include $CP mysql-test/std_data/*.dat mysql-test/std_data/*.*001 \ + mysql-test/std_data/*.cnf \ $BASE/mysql-test/std_data $CP mysql-test/std_data/des_key_file $BASE/mysql-test/std_data $CP mysql-test/t/*test mysql-test/t/*.opt mysql-test/t/*.slave-mi \ diff --git a/sql-common/client.c b/sql-common/client.c index ff5f1ef150a..3acd763c054 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -2215,6 +2215,8 @@ my_bool mysql_reconnect(MYSQL *mysql) { MYSQL tmp_mysql; DBUG_ENTER("mysql_reconnect"); + DBUG_ASSERT(mysql); + DBUG_PRINT("enter", ("mysql->reconnect: %d", mysql->reconnect)); if (!mysql->reconnect || (mysql->server_status & SERVER_STATUS_IN_TRANS) || !mysql->host_info) diff --git a/sql-common/my_time.c b/sql-common/my_time.c index 3c46a944ba9..baf9a3902d9 100644 --- a/sql-common/my_time.c +++ b/sql-common/my_time.c @@ -402,8 +402,10 @@ err: There may be an optional [.second_part] after seconds length Length of str l_time Store result here - was_cut Set to 1 if value was cut during conversion or to 0 - otherwise. + warning Set MYSQL_TIME_WARN_TRUNCATED flag if the input string + was cut during conversion, and/or + MYSQL_TIME_WARN_OUT_OF_RANGE flag, if the value is + out of range. NOTES Because of the extra days argument, this function can only @@ -414,16 +416,16 @@ err: 1 error */ -bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time, - int *was_cut) +bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time, int *warning) { - long date[5],value; + ulong date[5]; + ulonglong value; const char *end=str+length, *end_of_days; bool found_days,found_hours; uint state; l_time->neg=0; - *was_cut= 0; + *warning= 0; for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++) length--; if (str != end && *str == '-') @@ -438,13 +440,16 @@ bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time, /* Check first if this is a full TIMESTAMP */ if (length >= 12) { /* Probably full timestamp */ + int was_cut; enum enum_mysql_timestamp_type res= str_to_datetime(str, length, l_time, - (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), was_cut); + (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &was_cut); if ((int) res >= (int) MYSQL_TIMESTAMP_ERROR) + { + if (was_cut) + *warning|= MYSQL_TIME_WARN_TRUNCATED; return res == MYSQL_TIMESTAMP_ERROR; - /* We need to restore was_cut flag since str_to_datetime can modify it */ - *was_cut= 0; + } } /* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */ @@ -524,7 +529,7 @@ fractional: if (field_length > 0) value*= (long) log_10_int[field_length]; else if (field_length < 0) - *was_cut= 1; + *warning|= MYSQL_TIME_WARN_TRUNCATED; date[4]=value; } else @@ -538,10 +543,7 @@ fractional: ((str[1] == '-' || str[1] == '+') && (end - str) > 2 && my_isdigit(&my_charset_latin1, str[2])))) - { - *was_cut= 1; return 1; - } if (internal_format_positions[7] != 255) { @@ -560,12 +562,12 @@ fractional: } } - /* Some simple checks */ - if (date[2] >= 60 || date[3] >= 60) - { - *was_cut= 1; + /* Integer overflow checks */ + if (date[0] > UINT_MAX || date[1] > UINT_MAX || + date[2] > UINT_MAX || date[3] > UINT_MAX || + date[4] > UINT_MAX) return 1; - } + l_time->year= 0; /* For protocol::store_time */ l_time->month= 0; l_time->day= date[0]; @@ -575,6 +577,10 @@ fractional: l_time->second_part= date[4]; l_time->time_type= MYSQL_TIMESTAMP_TIME; + /* Check if the value is valid and fits into TIME range */ + if (check_time_range(l_time, warning)) + return 1; + /* Check if there is garbage at end of the TIME specification */ if (str != end) { @@ -582,7 +588,7 @@ fractional: { if (!my_isspace(&my_charset_latin1,*str)) { - *was_cut= 1; + *warning|= MYSQL_TIME_WARN_TRUNCATED; break; } } while (++str != end); @@ -592,6 +598,47 @@ fractional: /* + Check 'time' value to lie in the TIME range + + SYNOPSIS: + check_time_range() + time pointer to TIME value + warning set MYSQL_TIME_WARN_OUT_OF_RANGE flag if the value is out of range + + DESCRIPTION + If the time value lies outside of the range [-838:59:59, 838:59:59], + set it to the closest endpoint of the range and set + MYSQL_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable. + + RETURN + 0 time value is valid, but was possibly truncated + 1 time value is invalid +*/ + +int check_time_range(struct st_mysql_time *time, int *warning) +{ + longlong hour; + + if (time->minute >= 60 || time->second >= 60) + return 1; + + hour= time->hour + (24*time->day); + if (hour <= TIME_MAX_HOUR && + (hour != TIME_MAX_HOUR || time->minute != TIME_MAX_MINUTE || + time->second != TIME_MAX_SECOND || !time->second_part)) + return 0; + + time->day= 0; + time->hour= TIME_MAX_HOUR; + time->minute= TIME_MAX_MINUTE; + time->second= TIME_MAX_SECOND; + time->second_part= 0; + *warning|= MYSQL_TIME_WARN_OUT_OF_RANGE; + return 0; +} + + +/* Prepare offset of system time zone from UTC for my_system_gmt_sec() func. SYNOPSIS @@ -776,7 +823,7 @@ void set_zero_time(MYSQL_TIME *tm, enum enum_mysql_timestamp_type time_type) int my_time_to_str(const MYSQL_TIME *l_time, char *to) { uint extra_hours= 0; - return my_sprintf(to, (to, "%s%02d:%02d:%02d", + return my_sprintf(to, (to, "%s%02u:%02u:%02u", (l_time->neg ? "-" : ""), extra_hours+ l_time->hour, l_time->minute, @@ -785,7 +832,7 @@ int my_time_to_str(const MYSQL_TIME *l_time, char *to) int my_date_to_str(const MYSQL_TIME *l_time, char *to) { - return my_sprintf(to, (to, "%04d-%02d-%02d", + return my_sprintf(to, (to, "%04u-%02u-%02u", l_time->year, l_time->month, l_time->day)); @@ -793,7 +840,7 @@ int my_date_to_str(const MYSQL_TIME *l_time, char *to) int my_datetime_to_str(const MYSQL_TIME *l_time, char *to) { - return my_sprintf(to, (to, "%04d-%02d-%02d %02d:%02d:%02d", + return my_sprintf(to, (to, "%04u-%02u-%02u %02u:%02u:%02u", l_time->year, l_time->month, l_time->day, diff --git a/sql/field.cc b/sql/field.cc index b66a7d736d0..757c62cccd1 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -3961,9 +3961,10 @@ int Field_time::store(const char *from,uint len,CHARSET_INFO *cs) { TIME ltime; long tmp; - int error; + int error= 0; + int warning; - if (str_to_time(from, len, <ime, &error)) + if (str_to_time(from, len, <ime, &warning)) { tmp=0L; error= 2; @@ -3972,29 +3973,27 @@ int Field_time::store(const char *from,uint len,CHARSET_INFO *cs) } else { - if (error) + if (warning & MYSQL_TIME_WARN_TRUNCATED) set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, from, len, MYSQL_TIMESTAMP_TIME, 1); - - if (ltime.month) - ltime.day=0; - tmp=(ltime.day*24L+ltime.hour)*10000L+(ltime.minute*100+ltime.second); - if (tmp > 8385959) + if (warning & MYSQL_TIME_WARN_OUT_OF_RANGE) { - tmp=8385959; set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, from, len, MYSQL_TIMESTAMP_TIME, !error); error= 1; } + if (ltime.month) + ltime.day=0; + tmp=(ltime.day*24L+ltime.hour)*10000L+(ltime.minute*100+ltime.second); if (error > 1) error= 2; } if (ltime.neg) tmp= -tmp; - error |= Field_time::store((longlong) tmp); + int3store(ptr,tmp); return error; } @@ -4003,16 +4002,16 @@ int Field_time::store(double nr) { long tmp; int error= 0; - if (nr > 8385959.0) + if (nr > (double)TIME_MAX_VALUE) { - tmp=8385959L; + tmp= TIME_MAX_VALUE; set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME); error= 1; } - else if (nr < -8385959.0) + else if (nr < (double)-TIME_MAX_VALUE) { - tmp= -8385959L; + tmp= -TIME_MAX_VALUE; set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME); error= 1; @@ -4040,17 +4039,17 @@ int Field_time::store(longlong nr) { long tmp; int error= 0; - if (nr > (longlong) 8385959L) + if (nr > (longlong) TIME_MAX_VALUE) { - tmp=8385959L; + tmp= TIME_MAX_VALUE; set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME, 1); error= 1; } - else if (nr < (longlong) -8385959L) + else if (nr < (longlong) -TIME_MAX_VALUE) { - tmp= -8385959L; + tmp= -TIME_MAX_VALUE; set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME, 1); diff --git a/sql/handler.cc b/sql/handler.cc index 6847da5148e..0476b855e3c 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -648,7 +648,7 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) } #endif #ifdef HAVE_INNOBASE_DB - if (trans->innobase_tid) + if (thd->transaction.all.innodb_active_trans) { if ((error=innobase_rollback(thd, trans->innobase_tid))) { diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 98888226e58..f7408cb5e9f 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2838,6 +2838,7 @@ String *Item_func_compress::val_str(String *str) null_value= 1; return 0; } + null_value= 0; if (res->is_empty()) return res; /* diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 02c8ab19354..c1bca7afc60 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -96,6 +96,124 @@ static bool make_datetime(date_time_format_types format, TIME *ltime, /* + Wrapper over make_datetime() with validation of the input TIME value + + NOTE + see make_datetime() for more information + + RETURN + 1 if there was an error during converion + 0 otherwise +*/ + +static bool make_datetime_with_warn(date_time_format_types format, TIME *ltime, + String *str) +{ + int warning= 0; + bool rc; + + if (make_datetime(format, ltime, str)) + return 1; + if (check_time_range(ltime, &warning)) + return 1; + if (!warning) + return 0; + + make_truncated_value_warning(current_thd, str->ptr(), str->length(), + MYSQL_TIMESTAMP_TIME); + return make_datetime(format, ltime, str); +} + + +/* + Wrapper over make_time() with validation of the input TIME value + + NOTE + see make_time() for more info + + RETURN + 1 if there was an error during conversion + 0 otherwise +*/ + +static bool make_time_with_warn(const DATE_TIME_FORMAT *format, + TIME *l_time, String *str) +{ + int warning= 0; + make_time(format, l_time, str); + if (check_time_range(l_time, &warning)) + return 1; + if (warning) + { + make_truncated_value_warning(current_thd, str->ptr(), str->length(), + MYSQL_TIMESTAMP_TIME); + make_time(format, l_time, str); + } + + return 0; +} + + +/* + Convert seconds to TIME value with overflow checking + + SYNOPSIS: + sec_to_time() + seconds number of seconds + unsigned_flag 1, if 'seconds' is unsigned, 0, otherwise + ltime output TIME value + + DESCRIPTION + If the 'seconds' argument is inside TIME data range, convert it to a + corresponding value. + Otherwise, truncate the resulting value to the nearest endpoint, and + produce a warning message. + + RETURN + 1 if the value was truncated during conversion + 0 otherwise +*/ + +static bool sec_to_time(longlong seconds, bool unsigned_flag, TIME *ltime) +{ + uint sec; + + bzero((char *)ltime, sizeof(*ltime)); + + if (seconds < 0) + { + if (unsigned_flag) + goto overflow; + ltime->neg= 1; + if (seconds < -3020399) + goto overflow; + seconds= -seconds; + } + else if (seconds > 3020399) + goto overflow; + + sec= (uint) ((ulonglong) seconds % 3600); + ltime->hour= (uint) (seconds/3600); + ltime->minute= sec/60; + ltime->second= sec % 60; + + return 0; + +overflow: + ltime->hour= TIME_MAX_HOUR; + ltime->minute= TIME_MAX_MINUTE; + ltime->second= TIME_MAX_SECOND; + + char buf[22]; + int len= (int)(longlong10_to_str(seconds, buf, unsigned_flag ? 10 : -10) + - buf); + make_truncated_value_warning(current_thd, buf, len, MYSQL_TIMESTAMP_TIME); + + return 1; +} + + +/* Date formats corresponding to compound %r and %T conversion specifiers Note: We should init at least first element of "positions" array @@ -1488,8 +1606,6 @@ int Item_func_now::save_in_field(Field *to, bool no_conversions) String *Item_func_sec_to_time::val_str(String *str) { DBUG_ASSERT(fixed == 1); - longlong seconds=(longlong) args[0]->val_int(); - uint sec; TIME ltime; if ((null_value=args[0]->null_value) || str->alloc(19)) @@ -1498,19 +1614,8 @@ String *Item_func_sec_to_time::val_str(String *str) return (String*) 0; } - ltime.neg= 0; - if (seconds < 0) - { - seconds= -seconds; - ltime.neg= 1; - } - - sec= (uint) ((ulonglong) seconds % 3600); - ltime.day= 0; - ltime.hour= (uint) (seconds/3600); - ltime.minute= sec/60; - ltime.second= sec % 60; - + sec_to_time(args[0]->val_int(), args[0]->unsigned_flag, <ime); + make_time((DATE_TIME_FORMAT *) 0, <ime, str); return str; } @@ -1519,16 +1624,15 @@ String *Item_func_sec_to_time::val_str(String *str) longlong Item_func_sec_to_time::val_int() { DBUG_ASSERT(fixed == 1); - longlong seconds=args[0]->val_int(); - longlong sign=1; + TIME ltime; + if ((null_value=args[0]->null_value)) return 0; - if (seconds < 0) - { - seconds= -seconds; - sign= -1; - } - return sign*((seconds / 3600)*10000+((seconds/60) % 60)*100+ (seconds % 60)); + + sec_to_time(args[0]->val_int(), args[0]->unsigned_flag, <ime); + + return (ltime.neg ? -1 : 1) * + ((ltime.hour)*10000 + ltime.minute*100 + ltime.second); } @@ -2534,7 +2638,9 @@ String *Item_func_add_time::val_str(String *str) } if (l_time1.neg != l_time2.neg) l_sign= -l_sign; - + + bzero((char *)&l_time3, sizeof(l_time3)); + l_time3.neg= calc_time_diff(&l_time1, &l_time2, -l_sign, &seconds, µseconds); @@ -2563,9 +2669,9 @@ String *Item_func_add_time::val_str(String *str) } l_time3.hour+= days*24; - if (!make_datetime(l_time1.second_part || l_time2.second_part ? - TIME_MICROSECOND : TIME_ONLY, - &l_time3, str)) + if (!make_datetime_with_warn(l_time1.second_part || l_time2.second_part ? + TIME_MICROSECOND : TIME_ONLY, + &l_time3, str)) return str; null_date: @@ -2620,6 +2726,8 @@ String *Item_func_timediff::val_str(String *str) if (l_time1.neg != l_time2.neg) l_sign= -l_sign; + bzero((char *)&l_time3, sizeof(l_time3)); + l_time3.neg= calc_time_diff(&l_time1, &l_time2, l_sign, &seconds, µseconds); @@ -2633,9 +2741,9 @@ String *Item_func_timediff::val_str(String *str) calc_time_from_sec(&l_time3, (long) seconds, microseconds); - if (!make_datetime(l_time1.second_part || l_time2.second_part ? - TIME_MICROSECOND : TIME_ONLY, - &l_time3, str)) + if (!make_datetime_with_warn(l_time1.second_part || l_time2.second_part ? + TIME_MICROSECOND : TIME_ONLY, + &l_time3, str)) return str; null_date: @@ -2653,29 +2761,57 @@ String *Item_func_maketime::val_str(String *str) { DBUG_ASSERT(fixed == 1); TIME ltime; + bool overflow= 0; - long hour= (long) args[0]->val_int(); - long minute= (long) args[1]->val_int(); - long second= (long) args[2]->val_int(); + longlong hour= args[0]->val_int(); + longlong minute= args[1]->val_int(); + longlong second= args[2]->val_int(); if ((null_value=(args[0]->null_value || - args[1]->null_value || - args[2]->null_value || - minute > 59 || minute < 0 || - second > 59 || second < 0 || - str->alloc(19)))) + args[1]->null_value || + args[2]->null_value || + minute < 0 || minute > 59 || + second < 0 || second > 59 || + str->alloc(19)))) return 0; + bzero((char *)<ime, sizeof(ltime)); ltime.neg= 0; + + /* Check for integer overflows */ if (hour < 0) { - ltime.neg= 1; - hour= -hour; + if (args[0]->unsigned_flag) + overflow= 1; + else + ltime.neg= 1; + } + if (-hour > UINT_MAX || hour > UINT_MAX) + overflow= 1; + + if (!overflow) + { + ltime.hour= (uint) ((hour < 0 ? -hour : hour)); + ltime.minute= (uint) minute; + ltime.second= (uint) second; + } + else + { + ltime.hour= TIME_MAX_HOUR; + ltime.minute= TIME_MAX_MINUTE; + ltime.second= TIME_MAX_SECOND; + char buf[28]; + char *ptr= longlong10_to_str(hour, buf, args[0]->unsigned_flag ? 10 : -10); + int len = (int)(ptr - buf) + + my_sprintf(ptr, (ptr, ":%02u:%02u", (uint)minute, (uint)second)); + make_truncated_value_warning(current_thd, buf, len, MYSQL_TIMESTAMP_TIME); + } + + if (make_time_with_warn((DATE_TIME_FORMAT *) 0, <ime, str)) + { + null_value= 1; + return 0; } - ltime.hour= (ulong) hour; - ltime.minute= (ulong) minute; - ltime.second= (ulong) second; - make_time((DATE_TIME_FORMAT *) 0, <ime, str); return str; } @@ -2881,7 +3017,7 @@ bool Item_func_str_to_date::get_date(TIME *ltime, uint fuzzy_date) goto null_date; null_value= 0; - bzero((char*) ltime, sizeof(ltime)); + bzero((char*) ltime, sizeof(*ltime)); date_time_format.format.str= (char*) format->ptr(); date_time_format.format.length= format->length(); if (extract_date_time(&date_time_format, val->ptr(), val->length(), diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 6b129997e47..06e42ff363f 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -885,6 +885,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, key_parts->null_bit= key_part_info->null_bit; key_parts->image_type = (key_info->flags & HA_SPATIAL) ? Field::itMBR : Field::itRAW; + key_parts->flag= key_part_info->key_part_flag; } param.real_keynr[param.keys++]=idx; } @@ -1398,7 +1399,9 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, } break; case Item_func::GT_FUNC: - if (field_is_equal_to_item(field,value)) + /* Don't use open ranges for partial key_segments */ + if (field_is_equal_to_item(field,value) && + !(key_part->flag & HA_PART_KEY_SEG)) tree->min_flag=NEAR_MIN; /* fall through */ case Item_func::GE_FUNC: @@ -2899,6 +2902,7 @@ QUICK_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, TABLE_REF *ref) key_part->length= key_info->key_part[part].length; key_part->store_length= key_info->key_part[part].store_length; key_part->null_bit= key_info->key_part[part].null_bit; + key_part->flag= key_info->key_part[part].key_part_flag; } if (quick->ranges.push_back(range)) goto err; diff --git a/sql/opt_range.h b/sql/opt_range.h index 367a85dc6f2..4d425604921 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -36,7 +36,7 @@ typedef struct st_key_part { uint16 key,part, store_length, length; - uint8 null_bit; + uint8 null_bit, flag; Field *field; Field::imagetype image_type; } KEY_PART; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index bd292814bfa..fc9597cba87 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -181,6 +181,7 @@ THD::THD() // Must be reset to handle error with THD's created for init of mysqld lex->current_select= 0; start_time=(time_t) 0; + time_after_lock=(time_t) 0; current_linfo = 0; slave_thread = 0; variables.pseudo_thread_id= 0; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 1a20b9b7d91..1b00539ed71 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -84,6 +84,14 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, /* Handler didn't support fast delete; Delete rows one by one */ } + if (conds) + { + Item::cond_result result; + conds= remove_eq_conds(thd, conds, &result); + if (result == Item::COND_FALSE) // Impossible where + limit= 0; + } + table->used_keys.clear_all(); table->quick_keys.clear_all(); // Can't use 'only index' select=make_select(table,0,0,conds,&error); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2c92de560c8..0254e8f56dc 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7363,7 +7363,7 @@ part_of_refkey(TABLE *table,Field *field) for (uint part=0 ; part < ref_parts ; part++,key_part++) if (field->eq(key_part->field) && - !(key_part->key_part_flag & HA_PART_KEY_SEG)) + !(key_part->key_part_flag & (HA_PART_KEY_SEG | HA_NULL_PART))) return table->reginfo.join_tab->ref.items[part]; } return (Item*) 0; diff --git a/sql/stacktrace.c b/sql/stacktrace.c index 43f35c452f7..1b8267763a0 100644 --- a/sql/stacktrace.c +++ b/sql/stacktrace.c @@ -186,7 +186,7 @@ terribly wrong...\n"); #if defined(__alpha__) && defined(__GNUC__) uchar** new_fp = find_prev_fp(pc, fp); - if (frame_count == SIGRETURN_FRAME_COUNT - 1) + if (frame_count == sigreturn_frame_count - 1) { new_fp += 90; } diff --git a/sql/table.cc b/sql/table.cc index 0682bcf8998..aff1e6d11f6 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -690,7 +690,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, as we need to test for NULL = NULL. */ if (field->real_maybe_null()) - key_part->key_part_flag|= HA_PART_KEY_SEG; + key_part->key_part_flag|= HA_NULL_PART; } else { // Error: shorten key diff --git a/sql/time.cc b/sql/time.cc index ef832ac5a70..ddc5e6435be 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -255,9 +255,9 @@ my_time_t TIME_to_timestamp(THD *thd, const TIME *t, bool *in_dst_time_gap) bool str_to_time_with_warn(const char *str, uint length, TIME *l_time) { - int was_cut; - bool ret_val= str_to_time(str, length, l_time, &was_cut); - if (was_cut) + int warning; + bool ret_val= str_to_time(str, length, l_time, &warning); + if (ret_val || warning) make_truncated_value_warning(current_thd, str, length, MYSQL_TIMESTAMP_TIME); return ret_val; } diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 6ae9dcb9476..b1ee144e517 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -113,7 +113,9 @@ static void client_disconnect(); void die(const char *file, int line, const char *expr) { + fflush(stdout); fprintf(stderr, "%s:%d: check failed: '%s'\n", file, line, expr); + fflush(stderr); abort(); } @@ -11776,6 +11778,7 @@ static void test_bug11718() printf("return type: %s", (res->fields[0].type == MYSQL_TYPE_DATE)?"DATE": "not DATE"); DIE_UNLESS(res->fields[0].type == MYSQL_TYPE_DATE); + mysql_free_result(res); rc= mysql_query(mysql, "drop table t1, t2"); myquery(rc); } @@ -11847,6 +11850,7 @@ static void test_bug15613() DIE_UNLESS(field[4].length == 255); DIE_UNLESS(field[5].length == 255); DIE_UNLESS(field[6].length == 255); + mysql_free_result(metadata); /* III. Cleanup */ rc= mysql_query(mysql, "drop table t1"); |