diff options
Diffstat (limited to 'client/mysqltest.cc')
-rw-r--r-- | client/mysqltest.cc | 1698 |
1 files changed, 1176 insertions, 522 deletions
diff --git a/client/mysqltest.cc b/client/mysqltest.cc index bfed483134a..01613223d64 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -12,8 +12,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* mysqltest @@ -34,7 +33,7 @@ And many others */ -#define MTEST_VERSION "3.3" +#define MTEST_VERSION "3.4" #include "client_priv.h" #include <mysql_version.h> @@ -62,6 +61,12 @@ #define SIGNAL_FMT "signal %d" #endif +static my_bool non_blocking_api_enabled= 0; +#if !defined(EMBEDDED_LIBRARY) +#define WRAP_NONBLOCK_ENABLED non_blocking_api_enabled +#include "../tests/nonblock-wrappers.h" +#endif + /* Use cygwin for --exec and --system before 5.0 */ #if MYSQL_VERSION_ID < 50000 #define USE_CYGWIN @@ -73,6 +78,8 @@ #define MAX_DELIMITER_LENGTH 16 #define DEFAULT_MAX_CONN 64 +#define DIE_BUFF_SIZE 8192 + /* Flags controlling send and reap */ #define QUERY_SEND_FLAG 1 #define QUERY_REAP_FLAG 2 @@ -81,27 +88,28 @@ static int setenv(const char *name, const char *value, int overwrite); #endif +C_MODE_START +static sig_handler signal_handler(int sig); +static my_bool get_one_option(int optid, const struct my_option *, + char *argument); +C_MODE_END + enum { - OPT_SKIP_SAFEMALLOC=OPT_MAX_CLIENT_OPTION, - OPT_PS_PROTOCOL, OPT_SP_PROTOCOL, OPT_CURSOR_PROTOCOL, OPT_VIEW_PROTOCOL, - OPT_MAX_CONNECT_RETRIES, OPT_MAX_CONNECTIONS, - OPT_MARK_PROGRESS, OPT_LOG_DIR, OPT_TAIL_LINES, - OPT_GLOBAL_SUBST, OPT_MY_CONNECT_TIMEOUT + OPT_LOG_DIR=OPT_MAX_CLIENT_OPTION, OPT_RESULT_FORMAT_VERSION }; static int record= 0, opt_sleep= -1; static char *opt_db= 0, *opt_pass= 0; const char *opt_user= 0, *opt_host= 0, *unix_sock= 0, *opt_basedir= "./"; -#ifdef HAVE_SMEM static char *shared_memory_base_name=0; -#endif const char *opt_logdir= ""; -const char *opt_include= 0, *opt_charsets_dir; +const char *opt_prologue= 0, *opt_charsets_dir; static int opt_port= 0; static int opt_max_connect_retries; +static int opt_result_format_version; static int opt_max_connections= DEFAULT_MAX_CONN; +static int error_count= 0; static my_bool opt_compress= 0, silent= 0, verbose= 0; -static int opt_connect_timeout= -1; static my_bool debug_info_flag= 0, debug_check_flag= 0; static my_bool tty_password= 0; static my_bool opt_mark_progress= 0; @@ -117,8 +125,7 @@ static my_bool disable_connect_log= 1; static my_bool disable_warnings= 0, disable_column_names= 0; static my_bool prepare_warnings_enabled= 0; static my_bool disable_info= 1; -static char *opt_plugin_dir= 0, *opt_default_auth; -static my_bool abort_on_error= 1; +static my_bool abort_on_error= 1, opt_continue_on_error= 0; static my_bool server_initialized= 0; static my_bool is_windows= 0; static char **default_argv; @@ -126,12 +133,49 @@ static const char *load_default_groups[]= { "mysqltest", "client", "client-server", "client-mariadb", 0 }; static char line_buffer[MAX_DELIMITER_LENGTH], *line_buffer_pos= line_buffer; +/* Info on properties that can be set with --enable_X and --disable_X */ + +struct property { + my_bool *var; /* Actual variable */ + my_bool set; /* Has been set for ONE command */ + my_bool old; /* If set, thus is the old value */ + my_bool reverse; /* Varible is true if disabled */ + const char *env_name; /* Env. variable name */ +}; + +static struct property prop_list[] = { + { &abort_on_error, 0, 1, 0, "$ENABLED_ABORT_ON_ERROR" }, + { &disable_connect_log, 0, 1, 1, "$ENABLED_CONNECT_LOG" }, + { &disable_info, 0, 1, 1, "$ENABLED_INFO" }, + { &display_metadata, 0, 0, 0, "$ENABLED_METADATA" }, + { &ps_protocol_enabled, 0, 0, 0, "$ENABLED_PS_PROTOCOL" }, + { &disable_query_log, 0, 0, 1, "$ENABLED_QUERY_LOG" }, + { &disable_result_log, 0, 0, 1, "$ENABLED_RESULT_LOG" }, + { &disable_warnings, 0, 0, 1, "$ENABLED_WARNINGS" } +}; + +static my_bool once_property= FALSE; + +enum enum_prop { + P_ABORT= 0, + P_CONNECT, + P_INFO, + P_META, + P_PS, + P_QUERY, + P_RESULT, + P_WARN, + P_MAX +}; + static uint start_lineno= 0; /* Start line of current command */ static uint my_end_arg= 0; /* Number of lines of the result to include in failure report */ static uint opt_tail_lines= 0; +static uint opt_connect_timeout= 0; + static char delimiter[MAX_DELIMITER_LENGTH]= ";"; static uint delimiter_length= 1; @@ -163,7 +207,7 @@ static struct st_block *cur_block, *block_stack_end; struct st_test_file { FILE* file; - const char *file_name; + char *file_name; uint lineno; /* Current line in file */ }; @@ -171,7 +215,6 @@ static struct st_test_file file_stack[16]; static struct st_test_file* cur_file; static struct st_test_file* file_stack_end; - static CHARSET_INFO *charset_info= &my_charset_latin1; /* Default charset */ static const char *embedded_server_groups[]= @@ -197,6 +240,10 @@ static ulonglong timer_now(void); static ulong connection_retry_sleep= 100000; /* Microseconds */ +static const char *opt_plugin_dir; +static const char *opt_suite_dir, *opt_overlay_dir; +static size_t suite_dir_len, overlay_dir_len; + /* 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 */ @@ -238,8 +285,9 @@ typedef struct int str_val_len; int int_val; int alloced_len; - int int_dirty; /* do not update string if int is updated until first read */ - int alloced; + bool int_dirty; /* do not update string if int is updated until first read */ + bool is_int; + bool alloced; } VAR; /*Perl/shell-like variable registers */ @@ -259,13 +307,16 @@ struct st_connection my_bool pending; #ifdef EMBEDDED_LIBRARY + pthread_t tid; const char *cur_query; int cur_query_len; - pthread_mutex_t mutex; - pthread_cond_t cond; - pthread_t tid; + int command, result; + pthread_mutex_t query_mutex; + pthread_cond_t query_cond; + pthread_mutex_t result_mutex; + pthread_cond_t result_cond; int query_done; - my_bool has_thread, mutex_inited; + my_bool has_thread; #endif /*EMBEDDED_LIBRARY*/ }; @@ -292,8 +343,7 @@ enum enum_commands { 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_EVAL_RESULT, Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG, Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG, Q_ENABLE_CONNECT_LOG, Q_DISABLE_CONNECT_LOG, @@ -309,6 +359,7 @@ enum enum_commands { Q_LOWERCASE, Q_START_TIMER, Q_END_TIMER, Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL, + Q_ENABLE_NON_BLOCKING_API, Q_DISABLE_NON_BLOCKING_API, Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT, Q_IF, Q_DISABLE_PARSING, Q_ENABLE_PARSING, @@ -318,12 +369,13 @@ enum enum_commands { Q_SEND_QUIT, Q_CHANGE_USER, Q_MKDIR, Q_RMDIR, Q_LIST_FILES, Q_LIST_FILES_WRITE_FILE, Q_LIST_FILES_APPEND_FILE, Q_SEND_SHUTDOWN, Q_SHUTDOWN_SERVER, + Q_RESULT_FORMAT_VERSION, Q_MOVE_FILE, Q_REMOVE_FILES_WILDCARD, Q_SEND_EVAL, Q_ENABLE_PREPARE_WARNINGS, Q_DISABLE_PREPARE_WARNINGS, - Q_UNKNOWN, /* Unknown command. */ Q_COMMENT, /* Comments, ignored. */ - Q_COMMENT_WITH_COMMAND + Q_COMMENT_WITH_COMMAND, + Q_EMPTY_LINE }; @@ -356,9 +408,6 @@ const char *command_names[]= "replace_column", "ping", "eval", - "rpl_probe", - "enable_rpl_parse", - "disable_rpl_parse", "eval_result", /* Enable/disable that the _query_ is logged to result file */ "enable_query_log", @@ -392,6 +441,8 @@ const char *command_names[]= "character_set", "disable_ps_protocol", "enable_ps_protocol", + "enable_non_blocking_api", + "disable_non_blocking_api", "disable_reconnect", "enable_reconnect", "if", @@ -421,6 +472,7 @@ const char *command_names[]= "list_files_append_file", "send_shutdown", "shutdown_server", + "result_format", "move_file", "remove_files_wildcard", "send_eval", @@ -477,28 +529,56 @@ TYPELIB command_typelib= {array_elements(command_names),"", command_names, 0}; DYNAMIC_STRING ds_res; +/* Points to ds_warning in run_query, so it can be freed */ +DYNAMIC_STRING *ds_warn= 0; struct st_command *curr_command= 0; char builtin_echo[FN_REFLEN]; +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; + +struct st_replace; +struct st_replace *glob_replace= 0; +void replace_strings_append(struct st_replace *rep, DYNAMIC_STRING* ds, +const char *from, int len); + static void cleanup_and_exit(int exit_code) __attribute__((noreturn)); -void die(const char *fmt, ...) - ATTRIBUTE_FORMAT(printf, 1, 2) __attribute__((noreturn)); -void abort_not_supported_test(const char *fmt, ...) - ATTRIBUTE_FORMAT(printf, 1, 2) __attribute__((noreturn)); -void verbose_msg(const char *fmt, ...) - ATTRIBUTE_FORMAT(printf, 1, 2); -void log_msg(const char *fmt, ...) - ATTRIBUTE_FORMAT(printf, 1, 2); +void really_die(const char *msg) __attribute__((noreturn)); +void report_or_die(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2); +void die(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2) + __attribute__((noreturn)); +static void make_error_message(char *buf, size_t len, const char *fmt, va_list args); +void abort_not_supported_test(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2) + __attribute__((noreturn)); +void verbose_msg(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2); +void log_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, bool do_eval= true); +void eval_expr(VAR* v, const char *p, const char** p_end, + bool open_end=false, bool do_eval=true); my_bool match_delimiter(int c, const char *delim, uint length); void dump_result_to_reject_file(char *buf, int size); void dump_warning_messages(); @@ -509,6 +589,8 @@ void str_to_file(const char *fname, char *str, int size); void str_to_file2(const char *fname, char *str, int size, my_bool append); void fix_win_paths(const char *val, int len); +const char *get_errname_from_code (uint error_code); +int multi_reg_replace(struct st_replace_regex* r,char* val); #ifdef __WIN__ void free_tmp_sh_file(); @@ -559,8 +641,7 @@ public: void open(const char* dir, const char* name, const char* ext) { DBUG_ENTER("LogFile::open"); - DBUG_PRINT("enter", ("dir: '%s', name: '%s'", - val_or_null(dir), val_or_null(name))); + DBUG_PRINT("enter", ("dir: '%s', name: '%s'", dir, name)); if (!name) { m_file= stdout; @@ -655,9 +736,8 @@ public: DBUG_VOID_RETURN; } - IF_DBUG(buf[bytes]= '\0';) - DBUG_PRINT("info", ("Read %lu bytes from file, buf: %s", - (unsigned long)bytes, buf)); + DBUG_PRINT("info", ("Read %zu bytes from file, buf: %.*s", + bytes, (int)bytes, buf)); char* show_from= buf + bytes; while(show_from > buf && lines > 0 ) @@ -720,14 +800,6 @@ public: LogFile log_file; LogFile progress_file; - -/* Disable functions that only exist in MySQL 4.0 */ -#if MYSQL_VERSION_ID < 40000 -void mysql_enable_rpl_parse(MYSQL* mysql __attribute__((unused))) {} -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 void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val, int len); void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val); @@ -742,12 +814,17 @@ 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 revert_properties(); static void handle_no_active_connection(struct st_command* command, struct st_connection *cn, DYNAMIC_STRING *ds); #ifdef EMBEDDED_LIBRARY +#define EMB_SEND_QUERY 1 +#define EMB_READ_QUERY_RESULT 2 +#define EMB_END_CONNECTION 3 + /* workaround for MySQL BUG#57491 */ #undef MY_WME #define MY_WME 0 @@ -755,90 +832,140 @@ static void handle_no_active_connection(struct st_command* command, /* attributes of the query thread */ pthread_attr_t cn_thd_attrib; + /* - send_one_query executes query in separate thread, which is - necessary in embedded library to run 'send' in proper way. - This implementation doesn't handle errors returned - by mysql_send_query. It's technically possible, though - I don't see where it is needed. + This procedure represents the connection and actually + runs queries when in the EMBEDDED-SERVER mode. + The run_query_normal() just sends request for running + mysql_send_query and mysql_read_query_result() here. */ -pthread_handler_t send_one_query(void *arg) + +pthread_handler_t connection_thread(void *arg) { struct st_connection *cn= (struct st_connection*)arg; - if (!cn->mysql) - return 0; - mysql_thread_init(); - VOID(mysql_send_query(cn->mysql, cn->cur_query, cn->cur_query_len)); + while (cn->command != EMB_END_CONNECTION) + { + if (!cn->command) + { + pthread_mutex_lock(&cn->query_mutex); + while (!cn->command) + pthread_cond_wait(&cn->query_cond, &cn->query_mutex); + pthread_mutex_unlock(&cn->query_mutex); + } + switch (cn->command) + { + case EMB_END_CONNECTION: + goto end_thread; + case EMB_SEND_QUERY: + cn->result= mysql_send_query(cn->mysql, cn->cur_query, cn->cur_query_len); + break; + case EMB_READ_QUERY_RESULT: + cn->result= mysql_read_query_result(cn->mysql); + break; + default: + DBUG_ASSERT(0); + } + cn->command= 0; + pthread_mutex_lock(&cn->result_mutex); + cn->query_done= 1; + pthread_cond_signal(&cn->result_cond); + pthread_mutex_unlock(&cn->result_mutex); + } - mysql_thread_end(); - pthread_mutex_lock(&cn->mutex); +end_thread: cn->query_done= 1; - VOID(pthread_cond_signal(&cn->cond)); - pthread_mutex_unlock(&cn->mutex); + mysql_thread_end(); pthread_exit(0); return 0; } -static int do_send_query(struct st_connection *cn, const char *q, int q_len, - int flags) +static void wait_query_thread_done(struct st_connection *con) { - if (!cn->mysql) - die("Trying to send a query without a connection"); + DBUG_ASSERT(con->has_thread); + if (!con->query_done) + { + pthread_mutex_lock(&con->result_mutex); + while (!con->query_done) + pthread_cond_wait(&con->result_cond, &con->result_mutex); + pthread_mutex_unlock(&con->result_mutex); + } +} - if (flags & QUERY_REAP_FLAG) - return mysql_send_query(cn->mysql, q, q_len); - if (!cn->mutex_inited && - (pthread_mutex_init(&cn->mutex, NULL) || - pthread_cond_init(&cn->cond, NULL))) - die("Error in the thread library"); +static void signal_connection_thd(struct st_connection *cn, int command) +{ + DBUG_ASSERT(cn->has_thread); + cn->query_done= 0; + cn->command= command; + pthread_mutex_lock(&cn->query_mutex); + pthread_cond_signal(&cn->query_cond); + pthread_mutex_unlock(&cn->query_mutex); +} - cn->mutex_inited= 1; + +/* + Sometimes we try to execute queries when the connection is closed. + It's done to make sure it was closed completely. + So that if our connection is closed (cn->has_thread == 0), we just return + the mysql_send_query() result which is an error in this case. +*/ + +static int do_send_query(struct st_connection *cn, const char *q, int q_len) +{ + if (!cn->has_thread) + return mysql_send_query(cn->mysql, q, q_len); cn->cur_query= q; cn->cur_query_len= q_len; - cn->query_done= 0; - if (pthread_create(&cn->tid, &cn_thd_attrib, send_one_query, (void*)cn)) - die("Cannot start new thread for query"); - - cn->has_thread= TRUE; + signal_connection_thd(cn, EMB_SEND_QUERY); return 0; } -static void wait_query_thread_end(struct st_connection *con) +static int do_read_query_result(struct st_connection *cn) { - if (!con->query_done) - { - pthread_mutex_lock(&con->mutex); - while (!con->query_done) - pthread_cond_wait(&con->cond, &con->mutex); - pthread_mutex_unlock(&con->mutex); - } - if (con->has_thread) - { -#ifndef __WIN__ - /* May hang on Windows, but the problem it solves is not seen there */ - pthread_join(con->tid, NULL); -#endif - con->has_thread= FALSE; - } + DBUG_ASSERT(cn->has_thread); + wait_query_thread_done(cn); + signal_connection_thd(cn, EMB_READ_QUERY_RESULT); + wait_query_thread_done(cn); + + return cn->result; } -static void free_embedded_data(struct st_connection *con) + +static void emb_close_connection(struct st_connection *cn) { - if (con->mutex_inited) - { - con->mutex_inited= 0; - pthread_mutex_destroy(&con->mutex); - pthread_cond_destroy(&con->cond); - } + if (!cn->has_thread) + return; + wait_query_thread_done(cn); + signal_connection_thd(cn, EMB_END_CONNECTION); + pthread_join(cn->tid, NULL); + cn->has_thread= FALSE; + pthread_mutex_destroy(&cn->query_mutex); + pthread_cond_destroy(&cn->query_cond); + pthread_mutex_destroy(&cn->result_mutex); + pthread_cond_destroy(&cn->result_cond); +} + + +static void init_connection_thd(struct st_connection *cn) +{ + cn->query_done= 1; + cn->command= 0; + if (pthread_mutex_init(&cn->query_mutex, NULL) || + pthread_cond_init(&cn->query_cond, NULL) || + pthread_mutex_init(&cn->result_mutex, NULL) || + pthread_cond_init(&cn->result_cond, NULL) || + pthread_create(&cn->tid, &cn_thd_attrib, connection_thread, (void*)cn)) + die("Error in the thread library"); + cn->has_thread=TRUE; } #else /*EMBEDDED_LIBRARY*/ -#define do_send_query(cn,q,q_len,flags) mysql_send_query(cn->mysql, q, q_len) -#define free_embedded_data(next_con) do { } while(0) +#define init_connection_thd(X) do { } while(0) +#define do_send_query(cn,q,q_len) mysql_send_query(cn->mysql, q, q_len) +#define do_read_query_result(cn) mysql_read_query_result(cn->mysql) #endif /*EMBEDDED_LIBRARY*/ @@ -863,7 +990,10 @@ void do_eval(DYNAMIC_STRING *query_eval, const char *query, else { if (!(v= var_get(p, &p, 0, 0))) - die("Bad variable in eval"); + { + report_or_die( "Bad variable in eval"); + return; + } dynstr_append_mem(query_eval, v->str_val, v->str_val_len); } break; @@ -1150,9 +1280,13 @@ void handle_command_error(struct st_command *command, uint error, int i; if (command->abort_on_error) - die("command \"%.*s\" failed with error: %u my_errno: %d errno: %d", + { + report_or_die("command \"%.*s\" failed with error: %u my_errno: %d " + "errno: %d", command->first_word_len, command->query, error, my_errno, sys_errno); + return; + } i= match_expected_error(command, error, NULL); @@ -1161,20 +1295,25 @@ void handle_command_error(struct st_command *command, uint error, DBUG_PRINT("info", ("command \"%.*s\" failed with expected error: %u, errno: %d", command->first_word_len, command->query, error, sys_errno)); + revert_properties(); DBUG_VOID_RETURN; } if (command->expected_errors.count > 0) - die("command \"%.*s\" failed with wrong error: %u my_errno: %d errno: %d", - command->first_word_len, command->query, error, my_errno, sys_errno); + report_or_die("command \"%.*s\" failed with wrong error: %u " + "my_errno: %d errno: %d", + command->first_word_len, command->query, error, my_errno, + sys_errno); } 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...", + report_or_die("command \"%.*s\" succeeded - should have failed with " + "errno %d...", command->first_word_len, command->query, command->expected_errors.err[0].code.errnum); } + revert_properties(); DBUG_VOID_RETURN; } @@ -1184,6 +1323,9 @@ void close_connections() DBUG_ENTER("close_connections"); for (--next_con; next_con >= connections; --next_con) { +#ifdef EMBEDDED_LIBRARY + emb_close_connection(next_con); +#endif if (next_con->stmt) mysql_stmt_close(next_con->stmt); next_con->stmt= 0; @@ -1191,10 +1333,9 @@ void close_connections() next_con->mysql= 0; if (next_con->util_mysql) mysql_close(next_con->util_mysql); - my_free(next_con->name, MYF(MY_ALLOW_ZERO_PTR)); - free_embedded_data(next_con); + my_free(next_con->name); } - my_free(connections, MYF(MY_WME)); + my_free(connections); DBUG_VOID_RETURN; } @@ -1223,7 +1364,7 @@ void close_files() DBUG_PRINT("info", ("closing file: %s", cur_file->file_name)); fclose(cur_file->file); } - my_free((uchar*) cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR)); + my_free(cur_file->file_name); cur_file->file_name= 0; } DBUG_VOID_RETURN; @@ -1238,27 +1379,29 @@ void free_used_memory() if (connections) close_connections(); close_files(); - hash_free(&var_hash); + my_hash_free(&var_hash); for (i= 0 ; i < q_lines.elements ; i++) { struct st_command **q= dynamic_element(&q_lines, i, struct st_command**); - my_free((*q)->query_buf,MYF(MY_ALLOW_ZERO_PTR)); + my_free((*q)->query_buf); if ((*q)->content.str) dynstr_free(&(*q)->content); - my_free((*q),MYF(0)); + my_free((*q)); } for (i= 0; i < 10; i++) { if (var_reg[i].alloced_len) - my_free(var_reg[i].str_val, MYF(MY_WME)); + my_free(var_reg[i].str_val); } while (embedded_server_arg_count > 1) - my_free(embedded_server_args[--embedded_server_arg_count],MYF(0)); + my_free(embedded_server_args[--embedded_server_arg_count]); delete_dynamic(&q_lines); dynstr_free(&ds_res); + if (ds_warn) + dynstr_free(ds_warn); free_all_replace(); - my_free(opt_pass,MYF(MY_ALLOW_ZERO_PTR)); + my_free(opt_pass); free_defaults(default_argv); free_root(&require_file_root, MYF(0)); free_re(); @@ -1277,6 +1420,17 @@ static void cleanup_and_exit(int exit_code) /* Only call mysql_server_end if mysql_server_init has been called */ if (server_initialized) mysql_server_end(); + + /* + mysqltest is fundamentally written in a way that makes impossible + to free all memory before exit (consider memory allocated + for frame local DYNAMIC_STRING's and die() invoked down the stack. + + We close stderr here to stop unavoidable safemalloc reports + from polluting the output. + */ + fclose(stderr); + my_end(my_end_arg); if (!silent) { @@ -1296,52 +1450,63 @@ static void cleanup_and_exit(int exit_code) } } + sf_leaking_memory= 0; /* all memory should be freed by now */ exit(exit_code); } -void print_file_stack() +size_t print_file_stack(char *s, const char *end) { + char *start= s; struct st_test_file* err_file= cur_file; if (err_file == file_stack) - return; + return 0; for (;;) { err_file--; - fprintf(stderr, "included from %s at line %d:\n", - err_file->file_name, err_file->lineno); + s+= my_snprintf(s, end - s, "included from %s at line %d:\n", + err_file->file_name, err_file->lineno); if (err_file == file_stack) break; } + return s - start; } -void die(const char *fmt, ...) -{ - static int dying= 0; - va_list args; - DBUG_ENTER("die"); - DBUG_PRINT("enter", ("start_lineno: %d", start_lineno)); - fflush(stdout); - /* Print the error message */ - fprintf(stderr, "mysqltest: "); +static void make_error_message(char *buf, size_t len, const char *fmt, va_list args) +{ + char *s= buf, *end= buf + len; + s+= my_snprintf(s, end - s, "mysqltest: "); if (cur_file && cur_file != file_stack) { - fprintf(stderr, "In included file \"%s\": \n", - cur_file->file_name); - print_file_stack(); + s+= my_snprintf(s, end - s, "In included file \"%s\": \n", + cur_file->file_name); + s+= print_file_stack(s, end); } + if (start_lineno > 0) - fprintf(stderr, "At line %u: ", start_lineno); - if (fmt) - { - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - } - else - fprintf(stderr, "unknown error"); - fprintf(stderr, "\n"); + s+= my_snprintf(s, end -s, "At line %u: ", start_lineno); + if (!fmt) + fmt= "unknown error"; + + s+= my_vsnprintf(s, end - s, fmt, args); + s+= my_snprintf(s, end -s, "\n", start_lineno); +} + +void die(const char *fmt, ...) +{ + char buff[DIE_BUFF_SIZE]; + va_list args; + va_start(args, fmt); + make_error_message(buff, sizeof(buff), fmt, args); + really_die(buff); +} + +void really_die(const char *msg) +{ + static int dying= 0; + fflush(stdout); + fprintf(stderr, "%s", msg); fflush(stderr); /* @@ -1365,6 +1530,28 @@ void die(const char *fmt, ...) cleanup_and_exit(1); } +void report_or_die(const char *fmt, ...) +{ + va_list args; + DBUG_ENTER("report_or_die"); + + char buff[DIE_BUFF_SIZE]; + + va_start(args, fmt); + make_error_message(buff, sizeof(buff), fmt, args); + va_end(args); + + if (opt_continue_on_error) + { + /* Just log the error and continue */ + replace_dynstr_append(&ds_res, buff); + error_count++; + DBUG_VOID_RETURN; + } + + really_die(buff); +} + void abort_not_supported_test(const char *fmt, ...) { @@ -1377,7 +1564,10 @@ void abort_not_supported_test(const char *fmt, ...) file_stack->file_name); fprintf(stderr, "Detected in file %s at line %d\n", cur_file->file_name, cur_file->lineno); - print_file_stack(); + + char buff[DIE_BUFF_SIZE]; + print_file_stack(buff, buff + sizeof(buff)); + fprintf(stderr, "%s", buff); /* Print error message */ va_start(args, fmt); @@ -1509,7 +1699,10 @@ static int run_command(char* cmd, DBUG_PRINT("enter", ("cmd: %s", cmd)); if (!(res_file= popen(cmd, "r"))) - die("popen(\"%s\", \"r\") failed", cmd); + { + report_or_die("popen(\"%s\", \"r\") failed", cmd); + return -1; + } while (fgets(buf, sizeof(buf), res_file)) { @@ -1609,7 +1802,10 @@ static int diff_check(const char *diff_name) if (!(res_file= popen(buf, "r"))) die("popen(\"%s\", \"r\") failed", buf); - /* if diff is not present, nothing will be in stdout to increment have_diff */ + /* + if diff is not present, nothing will be in stdout to increment + have_diff + */ if (fgets(buf, sizeof(buf), res_file)) have_diff= 1; @@ -1922,7 +2118,7 @@ int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) void check_result() { - const char* mess= "Result content mismatch\n"; + const char *mess= 0; DBUG_ENTER("check_result"); DBUG_ASSERT(result_file_name); @@ -1930,9 +2126,13 @@ void check_result() switch (compare_files(log_file.file_name(), result_file_name)) { case RESULT_OK: - break; /* ok */ + if (!error_count) + break; /* ok */ + mess= "Got errors while running test"; + /* Fallthrough */ case RESULT_LENGTH_MISMATCH: - mess= "Result length mismatch\n"; + if (!mess) + mess= "Result length mismatch\n"; /* Fallthrough */ case RESULT_CONTENT_MISMATCH: { @@ -1942,6 +2142,10 @@ void check_result() */ char reject_file[FN_REFLEN]; size_t reject_length; + + if (!mess) + mess= "Result content mismatch\n"; + dirname_part(reject_file, result_file_name, &reject_length); if (access(reject_file, W_OK) == 0) @@ -2041,11 +2245,13 @@ static int strip_surrounding(char* str, char c1, char c2) static void strip_parentheses(struct st_command *command) { if (strip_surrounding(command->first_argument, '(', ')')) - die("%.*s - argument list started with '%c' must be ended with '%c'", - command->first_word_len, command->query, '(', ')'); + die("%.*s - argument list started with '%c' must be ended with '%c'", + command->first_word_len, command->query, '(', ')'); } +C_MODE_START + static uchar *get_var_key(const uchar* var, size_t *len, my_bool __attribute__((unused)) t) { @@ -2056,6 +2262,34 @@ static uchar *get_var_key(const uchar* var, size_t *len, } +static void var_free(void *v) +{ + VAR *var= (VAR*) v; + my_free(var->str_val); + if (var->alloced) + my_free(var); +} + +C_MODE_END + +void var_check_int(VAR *v) +{ + char *endptr; + char *str= v->str_val; + + /* Initially assume not a number */ + v->int_val= 0; + v->is_int= false; + v->int_dirty= false; + if (!str) return; + + v->int_val = (int) strtol(str, &endptr, 10); + /* It is an int if strtol consumed something up to end/space/tab */ + if (endptr > str && (!*endptr || *endptr == ' ' || *endptr == '\t')) + v->is_int= true; +} + + VAR *var_init(VAR *v, const char *name, int name_len, const char *val, int val_len) { @@ -2065,6 +2299,8 @@ VAR *var_init(VAR *v, const char *name, int name_len, const char *val, name_len = strlen(name); if (!val_len && val) val_len = strlen(val) ; + if (!val) + val_len= 0; val_alloc_len = val_len + 16; /* room to grow */ if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var) + name_len+2, MYF(MY_WME)))) @@ -2085,27 +2321,17 @@ VAR *var_init(VAR *v, const char *name, int name_len, const char *val, die("Out of memory"); if (val) - { memcpy(tmp_var->str_val, val, val_len); - tmp_var->str_val[val_len]= 0; - } + tmp_var->str_val[val_len]= 0; + + var_check_int(tmp_var); 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; } -void var_free(void *v) -{ - my_free(((VAR*) v)->str_val, MYF(MY_WME)); - if (((VAR*)v)->alloced) - my_free(v, MYF(MY_WME)); -} - - VAR* var_from_env(const char *name, const char *def_val) { const char *tmp; @@ -2147,8 +2373,8 @@ VAR* var_get(const char *var_name, const char **var_name_end, my_bool raw, if (length >= MAX_VAR_NAME_LENGTH) die("Too long variable name: %s", save_var_name); - if (!(v = (VAR*) hash_search(&var_hash, (const uchar*) save_var_name, - length))) + if (!(v = (VAR*) my_hash_search(&var_hash, (const uchar*) save_var_name, + length))) { char buff[MAX_VAR_NAME_LENGTH+1]; strmake(buff, save_var_name, length); @@ -2162,7 +2388,7 @@ VAR* var_get(const char *var_name, const char **var_name_end, my_bool raw, if (!raw && v->int_dirty) { sprintf(v->str_val, "%d", v->int_val); - v->int_dirty = 0; + v->int_dirty= false; v->str_val_len = strlen(v->str_val); } if (var_name_end) @@ -2179,7 +2405,7 @@ err: VAR *var_obtain(const char *name, int len) { VAR* v; - if ((v = (VAR*)hash_search(&var_hash, (const uchar *) name, len))) + if ((v = (VAR*)my_hash_search(&var_hash, (const uchar *) name, len))) return v; v = var_init(0, name, len, "", 0); my_hash_insert(&var_hash, (uchar*)v); @@ -2224,7 +2450,7 @@ void var_set(const char *var_name, const char *var_name_end, if (v->int_dirty) { sprintf(v->str_val, "%d", v->int_val); - v->int_dirty= 0; + v->int_dirty=false; v->str_val_len= strlen(v->str_val); } /* setenv() expects \0-terminated strings */ @@ -2257,6 +2483,51 @@ void var_set_int(const char* name, int value) void var_set_errno(int sql_errno) { var_set_int("$mysql_errno", sql_errno); + var_set_string("$mysql_errname", get_errname_from_code(sql_errno)); +} + +/* Functions to handle --disable and --enable properties */ + +void set_once_property(enum_prop prop, my_bool val) +{ + property &pr= prop_list[prop]; + pr.set= 1; + pr.old= *pr.var; + *pr.var= val; + var_set_int(pr.env_name, (val != pr.reverse)); + once_property= TRUE; +} + +void set_property(st_command *command, enum_prop prop, my_bool val) +{ + char* p= command->first_argument; + if (p && !strcmp (p, "ONCE")) + { + command->last_argument= p + 4; + set_once_property(prop, val); + return; + } + property &pr= prop_list[prop]; + *pr.var= val; + pr.set= 0; + var_set_int(pr.env_name, (val != pr.reverse)); +} + +void revert_properties() +{ + if (! once_property) + return; + for (int i= 0; i < (int) P_MAX; i++) + { + property &pr= prop_list[i]; + if (pr.set) + { + *pr.var= pr.old; + pr.set= 0; + var_set_int(pr.env_name, (pr.old != pr.reverse)); + } + } + once_property=FALSE; } @@ -2325,8 +2596,8 @@ void var_query_set(VAR *var, const char *query, const char** query_end) if (mysql_real_query(mysql, ds_query.str, ds_query.length)) { - handle_error (curr_command, mysql_errno(mysql), mysql_error(mysql), - mysql_sqlstate(mysql), &ds_res); + handle_error(curr_command, mysql_errno(mysql), mysql_error(mysql), + mysql_sqlstate(mysql), &ds_res); /* If error was acceptable, return empty string */ dynstr_free(&ds_query); eval_expr(var, "", 0); @@ -2334,7 +2605,12 @@ void var_query_set(VAR *var, const char *query, const char** query_end) } if (!(res= mysql_store_result(mysql))) - die("Query '%s' didn't return a result set", ds_query.str); + { + report_or_die("Query '%s' didn't return a result set", ds_query.str); + dynstr_free(&ds_query); + eval_expr(var, "", 0); + return; + } dynstr_free(&ds_query); if ((row= mysql_fetch_row(res)) && row[0]) @@ -2354,13 +2630,29 @@ void var_query_set(VAR *var, const char *query, const char** query_end) if (row[i]) { /* Add column to tab separated string */ - dynstr_append_mem(&result, row[i], lengths[i]); + char *val= row[i]; + int len= lengths[i]; + + if (glob_replace_regex) + { + /* Regex replace */ + if (!multi_reg_replace(glob_replace_regex, (char*)val)) + { + val= glob_replace_regex->buf; + len= strlen(val); + } + } + + if (glob_replace) + replace_strings_append(glob_replace, &result, val, len); + else + dynstr_append_mem(&result, val, len); } dynstr_append_mem(&result, "\t", 1); } end= result.str + result.length-1; /* Evaluation should not recurse via backtick */ - eval_expr(var, result.str, (const char**) &end, false); + eval_expr(var, result.str, (const char**) &end, false, false); dynstr_free(&result); } else @@ -2371,6 +2663,59 @@ void var_query_set(VAR *var, const char *query, const char** query_end) } +static void +set_result_format_version(ulong new_version) +{ + switch (new_version){ + case 1: + /* The first format */ + break; + case 2: + /* New format that also writes comments and empty lines + from test file to result */ + break; + default: + die("Version format %lu has not yet been implemented", new_version); + break; + } + opt_result_format_version= new_version; +} + + +/* + Set the result format version to use when generating + the .result file +*/ + +static void +do_result_format_version(struct st_command *command) +{ + long version; + static DYNAMIC_STRING ds_version; + const struct command_arg result_format_args[] = { + {"version", ARG_STRING, TRUE, &ds_version, "Version to use"} + }; + + DBUG_ENTER("do_result_format_version"); + + check_command_args(command, command->first_argument, + result_format_args, + sizeof(result_format_args)/sizeof(struct command_arg), + ','); + + /* Convert version number to int */ + if (!str2int(ds_version.str, 10, (long) 0, (long) INT_MAX, &version)) + die("Invalid version number: '%s'", ds_version.str); + + set_result_format_version(version); + + dynstr_append(&ds_res, "result_format: "); + dynstr_append_mem(&ds_res, ds_version.str, ds_version.length); + dynstr_append(&ds_res, "\n"); + dynstr_free(&ds_version); +} + + /* Set variable from the result of a field in a query @@ -2440,16 +2785,23 @@ void var_set_query_get_value(struct st_command *command, VAR *var) /* Run the query */ if (mysql_real_query(mysql, ds_query.str, ds_query.length)) { - handle_error (curr_command, mysql_errno(mysql), mysql_error(mysql), - mysql_sqlstate(mysql), &ds_res); + handle_error(curr_command, mysql_errno(mysql), mysql_error(mysql), + mysql_sqlstate(mysql), &ds_res); /* If error was acceptable, return empty string */ dynstr_free(&ds_query); + dynstr_free(&ds_col); eval_expr(var, "", 0); DBUG_VOID_RETURN; } if (!(res= mysql_store_result(mysql))) - die("Query '%s' didn't return a result set", ds_query.str); + { + report_or_die("Query '%s' didn't return a result set", ds_query.str); + dynstr_free(&ds_query); + dynstr_free(&ds_col); + eval_expr(var, "", 0); + return; + } { /* Find column number from the given column name */ @@ -2469,8 +2821,11 @@ void var_set_query_get_value(struct st_command *command, VAR *var) if (col_no == -1) { mysql_free_result(res); - die("Could not find column '%s' in the result of '%s'", - ds_col.str, ds_query.str); + report_or_die("Could not find column '%s' in the result of '%s'", + ds_col.str, ds_query.str); + dynstr_free(&ds_query); + dynstr_free(&ds_col); + return; } DBUG_PRINT("info", ("Found column %d with name '%s'", i, fields[i].name)); @@ -2499,7 +2854,7 @@ void var_set_query_get_value(struct st_command *command, VAR *var) break; } } - eval_expr(var, value, 0, false); + eval_expr(var, value, 0, false, false); } dynstr_free(&ds_query); mysql_free_result(res); @@ -2511,6 +2866,7 @@ void var_set_query_get_value(struct st_command *command, VAR *var) void var_copy(VAR *dest, VAR *src) { dest->int_val= src->int_val; + dest->is_int= src->is_int; dest->int_dirty= src->int_dirty; /* Alloc/realloc data for str_val in dest */ @@ -2529,7 +2885,8 @@ void var_copy(VAR *dest, VAR *src) } -void eval_expr(VAR *v, const char *p, const char **p_end, bool do_eval) +void eval_expr(VAR *v, const char *p, const char **p_end, + bool open_end, bool do_eval) { DBUG_ENTER("eval_expr"); @@ -2551,7 +2908,7 @@ void eval_expr(VAR *v, const char *p, const char **p_end, bool do_eval) /* Make sure there was just a $variable and nothing else */ const char* end= *p_end + 1; - if (end < expected_end) + if (end < expected_end && !open_end) die("Found junk '%.*s' after $variable in expression", (int)(expected_end - end - 1), end); @@ -2600,48 +2957,134 @@ void eval_expr(VAR *v, const char *p, const char **p_end, bool do_eval) 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); - DBUG_PRINT("info", ("atoi on '%s', returns: %d", p, v->int_val)); - v->int_dirty=0; + var_check_int(v); } DBUG_VOID_RETURN; } -int open_file(const char *name) +bool open_and_set_current(const char *name) +{ + FILE *opened= fopen(name, "rb"); + + if (!opened) + return false; + + cur_file++; + cur_file->file= opened; + cur_file->file_name= my_strdup(name, MYF(MY_FAE)); + cur_file->lineno=1; + return true; +} + + +void open_file(const char *name) { char buff[FN_REFLEN]; size_t length; + const char *curname= cur_file->file_name; DBUG_ENTER("open_file"); DBUG_PRINT("enter", ("name: %s", name)); - /* Extract path from current file and try it as base first */ - if (dirname_part(buff, cur_file->file_name, &length)) + if (cur_file == file_stack_end) + die("Source directives are nesting too deep"); + + if (test_if_hard_path(name)) { - strxmov(buff, buff, name, NullS); - if (access(buff, F_OK) == 0){ - DBUG_PRINT("info", ("The file exists")); - name= buff; - } + if (open_and_set_current(name)) + DBUG_VOID_RETURN; } - if (!test_if_hard_path(name)) + else { - strxmov(buff, opt_basedir, name, NullS); - name=buff; - } - fn_format(buff, name, "", "", MY_UNPACK_FILENAME); + /* + if overlay-dir is specified, and the file is located somewhere + under overlay-dir or under suite-dir, the search works as follows: + + 0.let suffix be current file dirname relative to siute-dir or overlay-dir + 1.try in overlay-dir/suffix + 2.try in suite-dir/suffix + 3.try in overlay-dir + 4.try in suite-dir + 5.try in basedir + + consider an example: 'rty' overlay of the 'qwe' suite, + file qwe/include/some.inc contains the line + --source thing.inc + we look for it in this order: + 0.suffix is "include/" + 1.try in rty/include/thing.inc + 2.try in qwe/include/thing.inc + 3.try in try/thing.inc | this is useful when t/a.test has + 4.try in qwe/thing.inc | source include/b.inc; + 5.try in mysql-test/include/thing.inc + + otherwise the search is as follows + 1.try in current file dirname + 3.try in overlay-dir (if any) + 4.try in suite-dir + 5.try in basedir + */ - if (cur_file == file_stack_end) - die("Source directives are nesting too deep"); - cur_file++; - if (!(cur_file->file = fopen(buff, "rb"))) - { - cur_file--; - die("Could not open '%s' for reading, errno: %d", buff, errno); + bool in_overlay= opt_overlay_dir && + !strncmp(curname, opt_overlay_dir, overlay_dir_len); + bool in_suiteir= opt_overlay_dir && !in_overlay && + !strncmp(curname, opt_suite_dir, suite_dir_len); + if (in_overlay || in_suiteir) + { + size_t prefix_len = in_overlay ? overlay_dir_len : suite_dir_len; + char buf2[FN_REFLEN], *suffix= buf2 + prefix_len; + dirname_part(buf2, curname, &length); + + /* 1. first we look in the overlay dir */ + strxnmov(buff, sizeof(buff), opt_overlay_dir, suffix, name, NullS); + + /* + Overlayed rty/include/thing.inc can contain the line + --source thing.inc + which would mean to include qwe/include/thing.inc. + But it looks like including "itself", so don't try to open the file, + if buff contains the same file name as curname. + */ + if (strcmp(buff, curname) && open_and_set_current(buff)) + DBUG_VOID_RETURN; + + /* 2. if that failed, we look in the suite dir */ + strxnmov(buff, sizeof(buff), opt_suite_dir, suffix, name, NullS); + + /* buff can not be equal to curname, as a file can never include itself */ + if (open_and_set_current(buff)) + DBUG_VOID_RETURN; + } + else + { + /* 1. try in current file dirname */ + dirname_part(buff, curname, &length); + strxnmov(buff, sizeof(buff), buff, name, NullS); + if (open_and_set_current(buff)) + DBUG_VOID_RETURN; + } + + /* 3. now, look in the overlay dir */ + if (opt_overlay_dir) + { + strxmov(buff, opt_overlay_dir, name, NullS); + if (open_and_set_current(buff)) + DBUG_VOID_RETURN; + } + + /* 4. if that failed - look in the suite dir */ + strxmov(buff, opt_suite_dir, name, NullS); + if (open_and_set_current(buff)) + DBUG_VOID_RETURN; + + /* 5. the last resort - look in the base dir */ + strxnmov(buff, sizeof(buff), opt_basedir, name, NullS); + if (open_and_set_current(buff)) + DBUG_VOID_RETURN; } - cur_file->file_name= my_strdup(buff, MYF(MY_FAE)); - cur_file->lineno=1; - DBUG_RETURN(0); + + die("Could not open '%s' for reading, errno: %d", name, errno); + DBUG_VOID_RETURN; } @@ -2822,7 +3265,10 @@ void do_exec(struct st_command *command) while (*cmd && my_isspace(charset_info, *cmd)) cmd++; if (!*cmd) - die("Missing argument in exec"); + { + report_or_die("Missing argument in exec"); + return; + } command->last_argument= command->end; init_dynamic_string(&ds_cmd, 0, command->query_len+256, 256); @@ -2854,10 +3300,12 @@ void do_exec(struct st_command *command) DBUG_PRINT("info", ("Executing '%s' as '%s'", command->first_argument, ds_cmd.str)); - if (!(res_file= my_popen(&ds_cmd, "r")) && command->abort_on_error) + if (!(res_file= my_popen(&ds_cmd, "r"))) { dynstr_free(&ds_cmd); - die("popen(\"%s\", \"r\") failed", command->first_argument); + if (command->abort_on_error) + report_or_die("popen(\"%s\", \"r\") failed", command->first_argument); + return; } ds_result= &ds_res; @@ -2894,11 +3342,12 @@ void do_exec(struct st_command *command) if (command->abort_on_error) { - log_msg("exec of '%s' failed, error: %d, status: %d, errno: %d", - ds_cmd.str, error, status, errno); + report_or_die("exec of '%s' failed, error: %d, status: %d, errno: %d\n" + "Output from before failure:\n%s\n", + ds_cmd.str, error, status, errno, + ds_res.str); dynstr_free(&ds_cmd); - die("command \"%s\" failed\n\nOutput from before failure:\n%s\n", - command->first_argument, ds_res.str); + return; } DBUG_PRINT("info", @@ -2913,8 +3362,8 @@ void do_exec(struct st_command *command) { dynstr_free(&ds_cmd); if (command->expected_errors.count > 0) - die("command \"%s\" failed with wrong error: %d", - command->first_argument, status); + report_or_die("command \"%s\" failed with wrong error: %d", + command->first_argument, status); } } else if (command->expected_errors.err[0].type == ERR_ERRNO && @@ -2924,8 +3373,10 @@ void do_exec(struct st_command *command) log_msg("exec of '%s failed, error: %d, errno: %d", ds_cmd.str, error, errno); dynstr_free(&ds_cmd); - die("command \"%s\" succeeded - should have failed with errno %d...", - command->first_argument, command->expected_errors.err[0].code.errnum); + report_or_die("command \"%s\" succeeded - should have failed with " + "errno %d...", + command->first_argument, + command->expected_errors.err[0].code.errnum); } dynstr_free(&ds_cmd); @@ -2959,11 +3410,14 @@ int do_modify_var(struct st_command *command, const char *p= command->first_argument; VAR* v; if (!*p) - die("Missing argument to %.*s", command->first_word_len, command->query); + die("Missing argument to %.*s", command->first_word_len, + command->query); if (*p != '$') die("The argument to %.*s must be a variable (start with $)", command->first_word_len, command->query); v= var_get(p, &p, 1, 0); + if (! v->is_int) + die("Cannot perform inc/dec on a non-numeric value"); switch (op) { case DO_DEC: v->int_val--; @@ -2975,7 +3429,7 @@ int do_modify_var(struct st_command *command, die("Invalid operator to do_modify_var"); break; } - v->int_dirty= 1; + v->int_dirty= true; command->last_argument= (char*)++p; return 0; } @@ -3023,7 +3477,10 @@ void do_system(struct st_command *command) DBUG_ENTER("do_system"); if (strlen(command->first_argument) == 0) - die("Missing arguments to system, nothing to do!"); + { + report_or_die("Missing arguments to system, nothing to do!"); + return; + } init_dynamic_string(&ds_cmd, 0, command->query_len + 64, 256); @@ -3044,12 +3501,14 @@ void do_system(struct st_command *command) if (my_system(&ds_cmd)) { if (command->abort_on_error) - die("system command '%s' failed", command->first_argument); - - /* If ! abort_on_error, log message and continue */ - dynstr_append(&ds_res, "system command '"); - replace_dynstr_append(&ds_res, command->first_argument); - dynstr_append(&ds_res, "' failed\n"); + report_or_die("system command '%s' failed", command->first_argument); + else + { + /* If ! abort_on_error, log message and continue */ + dynstr_append(&ds_res, "system command '"); + replace_dynstr_append(&ds_res, command->first_argument); + dynstr_append(&ds_res, "' failed\n"); + } } command->last_argument= command->end; @@ -3235,8 +3694,9 @@ void do_copy_file(struct st_command *command) ' '); DBUG_PRINT("info", ("Copy %s to %s", ds_from_file.str, ds_to_file.str)); + /* MY_HOLD_ORIGINAL_MODES prevents attempts to chown the file */ error= (my_copy(ds_from_file.str, ds_to_file.str, - MYF(MY_DONT_OVERWRITE_FILE | MY_WME)) != 0); + MYF(MY_DONT_OVERWRITE_FILE | MY_WME | MY_HOLD_ORIGINAL_MODES)) != 0); handle_command_error(command, error, my_errno); dynstr_free(&ds_from_file); dynstr_free(&ds_to_file); @@ -3293,8 +3753,8 @@ void do_move_file(struct st_command *command) void do_chmod_file(struct st_command *command) { - int error; long mode= 0; + int err_code; static DYNAMIC_STRING ds_mode; static DYNAMIC_STRING ds_file; const struct command_arg chmod_file_args[] = { @@ -3314,10 +3774,10 @@ void do_chmod_file(struct st_command *command) die("You must write a 4 digit octal number for mode"); DBUG_PRINT("info", ("chmod %o %s", (uint)mode, ds_file.str)); - error= 0; - if (chmod(ds_file.str, mode)) - error= 1; - handle_command_error(command, error, errno); + err_code= chmod(ds_file.str, mode); + if (err_code < 0) + err_code= 1; + handle_command_error(command, err_code, errno); dynstr_free(&ds_mode); dynstr_free(&ds_file); DBUG_VOID_RETURN; @@ -3595,12 +4055,12 @@ void read_until_delimiter(DYNAMIC_STRING *ds, No characters except \n are allowed on the same line as the command */ - die("Trailing characters found after command"); + report_or_die("Trailing characters found after command"); } if (feof(cur_file->file)) - die("End of file encountered before '%s' delimiter was found", - ds_delimiter->str); + report_or_die("End of file encountered before '%s' delimiter was found", + ds_delimiter->str); if (match_delimiter(c, ds_delimiter->str, ds_delimiter->length)) { @@ -4004,8 +4464,13 @@ void do_perl(struct st_command *command) /* Format the "perl <filename>" command */ my_snprintf(buf, sizeof(buf), "perl %s", temp_file_path); - if (!(res_file= popen(buf, "r")) && command->abort_on_error) - die("popen(\"%s\", \"r\") failed", buf); + if (!(res_file= popen(buf, "r"))) + { + if (command->abort_on_error) + die("popen(\"%s\", \"r\") failed", buf); + dynstr_free(&ds_delimiter); + return; + } while (fgets(buf, sizeof(buf), res_file)) { @@ -4036,7 +4501,7 @@ void do_perl(struct st_command *command) abort_not_supported_test("perl not found in path"); #endif else - handle_command_error(command, WEXITSTATUS(error), my_errno); + handle_command_error(command, exstat, my_errno); } dynstr_free(&ds_delimiter); DBUG_VOID_RETURN; @@ -4160,14 +4625,14 @@ void do_sync_with_master2(struct st_command *command, long offset) information is not initialized, the arguments are incorrect, or an error has occured */ - die("%.*s failed: '%s' returned NULL "\ + die("%.*s failed: '%s' returned NULL " \ "indicating slave SQL thread failure", command->first_word_len, command->query, query_buf); } if (result == -1) - die("%.*s failed: '%s' returned -1 "\ + die("%.*s failed: '%s' returned -1 " \ "indicating timeout after %d seconds", command->first_word_len, command->query, query_buf, timeout); else @@ -4207,12 +4672,8 @@ int do_save_master_pos() MYSQL_ROW row; MYSQL *mysql = cur_con->mysql; const char *query; - int rpl_parse; DBUG_ENTER("do_save_master_pos"); - 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 @@ -4253,7 +4714,7 @@ int do_save_master_pos() const char latest_applied_binlog_epoch_str[]= "latest_applied_binlog_epoch="; if (count) - sleep(1); + my_sleep(100*1000); /* 100ms */ if (mysql_query(mysql, query= "show engine ndb status")) die("failed in '%s': %d %s", query, mysql_errno(mysql), mysql_error(mysql)); @@ -4342,7 +4803,7 @@ int do_save_master_pos() count++; if (latest_handled_binlog_epoch >= start_epoch) do_continue= 0; - else if (count > 30) + else if (count > 300) /* 30s */ { break; } @@ -4362,10 +4823,6 @@ int do_save_master_pos() strnmov(master_pos.file, row[0], sizeof(master_pos.file)-1); master_pos.pos = strtoul(row[1], (char**) 0, 10); mysql_free_result(res); - - if (rpl_parse) - mysql_enable_rpl_parse(mysql); - DBUG_RETURN(0); } @@ -4424,33 +4881,11 @@ void do_let(struct st_command *command) var_set(var_name, var_name_end, let_rhs_expr.str, (let_rhs_expr.str + let_rhs_expr.length)); dynstr_free(&let_rhs_expr); + revert_properties(); DBUG_VOID_RETURN; } -int do_rpl_probe(struct st_command *command __attribute__((unused))) -{ - DBUG_ENTER("do_rpl_probe"); - if (mysql_rpl_probe(cur_con->mysql)) - die("Failed in mysql_rpl_probe(): '%s'", mysql_error(cur_con->mysql)); - DBUG_RETURN(0); -} - - -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_command *command __attribute__((unused))) -{ - mysql_disable_rpl_parse(cur_con->mysql); - return 0; -} - - /* Sleep the number of specified seconds @@ -4492,7 +4927,8 @@ int do_sleep(struct st_command *command, my_bool real_sleep) while (my_isspace(charset_info, *p)) p++; if (!*p) - die("Missing argument to %.*s", command->first_word_len, command->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)) @@ -4564,16 +5000,21 @@ int query_get_string(MYSQL* mysql, const char* query, MYSQL_ROW row; if (mysql_query(mysql, query)) - die("'%s' failed: %d %s", query, - mysql_errno(mysql), mysql_error(mysql)); + { + report_or_die("'%s' failed: %d %s", query, + mysql_errno(mysql), mysql_error(mysql)); + return 1; + } if ((res= mysql_store_result(mysql)) == NULL) - die("Failed to store result: %d %s", - mysql_errno(mysql), mysql_error(mysql)); + { + report_or_die("Failed to store result: %d %s", + mysql_errno(mysql), mysql_error(mysql)); + return 1; + } if ((row= mysql_fetch_row(res)) == NULL) { mysql_free_result(res); - ds= 0; return 1; } init_dynamic_string(ds, (row[column] ? row[column] : "NULL"), ~0, 32); @@ -4693,18 +5134,19 @@ void do_shutdown_server(struct st_command *command) } -#if MYSQL_VERSION_ID >= 50000 -/* List of error names to error codes, available from 5.0 */ +/* List of error names to error codes */ typedef struct { const char *name; uint code; + const char *text; } st_error; static st_error global_error_names[] = { + { "<No error>", -1, "" }, #include <mysqld_ername.h> - { 0, 0 } + { 0, 0, 0 } }; uint get_errcode_from_name(char *error_name, char *error_end) @@ -4733,22 +5175,35 @@ uint get_errcode_from_name(char *error_name, char *error_end) 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))) + +const char *get_errname_from_code (uint error_code) { - abort_not_in_this_version(); - return 0; /* Never reached */ -} -#endif + st_error *e= global_error_names; + DBUG_ENTER("get_errname_from_code"); + DBUG_PRINT("enter", ("error_code: %d", error_code)); + if (! error_code) + { + DBUG_RETURN(""); + } + for (; e->name; e++) + { + if (e->code == error_code) + { + DBUG_RETURN(e->name); + } + } + /* Apparently, errors without known names may occur */ + DBUG_RETURN("<Unknown>"); +} void do_get_errcodes(struct st_command *command) { struct st_match_err *to= saved_expected_errors.err; char *p= command->first_argument; uint count= 0; + char *next; DBUG_ENTER("do_get_errcodes"); @@ -4768,6 +5223,17 @@ void do_get_errcodes(struct st_command *command) while (*end && *end != ',' && *end != ' ') end++; + next=end; + + /* code to handle variables passed to mysqltest */ + if( *p == '$') + { + const char* fin; + VAR *var = var_get(p,&fin,0,0); + p=var->str_val; + end=p+var->str_val_len; + } + if (*p == 'S') { char *to_ptr= to->code.sqlstate; @@ -4820,7 +5286,7 @@ void do_get_errcodes(struct st_command *command) while (*p && p != end) { if (!my_isdigit(charset_info, *p)) - die("Invalid argument to error: '%s' - "\ + die("Invalid argument to error: '%s' - " \ "the errno may only consist of digits[0-9]", command->first_argument); p++; @@ -4842,7 +5308,7 @@ void do_get_errcodes(struct st_command *command) die("Too many errorcodes specified"); /* Set pointer to the end of the last error code */ - p= end; + p= next; /* Find next ',' */ while (*p && *p != ',') @@ -5050,7 +5516,7 @@ void do_close_connection(struct st_command *command) we need to check if the query's thread was finished and probably wait (embedded-server specific) */ - wait_query_thread_end(con); + emb_close_connection(con); #endif /*EMBEDDED_LIBRARY*/ if (con->stmt) mysql_stmt_close(con->stmt); @@ -5058,14 +5524,13 @@ void do_close_connection(struct st_command *command) mysql_close(con->mysql); con->mysql= 0; - free_embedded_data(con); if (con->util_mysql) mysql_close(con->util_mysql); con->util_mysql= 0; con->pending= FALSE; - my_free(con->name, MYF(MY_ALLOW_ZERO_PTR)); + my_free(con->name); /* When the connection is closed set name to "-closed_connection-" @@ -5276,6 +5741,7 @@ do_handle_error: var_set_errno(0); handle_no_error(command); + revert_properties(); return 1; /* Connected */ } @@ -5324,6 +5790,7 @@ void do_connect(struct st_command *command) static DYNAMIC_STRING ds_port; static DYNAMIC_STRING ds_sock; static DYNAMIC_STRING ds_options; + static DYNAMIC_STRING ds_default_auth; #ifdef HAVE_SMEM static DYNAMIC_STRING ds_shm; #endif @@ -5335,7 +5802,8 @@ void do_connect(struct st_command *command) { "database", ARG_STRING, FALSE, &ds_database, "Database 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" } + { "options", ARG_STRING, FALSE, &ds_options, "Options to use while connecting" }, + { "default_auth", ARG_STRING, FALSE, &ds_default_auth, "Default authentication to use" } }; DBUG_ENTER("do_connect"); @@ -5420,16 +5888,20 @@ void do_connect(struct st_command *command) if (!(con_slot= find_connection_by_name("-closed_connection-"))) die("Connection limit exhausted, you can have max %d connections", opt_max_connections); - my_free(con_slot->name, MYF(0)); + my_free(con_slot->name); con_slot->name= 0; } -#ifdef EMBEDDED_LIBRARY - con_slot->query_done= 1; - con_slot->has_thread= FALSE; -#endif + init_connection_thd(con_slot); + if (!(con_slot->mysql= mysql_init(0))) die("Failed on mysql_init()"); + + if (opt_connect_timeout) + mysql_options(con_slot->mysql, MYSQL_OPT_CONNECT_TIMEOUT, + (void *) &opt_connect_timeout); + + mysql_options(con_slot->mysql, MYSQL_OPT_NONBLOCK, 0); if (opt_compress || con_compress) mysql_options(con_slot->mysql, MYSQL_OPT_COMPRESS, NullS); mysql_options(con_slot->mysql, MYSQL_OPT_LOCAL_INFILE, 0); @@ -5438,18 +5910,14 @@ void do_connect(struct st_command *command) if (opt_charsets_dir) mysql_options(con_slot->mysql, MYSQL_SET_CHARSET_DIR, opt_charsets_dir); - if (opt_connect_timeout >= 0) - mysql_options(con_slot->mysql, MYSQL_OPT_CONNECT_TIMEOUT, - &opt_connect_timeout); - -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) if (opt_use_ssl) con_ssl= 1; #endif if (con_ssl) { -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) mysql_ssl_set(con_slot->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); #if MYSQL_VERSION_ID >= 50000 @@ -5491,6 +5959,12 @@ void do_connect(struct st_command *command) if (ds_database.length == 0) dynstr_set(&ds_database, opt_db); + if (opt_plugin_dir && *opt_plugin_dir) + mysql_options(con_slot->mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir); + + if (ds_default_auth.length) + mysql_options(con_slot->mysql, MYSQL_DEFAULT_AUTH, ds_default_auth.str); + /* Special database to allow one to connect without a database name */ if (ds_database.length && !strcmp(ds_database.str,"*NO-ONE*")) dynstr_set(&ds_database, ""); @@ -5519,6 +5993,7 @@ void do_connect(struct st_command *command) dynstr_free(&ds_port); dynstr_free(&ds_sock); dynstr_free(&ds_options); + dynstr_free(&ds_default_auth); #ifdef HAVE_SMEM dynstr_free(&ds_shm); #endif @@ -5558,6 +6033,40 @@ int do_done(struct st_command *command) return 0; } +/* Operands available in if or while conditions */ + +enum block_op { + EQ_OP, + NE_OP, + GT_OP, + GE_OP, + LT_OP, + LE_OP, + ILLEG_OP +}; + + +enum block_op find_operand(const char *start) +{ + char first= *start; + char next= *(start+1); + + if (first == '=' && next == '=') + return EQ_OP; + if (first == '!' && next == '=') + return NE_OP; + if (first == '>' && next == '=') + return GE_OP; + if (first == '>') + return GT_OP; + if (first == '<' && next == '=') + return LE_OP; + if (first == '<') + return LT_OP; + + return ILLEG_OP; +} + /* Process start of a "if" or "while" statement @@ -5583,6 +6092,13 @@ int do_done(struct st_command *command) A '!' can be used before the <expr> to indicate it should be executed if it evaluates to zero. + <expr> can also be a simple comparison condition: + + <variable> <op> <expr> + + The left hand side must be a variable, the right hand side can be a + variable, number, string or `query`. Operands are ==, !=, <, <=, >, >=. + == and != can be used for strings, all can be used for numerical values. */ void do_block(enum block_cmd cmd, struct st_command* command) @@ -5618,6 +6134,9 @@ void do_block(enum block_cmd cmd, struct st_command* command) if (!expr_start++) die("missing '(' in %s", cmd_name); + while (my_isspace(charset_info, *expr_start)) + expr_start++; + /* Check for !<expr> */ if (*expr_start == '!') { @@ -5638,14 +6157,110 @@ void do_block(enum block_cmd cmd, struct st_command* command) die("Missing '{' after %s. Found \"%s\"", cmd_name, p); var_init(&v,0,0,0,0); - eval_expr(&v, expr_start, &expr_end); + /* If expression starts with a variable, it may be a compare condition */ + + if (*expr_start == '$') + { + const char *curr_ptr= expr_end; + eval_expr(&v, expr_start, &curr_ptr, true); + while (my_isspace(charset_info, *++curr_ptr)) + {} + /* If there was nothing past the variable, skip condition part */ + if (curr_ptr == expr_end) + goto NO_COMPARE; + + enum block_op operand= find_operand(curr_ptr); + if (operand == ILLEG_OP) + die("Found junk '%.*s' after $variable in condition", + (int)(expr_end - curr_ptr), curr_ptr); + + /* We could silently allow this, but may be confusing */ + if (not_expr) + die("Negation and comparison should not be combined, please rewrite"); + + /* Skip the 1 or 2 chars of the operand, then white space */ + if (operand == LT_OP || operand == GT_OP) + { + curr_ptr++; + } + else + { + curr_ptr+= 2; + } + while (my_isspace(charset_info, *curr_ptr)) + curr_ptr++; + if (curr_ptr == expr_end) + die("Missing right operand in comparison"); + + /* Strip off trailing white space */ + while (my_isspace(charset_info, expr_end[-1])) + expr_end--; + /* strip off ' or " around the string */ + if (*curr_ptr == '\'' || *curr_ptr == '"') + { + if (expr_end[-1] != *curr_ptr) + die("Unterminated string value"); + curr_ptr++; + expr_end--; + } + VAR v2; + var_init(&v2,0,0,0,0); + eval_expr(&v2, curr_ptr, &expr_end); + + if ((operand!=EQ_OP && operand!=NE_OP) && ! (v.is_int && v2.is_int)) + die("Only == and != are supported for string values"); + + /* Now we overwrite the first variable with 0 or 1 (for false or true) */ + + switch (operand) + { + case EQ_OP: + if (v.is_int) + v.int_val= (v2.is_int && v2.int_val == v.int_val); + else + v.int_val= !strcmp (v.str_val, v2.str_val); + break; + + case NE_OP: + if (v.is_int) + v.int_val= ! (v2.is_int && v2.int_val == v.int_val); + else + v.int_val= (strcmp (v.str_val, v2.str_val) != 0); + break; + + case LT_OP: + v.int_val= (v.int_val < v2.int_val); + break; + case LE_OP: + v.int_val= (v.int_val <= v2.int_val); + break; + case GT_OP: + v.int_val= (v.int_val > v2.int_val); + break; + case GE_OP: + v.int_val= (v.int_val >= v2.int_val); + break; + case ILLEG_OP: + die("Impossible operator, this cannot happen"); + } + + v.is_int= TRUE; + var_free(&v2); + } else + { + if (*expr_start != '`' && ! my_isdigit(charset_info, *expr_start)) + die("Expression in if/while must beging with $, ` or a number"); + eval_expr(&v, expr_start, &expr_end); + } + + NO_COMPARE: /* Define inner block */ cur_block++; cur_block->cmd= cmd; - if (v.int_val) + if (v.is_int) { - cur_block->ok= TRUE; + cur_block->ok= (v.int_val != 0); } else /* Any non-empty string which does not begin with 0 is also TRUE */ { @@ -5753,7 +6368,7 @@ my_bool end_of_query(int c) int read_line(char *buf, int size) { - char c, UNINIT_VAR(last_quote); + char c, UNINIT_VAR(last_quote), last_char= 0; char *p= buf, *buf_end= buf + size - 1; int skip_char= 0; my_bool have_slash= FALSE; @@ -5776,7 +6391,7 @@ int read_line(char *buf, int size) fclose(cur_file->file); cur_file->file= 0; } - my_free((uchar*) cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR)); + my_free(cur_file->file_name); cur_file->file_name= 0; if (cur_file == file_stack) { @@ -5857,14 +6472,24 @@ int read_line(char *buf, int size) } else if (my_isspace(charset_info, c)) { - /* Skip all space at begining of line */ if (c == '\n') { + if (last_char == '\n') + { + /* Two new lines in a row, return empty line */ + DBUG_PRINT("info", ("Found two new lines in a row")); + *p++= c; + *p= 0; + DBUG_RETURN(0); + } + /* Query hasn't started yet */ start_lineno= cur_file->lineno; DBUG_PRINT("info", ("Query hasn't started yet, start_lineno: %d", start_lineno)); } + + /* Skip all space at begining of line */ skip_char= 1; } else if (end_of_query(c)) @@ -5905,6 +6530,8 @@ int read_line(char *buf, int size) } + last_char= c; + if (!skip_char) { /* Could be a multibyte character */ @@ -5940,7 +6567,7 @@ int read_line(char *buf, int size) *p++= c; } } - die("The input buffer is too small for this query.x\n" \ + die("The input buffer is too small for this query.x\n" \ "check your query or increase MAX_QUERY and recompile"); DBUG_RETURN(0); } @@ -6114,9 +6741,10 @@ int read_command(struct st_command** command_ptr) DBUG_RETURN(1); } - convert_to_format_v1(read_command_buf); + if (opt_result_format_version == 1) + convert_to_format_v1(read_command_buf); - DBUG_PRINT("info", ("query: %s", read_command_buf)); + DBUG_PRINT("info", ("query: '%s'", read_command_buf)); if (*p == '#') { command->type= Q_COMMENT; @@ -6126,6 +6754,10 @@ int read_command(struct st_command** command_ptr) command->type= Q_COMMENT_WITH_COMMAND; p+= 2; /* Skip past -- */ } + else if (*p == '\n') + { + command->type= Q_EMPTY_LINE; + } /* Skip leading spaces */ while (*p && my_isspace(charset_info, *p)) @@ -6162,13 +6794,19 @@ static struct my_option my_long_options[] = 0, 0, 0, 0, 0, 0}, {"basedir", 'b', "Basedir for tests.", &opt_basedir, &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"character-sets-dir", OPT_CHARSETS_DIR, + {"character-sets-dir", 0, "Directory for character set files.", &opt_charsets_dir, &opt_charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"compress", 'C', "Use the compressed server/client protocol.", &opt_compress, &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"cursor-protocol", OPT_CURSOR_PROTOCOL, "Use cursors for prepared statements.", + {"continue-on-error", 0, + "Continue test even if we got an error. " + "This is mostly useful when testing a storage engine to see what from a test file it can execute, " + "or to find all syntax errors in a newly created big test file", + &opt_continue_on_error, &opt_continue_on_error, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"cursor-protocol", 0, "Use cursors for prepared statements.", &cursor_protocol, &cursor_protocol, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"database", 'D', "Database to use.", &opt_db, &opt_db, 0, @@ -6180,32 +6818,27 @@ 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}, #endif - {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.", + {"debug-check", 0, "Check memory and open file usage at exit.", &debug_check_flag, &debug_check_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"debug-info", OPT_DEBUG_INFO, "Print some debug info at exit.", + {"debug-info", 0, "Print some debug info at exit.", &debug_info_flag, &debug_info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"global-subst", OPT_GLOBAL_SUBST, "argument should be 'X,Y' ;" - " substitute string X with another Y accross the whole test's current" - " result before comparing with expected result file", - &global_subst, &global_subst, 0, - GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"host", 'h', "Connect to host.", &opt_host, &opt_host, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"include", 'i', "Include SQL before each test case.", &opt_include, - &opt_include, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"prologue", 0, "Include SQL before each test case.", &opt_prologue, + &opt_prologue, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"logdir", OPT_LOG_DIR, "Directory for log files", &opt_logdir, &opt_logdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"mark-progress", OPT_MARK_PROGRESS, + {"mark-progress", 0, "Write line number and elapsed time to <testname>.progress.", &opt_mark_progress, &opt_mark_progress, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"max-connect-retries", OPT_MAX_CONNECT_RETRIES, + {"max-connect-retries", 0, "Maximum number of attempts to connect to server.", &opt_max_connect_retries, &opt_max_connect_retries, 0, GET_INT, REQUIRED_ARG, 500, 1, 10000, 0, 0, 0}, - {"max-connections", OPT_MAX_CONNECTIONS, + {"max-connections", 0, "Max number of open connections to server", &opt_max_connections, &opt_max_connections, 0, GET_INT, REQUIRED_ARG, DEFAULT_MAX_CONN, 8, 5120, 0, 0, 0}, @@ -6220,10 +6853,14 @@ static struct my_option my_long_options[] = #endif "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").", &opt_port, &opt_port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"ps-protocol", OPT_PS_PROTOCOL, + {"ps-protocol", 0, "Use prepared-statement protocol for communication.", &ps_protocol, &ps_protocol, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"non-blocking-api", 0, + "Use the non-blocking client API for communication.", + &non_blocking_api_enabled, &non_blocking_api_enabled, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"quiet", 's', "Suppress all normal output.", &silent, &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"record", 'r', "Record output of test_file into result file.", @@ -6231,40 +6868,37 @@ static struct my_option my_long_options[] = {"result-file", 'R', "Read/store result from/in this file.", &result_file_name, &result_file_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"result-format-version", OPT_RESULT_FORMAT_VERSION, + "Version of the result file format to use", + &opt_result_format_version, + &opt_result_format_version, 0, + GET_INT, REQUIRED_ARG, 1, 1, 2, 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.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, -#ifdef HAVE_SMEM - {"shared-memory-base-name", OPT_SHARED_MEMORY_BASE_NAME, + {"shared-memory-base-name", 0, "Base name of shared memory.", &shared_memory_base_name, &shared_memory_base_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, -#endif {"silent", 's', "Suppress all normal output. Synonym for --quiet.", &silent, &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"skip-safemalloc", OPT_SKIP_SAFEMALLOC, - "Don't use the memory allocation checking.", 0, 0, 0, GET_NO_ARG, NO_ARG, - 0, 0, 0, 0, 0, 0}, {"sleep", 'T', "Always sleep this many seconds on sleep commands.", &opt_sleep, &opt_sleep, 0, GET_INT, REQUIRED_ARG, -1, -1, 0, 0, 0, 0}, {"socket", 'S', "The socket file to use for connection.", &unix_sock, &unix_sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"sp-protocol", OPT_SP_PROTOCOL, "Use stored procedures for select.", + {"sp-protocol", 0, "Use stored procedures for select.", &sp_protocol, &sp_protocol, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #include "sslopt-longopts.h" - {"tail-lines", OPT_TAIL_LINES, + {"tail-lines", 0, "Number of lines of the result to include in a failure report.", &opt_tail_lines, &opt_tail_lines, 0, GET_INT, REQUIRED_ARG, 0, 0, 10000, 0, 0, 0}, {"test-file", 'x', "Read test from/in this file (default stdin).", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"connect-timeout", OPT_MY_CONNECT_TIMEOUT, "Client connection timeout", - (uchar**) &opt_connect_timeout, (uchar**) &opt_connect_timeout, 0, - GET_INT, REQUIRED_ARG, -1, -1, 0, 0, 0, 0}, {"timer-file", 'm', "File where the timing in microseconds is stored.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"tmpdir", 't', "Temporary directory where sockets are put.", @@ -6275,22 +6909,24 @@ 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.", + {"view-protocol", 0, "Use views for select.", &view_protocol, &view_protocol, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.", - (uchar**) &opt_plugin_dir, (uchar**) &opt_plugin_dir, 0, - GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"default_auth", OPT_PLUGIN_DIR, - "Default authentication client-side plugin to use.", - (uchar**) &opt_default_auth, (uchar**) &opt_default_auth, 0, + {"connect_timeout", 0, + "Number of seconds before connection timeout.", + &opt_connect_timeout, &opt_connect_timeout, 0, GET_UINT, REQUIRED_ARG, + 120, 0, 3600 * 12, 0, 0, 0}, + {"plugin_dir", 0, "Directory for client-side plugins.", + &opt_plugin_dir, &opt_plugin_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"overlay-dir", 0, "Overlay directory.", &opt_overlay_dir, + &opt_overlay_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"suite-dir", 0, "Suite directory.", &opt_suite_dir, + &opt_suite_dir, 0, GET_STR, REQUIRED_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> - void print_version(void) { printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname,MTEST_VERSION, @@ -6309,8 +6945,6 @@ void usage() my_print_variables(my_long_options); } -#include <help_end.h> - /* Read arguments for embedded server and put them into @@ -6359,8 +6993,7 @@ void read_embedded_server_arguments(const char *name) static my_bool -get_one_option(int optid, const struct my_option *opt, - char *argument) +get_one_option(int optid, const struct my_option *opt, char *argument) { switch(optid) { case '#': @@ -6408,7 +7041,7 @@ get_one_option(int optid, const struct my_option *opt, argument= (char*) ""; // Don't require password if (argument) { - my_free(opt_pass, MYF(MY_ALLOW_ZERO_PTR)); + my_free(opt_pass); opt_pass= my_strdup(argument, MYF(MY_FAE)); while (*argument) *argument++= 'x'; /* Destroy argument */ tty_password= 0; @@ -6441,10 +7074,8 @@ get_one_option(int optid, const struct my_option *opt, case 'F': read_embedded_server_arguments(argument); break; - case OPT_SKIP_SAFEMALLOC: -#ifdef SAFEMALLOC - sf_malloc_quick=1; -#endif + case OPT_RESULT_FORMAT_VERSION: + set_result_format_version(opt_result_format_version); break; case 'V': print_version(); @@ -6465,7 +7096,9 @@ get_one_option(int optid, const struct my_option *opt, int parse_args(int argc, char **argv) { - load_defaults("my",load_default_groups,&argc,&argv); + if (load_defaults("my",load_default_groups,&argc,&argv)) + exit(1); + default_argv= argv; if ((handle_options(&argc, &argv, my_long_options, get_one_option))) @@ -6495,11 +7128,17 @@ int parse_args(int argc, char **argv) memcpy(global_subst_to, comma+1, strlen(comma)); } + if (!opt_suite_dir) + opt_suite_dir= "./"; + suite_dir_len= strlen(opt_suite_dir); + overlay_dir_len= opt_overlay_dir ? strlen(opt_overlay_dir) : 0; + if (!record) { /* Check that the result file exists */ if (result_file_name && access(result_file_name, F_OK) != 0) - die("The specified result file '%s' does not exist", result_file_name); + die("The specified result file '%s' does not exist", + result_file_name); } return 0; @@ -6588,6 +7227,8 @@ void init_win_path_patterns() "$MYSQL_TMP_DIR", "$MYSQLTEST_VARDIR", "$MASTER_MYSOCK", + "$MYSQL_SHAREDIR", + "$MYSQL_LIBDIR", "./test/" }; int num_paths= sizeof(paths)/sizeof(char*); int i; @@ -6612,7 +7253,7 @@ void init_win_path_patterns() /* Don't insert zero length strings in patterns array */ if (strlen(p) == 0) { - my_free(p, MYF(0)); + my_free(p); continue; } @@ -6636,7 +7277,7 @@ void free_win_path_patterns() for (i=0 ; i < patterns.elements ; i++) { const char** pattern= dynamic_element(&patterns, i, const char**); - my_free((char*) *pattern, MYF(0)); + my_free((void *) *pattern); } delete_dynamic(&patterns); } @@ -6824,8 +7465,8 @@ void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, } if (error != MYSQL_NO_DATA) - die("mysql_fetch didn't end with MYSQL_NO_DATA from statement: error: %d", - error); + die("mysql_fetch didn't end with MYSQL_NO_DATA from statement: " + "error: %d", error); if (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) die("mysql_fetch didn't end with MYSQL_NO_DATA from statement: %d %s", mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); @@ -6833,12 +7474,12 @@ void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, for (i= 0; i < num_fields; i++) { /* Free data for output */ - my_free(my_bind[i].buffer, MYF(MY_WME | MY_FAE)); + my_free(my_bind[i].buffer); } /* Free array with bind structs, lengths and NULL flags */ - my_free(my_bind , MYF(MY_WME | MY_FAE)); - my_free(length , MYF(MY_WME | MY_FAE)); - my_free(is_null , MYF(MY_WME | MY_FAE)); + my_free(my_bind); + my_free(length); + my_free(is_null); } @@ -7023,21 +7664,13 @@ void run_query_normal(struct st_connection *cn, struct st_command *command, /* Send the query */ - if (do_send_query(cn, query, query_len, flags)) + if (do_send_query(cn, query, query_len)) { handle_error(command, mysql_errno(mysql), mysql_error(mysql), mysql_sqlstate(mysql), ds); goto end; } } -#ifdef EMBEDDED_LIBRARY - /* - Here we handle 'reap' command, so we need to check if the - query's thread was finished and probably wait - */ - else if (flags & QUERY_REAP_FLAG) - wait_query_thread_end(cn); -#endif /*EMBEDDED_LIBRARY*/ if (!(flags & QUERY_REAP_FLAG)) { cn->pending= TRUE; @@ -7050,7 +7683,7 @@ void run_query_normal(struct st_connection *cn, struct st_command *command, When on first result set, call mysql_read_query_result to retrieve answer to the query sent earlier */ - if ((counter==0) && mysql_read_query_result(mysql)) + if ((counter==0) && do_read_query_result(cn)) { handle_error(command, mysql_errno(mysql), mysql_error(mysql), mysql_sqlstate(mysql), ds); @@ -7070,8 +7703,6 @@ void run_query_normal(struct st_connection *cn, struct st_command *command, if (!disable_result_log) { - ulonglong UNINIT_VAR(affected_rows); /* Ok to be undef if 'disable_info' is set */ - if (res) { MYSQL_FIELD *fields= mysql_fetch_fields(res); @@ -7088,10 +7719,10 @@ void run_query_normal(struct st_connection *cn, struct st_command *command, /* Need to call mysql_affected_rows() before the "new" - query to find the warnings + query to find the warnings. */ if (!disable_info) - affected_rows= mysql_affected_rows(mysql); + append_info(ds, mysql_affected_rows(mysql), mysql_info(mysql)); /* Add all warnings to the result. We can't do this if we are in @@ -7106,9 +7737,6 @@ void run_query_normal(struct st_connection *cn, struct st_command *command, dynstr_append_mem(ds, ds_warnings->str, ds_warnings->length); } } - - if (!disable_info) - append_info(ds, affected_rows, mysql_info(mysql)); } if (res) @@ -7129,6 +7757,7 @@ void run_query_normal(struct st_connection *cn, struct st_command *command, /* If we come here the query is both executed and read successfully */ handle_no_error(command); + revert_properties(); end: @@ -7181,7 +7810,8 @@ static int match_expected_error(struct st_command *command, NULL is quite likely, but not in conjunction with a SQL-state expect! */ if (unlikely(err_sqlstate == NULL)) - die("expecting a SQL-state (%s) from query '%s' which cannot produce one...", + die("expecting a SQL-state (%s) from query '%s' which cannot " + "produce one...", command->expected_errors.err[i].code.sqlstate, command->query); if (strncmp(command->expected_errors.err[i].code.sqlstate, @@ -7235,7 +7865,11 @@ void handle_error(struct st_command *command, } if (command->abort_on_error) - die("query '%s' failed: %d: %s", command->query, err_errno, err_error); + { + report_or_die("query '%s' failed: %d: %s", command->query, err_errno, + err_error); + DBUG_VOID_RETURN; + } DBUG_PRINT("info", ("expected_errors.count: %d", command->expected_errors.count)); @@ -7262,6 +7896,7 @@ void handle_error(struct st_command *command, dynstr_append(ds,"Got one of the listed errors\n"); } /* OK */ + revert_properties(); DBUG_VOID_RETURN; } @@ -7280,15 +7915,18 @@ void handle_error(struct st_command *command, if (command->expected_errors.count > 0) { 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); + report_or_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); + report_or_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); } + revert_properties(); DBUG_VOID_RETURN; } @@ -7312,17 +7950,18 @@ void handle_no_error(struct st_command *command) command->expected_errors.err[0].code.errnum != 0) { /* Error code we wanted was != 0, i.e. not an expected success */ - die("query '%s' succeeded - should have failed with errno %d...", - command->query, command->expected_errors.err[0].code.errnum); + report_or_die("query '%s' succeeded - should have failed with errno %d...", + command->query, command->expected_errors.err[0].code.errnum); } else if (command->expected_errors.err[0].type == ERR_SQLSTATE && strcmp(command->expected_errors.err[0].code.sqlstate,"00000") != 0) { /* SQLSTATE we wanted was != "00000", i.e. not an expected success */ - die("query '%s' succeeded - should have failed with sqlstate %s...", - command->query, command->expected_errors.err[0].code.sqlstate); + report_or_die("query '%s' succeeded - should have failed with " + "sqlstate %s...", + command->query, + command->expected_errors.err[0].code.sqlstate); } - DBUG_VOID_RETURN; } @@ -7450,9 +8089,6 @@ void run_query_stmt(MYSQL *mysql, struct st_command *command, handle_no_error(command); if (!disable_result_log) { - ulonglong affected_rows; - LINT_INIT(affected_rows); - /* Not all statements creates a result set. If there is one we can now create another normal result set that contains the meta @@ -7495,11 +8131,11 @@ void run_query_stmt(MYSQL *mysql, struct st_command *command, } /* - Need to grab affected rows information before getting - warnings here + Fetch info before fetching warnings, since it will be reset + otherwise. */ if (!disable_info) - affected_rows= mysql_affected_rows(mysql); + append_info(ds, mysql_stmt_affected_rows(stmt), mysql_info(mysql)); if (!disable_warnings) { @@ -7523,8 +8159,6 @@ void run_query_stmt(MYSQL *mysql, struct st_command *command, ds_execute_warnings.length); } } - if (!disable_info) - append_info(ds, affected_rows, mysql_info(mysql)); } end: @@ -7542,6 +8176,8 @@ end: var_set_errno(mysql_stmt_errno(stmt)); + revert_properties(); + /* Close the statement if reconnect, need new prepare */ if (mysql->reconnect) { @@ -7572,8 +8208,13 @@ int util_query(MYSQL* org_mysql, const char* query){ if (!(mysql= mysql_init(mysql))) die("Failed in mysql_init()"); + if (opt_connect_timeout) + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, + (void *) &opt_connect_timeout); + /* enable local infile, in non-binary builds often disabled by default */ mysql_options(mysql, MYSQL_OPT_LOCAL_INFILE, 0); + mysql_options(mysql, MYSQL_OPT_NONBLOCK, 0); safe_connect(mysql, "util", org_mysql->host, org_mysql->user, org_mysql->passwd, org_mysql->db, org_mysql->port, org_mysql->unix_socket); @@ -7617,12 +8258,14 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) DBUG_ENTER("run_query"); if (cn->pending && (flags & QUERY_SEND_FLAG)) - die ("Cannot run query on connection between send and reap"); + die("Cannot run query on connection between send and reap"); if (!(flags & QUERY_SEND_FLAG) && !cn->pending) - die ("Cannot reap on a connection without pending send"); + die("Cannot reap on a connection without pending send"); init_dynamic_string(&ds_warnings, NULL, 0, 256); + ds_warn= &ds_warnings; + /* Evaluate query if this is an eval command */ @@ -7788,7 +8431,8 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) ds, &ds_warnings); dynstr_free(&ds_warnings); - if (command->type == Q_EVAL) + ds_warn= 0; + if (command->type == Q_EVAL || command->type == Q_SEND_EVAL) dynstr_free(&eval_query); if (display_result_sorted) @@ -7802,13 +8446,14 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) if (sp_created) { if (util_query(mysql, "DROP PROCEDURE mysqltest_tmp_sp ")) - die("Failed to drop sp: %d: %s", mysql_errno(mysql), mysql_error(mysql)); + report_or_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", + report_or_die("Failed to drop view: %d: %s", mysql_errno(mysql), mysql_error(mysql)); } @@ -7834,8 +8479,8 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) char *re_eprint(int err) { static char epbuf[100]; - size_t len= my_regerror(REG_ITOA|err, (my_regex_t *)NULL, - epbuf, sizeof(epbuf)); + size_t len __attribute__((unused))= + my_regerror(REG_ITOA|err, (my_regex_t *)NULL, epbuf, sizeof(epbuf)); assert(len <= sizeof(epbuf)); return(epbuf); } @@ -7944,7 +8589,7 @@ void get_command_type(struct st_command* command) save= command->query[command->first_word_len]; command->query[command->first_word_len]= 0; - type= find_type(command->query, &command_typelib, 1+2); + type= find_type(command->query, &command_typelib, FIND_TYPE_NO_PREFIX); command->query[command->first_word_len]= save; if (type > 0) { @@ -7972,9 +8617,10 @@ void get_command_type(struct st_command* command) else { /* -- "comment" that didn't contain a mysqltest command */ - die("Found line beginning with -- that didn't contain "\ - "a valid mysqltest command, check your syntax or "\ + report_or_die("Found line beginning with -- that didn't contain " \ + "a valid mysqltest command, check your syntax or " \ "use # if you intended to write a comment"); + command->type= Q_COMMENT; } } @@ -8044,14 +8690,17 @@ static void dump_backtrace(void) fprintf(stderr, "read_command_buf (%p): ", read_command_buf); my_safe_print_str(read_command_buf, sizeof(read_command_buf)); + fputc('\n', stderr); if (conn) { fprintf(stderr, "conn->name (%p): ", conn->name); my_safe_print_str(conn->name, conn->name_len); + fputc('\n', stderr); #ifdef EMBEDDED_LIBRARY fprintf(stderr, "conn->cur_query (%p): ", conn->cur_query); my_safe_print_str(conn->cur_query, conn->cur_query_len); + fputc('\n', stderr); #endif } fputs("Attempting backtrace...\n", stderr); @@ -8156,6 +8805,9 @@ int main(int argc, char **argv) MY_INIT(argv[0]); DBUG_ENTER("main"); + /* mysqltest has no way to free all its memory correctly */ + sf_leaking_memory= 1; + save_file[0]= 0; TMPDIR[0]= 0; @@ -8186,7 +8838,7 @@ int main(int argc, char **argv) my_init_dynamic_array(&q_lines, sizeof(struct st_command*), 1024, 1024); - if (hash_init2(&var_hash, 64, charset_info, + if (my_hash_init2(&var_hash, 64, charset_info, 128, 0, 0, get_var_key, var_free, MYF(0))) die("Variable hash initialization failed"); @@ -8234,6 +8886,7 @@ int main(int argc, char **argv) next_con= connections + 1; var_set_int("$PS_PROTOCOL", ps_protocol); + var_set_int("$NON_BLOCKING_API", non_blocking_api_enabled); var_set_int("$SP_PROTOCOL", sp_protocol); var_set_int("$VIEW_PROTOCOL", view_protocol); var_set_int("$CURSOR_PROTOCOL", cursor_protocol); @@ -8261,6 +8914,7 @@ int main(int argc, char **argv) cur_file->file_name= my_strdup("<stdin>", MYF(MY_WME)); cur_file->lineno= 1; } + var_set_string("MYSQLTEST_FILE", cur_file->file_name); init_re(); /* Cursor protcol implies ps protocol */ @@ -8273,8 +8927,12 @@ int main(int argc, char **argv) cursor_protocol_enabled= cursor_protocol; st_connection *con= connections; + init_connection_thd(con); if (! (con->mysql= mysql_init(0))) die("Failed in mysql_init()"); + if (opt_connect_timeout) + mysql_options(con->mysql, MYSQL_OPT_CONNECT_TIMEOUT, + (void *) &opt_connect_timeout); if (opt_compress) mysql_options(con->mysql,MYSQL_OPT_COMPRESS,NullS); mysql_options(con->mysql, MYSQL_OPT_LOCAL_INFILE, 0); @@ -8290,10 +8948,7 @@ int main(int argc, char **argv) if (opt_plugin_dir && *opt_plugin_dir) mysql_options(con->mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir); - if (opt_default_auth && *opt_default_auth) - mysql_options(con->mysql, MYSQL_DEFAULT_AUTH, opt_default_auth); - -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) if (opt_use_ssl) { @@ -8315,6 +8970,7 @@ int main(int argc, char **argv) if (!(con->name = my_strdup("default", MYF(MY_WME)))) die("Out of memory"); + mysql_options(con->mysql, MYSQL_OPT_NONBLOCK, 0); safe_connect(con->mysql, con->name, opt_host, opt_user, opt_pass, opt_db, opt_port, unix_sock); @@ -8331,9 +8987,9 @@ int main(int argc, char **argv) set_current_connection(con); - if (opt_include) + if (opt_prologue) { - open_file(opt_include); + open_file(opt_prologue); } verbose_msg("Start processing test commands from '%s' ...", cur_file->file_name); @@ -8399,66 +9055,49 @@ int main(int argc, char **argv) case Q_DISCONNECT: case Q_DIRTY_CLOSE: 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_PREPARE_WARNINGS: prepare_warnings_enabled=1; break; case Q_DISABLE_PREPARE_WARNINGS: prepare_warnings_enabled=0; break; case Q_ENABLE_QUERY_LOG: - disable_query_log= 0; - var_set_int("$ENABLED_QUERY_LOG", 1); + set_property(command, P_QUERY, 0); break; case Q_DISABLE_QUERY_LOG: - disable_query_log= 1; - var_set_int("$ENABLED_QUERY_LOG", 0); + set_property(command, P_QUERY, 1); break; case Q_ENABLE_ABORT_ON_ERROR: - abort_on_error= 1; - var_set_int("$ENABLED_ABORT_ON_ERROR", 1); + set_property(command, P_ABORT, 1); break; case Q_DISABLE_ABORT_ON_ERROR: - abort_on_error= 0; - var_set_int("$ENABLED_ABORT_ON_ERROR", 0); + set_property(command, P_ABORT, 0); break; case Q_ENABLE_RESULT_LOG: - disable_result_log= 0; - var_set_int("$ENABLED_RESULT_LOG", 1); + set_property(command, P_RESULT, 0); break; case Q_DISABLE_RESULT_LOG: - disable_result_log=1; - var_set_int("$ENABLED_RESULT_LOG", 0); + set_property(command, P_RESULT, 1); break; case Q_ENABLE_CONNECT_LOG: - disable_connect_log=0; - var_set_int("$ENABLED_CONNECT_LOG", 1); + set_property(command, P_CONNECT, 0); break; case Q_DISABLE_CONNECT_LOG: - disable_connect_log=1; - var_set_int("$ENABLED_CONNECT_LOG", 0); + set_property(command, P_CONNECT, 1); break; case Q_ENABLE_WARNINGS: - disable_warnings= 0; - var_set_int("$ENABLED_WARNINGS", 1); + set_property(command, P_WARN, 0); break; case Q_DISABLE_WARNINGS: - disable_warnings= 1; - var_set_int("$ENABLED_WARNINGS", 0); + set_property(command, P_WARN, 1); break; case Q_ENABLE_INFO: - disable_info= 0; - var_set_int("$ENABLED_INFO", 1); + set_property(command, P_INFO, 0); break; case Q_DISABLE_INFO: - disable_info= 1; - var_set_int("$ENABLED_INFO", 0); + set_property(command, P_INFO, 1); break; case Q_ENABLE_METADATA: - display_metadata= 1; - var_set_int("$ENABLED_METADATA", 1); + set_property(command, P_META, 1); break; case Q_DISABLE_METADATA: - display_metadata= 0; - var_set_int("$ENABLED_METADATA", 0); + set_property(command, P_META, 0); break; case Q_ENABLE_COLUMN_NAMES: disable_column_names= 0; @@ -8498,6 +9137,7 @@ int main(int argc, char **argv) case Q_MOVE_FILE: do_move_file(command); break; case Q_CHMOD_FILE: do_chmod_file(command); break; case Q_PERL: do_perl(command); break; + case Q_RESULT_FORMAT_VERSION: do_result_format_version(command); break; case Q_DELIMITER: do_delimiter(command); break; @@ -8624,9 +9264,38 @@ int main(int argc, char **argv) do_sync_with_master2(command, 0); break; } - case Q_COMMENT: /* Ignore row */ + case Q_COMMENT: + { command->last_argument= command->end; + + /* Don't output comments in v1 */ + if (opt_result_format_version == 1) + break; + + /* Don't output comments if query logging is off */ + if (disable_query_log) + break; + + /* Write comment's with two starting #'s to result file */ + const char* p= command->query; + if (p && *p == '#' && *(p+1) == '#') + { + dynstr_append_mem(&ds_res, command->query, command->query_len); + dynstr_append(&ds_res, "\n"); + } break; + } + case Q_EMPTY_LINE: + /* Don't output newline in v1 */ + if (opt_result_format_version == 1) + break; + + /* Don't output newline if query logging is off */ + if (disable_query_log) + break; + + dynstr_append(&ds_res, "\n"); + break; case Q_PING: handle_command_error(command, mysql_ping(cur_con->mysql), -1); break; @@ -8654,12 +9323,18 @@ int main(int argc, char **argv) do_set_charset(command); break; case Q_DISABLE_PS_PROTOCOL: - ps_protocol_enabled= 0; + set_property(command, P_PS, 0); /* Close any open statements */ close_statements(); break; case Q_ENABLE_PS_PROTOCOL: - ps_protocol_enabled= ps_protocol; + set_property(command, P_PS, ps_protocol); + break; + case Q_DISABLE_NON_BLOCKING_API: + non_blocking_api_enabled= 0; + break; + case Q_ENABLE_NON_BLOCKING_API: + non_blocking_api_enabled= 1; break; case Q_DISABLE_RECONNECT: set_reconnect(cur_con->mysql, 0); @@ -8673,7 +9348,7 @@ int main(int argc, char **argv) if (parsing_disabled == 0) parsing_disabled= 1; else - die("Parsing is already disabled"); + report_or_die("Parsing is already disabled"); break; case Q_ENABLE_PARSING: /* @@ -8683,7 +9358,7 @@ int main(int argc, char **argv) if (parsing_disabled == 1) parsing_disabled= 0; else - die("Parsing is already enabled"); + report_or_die("Parsing is already enabled"); break; case Q_DIE: /* Abort test with error code and error message */ @@ -8887,13 +9562,14 @@ void do_get_replace_column(struct st_command *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); + 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); + my_free(replace_column[column_number-1]); replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE)); set_if_bigger(max_replace_column, column_number); } - my_free(start, MYF(0)); + my_free(start); command->last_argument= command->end; DBUG_VOID_RETURN; @@ -8907,7 +9583,7 @@ void free_replace_column() { if (replace_column[i]) { - my_free(replace_column[i], 0); + my_free(replace_column[i]); replace_column[i]= 0; } } @@ -8925,20 +9601,15 @@ void free_replace_column() typedef struct st_pointer_array { /* when using array-strings */ TYPELIB typelib; /* Pointer to strings */ uchar *str; /* Strings is here */ - int7 *flag; /* Flag about each var. */ + uint8 *flag; /* Flag about each var. */ uint array_allocs,max_count,length,max_length; } POINTER_ARRAY; -struct st_replace; struct st_replace *init_replace(char * *from, char * *to, uint count, char * word_end_chars); int insert_pointer_name(reg1 POINTER_ARRAY *pa,char * 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 ...] @@ -8988,7 +9659,7 @@ void do_get_replace(struct st_command *command) die("Can't initialize replace from '%s'", command->query); free_pointer_array(&from_array); free_pointer_array(&to_array); - my_free(start, MYF(0)); + my_free(start); command->last_argument= command->end; DBUG_VOID_RETURN; } @@ -8997,11 +9668,8 @@ void do_get_replace(struct st_command *command) void free_replace() { DBUG_ENTER("free_replace"); - if (glob_replace) - { - my_free(glob_replace,MYF(0)); - glob_replace=0; - } + my_free(glob_replace); + glob_replace= NULL; DBUG_VOID_RETURN; } @@ -9085,26 +9753,6 @@ struct st_regex int icase; /* true if the match is case insensitive */ }; -struct st_replace_regex -{ - DYNAMIC_ARRAY regex_arr; /* stores a list of st_regex subsitutions */ - - /* - Temporary storage areas for substitutions. To reduce unnessary copying - and memory freeing/allocation, we pre-allocate two buffers, and alternate - their use, one for input/one for output, the roles changing on the next - st_regex substition. At the end of substitutions buf points to the - one containing the final result. - */ - char* buf; - char* even_buf; - char* odd_buf; - int even_buf_len; - int odd_buf_len; -}; - -struct st_replace_regex *glob_replace_regex= 0; - int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace, char *string, int icase); @@ -9221,7 +9869,7 @@ struct st_replace_regex* init_replace_regex(char* expr) return res; err: - my_free(res,0); + my_free(res); die("Error parsing replace_regex \"%s\"", expr); return 0; } @@ -9303,7 +9951,13 @@ 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))) + /* Allow variable for the *entire* list of replacements */ + if (*expr == '$') + { + VAR *val= var_get(expr, NULL, 0, 1); + expr= val ? val->str_val : NULL; + } + if (expr && *expr && !(glob_replace_regex=init_replace_regex(expr))) die("Could not init replace_regex"); command->last_argument= command->end; } @@ -9313,9 +9967,9 @@ void free_replace_regex() if (glob_replace_regex) { delete_dynamic(&glob_replace_regex->regex_arr); - my_free(glob_replace_regex->even_buf,MYF(MY_ALLOW_ZERO_PTR)); - my_free(glob_replace_regex->odd_buf,MYF(MY_ALLOW_ZERO_PTR)); - my_free(glob_replace_regex,MYF(0)); + my_free(glob_replace_regex->even_buf); + my_free(glob_replace_regex->odd_buf); + my_free(glob_replace_regex); glob_replace_regex=0; } } @@ -9510,7 +10164,7 @@ int reg_replace(char** buf_p, int* buf_len_p, char *pattern, str_p= str_end; } } - my_free(subs, MYF(0)); + my_free(subs); my_regfree(&r); *res_p= 0; *buf_p= buf; @@ -9634,7 +10288,7 @@ REPLACE *init_replace(char * *from, char * *to,uint count, free_sets(&sets); DBUG_RETURN(0); } - VOID(make_new_set(&sets)); /* Set starting set */ + (void) make_new_set(&sets); /* Set starting set */ make_sets_invisible(&sets); /* Hide previus sets */ used_sets=-1; word_states=make_new_set(&sets); /* Start of new word */ @@ -9642,7 +10296,7 @@ REPLACE *init_replace(char * *from, char * *to,uint count, if (!(follow=(FOLLOWS*) my_malloc((states+2)*sizeof(FOLLOWS),MYF(MY_WME)))) { free_sets(&sets); - my_free(found_set,MYF(0)); + my_free(found_set); DBUG_RETURN(0); } @@ -9836,9 +10490,9 @@ REPLACE *init_replace(char * *from, char * *to,uint count, replace[i].next[j]=(REPLACE*) (rep_str+(-sets.set[i].next[j]-1)); } } - my_free(follow,MYF(0)); + my_free(follow); free_sets(&sets); - my_free(found_set,MYF(0)); + my_free(found_set); DBUG_PRINT("exit",("Replace table has %d states",sets.count)); DBUG_RETURN(replace); } @@ -9854,7 +10508,7 @@ int init_sets(REP_SETS *sets,uint states) if (!(sets->bit_buffer=(uint*) my_malloc(sizeof(uint)*sets->size_of_bits* SET_MALLOC_HUNC,MYF(MY_WME)))) { - my_free(sets->set,MYF(0)); + my_free(sets->set); return 1; } return 0; @@ -9915,8 +10569,8 @@ void free_last_set(REP_SETS *sets) void free_sets(REP_SETS *sets) { - my_free(sets->set_buffer,MYF(0)); - my_free(sets->bit_buffer,MYF(0)); + my_free(sets->set_buffer); + my_free(sets->bit_buffer); return; } @@ -10054,12 +10708,12 @@ int insert_pointer_name(reg1 POINTER_ARRAY *pa,char * name) if (!(pa->str= (uchar*) my_malloc((uint) (PS_MALLOC-MALLOC_OVERHEAD), MYF(MY_WME)))) { - my_free((char*) pa->typelib.type_names,MYF(0)); + my_free(pa->typelib.type_names); DBUG_RETURN (-1); } pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(uchar*)+ sizeof(*pa->flag)); - pa->flag= (int7*) (pa->typelib.type_names+pa->max_count); + pa->flag= (uint8*) (pa->typelib.type_names+pa->max_count); pa->length=0; pa->max_length=PS_MALLOC-MALLOC_OVERHEAD; pa->array_allocs=1; @@ -10095,14 +10749,14 @@ int insert_pointer_name(reg1 POINTER_ARRAY *pa,char * name) pa->typelib.type_names=new_array; old_count=pa->max_count; pa->max_count=len/(sizeof(uchar*) + sizeof(*pa->flag)); - pa->flag= (int7*) (pa->typelib.type_names+pa->max_count); + pa->flag= (uint8*) (pa->typelib.type_names+pa->max_count); memcpy((uchar*) pa->flag,(char *) (pa->typelib.type_names+old_count), old_count*sizeof(*pa->flag)); } pa->flag[pa->typelib.count]=0; /* Reset flag */ pa->typelib.type_names[pa->typelib.count++]= (char*) pa->str+pa->length; pa->typelib.type_names[pa->typelib.count]= NullS; /* Put end-mark */ - VOID(strmov((char*) pa->str+pa->length,name)); + (void) strmov((char*) pa->str+pa->length,name); pa->length+=length; DBUG_RETURN(0); } /* insert_pointer_name */ @@ -10115,9 +10769,9 @@ void free_pointer_array(POINTER_ARRAY *pa) if (pa->typelib.count) { pa->typelib.count=0; - my_free((char*) pa->typelib.type_names,MYF(0)); + my_free(pa->typelib.type_names); pa->typelib.type_names=0; - my_free(pa->str,MYF(0)); + my_free(pa->str); } } /* free_pointer_array */ |