diff options
author | Magnus Svensson <msvensson@mysql.com> | 2008-08-04 21:54:44 +0200 |
---|---|---|
committer | Magnus Svensson <msvensson@mysql.com> | 2008-08-04 21:54:44 +0200 |
commit | 27e858427111ec88407c2863608768e6074c284f (patch) | |
tree | 1c5de4faf1aee6e60daf0fdc27ae8af369fb025e /client/mysqltest.c | |
parent | 106e64a67f8325b24d26b66426e46e837fa9d4f3 (diff) | |
download | mariadb-git-27e858427111ec88407c2863608768e6074c284f.tar.gz |
Bug #38181 Please print more debug info when tests fail
Diffstat (limited to 'client/mysqltest.c')
-rw-r--r-- | client/mysqltest.c | 9063 |
1 files changed, 0 insertions, 9063 deletions
diff --git a/client/mysqltest.c b/client/mysqltest.c deleted file mode 100644 index f700ea9479d..00000000000 --- a/client/mysqltest.c +++ /dev/null @@ -1,9063 +0,0 @@ -/* Copyright (C) 2000 MySQL AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -/* - mysqltest - - Tool used for executing a .test file - - See the "MySQL Test framework manual" for more information - http://dev.mysql.com/doc/mysqltest/en/index.html - - Please keep the test framework tools identical in all versions! - - Written by: - Sasha Pachev <sasha@mysql.com> - Matt Wagner <matt@mysql.com> - Monty - Jani - Holyfoot -*/ - -#define MTEST_VERSION "3.3" - -#include "client_priv.h" -#include <mysql_version.h> -#include <mysqld_error.h> -#include <m_ctype.h> -#include <my_dir.h> -#include <hash.h> -#include <stdarg.h> -#include <violite.h> -#include "my_regex.h" /* Our own version of regex */ -#ifdef HAVE_SYS_WAIT_H -#include <sys/wait.h> -#endif -#ifdef __WIN__ -#include <direct.h> -#endif -#include <signal.h> -#include <my_stacktrace.h> - -#ifdef __WIN__ -#include <crtdbg.h> -#define SIGNAL_FMT "exception 0x%x" -#else -#define SIGNAL_FMT "signal %d" -#endif - -/* Use cygwin for --exec and --system before 5.0 */ -#if MYSQL_VERSION_ID < 50000 -#define USE_CYGWIN -#endif - -#define MAX_VAR_NAME_LENGTH 256 -#define MAX_COLUMNS 256 -#define MAX_EMBEDDED_SERVER_ARGS 64 -#define MAX_DELIMITER_LENGTH 16 - -/* Flags controlling send and reap */ -#define QUERY_SEND_FLAG 1 -#define QUERY_REAP_FLAG 2 - -enum { - OPT_SKIP_SAFEMALLOC=OPT_MAX_CLIENT_OPTION, - OPT_PS_PROTOCOL, OPT_SP_PROTOCOL, OPT_CURSOR_PROTOCOL, OPT_VIEW_PROTOCOL, - OPT_MAX_CONNECT_RETRIES, OPT_MARK_PROGRESS, OPT_LOG_DIR, OPT_TAIL_LINES -}; - -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= "./"; -const char *opt_logdir= ""; -const char *opt_include= 0, *opt_charsets_dir; -static int opt_port= 0; -static int opt_max_connect_retries; -static my_bool opt_compress= 0, silent= 0, verbose= 0; -static my_bool debug_info_flag= 0, debug_check_flag= 0; -static my_bool tty_password= 0; -static my_bool opt_mark_progress= 0; -static my_bool ps_protocol= 0, ps_protocol_enabled= 0; -static my_bool sp_protocol= 0, sp_protocol_enabled= 0; -static my_bool view_protocol= 0, view_protocol_enabled= 0; -static my_bool cursor_protocol= 0, cursor_protocol_enabled= 0; -static my_bool parsing_disabled= 0; -static my_bool display_result_vertically= FALSE, - display_metadata= FALSE, display_result_sorted= FALSE; -static my_bool disable_query_log= 0, disable_result_log= 0; -static my_bool disable_warnings= 0; -static my_bool disable_info= 1; -static my_bool abort_on_error= 1; -static my_bool server_initialized= 0; -static my_bool is_windows= 0; -static char **default_argv; -static const char *load_default_groups[]= { "mysqltest", "client", 0 }; -static char line_buffer[MAX_DELIMITER_LENGTH], *line_buffer_pos= line_buffer; - -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 char delimiter[MAX_DELIMITER_LENGTH]= ";"; -static uint delimiter_length= 1; - -static char TMPDIR[FN_REFLEN]; - -/* Block stack */ -enum block_cmd { - cmd_none, - cmd_if, - cmd_while -}; - -struct st_block -{ - int line; /* Start line of block */ - my_bool ok; /* Should block be executed */ - enum block_cmd cmd; /* Command owning the block */ -}; - -static struct st_block block_stack[32]; -static struct st_block *cur_block, *block_stack_end; - -/* Open file stack */ -struct st_test_file -{ - FILE* file; - const char *file_name; - uint lineno; /* Current line in file */ -}; - -static struct st_test_file file_stack[16]; -static struct st_test_file* cur_file; -static struct st_test_file* file_stack_end; - - -static CHARSET_INFO *charset_info= &my_charset_latin1; /* Default charset */ - -static const char *embedded_server_groups[]= -{ - "server", - "embedded", - "mysqltest_SERVER", - NullS -}; - -static int embedded_server_arg_count=0; -static char *embedded_server_args[MAX_EMBEDDED_SERVER_ARGS]; - -/* - Timer related variables - See the timer_output() definition for details -*/ -static char *timer_file = NULL; -static ulonglong timer_start; -static void timer_output(void); -static ulonglong timer_now(void); - -static ulonglong progress_start= 0; - -/* Precompiled re's */ -static my_regex_t ps_re; /* the query can be run using PS protocol */ -static my_regex_t sp_re; /* the query can be run as a SP */ -static my_regex_t view_re; /* the query can be run as a view*/ - -static void init_re(void); -static int match_re(my_regex_t *, char *); -static void free_re(void); - -DYNAMIC_ARRAY q_lines; - -#include "sslopt-vars.h" - -struct -{ - int read_lines,current_line; -} parser; - -struct -{ - char file[FN_REFLEN]; - ulong pos; -} master_pos; - -/* if set, all results are concated and compared against this file */ -const char *result_file_name= 0; - -typedef struct st_var -{ - char *name; - int name_len; - char *str_val; - 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; - char *env_s; -} VAR; - -/*Perl/shell-like variable registers */ -VAR var_reg[10]; - -HASH var_hash; - -struct st_connection -{ - MYSQL mysql; - /* Used when creating views and sp, to avoid implicit commit */ - MYSQL* util_mysql; - char *name; - size_t name_len; - MYSQL_STMT* stmt; - -#ifdef EMBEDDED_LIBRARY - const char *cur_query; - int cur_query_len; - pthread_mutex_t mutex; - pthread_cond_t cond; - int query_done; -#endif /*EMBEDDED_LIBRARY*/ -}; -struct st_connection connections[128]; -struct st_connection* cur_con= NULL, *next_con, *connections_end; - -/* - List of commands in mysqltest - Must match the "command_names" array - Add new commands before Q_UNKNOWN! -*/ -enum enum_commands { - Q_CONNECTION=1, Q_QUERY, - Q_CONNECT, Q_SLEEP, Q_REAL_SLEEP, - Q_INC, Q_DEC, - Q_SOURCE, Q_DISCONNECT, - Q_LET, Q_ECHO, - Q_WHILE, Q_END_BLOCK, - Q_SYSTEM, Q_RESULT, - Q_REQUIRE, Q_SAVE_MASTER_POS, - Q_SYNC_WITH_MASTER, - Q_SYNC_SLAVE_WITH_MASTER, - Q_ERROR, - Q_SEND, Q_REAP, - Q_DIRTY_CLOSE, Q_REPLACE, Q_REPLACE_COLUMN, - Q_PING, Q_EVAL, - Q_RPL_PROBE, Q_ENABLE_RPL_PARSE, - Q_DISABLE_RPL_PARSE, Q_EVAL_RESULT, - Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG, - Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG, - Q_WAIT_FOR_SLAVE_TO_STOP, - Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS, - Q_ENABLE_INFO, Q_DISABLE_INFO, - Q_ENABLE_METADATA, Q_DISABLE_METADATA, - Q_EXEC, Q_DELIMITER, - Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR, - Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS, - Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, Q_SORTED_RESULT, - Q_START_TIMER, Q_END_TIMER, - Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL, - Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT, - Q_IF, - Q_DISABLE_PARSING, Q_ENABLE_PARSING, - Q_REPLACE_REGEX, Q_REMOVE_FILE, Q_FILE_EXIST, - Q_WRITE_FILE, Q_COPY_FILE, Q_PERL, Q_DIE, Q_EXIT, Q_SKIP, - Q_CHMOD_FILE, Q_APPEND_FILE, Q_CAT_FILE, Q_DIFF_FILES, - Q_SEND_QUIT, Q_CHANGE_USER, Q_MKDIR, Q_RMDIR, - Q_SEND_SHUTDOWN, Q_SHUTDOWN_SERVER, - - Q_UNKNOWN, /* Unknown command. */ - Q_COMMENT, /* Comments, ignored. */ - Q_COMMENT_WITH_COMMAND -}; - - -const char *command_names[]= -{ - "connection", - "query", - "connect", - "sleep", - "real_sleep", - "inc", - "dec", - "source", - "disconnect", - "let", - "echo", - "while", - "end", - "system", - "result", - "require", - "save_master_pos", - "sync_with_master", - "sync_slave_with_master", - "error", - "send", - "reap", - "dirty_close", - "replace_result", - "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", - "disable_query_log", - /* Enable/disable that the _result_ from a query is logged to result file */ - "enable_result_log", - "disable_result_log", - "wait_for_slave_to_stop", - "enable_warnings", - "disable_warnings", - "enable_info", - "disable_info", - "enable_metadata", - "disable_metadata", - "exec", - "delimiter", - "disable_abort_on_error", - "enable_abort_on_error", - "vertical_results", - "horizontal_results", - "query_vertical", - "query_horizontal", - "sorted_result", - "start_timer", - "end_timer", - "character_set", - "disable_ps_protocol", - "enable_ps_protocol", - "disable_reconnect", - "enable_reconnect", - "if", - "disable_parsing", - "enable_parsing", - "replace_regex", - "remove_file", - "file_exists", - "write_file", - "copy_file", - "perl", - "die", - - /* Don't execute any more commands, compare result */ - "exit", - "skip", - "chmod", - "append_file", - "cat_file", - "diff_files", - "send_quit", - "change_user", - "mkdir", - "rmdir", - "send_shutdown", - "shutdown_server", - - 0 -}; - - -/* - The list of error codes to --error are stored in an internal array of - structs. This struct can hold numeric SQL error codes, error names or - SQLSTATE codes as strings. The element next to the last active element - in the list is set to type ERR_EMPTY. When an SQL statement returns an - error, we use this list to check if this is an expected error. -*/ -enum match_err_type -{ - ERR_EMPTY= 0, - ERR_ERRNO, - ERR_SQLSTATE -}; - -struct st_match_err -{ - enum match_err_type type; - union - { - uint errnum; - char sqlstate[SQLSTATE_LENGTH+1]; /* \0 terminated string */ - } code; -}; - -struct st_expected_errors -{ - struct st_match_err err[10]; - uint count; -}; -static struct st_expected_errors saved_expected_errors; - -struct st_command -{ - char *query, *query_buf,*first_argument,*last_argument,*end; - int first_word_len, query_len; - my_bool abort_on_error; - struct st_expected_errors expected_errors; - char require_file[FN_REFLEN]; - enum enum_commands type; -}; - -TYPELIB command_typelib= {array_elements(command_names),"", - command_names, 0}; - -DYNAMIC_STRING ds_res, ds_progress, ds_warning_messages; - -char builtin_echo[FN_REFLEN]; - -void die(const char *fmt, ...) - ATTRIBUTE_FORMAT(printf, 1, 2); -void abort_not_supported_test(const char *fmt, ...) - ATTRIBUTE_FORMAT(printf, 1, 2); -void verbose_msg(const char *fmt, ...) - ATTRIBUTE_FORMAT(printf, 1, 2); -void warning_msg(const char *fmt, ...) - ATTRIBUTE_FORMAT(printf, 1, 2); -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); -my_bool match_delimiter(int c, const char *delim, uint length); -void dump_result_to_reject_file(char *buf, int size); -void dump_result_to_log_file(char *buf, int size); -void dump_warning_messages(); -void dump_progress(); - -void do_eval(DYNAMIC_STRING *query_eval, const char *query, - const char *query_end, my_bool pass_through_escape_chars); -void str_to_file(const char *fname, char *str, int size); -void str_to_file2(const char *fname, char *str, int size, my_bool append); - -void fix_win_paths(const char *val, int len); - -#ifdef __WIN__ -void free_tmp_sh_file(); -void free_win_path_patterns(); -#endif - - -/* For replace_column */ -static char *replace_column[MAX_COLUMNS]; -static uint max_replace_column= 0; -void do_get_replace_column(struct st_command*); -void free_replace_column(); - -/* For replace */ -void do_get_replace(struct st_command *command); -void free_replace(); - -/* For replace_regex */ -void do_get_replace_regex(struct st_command *command); -void free_replace_regex(); - - -void free_all_replace(){ - free_replace(); - free_replace_regex(); - free_replace_column(); -} - - -/* 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); -void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val); -void dynstr_append_sorted(DYNAMIC_STRING* ds, DYNAMIC_STRING* ds_input); - -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*); - -#ifdef EMBEDDED_LIBRARY - -/* 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. -*/ -pthread_handler_t send_one_query(void *arg) -{ - struct st_connection *cn= (struct st_connection*)arg; - - mysql_thread_init(); - VOID(mysql_send_query(&cn->mysql, cn->cur_query, cn->cur_query_len)); - - mysql_thread_end(); - pthread_mutex_lock(&cn->mutex); - cn->query_done= 1; - VOID(pthread_cond_signal(&cn->cond)); - pthread_mutex_unlock(&cn->mutex); - pthread_exit(0); - return 0; -} - -static int do_send_query(struct st_connection *cn, const char *q, int q_len, - int flags) -{ - pthread_t tid; - - if (flags & QUERY_REAP_FLAG) - return mysql_send_query(&cn->mysql, q, q_len); - - if (pthread_mutex_init(&cn->mutex, NULL) || - pthread_cond_init(&cn->cond, NULL)) - die("Error in the thread library"); - - cn->cur_query= q; - cn->cur_query_len= q_len; - cn->query_done= 0; - if (pthread_create(&tid, &cn_thd_attrib, send_one_query, (void*)cn)) - die("Cannot start new thread for query"); - - return 0; -} - -static void wait_query_thread_end(struct st_connection *con) -{ - 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); - } -} - -#else /*EMBEDDED_LIBRARY*/ - -#define do_send_query(cn,q,q_len,flags) mysql_send_query(&cn->mysql, q, q_len) - -#endif /*EMBEDDED_LIBRARY*/ - -void do_eval(DYNAMIC_STRING *query_eval, const char *query, - const char *query_end, my_bool pass_through_escape_chars) -{ - const char *p; - register char c, next_c; - register int escaped = 0; - VAR *v; - DBUG_ENTER("do_eval"); - - for (p= query; (c= *p) && p < query_end; ++p) - { - switch(c) { - case '$': - if (escaped) - { - escaped= 0; - dynstr_append_mem(query_eval, p, 1); - } - else - { - if (!(v= var_get(p, &p, 0, 0))) - die("Bad variable in eval"); - dynstr_append_mem(query_eval, v->str_val, v->str_val_len); - } - break; - case '\\': - next_c= *(p+1); - if (escaped) - { - escaped= 0; - dynstr_append_mem(query_eval, p, 1); - } - else if (next_c == '\\' || next_c == '$' || next_c == '"') - { - /* Set escaped only if next char is \, " or $ */ - escaped= 1; - - if (pass_through_escape_chars) - { - /* The escape char should be added to the output string. */ - dynstr_append_mem(query_eval, p, 1); - } - } - else - dynstr_append_mem(query_eval, p, 1); - break; - default: - escaped= 0; - dynstr_append_mem(query_eval, p, 1); - break; - } - } -#ifdef __WIN__ - fix_win_paths(query_eval->str, query_eval->length); -#endif - DBUG_VOID_RETURN; -} - - -/* - Run query and dump the result to stdout in vertical format - - NOTE! This function should be safe to call when an error - has occured and thus any further errors will be ignored(although logged) - - SYNOPSIS - show_query - mysql - connection to use - query - query to run - -*/ - -static void show_query(MYSQL* mysql, const char* query) -{ - MYSQL_RES* res; - DBUG_ENTER("show_query"); - - if (!mysql) - DBUG_VOID_RETURN; - - if (mysql_query(mysql, query)) - { - log_msg("Error running query '%s': %d %s", - query, mysql_errno(mysql), mysql_error(mysql)); - DBUG_VOID_RETURN; - } - - if ((res= mysql_store_result(mysql)) == NULL) - { - /* No result set returned */ - DBUG_VOID_RETURN; - } - - { - MYSQL_ROW row; - unsigned int i; - unsigned int row_num= 0; - unsigned int num_fields= mysql_num_fields(res); - MYSQL_FIELD *fields= mysql_fetch_fields(res); - - fprintf(stderr, "=== %s ===\n", query); - while ((row= mysql_fetch_row(res))) - { - unsigned long *lengths= mysql_fetch_lengths(res); - row_num++; - - fprintf(stderr, "---- %d. ----\n", row_num); - for(i= 0; i < num_fields; i++) - { - fprintf(stderr, "%s\t%.*s\n", - fields[i].name, - (int)lengths[i], row[i] ? row[i] : "NULL"); - } - } - for (i= 0; i < strlen(query)+8; i++) - fprintf(stderr, "="); - fprintf(stderr, "\n\n"); - } - mysql_free_result(res); - - DBUG_VOID_RETURN; -} - - -/* - Show any warnings just before the error. Since the last error - is added to the warning stack, only print @@warning_count-1 warnings. - - NOTE! This function should be safe to call when an error - has occured and this any further errors will be ignored(although logged) - - SYNOPSIS - show_warnings_before_error - mysql - connection to use - -*/ - -static void show_warnings_before_error(MYSQL* mysql) -{ - MYSQL_RES* res; - const char* query= "SHOW WARNINGS"; - DBUG_ENTER("show_warnings_before_error"); - - if (!mysql) - DBUG_VOID_RETURN; - - if (mysql_query(mysql, query)) - { - log_msg("Error running query '%s': %d %s", - query, mysql_errno(mysql), mysql_error(mysql)); - DBUG_VOID_RETURN; - } - - if ((res= mysql_store_result(mysql)) == NULL) - { - /* No result set returned */ - DBUG_VOID_RETURN; - } - - if (mysql_num_rows(res) <= 1) - { - /* Don't display the last row, it's "last error" */ - } - else - { - MYSQL_ROW row; - unsigned int row_num= 0; - unsigned int num_fields= mysql_num_fields(res); - - fprintf(stderr, "\nWarnings from just before the error:\n"); - while ((row= mysql_fetch_row(res))) - { - unsigned int i; - unsigned long *lengths= mysql_fetch_lengths(res); - - if (++row_num >= mysql_num_rows(res)) - { - /* Don't display the last row, it's "last error" */ - break; - } - - for(i= 0; i < num_fields; i++) - { - fprintf(stderr, "%.*s ", (int)lengths[i], - row[i] ? row[i] : "NULL"); - } - fprintf(stderr, "\n"); - } - } - mysql_free_result(res); - - DBUG_VOID_RETURN; -} - - -enum arg_type -{ - ARG_STRING, - ARG_REST -}; - -struct command_arg { - const char *argname; /* Name of argument */ - enum arg_type type; /* Type of argument */ - my_bool required; /* Argument required */ - DYNAMIC_STRING *ds; /* Storage for argument */ - const char *description; /* Description of the argument */ -}; - - -void check_command_args(struct st_command *command, - const char *arguments, - const struct command_arg *args, - int num_args, const char delimiter_arg) -{ - int i; - const char *ptr= arguments; - const char *start; - DBUG_ENTER("check_command_args"); - DBUG_PRINT("enter", ("num_args: %d", num_args)); - - for (i= 0; i < num_args; i++) - { - const struct command_arg *arg= &args[i]; - - switch (arg->type) { - /* A string */ - case ARG_STRING: - /* Skip leading spaces */ - while (*ptr && *ptr == ' ') - ptr++; - start= ptr; - /* Find end of arg, terminated by "delimiter_arg" */ - while (*ptr && *ptr != delimiter_arg) - ptr++; - if (ptr > start) - { - init_dynamic_string(arg->ds, 0, ptr-start, 32); - do_eval(arg->ds, start, ptr, FALSE); - } - else - { - /* Empty string */ - init_dynamic_string(arg->ds, "", 0, 0); - } - command->last_argument= (char*)ptr; - - /* Step past the delimiter */ - if (*ptr && *ptr == delimiter_arg) - ptr++; - DBUG_PRINT("info", ("val: %s", arg->ds->str)); - break; - - /* Rest of line */ - case ARG_REST: - start= ptr; - init_dynamic_string(arg->ds, 0, command->query_len, 256); - do_eval(arg->ds, start, command->end, FALSE); - command->last_argument= command->end; - DBUG_PRINT("info", ("val: %s", arg->ds->str)); - break; - - default: - DBUG_ASSERT("Unknown argument type"); - break; - } - - /* Check required arg */ - if (arg->ds->length == 0 && arg->required) - die("Missing required argument '%s' to command '%.*s'", arg->argname, - command->first_word_len, command->query); - - } - /* Check for too many arguments passed */ - ptr= command->last_argument; - while(ptr <= command->end) - { - if (*ptr && *ptr != ' ') - die("Extra argument '%s' passed to '%.*s'", - ptr, command->first_word_len, command->query); - ptr++; - } - DBUG_VOID_RETURN; -} - - -void handle_command_error(struct st_command *command, uint error) -{ - DBUG_ENTER("handle_command_error"); - DBUG_PRINT("enter", ("error: %d", error)); - if (error != 0) - { - uint i; - - if (command->abort_on_error) - die("command \"%.*s\" failed with error %d", - command->first_word_len, command->query, error); - for (i= 0; i < command->expected_errors.count; i++) - { - DBUG_PRINT("info", ("expected error: %d", - command->expected_errors.err[i].code.errnum)); - if ((command->expected_errors.err[i].type == ERR_ERRNO) && - (command->expected_errors.err[i].code.errnum == error)) - { - DBUG_PRINT("info", ("command \"%.*s\" failed with expected error: %d", - command->first_word_len, command->query, error)); - DBUG_VOID_RETURN; - } - } - die("command \"%.*s\" failed with wrong error: %d", - command->first_word_len, command->query, error); - } - else if (command->expected_errors.err[0].type == ERR_ERRNO && - command->expected_errors.err[0].code.errnum != 0) - { - /* Error code we wanted was != 0, i.e. not an expected success */ - die("command \"%.*s\" succeeded - should have failed with errno %d...", - command->first_word_len, command->query, - command->expected_errors.err[0].code.errnum); - } - DBUG_VOID_RETURN; -} - - -void close_connections() -{ - DBUG_ENTER("close_connections"); - for (--next_con; next_con >= connections; --next_con) - { - if (next_con->stmt) - mysql_stmt_close(next_con->stmt); - next_con->stmt= 0; - mysql_close(&next_con->mysql); - if (next_con->util_mysql) - mysql_close(next_con->util_mysql); - my_free(next_con->name, MYF(MY_ALLOW_ZERO_PTR)); - } - DBUG_VOID_RETURN; -} - - -void close_statements() -{ - struct st_connection *con; - DBUG_ENTER("close_statements"); - for (con= connections; con < next_con; con++) - { - if (con->stmt) - mysql_stmt_close(con->stmt); - con->stmt= 0; - } - DBUG_VOID_RETURN; -} - - -void close_files() -{ - DBUG_ENTER("close_files"); - for (; cur_file >= file_stack; cur_file--) - { - if (cur_file->file && cur_file->file != stdin) - { - DBUG_PRINT("info", ("closing file: %s", cur_file->file_name)); - my_fclose(cur_file->file, MYF(0)); - } - my_free((uchar*) cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR)); - cur_file->file_name= 0; - } - DBUG_VOID_RETURN; -} - - -void free_used_memory() -{ - uint i; - DBUG_ENTER("free_used_memory"); - - close_connections(); - close_files(); - 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),MYF(0)); - } - for (i= 0; i < 10; i++) - { - if (var_reg[i].alloced_len) - my_free(var_reg[i].str_val, MYF(MY_WME)); - } - while (embedded_server_arg_count > 1) - my_free(embedded_server_args[--embedded_server_arg_count],MYF(0)); - delete_dynamic(&q_lines); - dynstr_free(&ds_res); - dynstr_free(&ds_progress); - dynstr_free(&ds_warning_messages); - free_all_replace(); - my_free(opt_pass,MYF(MY_ALLOW_ZERO_PTR)); - free_defaults(default_argv); - free_re(); -#ifdef __WIN__ - free_tmp_sh_file(); - free_win_path_patterns(); -#endif - - /* Only call mysql_server_end if mysql_server_init has been called */ - if (server_initialized) - mysql_server_end(); - - /* Don't use DBUG after mysql_server_end() */ - return; -} - - -static void cleanup_and_exit(int exit_code) -{ - free_used_memory(); - my_end(my_end_arg); - - if (!silent) { - switch (exit_code) { - case 1: - printf("not ok\n"); - break; - case 0: - printf("ok\n"); - break; - case 62: - printf("skipped\n"); - break; - default: - printf("unknown exit code: %d\n", exit_code); - DBUG_ASSERT(0); - } - } - - exit(exit_code); -} - -void die(const char *fmt, ...) -{ - static int dying= 0; - va_list args; - DBUG_ENTER("die"); - DBUG_PRINT("enter", ("start_lineno: %d", start_lineno)); - - /* - Protect against dying twice - first time 'die' is called, try to write log files - second time, just exit - */ - if (dying) - cleanup_and_exit(1); - dying= 1; - - /* Print the error message */ - fprintf(stderr, "mysqltest: "); - if (cur_file && cur_file != file_stack) - fprintf(stderr, "In included file \"%s\": ", - cur_file->file_name); - if (start_lineno > 0) - fprintf(stderr, "At line %u: ", start_lineno); - if (fmt) - { - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - } - else - fprintf(stderr, "unknown error"); - fprintf(stderr, "\n"); - fflush(stderr); - - /* Show results from queries just before failure */ - if (ds_res.length && opt_tail_lines) - { - int tail_lines= opt_tail_lines; - char* show_from= ds_res.str + ds_res.length - 1; - while(show_from > ds_res.str && tail_lines > 0 ) - { - show_from--; - if (*show_from == '\n') - tail_lines--; - } - fprintf(stderr, "\nThe result from queries just before the failure was:\n"); - if (show_from > ds_res.str) - fprintf(stderr, "< snip >"); - fprintf(stderr, "%s", show_from); - fflush(stderr); - } - - /* Dump the result that has been accumulated so far to .log file */ - if (result_file_name && ds_res.length) - dump_result_to_log_file(ds_res.str, ds_res.length); - - /* Dump warning messages */ - if (result_file_name && ds_warning_messages.length) - dump_warning_messages(); - - /* - Help debugging by displaying any warnings that might have - been produced prior to the error - */ - if (cur_con) - show_warnings_before_error(&cur_con->mysql); - - cleanup_and_exit(1); -} - - -void abort_not_supported_test(const char *fmt, ...) -{ - va_list args; - struct st_test_file* err_file= cur_file; - DBUG_ENTER("abort_not_supported_test"); - - /* Print include filestack */ - fprintf(stderr, "The test '%s' is not supported by this installation\n", - file_stack->file_name); - fprintf(stderr, "Detected in file %s at line %d\n", - err_file->file_name, err_file->lineno); - while (err_file != file_stack) - { - err_file--; - fprintf(stderr, "included from %s at line %d\n", - err_file->file_name, err_file->lineno); - } - - /* Print error message */ - va_start(args, fmt); - if (fmt) - { - fprintf(stderr, "reason: "); - vfprintf(stderr, fmt, args); - fprintf(stderr, "\n"); - fflush(stderr); - } - va_end(args); - - cleanup_and_exit(62); -} - - -void abort_not_in_this_version() -{ - die("Not available in this version of mysqltest"); -} - - -void verbose_msg(const char *fmt, ...) -{ - va_list args; - DBUG_ENTER("verbose_msg"); - if (!verbose) - DBUG_VOID_RETURN; - - va_start(args, fmt); - fprintf(stderr, "mysqltest: "); - if (cur_file && cur_file != file_stack) - fprintf(stderr, "In included file \"%s\": ", - cur_file->file_name); - if (start_lineno != 0) - fprintf(stderr, "At line %u: ", start_lineno); - vfprintf(stderr, fmt, args); - fprintf(stderr, "\n"); - va_end(args); - - DBUG_VOID_RETURN; -} - - -void warning_msg(const char *fmt, ...) -{ - va_list args; - char buff[512]; - size_t len; - DBUG_ENTER("warning_msg"); - - va_start(args, fmt); - dynstr_append(&ds_warning_messages, "mysqltest: "); - if (start_lineno != 0) - { - dynstr_append(&ds_warning_messages, "Warning detected "); - if (cur_file && cur_file != file_stack) - { - len= my_snprintf(buff, sizeof(buff), "in included file %s ", - cur_file->file_name); - dynstr_append_mem(&ds_warning_messages, - buff, len); - } - len= my_snprintf(buff, sizeof(buff), "at line %d: ", - start_lineno); - dynstr_append_mem(&ds_warning_messages, - buff, len); - } - - len= my_vsnprintf(buff, sizeof(buff), fmt, args); - dynstr_append_mem(&ds_warning_messages, buff, len); - - dynstr_append(&ds_warning_messages, "\n"); - va_end(args); - - DBUG_VOID_RETURN; -} - - -void log_msg(const char *fmt, ...) -{ - va_list args; - char buff[1024]; - size_t len; - DBUG_ENTER("log_msg"); - - va_start(args, fmt); - len= my_vsnprintf(buff, sizeof(buff)-1, fmt, args); - va_end(args); - - dynstr_append_mem(&ds_res, buff, len); - dynstr_append(&ds_res, "\n"); - - DBUG_VOID_RETURN; -} - - -/* - Read a file and append it to ds - - SYNOPSIS - cat_file - ds - pointer to dynamic string where to add the files content - filename - name of the file to read - -*/ - -void cat_file(DYNAMIC_STRING* ds, const char* filename) -{ - int fd; - uint len; - char buff[512]; - - if ((fd= my_open(filename, O_RDONLY, MYF(0))) < 0) - die("Failed to open file '%s'", filename); - while((len= my_read(fd, (uchar*)&buff, - sizeof(buff), MYF(0))) > 0) - { - char *p= buff, *start= buff; - while (p < buff+len) - { - /* Convert cr/lf to lf */ - if (*p == '\r' && *(p+1) && *(p+1)== '\n') - { - /* Add fake newline instead of cr and output the line */ - *p= '\n'; - p++; /* Step past the "fake" newline */ - dynstr_append_mem(ds, start, p-start); - p++; /* Step past the "fake" newline */ - start= p; - } - else - p++; - } - /* Output any chars that migh be left */ - dynstr_append_mem(ds, start, p-start); - } - my_close(fd, MYF(0)); -} - - -/* - Run the specified command with popen - - SYNOPSIS - run_command - cmd - command to execute(should be properly quoted - ds_res- pointer to dynamic string where to store the result - -*/ - -static int run_command(char* cmd, - DYNAMIC_STRING *ds_res) -{ - char buf[512]= {0}; - FILE *res_file; - int error; - - if (!(res_file= popen(cmd, "r"))) - die("popen(\"%s\", \"r\") failed", cmd); - - while (fgets(buf, sizeof(buf), res_file)) - { - DBUG_PRINT("info", ("buf: %s", buf)); - if(ds_res) - { - /* Save the output of this command in the supplied string */ - dynstr_append(ds_res, buf); - } - else - { - /* Print it directly on screen */ - fprintf(stdout, "%s", buf); - } - } - - error= pclose(res_file); - return WEXITSTATUS(error); -} - - -/* - Run the specified tool with variable number of arguments - - SYNOPSIS - run_tool - tool_path - the name of the tool to run - ds_res - pointer to dynamic string where to store the result - ... - variable number of arguments that will be properly - quoted and appended after the tool's name - -*/ - -static int run_tool(const char *tool_path, DYNAMIC_STRING *ds_res, ...) -{ - int ret; - const char* arg; - va_list args; - DYNAMIC_STRING ds_cmdline; - - DBUG_ENTER("run_tool"); - DBUG_PRINT("enter", ("tool_path: %s", tool_path)); - - if (init_dynamic_string(&ds_cmdline, IF_WIN("\"", ""), FN_REFLEN, FN_REFLEN)) - die("Out of memory"); - - dynstr_append_os_quoted(&ds_cmdline, tool_path, NullS); - dynstr_append(&ds_cmdline, " "); - - va_start(args, ds_res); - - while ((arg= va_arg(args, char *))) - { - /* Options should be os quoted */ - if (strncmp(arg, "--", 2) == 0) - dynstr_append_os_quoted(&ds_cmdline, arg, NullS); - else - dynstr_append(&ds_cmdline, arg); - dynstr_append(&ds_cmdline, " "); - } - - va_end(args); - -#ifdef __WIN__ - dynstr_append(&ds_cmdline, "\""); -#endif - - DBUG_PRINT("info", ("Running: %s", ds_cmdline.str)); - ret= run_command(ds_cmdline.str, ds_res); - DBUG_PRINT("exit", ("ret: %d", ret)); - dynstr_free(&ds_cmdline); - DBUG_RETURN(ret); -} - - -/* - Show the diff of two files using the systems builtin diff - command. If no such diff command exist, just dump the content - of the two files and inform about how to get "diff" - - SYNOPSIS - show_diff - ds - pointer to dynamic string where to add the diff(may be NULL) - filename1 - name of first file - filename2 - name of second file - -*/ - -void show_diff(DYNAMIC_STRING* ds, - const char* filename1, const char* filename2) -{ - - const char* diff_failed= 0; - DYNAMIC_STRING ds_tmp; - - if (init_dynamic_string(&ds_tmp, "", 256, 256)) - die("Out of memory"); - - /* First try with unified diff */ - if (run_tool("diff", - &ds_tmp, /* Get output from diff in ds_tmp */ - "-u", - filename1, - filename2, - "2>&1", - NULL) > 1) /* Most "diff" tools return >1 if error */ - { - dynstr_set(&ds_tmp, ""); - - /* Fallback to context diff with "diff -c" */ - if (run_tool("diff", - &ds_tmp, /* Get output from diff in ds_tmp */ - "-c", - filename1, - filename2, - "2>&1", - NULL) > 1) /* Most "diff" tools return >1 if error */ - { - dynstr_set(&ds_tmp, ""); - - /* Fallback to plain "diff" */ - if (run_tool("diff", - &ds_tmp, /* Get output from diff in ds_tmp */ - filename1, - filename2, - "2>&1", - NULL) > 1) /* Most "diff" tools return >1 if error */ - { - dynstr_set(&ds_tmp, ""); - - diff_failed= "Could not execute 'diff -u', 'diff -c' or 'diff'"; - } - } - } - - if (diff_failed) - { - /* - Fallback to dump both files to result file and inform - about installing "diff" - */ - dynstr_append(&ds_tmp, "\n"); - dynstr_append(&ds_tmp, diff_failed); - dynstr_append(&ds_tmp, -"\n" -"The two files differ but it was not possible to execute 'diff' in\n" -"order to show only the difference. Instead the whole content of the\n" -"two files was shown for you to diff manually.\n\n" -"To get a better report you should install 'diff' on your system, which you\n" -"for example can get from http://www.gnu.org/software/diffutils/diffutils.html\n" -#ifdef __WIN__ -"or http://gnuwin32.sourceforge.net/packages/diffutils.htm\n" -#endif -"\n"); - - dynstr_append(&ds_tmp, " --- "); - dynstr_append(&ds_tmp, filename1); - dynstr_append(&ds_tmp, " >>>\n"); - cat_file(&ds_tmp, filename1); - dynstr_append(&ds_tmp, "<<<\n --- "); - dynstr_append(&ds_tmp, filename1); - dynstr_append(&ds_tmp, " >>>\n"); - cat_file(&ds_tmp, filename2); - dynstr_append(&ds_tmp, "<<<<\n"); - } - - if (ds) - { - /* Add the diff to output */ - dynstr_append_mem(ds, ds_tmp.str, ds_tmp.length); - } - else - { - /* Print diff directly to stdout */ - fprintf(stderr, "%s\n", ds_tmp.str); - } - - dynstr_free(&ds_tmp); - -} - - -enum compare_files_result_enum { - RESULT_OK= 0, - RESULT_CONTENT_MISMATCH= 1, - RESULT_LENGTH_MISMATCH= 2 -}; - -/* - Compare two files, given a fd to the first file and - name of the second file - - SYNOPSIS - compare_files2 - fd - Open file descriptor of the first file - filename2 - Name of second file - - RETURN VALUES - According to the values in "compare_files_result_enum" - -*/ - -int compare_files2(File fd, const char* filename2) -{ - int error= RESULT_OK; - File fd2; - uint len, len2; - char buff[512], buff2[512]; - - if ((fd2= my_open(filename2, O_RDONLY, MYF(0))) < 0) - { - my_close(fd, MYF(0)); - die("Failed to open second file: '%s'", filename2); - } - while((len= my_read(fd, (uchar*)&buff, - sizeof(buff), MYF(0))) > 0) - { - if ((len2= my_read(fd2, (uchar*)&buff2, - sizeof(buff2), MYF(0))) < len) - { - /* File 2 was smaller */ - error= RESULT_LENGTH_MISMATCH; - break; - } - if (len2 > len) - { - /* File 1 was smaller */ - error= RESULT_LENGTH_MISMATCH; - break; - } - if ((memcmp(buff, buff2, len))) - { - /* Content of this part differed */ - error= RESULT_CONTENT_MISMATCH; - break; - } - } - if (!error && my_read(fd2, (uchar*)&buff2, - sizeof(buff2), MYF(0)) > 0) - { - /* File 1 was smaller */ - error= RESULT_LENGTH_MISMATCH; - } - - my_close(fd2, MYF(0)); - - return error; -} - - -/* - Compare two files, given their filenames - - SYNOPSIS - compare_files - filename1 - Name of first file - filename2 - Name of second file - - RETURN VALUES - See 'compare_files2' - -*/ - -int compare_files(const char* filename1, const char* filename2) -{ - File fd; - int error; - - if ((fd= my_open(filename1, O_RDONLY, MYF(0))) < 0) - die("Failed to open first file: '%s'", filename1); - - error= compare_files2(fd, filename2); - - my_close(fd, MYF(0)); - - return error; -} - - -/* - Compare content of the string in ds to content of file fname - - SYNOPSIS - dyn_string_cmp - ds - Dynamic string containing the string o be compared - fname - Name of file to compare with - - RETURN VALUES - See 'compare_files2' -*/ - -int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) -{ - int error; - File fd; - char temp_file_path[FN_REFLEN]; - - DBUG_ENTER("dyn_string_cmp"); - DBUG_PRINT("enter", ("fname: %s", fname)); - - if ((fd= create_temp_file(temp_file_path, TMPDIR, - "tmp", O_CREAT | O_SHARE | O_RDWR, - MYF(MY_WME))) < 0) - die("Failed to create temporary file for ds"); - - /* Write ds to temporary file and set file pos to beginning*/ - if (my_write(fd, (uchar *) ds->str, ds->length, - MYF(MY_FNABP | MY_WME)) || - my_seek(fd, 0, SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR) - { - my_close(fd, MYF(0)); - /* Remove the temporary file */ - my_delete(temp_file_path, MYF(0)); - die("Failed to write file '%s'", temp_file_path); - } - - error= compare_files2(fd, fname); - - my_close(fd, MYF(0)); - /* Remove the temporary file */ - my_delete(temp_file_path, MYF(0)); - - DBUG_RETURN(error); -} - - -/* - Check the content of ds against result file - - SYNOPSIS - check_result - ds - content to be checked - - RETURN VALUES - error - the function will not return - -*/ - -void check_result(DYNAMIC_STRING* ds) -{ - const char* mess= "Result content mismatch\n"; - - DBUG_ENTER("check_result"); - DBUG_ASSERT(result_file_name); - DBUG_PRINT("enter", ("result_file_name: %s", result_file_name)); - - if (access(result_file_name, F_OK) != 0) - die("The specified result file does not exist: '%s'", result_file_name); - - switch (dyn_string_cmp(ds, result_file_name)) { - case RESULT_OK: - break; /* ok */ - case RESULT_LENGTH_MISMATCH: - mess= "Result length mismatch\n"; - /* Fallthrough */ - case RESULT_CONTENT_MISMATCH: - { - /* - Result mismatched, dump results to .reject file - and then show the diff - */ - char reject_file[FN_REFLEN]; - size_t reject_length; - dirname_part(reject_file, result_file_name, &reject_length); - - if (access(reject_file, W_OK) == 0) - { - /* Result file directory is writable, save reject file there */ - fn_format(reject_file, result_file_name, NULL, - ".reject", MY_REPLACE_EXT); - } - else - { - /* Put reject file in opt_logdir */ - fn_format(reject_file, result_file_name, opt_logdir, - ".reject", MY_REPLACE_DIR | MY_REPLACE_EXT); - } - str_to_file(reject_file, ds->str, ds->length); - - dynstr_set(ds, NULL); /* Don't create a .log file */ - - show_diff(NULL, result_file_name, reject_file); - die(mess); - break; - } - default: /* impossible */ - die("Unknown error code from dyn_string_cmp()"); - } - - DBUG_VOID_RETURN; -} - - -/* - Check the content of ds against a require file - If match fails, abort the test with special error code - indicating that test is not supported - - SYNOPSIS - check_require - ds - content to be checked - fname - name of file to check against - - RETURN VALUES - error - the function will not return - -*/ - -void check_require(DYNAMIC_STRING* ds, const char *fname) -{ - DBUG_ENTER("check_require"); - - if (dyn_string_cmp(ds, fname)) - { - char reason[FN_REFLEN]; - fn_format(reason, fname, "", "", MY_REPLACE_EXT | MY_REPLACE_DIR); - abort_not_supported_test("Test requires: '%s'", reason); - } - DBUG_VOID_RETURN; -} - - -/* - Remove surrounding chars from string - - Return 1 if first character is found but not last -*/ -static int strip_surrounding(char* str, char c1, char c2) -{ - char* ptr= str; - - /* Check if the first non space character is c1 */ - while(*ptr && my_isspace(charset_info, *ptr)) - ptr++; - if (*ptr == c1) - { - /* Replace it with a space */ - *ptr= ' '; - - /* Last non space charecter should be c2 */ - ptr= strend(str)-1; - while(*ptr && my_isspace(charset_info, *ptr)) - ptr--; - if (*ptr == c2) - { - /* Replace it with \0 */ - *ptr= 0; - } - else - { - /* Mismatch detected */ - return 1; - } - } - return 0; -} - - -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, '(', ')'); -} - - -static uchar *get_var_key(const uchar* var, size_t *len, - my_bool __attribute__((unused)) t) -{ - register char* key; - key = ((VAR*)var)->name; - *len = ((VAR*)var)->name_len; - return (uchar*)key; -} - - -VAR *var_init(VAR *v, const char *name, int name_len, const char *val, - int val_len) -{ - int val_alloc_len; - VAR *tmp_var; - if (!name_len && name) - name_len = strlen(name); - if (!val_len && val) - val_len = strlen(val) ; - val_alloc_len = val_len + 16; /* room to grow */ - if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var) - + name_len+1, MYF(MY_WME)))) - die("Out of memory"); - - tmp_var->name = (name) ? (char*) tmp_var + sizeof(*tmp_var) : 0; - tmp_var->alloced = (v == 0); - - if (!(tmp_var->str_val = my_malloc(val_alloc_len+1, MYF(MY_WME)))) - die("Out of memory"); - - memcpy(tmp_var->name, name, name_len); - if (val) - { - memcpy(tmp_var->str_val, val, val_len); - tmp_var->str_val[val_len]= 0; - } - tmp_var->name_len = name_len; - tmp_var->str_val_len = val_len; - tmp_var->alloced_len = val_alloc_len; - tmp_var->int_val = (val) ? atoi(val) : 0; - tmp_var->int_dirty = 0; - tmp_var->env_s = 0; - return tmp_var; -} - - -void var_free(void *v) -{ - my_free(((VAR*) v)->str_val, MYF(MY_WME)); - if (((VAR*)v)->alloced) - my_free(v, MYF(MY_WME)); -} - - -VAR* var_from_env(const char *name, const char *def_val) -{ - const char *tmp; - VAR *v; - if (!(tmp = getenv(name))) - tmp = def_val; - - v = var_init(0, name, strlen(name), tmp, strlen(tmp)); - my_hash_insert(&var_hash, (uchar*)v); - return v; -} - - -VAR* var_get(const char *var_name, const char **var_name_end, my_bool raw, - my_bool ignore_not_existing) -{ - int digit; - VAR *v; - DBUG_ENTER("var_get"); - DBUG_PRINT("enter", ("var_name: %s",var_name)); - - if (*var_name != '$') - goto err; - digit = *++var_name - '0'; - if (digit < 0 || digit >= 10) - { - const char *save_var_name = var_name, *end; - uint length; - end = (var_name_end) ? *var_name_end : 0; - while (my_isvar(charset_info,*var_name) && var_name != end) - var_name++; - if (var_name == save_var_name) - { - if (ignore_not_existing) - DBUG_RETURN(0); - die("Empty variable"); - } - length= (uint) (var_name - save_var_name); - 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))) - { - char buff[MAX_VAR_NAME_LENGTH+1]; - strmake(buff, save_var_name, length); - v= var_from_env(buff, ""); - } - var_name--; /* Point at last character */ - } - else - v = var_reg + digit; - - if (!raw && v->int_dirty) - { - sprintf(v->str_val, "%d", v->int_val); - v->int_dirty = 0; - v->str_val_len = strlen(v->str_val); - } - if (var_name_end) - *var_name_end = var_name ; - DBUG_RETURN(v); -err: - if (var_name_end) - *var_name_end = 0; - die("Unsupported variable name: %s", var_name); - DBUG_RETURN(0); -} - - -VAR *var_obtain(const char *name, int len) -{ - VAR* v; - if ((v = (VAR*)hash_search(&var_hash, (const uchar *) name, len))) - return v; - v = var_init(0, name, len, "", 0); - my_hash_insert(&var_hash, (uchar*)v); - return v; -} - - -/* - - if variable starts with a $ it is regarded as a local test varable - - if not it is treated as a environment variable, and the corresponding - environment variable will be updated -*/ - -void var_set(const char *var_name, const char *var_name_end, - const char *var_val, const char *var_val_end) -{ - int digit, env_var= 0; - VAR *v; - DBUG_ENTER("var_set"); - DBUG_PRINT("enter", ("var_name: '%.*s' = '%.*s' (length: %d)", - (int) (var_name_end - var_name), var_name, - (int) (var_val_end - var_val), var_val, - (int) (var_val_end - var_val))); - - if (*var_name != '$') - env_var= 1; - else - var_name++; - - digit= *var_name - '0'; - if (!(digit < 10 && digit >= 0)) - { - v= var_obtain(var_name, (uint) (var_name_end - var_name)); - } - else - v= var_reg + digit; - - eval_expr(v, var_val, (const char**) &var_val_end); - - if (env_var) - { - char buf[1024], *old_env_s= v->env_s; - if (v->int_dirty) - { - sprintf(v->str_val, "%d", v->int_val); - v->int_dirty= 0; - v->str_val_len= strlen(v->str_val); - } - my_snprintf(buf, sizeof(buf), "%.*s=%.*s", - v->name_len, v->name, - v->str_val_len, v->str_val); - if (!(v->env_s= my_strdup(buf, MYF(MY_WME)))) - die("Out of memory"); - putenv(v->env_s); - my_free(old_env_s, MYF(MY_ALLOW_ZERO_PTR)); - } - DBUG_VOID_RETURN; -} - - -void var_set_string(const char* name, const char* value) -{ - var_set(name, name + strlen(name), value, value + strlen(value)); -} - - -void var_set_int(const char* name, int value) -{ - char buf[21]; - my_snprintf(buf, sizeof(buf), "%d", value); - var_set_string(name, buf); -} - - -/* - Store an integer (typically the returncode of the last SQL) - statement in the mysqltest builtin variable $mysql_errno -*/ - -void var_set_errno(int sql_errno) -{ - var_set_int("$mysql_errno", sql_errno); -} - - -/* - Update $mysql_get_server_version variable with version - of the currently connected server -*/ - -void var_set_mysql_get_server_version(MYSQL* mysql) -{ - var_set_int("$mysql_get_server_version", mysql_get_server_version(mysql)); -} - - -/* - Set variable from the result of a query - - SYNOPSIS - var_query_set() - var variable to set from query - query start of query string to execute - query_end end of the query string to execute - - - DESCRIPTION - let @<var_name> = `<query>` - - Execute the query and assign the first row of result to var as - a tab separated strings - - Also assign each column of the result set to - variable "$<var_name>_<column_name>" - Thus the tab separated output can be read from $<var_name> and - and each individual column can be read as $<var_name>_<col_name> - -*/ - -void var_query_set(VAR *var, const char *query, const char** query_end) -{ - char *end = (char*)((query_end && *query_end) ? - *query_end : query + strlen(query)); - MYSQL_RES *res; - MYSQL_ROW row; - MYSQL* mysql = &cur_con->mysql; - DYNAMIC_STRING ds_query; - DBUG_ENTER("var_query_set"); - LINT_INIT(res); - - while (end > query && *end != '`') - --end; - if (query == end) - die("Syntax error in query, missing '`'"); - ++query; - - /* Eval the query, thus replacing all environment variables */ - init_dynamic_string(&ds_query, 0, (end - query) + 32, 256); - do_eval(&ds_query, query, end, FALSE); - - if (mysql_real_query(mysql, ds_query.str, ds_query.length)) - die("Error running query '%s': %d %s", ds_query.str, - mysql_errno(mysql), mysql_error(mysql)); - if (!(res= mysql_store_result(mysql))) - die("Query '%s' didn't return a result set", ds_query.str); - dynstr_free(&ds_query); - - if ((row= mysql_fetch_row(res)) && row[0]) - { - /* - Concatenate all fields in the first row with tab in between - and assign that string to the $variable - */ - DYNAMIC_STRING result; - uint i; - ulong *lengths; - - init_dynamic_string(&result, "", 512, 512); - lengths= mysql_fetch_lengths(res); - for (i= 0; i < mysql_num_fields(res); i++) - { - if (row[i]) - { - /* Add column to tab separated string */ - dynstr_append_mem(&result, row[i], lengths[i]); - } - dynstr_append_mem(&result, "\t", 1); - } - end= result.str + result.length-1; - eval_expr(var, result.str, (const char**) &end); - dynstr_free(&result); - } - else - eval_expr(var, "", 0); - - mysql_free_result(res); - DBUG_VOID_RETURN; -} - - -/* - Set variable from the result of a field in a query - - This function is useful when checking for a certain value - in the output from a query that can't be restricted to only - return some values. A very good example of that is most SHOW - commands. - - SYNOPSIS - var_set_query_get_value() - - DESCRIPTION - let $variable= query_get_value(<query to run>,<column name>,<row no>); - - <query to run> - The query that should be sent to the server - <column name> - Name of the column that holds the field be compared - against the expected value - <row no> - Number of the row that holds the field to be - compared against the expected value - -*/ - -void var_set_query_get_value(struct st_command *command, VAR *var) -{ - long row_no; - int col_no= -1; - MYSQL_RES* res; - MYSQL* mysql= &cur_con->mysql; - - static DYNAMIC_STRING ds_query; - static DYNAMIC_STRING ds_col; - static DYNAMIC_STRING ds_row; - const struct command_arg query_get_value_args[] = { - {"query", ARG_STRING, TRUE, &ds_query, "Query to run"}, - {"column name", ARG_STRING, TRUE, &ds_col, "Name of column"}, - {"row number", ARG_STRING, TRUE, &ds_row, "Number for row"} - }; - - DBUG_ENTER("var_set_query_get_value"); - LINT_INIT(res); - - strip_parentheses(command); - DBUG_PRINT("info", ("query: %s", command->query)); - check_command_args(command, command->first_argument, query_get_value_args, - sizeof(query_get_value_args)/sizeof(struct command_arg), - ','); - - DBUG_PRINT("info", ("query: %s", ds_query.str)); - DBUG_PRINT("info", ("col: %s", ds_col.str)); - - /* Convert row number to int */ - if (!str2int(ds_row.str, 10, (long) 0, (long) INT_MAX, &row_no)) - die("Invalid row number: '%s'", ds_row.str); - DBUG_PRINT("info", ("row: %s, row_no: %ld", ds_row.str, row_no)); - dynstr_free(&ds_row); - - /* Remove any surrounding "'s from the query - if there is any */ - if (strip_surrounding(ds_query.str, '"', '"')) - die("Mismatched \"'s around query '%s'", ds_query.str); - - /* Run the query */ - if (mysql_real_query(mysql, ds_query.str, ds_query.length)) - die("Error running query '%s': %d %s", ds_query.str, - mysql_errno(mysql), mysql_error(mysql)); - if (!(res= mysql_store_result(mysql))) - die("Query '%s' didn't return a result set", ds_query.str); - - { - /* Find column number from the given column name */ - uint i; - uint num_fields= mysql_num_fields(res); - MYSQL_FIELD *fields= mysql_fetch_fields(res); - - for (i= 0; i < num_fields; i++) - { - if (strcmp(fields[i].name, ds_col.str) == 0 && - strlen(fields[i].name) == ds_col.length) - { - col_no= i; - break; - } - } - 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); - } - DBUG_PRINT("info", ("Found column %d with name '%s'", - i, fields[i].name)); - } - dynstr_free(&ds_col); - - { - /* Get the value */ - MYSQL_ROW row; - long rows= 0; - const char* value= "No such row"; - - while ((row= mysql_fetch_row(res))) - { - if (++rows == row_no) - { - - DBUG_PRINT("info", ("At row %ld, column %d is '%s'", - row_no, col_no, row[col_no])); - /* Found the row to get */ - if (row[col_no]) - value= row[col_no]; - else - value= "NULL"; - - break; - } - } - eval_expr(var, value, 0); - } - dynstr_free(&ds_query); - mysql_free_result(res); - - DBUG_VOID_RETURN; -} - - -void var_copy(VAR *dest, VAR *src) -{ - dest->int_val= src->int_val; - dest->int_dirty= src->int_dirty; - - /* Alloc/realloc data for str_val in dest */ - if (dest->alloced_len < src->alloced_len && - !(dest->str_val= dest->str_val - ? my_realloc(dest->str_val, src->alloced_len, MYF(MY_WME)) - : my_malloc(src->alloced_len, MYF(MY_WME)))) - die("Out of memory"); - else - dest->alloced_len= src->alloced_len; - - /* Copy str_val data to dest */ - dest->str_val_len= src->str_val_len; - if (src->str_val_len) - memcpy(dest->str_val, src->str_val, src->str_val_len); -} - - -void eval_expr(VAR *v, const char *p, const char **p_end) -{ - - DBUG_ENTER("eval_expr"); - DBUG_PRINT("enter", ("p: '%s'", p)); - - if (*p == '$') - { - VAR *vp; - if ((vp= var_get(p, p_end, 0, 0))) - var_copy(v, vp); - DBUG_VOID_RETURN; - } - - if (*p == '`') - { - var_query_set(v, p, p_end); - DBUG_VOID_RETURN; - } - - { - /* Check if this is a "let $var= query_get_value()" */ - const char* get_value_str= "query_get_value"; - const size_t len= strlen(get_value_str); - if (strncmp(p, get_value_str, len)==0) - { - struct st_command command; - memset(&command, 0, sizeof(command)); - command.query= (char*)p; - command.first_word_len= len; - command.first_argument= command.query + len; - command.end= (char*)*p_end; - var_set_query_get_value(&command, v); - DBUG_VOID_RETURN; - } - } - - { - int new_val_len = (p_end && *p_end) ? - (int) (*p_end - p) : (int) strlen(p); - if (new_val_len + 1 >= v->alloced_len) - { - static int MIN_VAR_ALLOC= 32; - v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ? - MIN_VAR_ALLOC : new_val_len + 1; - if (!(v->str_val = - v->str_val ? my_realloc(v->str_val, v->alloced_len+1, - MYF(MY_WME)) : - my_malloc(v->alloced_len+1, MYF(MY_WME)))) - die("Out of memory"); - } - v->str_val_len = new_val_len; - memcpy(v->str_val, p, new_val_len); - v->str_val[new_val_len] = 0; - v->int_val=atoi(p); - DBUG_PRINT("info", ("atoi on '%s', returns: %d", p, v->int_val)); - v->int_dirty=0; - } - DBUG_VOID_RETURN; -} - - -int open_file(const char *name) -{ - char buff[FN_REFLEN]; - size_t length; - 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)) - { - strxmov(buff, buff, name, NullS); - if (access(buff, F_OK) == 0){ - DBUG_PRINT("info", ("The file exists")); - name= buff; - } - } - if (!test_if_hard_path(name)) - { - strxmov(buff, opt_basedir, name, NullS); - name=buff; - } - fn_format(buff, name, "", "", MY_UNPACK_FILENAME); - - if (cur_file == file_stack_end) - die("Source directives are nesting too deep"); - cur_file++; - if (!(cur_file->file = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0)))) - { - cur_file--; - die("Could not open '%s' for reading, errno: %d", buff, errno); - } - cur_file->file_name= my_strdup(buff, MYF(MY_FAE)); - cur_file->lineno=1; - DBUG_RETURN(0); -} - - -/* - Source and execute the given file - - SYNOPSIS - do_source() - query called command - - DESCRIPTION - source <file_name> - - Open the file <file_name> and execute it - -*/ - -void do_source(struct st_command *command) -{ - static DYNAMIC_STRING ds_filename; - const struct command_arg source_args[] = { - { "filename", ARG_STRING, TRUE, &ds_filename, "File to source" } - }; - DBUG_ENTER("do_source"); - - check_command_args(command, command->first_argument, source_args, - sizeof(source_args)/sizeof(struct command_arg), - ' '); - - /* - If this file has already been sourced, don't source it again. - It's already available in the q_lines cache. - */ - if (parser.current_line < (parser.read_lines - 1)) - ; /* Do nothing */ - else - { - DBUG_PRINT("info", ("sourcing file: %s", ds_filename.str)); - open_file(ds_filename.str); - } - - dynstr_free(&ds_filename); - return; -} - - -#if defined __WIN__ - -#ifdef USE_CYGWIN -/* Variables used for temporary sh files used for emulating Unix on Windows */ -char tmp_sh_name[64], tmp_sh_cmd[70]; -#endif - -void init_tmp_sh_file() -{ -#ifdef USE_CYGWIN - /* Format a name for the tmp sh file that is unique for this process */ - my_snprintf(tmp_sh_name, sizeof(tmp_sh_name), "tmp_%d.sh", getpid()); - /* Format the command to execute in order to run the script */ - my_snprintf(tmp_sh_cmd, sizeof(tmp_sh_cmd), "sh %s", tmp_sh_name); -#endif -} - - -void free_tmp_sh_file() -{ -#ifdef USE_CYGWIN - my_delete(tmp_sh_name, MYF(0)); -#endif -} -#endif - - -FILE* my_popen(DYNAMIC_STRING *ds_cmd, const char *mode) -{ -#if defined __WIN__ && defined USE_CYGWIN - /* Dump the command into a sh script file and execute with popen */ - str_to_file(tmp_sh_name, ds_cmd->str, ds_cmd->length); - return popen(tmp_sh_cmd, mode); -#else - return popen(ds_cmd->str, mode); -#endif -} - - -static void init_builtin_echo(void) -{ -#ifdef __WIN__ - size_t echo_length; - - /* Look for "echo.exe" in same dir as mysqltest was started from */ - dirname_part(builtin_echo, my_progname, &echo_length); - fn_format(builtin_echo, ".\\echo.exe", - builtin_echo, "", MYF(MY_REPLACE_DIR)); - - /* Make sure echo.exe exists */ - if (access(builtin_echo, F_OK) != 0) - builtin_echo[0]= 0; - return; - -#else - - builtin_echo[0]= 0; - return; - -#endif -} - - -/* - Replace a substring - - SYNOPSIS - replace - ds_str The string to search and perform the replace in - search_str The string to search for - search_len Length of the string to search for - replace_str The string to replace with - replace_len Length of the string to replace with - - RETURN - 0 String replaced - 1 Could not find search_str in str -*/ - -static int replace(DYNAMIC_STRING *ds_str, - const char *search_str, ulong search_len, - const char *replace_str, ulong replace_len) -{ - DYNAMIC_STRING ds_tmp; - const char *start= strstr(ds_str->str, search_str); - if (!start) - return 1; - init_dynamic_string(&ds_tmp, "", - ds_str->length + replace_len, 256); - dynstr_append_mem(&ds_tmp, ds_str->str, start - ds_str->str); - dynstr_append_mem(&ds_tmp, replace_str, replace_len); - dynstr_append(&ds_tmp, start + search_len); - dynstr_set(ds_str, ds_tmp.str); - dynstr_free(&ds_tmp); - return 0; -} - - -/* - Execute given command. - - SYNOPSIS - do_exec() - query called command - - DESCRIPTION - exec <command> - - Execute the text between exec and end of line in a subprocess. - The error code returned from the subprocess is checked against the - expected error array, previously set with the --error command. - It can thus be used to execute a command that shall fail. - - NOTE - Although mysqltest is executed from cygwin shell, the command will be - executed in "cmd.exe". Thus commands like "rm" etc can NOT be used, use - mysqltest commmand(s) like "remove_file" for that -*/ - -void do_exec(struct st_command *command) -{ - int error; - char buf[512]; - FILE *res_file; - char *cmd= command->first_argument; - DYNAMIC_STRING ds_cmd; - DBUG_ENTER("do_exec"); - DBUG_PRINT("enter", ("cmd: '%s'", cmd)); - - /* Skip leading space */ - while (*cmd && my_isspace(charset_info, *cmd)) - cmd++; - if (!*cmd) - die("Missing argument in exec"); - command->last_argument= command->end; - - init_dynamic_string(&ds_cmd, 0, command->query_len+256, 256); - /* Eval the command, thus replacing all environment variables */ - do_eval(&ds_cmd, cmd, command->end, !is_windows); - - /* Check if echo should be replaced with "builtin" echo */ - if (builtin_echo[0] && strncmp(cmd, "echo", 4) == 0) - { - /* Replace echo with our "builtin" echo */ - replace(&ds_cmd, "echo", 4, builtin_echo, strlen(builtin_echo)); - } - -#ifdef __WIN__ -#ifndef USE_CYGWIN - /* Replace /dev/null with NUL */ - while(replace(&ds_cmd, "/dev/null", 9, "NUL", 3) == 0) - ; - /* Replace "closed stdout" with non existing output fd */ - while(replace(&ds_cmd, ">&-", 3, ">&4", 3) == 0) - ; -#endif -#endif - - 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) - { - dynstr_free(&ds_cmd); - die("popen(\"%s\", \"r\") failed", command->first_argument); - } - - while (fgets(buf, sizeof(buf), res_file)) - { - if (disable_result_log) - { - buf[strlen(buf)-1]=0; - DBUG_PRINT("exec_result",("%s", buf)); - } - else - { - replace_dynstr_append(&ds_res, buf); - } - } - error= pclose(res_file); - if (error > 0) - { - uint status= WEXITSTATUS(error), i; - my_bool ok= 0; - - if (command->abort_on_error) - { - log_msg("exec of '%s' failed, error: %d, status: %d, errno: %d", - ds_cmd.str, error, status, errno); - dynstr_free(&ds_cmd); - die("command \"%s\" failed", command->first_argument); - } - - DBUG_PRINT("info", - ("error: %d, status: %d", error, status)); - for (i= 0; i < command->expected_errors.count; i++) - { - DBUG_PRINT("info", ("expected error: %d", - command->expected_errors.err[i].code.errnum)); - if ((command->expected_errors.err[i].type == ERR_ERRNO) && - (command->expected_errors.err[i].code.errnum == status)) - { - ok= 1; - DBUG_PRINT("info", ("command \"%s\" failed with expected error: %d", - command->first_argument, status)); - } - } - if (!ok) - { - dynstr_free(&ds_cmd); - die("command \"%s\" failed with wrong error: %d", - command->first_argument, status); - } - } - else if (command->expected_errors.err[0].type == ERR_ERRNO && - command->expected_errors.err[0].code.errnum != 0) - { - /* Error code we wanted was != 0, i.e. not an expected success */ - 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); - } - - dynstr_free(&ds_cmd); - DBUG_VOID_RETURN; -} - -enum enum_operator -{ - DO_DEC, - DO_INC -}; - - -/* - Decrease or increase the value of a variable - - SYNOPSIS - do_modify_var() - query called command - operator operation to perform on the var - - DESCRIPTION - dec $var_name - inc $var_name - -*/ - -int do_modify_var(struct st_command *command, - enum enum_operator operator) -{ - const char *p= command->first_argument; - VAR* v; - if (!*p) - die("Missing argument to %.*s", command->first_word_len, command->query); - if (*p != '$') - die("The argument to %.*s must be a variable (start with $)", - command->first_word_len, command->query); - v= var_get(p, &p, 1, 0); - switch (operator) { - case DO_DEC: - v->int_val--; - break; - case DO_INC: - v->int_val++; - break; - default: - die("Invalid operator to do_modify_var"); - break; - } - v->int_dirty= 1; - command->last_argument= (char*)++p; - return 0; -} - - -/* - Wrapper for 'system' function - - NOTE - If mysqltest is executed from cygwin shell, the command will be - executed in the "windows command interpreter" cmd.exe and we prepend "sh" - to make it be executed by cygwins "bash". Thus commands like "rm", - "mkdir" as well as shellscripts can executed by "system" in Windows. - -*/ - -int my_system(DYNAMIC_STRING* ds_cmd) -{ -#if defined __WIN__ && defined USE_CYGWIN - /* Dump the command into a sh script file and execute with system */ - str_to_file(tmp_sh_name, ds_cmd->str, ds_cmd->length); - return system(tmp_sh_cmd); -#else - return system(ds_cmd->str); -#endif -} - - -/* - SYNOPSIS - do_system - command called command - - DESCRIPTION - system <command> - - Eval the query to expand any $variables in the command. - Execute the command with the "system" command. - -*/ - -void do_system(struct st_command *command) -{ - DYNAMIC_STRING ds_cmd; - DBUG_ENTER("do_system"); - - if (strlen(command->first_argument) == 0) - die("Missing arguments to system, nothing to do!"); - - init_dynamic_string(&ds_cmd, 0, command->query_len + 64, 256); - - /* Eval the system command, thus replacing all environment variables */ - do_eval(&ds_cmd, command->first_argument, command->end, !is_windows); - -#ifdef __WIN__ -#ifndef USE_CYGWIN - /* Replace /dev/null with NUL */ - while(replace(&ds_cmd, "/dev/null", 9, "NUL", 3) == 0) - ; -#endif -#endif - - - DBUG_PRINT("info", ("running system command '%s' as '%s'", - command->first_argument, ds_cmd.str)); - if (my_system(&ds_cmd)) - { - if (command->abort_on_error) - die("system command '%s' failed", command->first_argument); - - /* If ! abort_on_error, log message and continue */ - dynstr_append(&ds_res, "system command '"); - replace_dynstr_append(&ds_res, command->first_argument); - dynstr_append(&ds_res, "' failed\n"); - } - - command->last_argument= command->end; - dynstr_free(&ds_cmd); - DBUG_VOID_RETURN; -} - - -/* - SYNOPSIS - do_remove_file - command called command - - DESCRIPTION - remove_file <file_name> - Remove the file <file_name> -*/ - -void do_remove_file(struct st_command *command) -{ - int error; - static DYNAMIC_STRING ds_filename; - const struct command_arg rm_args[] = { - { "filename", ARG_STRING, TRUE, &ds_filename, "File to delete" } - }; - DBUG_ENTER("do_remove_file"); - - check_command_args(command, command->first_argument, - rm_args, sizeof(rm_args)/sizeof(struct command_arg), - ' '); - - DBUG_PRINT("info", ("removing file: %s", ds_filename.str)); - error= my_delete(ds_filename.str, MYF(0)) != 0; - handle_command_error(command, error); - dynstr_free(&ds_filename); - DBUG_VOID_RETURN; -} - - -/* - SYNOPSIS - do_copy_file - command command handle - - DESCRIPTION - copy_file <from_file> <to_file> - Copy <from_file> to <to_file> - - NOTE! Will fail if <to_file> exists -*/ - -void do_copy_file(struct st_command *command) -{ - int error; - static DYNAMIC_STRING ds_from_file; - static DYNAMIC_STRING ds_to_file; - const struct command_arg copy_file_args[] = { - { "from_file", ARG_STRING, TRUE, &ds_from_file, "Filename to copy from" }, - { "to_file", ARG_STRING, TRUE, &ds_to_file, "Filename to copy to" } - }; - DBUG_ENTER("do_copy_file"); - - check_command_args(command, command->first_argument, - copy_file_args, - sizeof(copy_file_args)/sizeof(struct command_arg), - ' '); - - DBUG_PRINT("info", ("Copy %s to %s", ds_from_file.str, ds_to_file.str)); - error= (my_copy(ds_from_file.str, ds_to_file.str, - MYF(MY_DONT_OVERWRITE_FILE)) != 0); - handle_command_error(command, error); - dynstr_free(&ds_from_file); - dynstr_free(&ds_to_file); - DBUG_VOID_RETURN; -} - - -/* - SYNOPSIS - do_chmod_file - command command handle - - DESCRIPTION - chmod <octal> <file_name> - Change file permission of <file_name> - -*/ - -void do_chmod_file(struct st_command *command) -{ - long mode= 0; - static DYNAMIC_STRING ds_mode; - static DYNAMIC_STRING ds_file; - const struct command_arg chmod_file_args[] = { - { "mode", ARG_STRING, TRUE, &ds_mode, "Mode of file(octal) ex. 0660"}, - { "filename", ARG_STRING, TRUE, &ds_file, "Filename of file to modify" } - }; - DBUG_ENTER("do_chmod_file"); - - check_command_args(command, command->first_argument, - chmod_file_args, - sizeof(chmod_file_args)/sizeof(struct command_arg), - ' '); - - /* Parse what mode to set */ - if (ds_mode.length != 4 || - str2int(ds_mode.str, 8, 0, INT_MAX, &mode) == NullS) - die("You must write a 4 digit octal number for mode"); - - DBUG_PRINT("info", ("chmod %o %s", (uint)mode, ds_file.str)); - handle_command_error(command, chmod(ds_file.str, mode)); - dynstr_free(&ds_mode); - dynstr_free(&ds_file); - DBUG_VOID_RETURN; -} - - -/* - SYNOPSIS - do_file_exists - command called command - - DESCRIPTION - fiile_exist <file_name> - Check if file <file_name> exists -*/ - -void do_file_exist(struct st_command *command) -{ - int error; - static DYNAMIC_STRING ds_filename; - const struct command_arg file_exist_args[] = { - { "filename", ARG_STRING, TRUE, &ds_filename, "File to check if it exist" } - }; - DBUG_ENTER("do_file_exist"); - - check_command_args(command, command->first_argument, - file_exist_args, - sizeof(file_exist_args)/sizeof(struct command_arg), - ' '); - - DBUG_PRINT("info", ("Checking for existence of file: %s", ds_filename.str)); - error= (access(ds_filename.str, F_OK) != 0); - handle_command_error(command, error); - dynstr_free(&ds_filename); - DBUG_VOID_RETURN; -} - - -/* - SYNOPSIS - do_mkdir - command called command - - DESCRIPTION - mkdir <dir_name> - Create the directory <dir_name> -*/ - -void do_mkdir(struct st_command *command) -{ - int error; - static DYNAMIC_STRING ds_dirname; - const struct command_arg mkdir_args[] = { - {"dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to create"} - }; - DBUG_ENTER("do_mkdir"); - - check_command_args(command, command->first_argument, - mkdir_args, sizeof(mkdir_args)/sizeof(struct command_arg), - ' '); - - DBUG_PRINT("info", ("creating directory: %s", ds_dirname.str)); - error= my_mkdir(ds_dirname.str, 0777, MYF(0)) != 0; - handle_command_error(command, error); - dynstr_free(&ds_dirname); - DBUG_VOID_RETURN; -} - -/* - SYNOPSIS - do_rmdir - command called command - - DESCRIPTION - rmdir <dir_name> - Remove the empty directory <dir_name> -*/ - -void do_rmdir(struct st_command *command) -{ - int error; - static DYNAMIC_STRING ds_dirname; - const struct command_arg rmdir_args[] = { - {"dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to remove"} - }; - DBUG_ENTER("do_rmdir"); - - check_command_args(command, command->first_argument, - rmdir_args, sizeof(rmdir_args)/sizeof(struct command_arg), - ' '); - - DBUG_PRINT("info", ("removing directory: %s", ds_dirname.str)); - error= rmdir(ds_dirname.str) != 0; - handle_command_error(command, error); - dynstr_free(&ds_dirname); - DBUG_VOID_RETURN; -} - - -/* - Read characters from line buffer or file. This is needed to allow - my_ungetc() to buffer MAX_DELIMITER_LENGTH characters for a file - - NOTE: - This works as long as one doesn't change files (with 'source file_name') - when there is things pushed into the buffer. This should however not - happen for any tests in the test suite. -*/ - -int my_getc(FILE *file) -{ - if (line_buffer_pos == line_buffer) - return fgetc(file); - return *--line_buffer_pos; -} - - -void my_ungetc(int c) -{ - *line_buffer_pos++= (char) c; -} - - -void read_until_delimiter(DYNAMIC_STRING *ds, - DYNAMIC_STRING *ds_delimiter) -{ - char c; - DBUG_ENTER("read_until_delimiter"); - DBUG_PRINT("enter", ("delimiter: %s, length: %u", - ds_delimiter->str, (uint) ds_delimiter->length)); - - if (ds_delimiter->length > MAX_DELIMITER_LENGTH) - die("Max delimiter length(%d) exceeded", MAX_DELIMITER_LENGTH); - - /* Read from file until delimiter is found */ - while (1) - { - c= my_getc(cur_file->file); - - if (c == '\n') - { - cur_file->lineno++; - - /* Skip newline from the same line as the command */ - if (start_lineno == (cur_file->lineno - 1)) - continue; - } - else if (start_lineno == cur_file->lineno) - { - /* - No characters except \n are allowed on - the same line as the command - */ - die("Trailing characters found after command"); - } - - if (feof(cur_file->file)) - die("End of file encountered before '%s' delimiter was found", - ds_delimiter->str); - - if (match_delimiter(c, ds_delimiter->str, ds_delimiter->length)) - { - DBUG_PRINT("exit", ("Found delimiter '%s'", ds_delimiter->str)); - break; - } - dynstr_append_mem(ds, (const char*)&c, 1); - } - DBUG_PRINT("exit", ("ds: %s", ds->str)); - DBUG_VOID_RETURN; -} - - -void do_write_file_command(struct st_command *command, my_bool append) -{ - static DYNAMIC_STRING ds_content; - static DYNAMIC_STRING ds_filename; - static DYNAMIC_STRING ds_delimiter; - const struct command_arg write_file_args[] = { - { "filename", ARG_STRING, TRUE, &ds_filename, "File to write to" }, - { "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until" } - }; - DBUG_ENTER("do_write_file"); - - check_command_args(command, - command->first_argument, - write_file_args, - sizeof(write_file_args)/sizeof(struct command_arg), - ' '); - - /* If no delimiter was provided, use EOF */ - if (ds_delimiter.length == 0) - dynstr_set(&ds_delimiter, "EOF"); - - if (!append && access(ds_filename.str, F_OK) == 0) - { - /* The file should not be overwritten */ - die("File already exist: '%s'", ds_filename.str); - } - - init_dynamic_string(&ds_content, "", 1024, 1024); - read_until_delimiter(&ds_content, &ds_delimiter); - DBUG_PRINT("info", ("Writing to file: %s", ds_filename.str)); - str_to_file2(ds_filename.str, ds_content.str, ds_content.length, append); - dynstr_free(&ds_content); - dynstr_free(&ds_filename); - dynstr_free(&ds_delimiter); - DBUG_VOID_RETURN; -} - - -/* - SYNOPSIS - do_write_file - command called command - - DESCRIPTION - write_file <file_name> [<delimiter>]; - <what to write line 1> - <...> - < what to write line n> - EOF - - --write_file <file_name>; - <what to write line 1> - <...> - < what to write line n> - EOF - - Write everything between the "write_file" command and 'delimiter' - to "file_name" - - NOTE! Will fail if <file_name> exists - - Default <delimiter> is EOF - -*/ - -void do_write_file(struct st_command *command) -{ - do_write_file_command(command, FALSE); -} - - -/* - SYNOPSIS - do_append_file - command called command - - DESCRIPTION - append_file <file_name> [<delimiter>]; - <what to write line 1> - <...> - < what to write line n> - EOF - - --append_file <file_name>; - <what to write line 1> - <...> - < what to write line n> - EOF - - Append everything between the "append_file" command - and 'delimiter' to "file_name" - - Default <delimiter> is EOF - -*/ - -void do_append_file(struct st_command *command) -{ - do_write_file_command(command, TRUE); -} - - -/* - SYNOPSIS - do_cat_file - command called command - - DESCRIPTION - cat_file <file_name>; - - Print the given file to result log - -*/ - -void do_cat_file(struct st_command *command) -{ - static DYNAMIC_STRING ds_filename; - const struct command_arg cat_file_args[] = { - { "filename", ARG_STRING, TRUE, &ds_filename, "File to read from" } - }; - DBUG_ENTER("do_cat_file"); - - check_command_args(command, - command->first_argument, - cat_file_args, - sizeof(cat_file_args)/sizeof(struct command_arg), - ' '); - - DBUG_PRINT("info", ("Reading from, file: %s", ds_filename.str)); - - cat_file(&ds_res, ds_filename.str); - - dynstr_free(&ds_filename); - DBUG_VOID_RETURN; -} - - -/* - SYNOPSIS - do_diff_files - command called command - - DESCRIPTION - diff_files <file1> <file2>; - - Fails if the two files differ. - -*/ - -void do_diff_files(struct st_command *command) -{ - int error= 0; - static DYNAMIC_STRING ds_filename; - static DYNAMIC_STRING ds_filename2; - const struct command_arg diff_file_args[] = { - { "file1", ARG_STRING, TRUE, &ds_filename, "First file to diff" }, - { "file2", ARG_STRING, TRUE, &ds_filename2, "Second file to diff" } - }; - DBUG_ENTER("do_diff_files"); - - check_command_args(command, - command->first_argument, - diff_file_args, - sizeof(diff_file_args)/sizeof(struct command_arg), - ' '); - - if ((error= compare_files(ds_filename.str, ds_filename2.str))) - { - /* Compare of the two files failed, append them to output - so the failure can be analyzed - */ - show_diff(&ds_res, ds_filename.str, ds_filename2.str); - } - - dynstr_free(&ds_filename); - dynstr_free(&ds_filename2); - handle_command_error(command, error); - DBUG_VOID_RETURN; -} - - -struct st_connection * find_connection_by_name(const char *name) -{ - struct st_connection *con; - for (con= connections; con < next_con; con++) - { - if (!strcmp(con->name, name)) - { - return con; - } - } - return 0; /* Connection not found */ -} - - -/* - SYNOPSIS - do_send_quit - command called command - - DESCRIPTION - Sends a simple quit command to the server for the named connection. - -*/ - -void do_send_quit(struct st_command *command) -{ - char *p= command->first_argument, *name; - struct st_connection *con; - - DBUG_ENTER("do_send_quit"); - DBUG_PRINT("enter",("name: '%s'",p)); - - if (!*p) - die("Missing connection name in send_quit"); - name= p; - while (*p && !my_isspace(charset_info,*p)) - p++; - - if (*p) - *p++= 0; - command->last_argument= p; - - if (!(con= find_connection_by_name(name))) - die("connection '%s' not found in connection pool", name); - - simple_command(&con->mysql,COM_QUIT,0,0,1); - - DBUG_VOID_RETURN; -} - - -/* - SYNOPSIS - do_change_user - command called command - - DESCRIPTION - change_user [<user>], [<passwd>], [<db>] - <user> - user to change to - <passwd> - user password - <db> - default database - - Changes the user and causes the database specified by db to become - the default (current) database for the the current connection. - -*/ - -void do_change_user(struct st_command *command) -{ - MYSQL *mysql = &cur_con->mysql; - /* static keyword to make the NetWare compiler happy. */ - static DYNAMIC_STRING ds_user, ds_passwd, ds_db; - const struct command_arg change_user_args[] = { - { "user", ARG_STRING, FALSE, &ds_user, "User to connect as" }, - { "password", ARG_STRING, FALSE, &ds_passwd, "Password used when connecting" }, - { "database", ARG_STRING, FALSE, &ds_db, "Database to select after connect" }, - }; - - DBUG_ENTER("do_change_user"); - - check_command_args(command, command->first_argument, - change_user_args, - sizeof(change_user_args)/sizeof(struct command_arg), - ','); - - if (cur_con->stmt) - { - mysql_stmt_close(cur_con->stmt); - cur_con->stmt= NULL; - } - - if (!ds_user.length) - dynstr_set(&ds_user, mysql->user); - - if (!ds_passwd.length) - dynstr_set(&ds_passwd, mysql->passwd); - - if (!ds_db.length) - dynstr_set(&ds_db, mysql->db); - - DBUG_PRINT("info",("connection: '%s' user: '%s' password: '%s' database: '%s'", - cur_con->name, ds_user.str, ds_passwd.str, ds_db.str)); - - if (mysql_change_user(mysql, ds_user.str, ds_passwd.str, ds_db.str)) - die("change user failed: %s", mysql_error(mysql)); - - dynstr_free(&ds_user); - dynstr_free(&ds_passwd); - dynstr_free(&ds_db); - - DBUG_VOID_RETURN; -} - - -/* - SYNOPSIS - do_perl - command command handle - - DESCRIPTION - perl [<delimiter>]; - <perlscript line 1> - <...> - <perlscript line n> - EOF - - Execute everything after "perl" until <delimiter> as perl. - Useful for doing more advanced things - but still being able to execute it on all platforms. - - Default <delimiter> is EOF -*/ - -void do_perl(struct st_command *command) -{ - int error; - File fd; - FILE *res_file; - char buf[FN_REFLEN]; - char temp_file_path[FN_REFLEN]; - static DYNAMIC_STRING ds_script; - static DYNAMIC_STRING ds_delimiter; - const struct command_arg perl_args[] = { - { "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until" } - }; - DBUG_ENTER("do_perl"); - - check_command_args(command, - command->first_argument, - perl_args, - sizeof(perl_args)/sizeof(struct command_arg), - ' '); - - /* If no delimiter was provided, use EOF */ - if (ds_delimiter.length == 0) - dynstr_set(&ds_delimiter, "EOF"); - - init_dynamic_string(&ds_script, "", 1024, 1024); - read_until_delimiter(&ds_script, &ds_delimiter); - - DBUG_PRINT("info", ("Executing perl: %s", ds_script.str)); - - /* Create temporary file name */ - if ((fd= create_temp_file(temp_file_path, getenv("MYSQLTEST_VARDIR"), - "tmp", O_CREAT | O_SHARE | O_RDWR, - MYF(MY_WME))) < 0) - die("Failed to create temporary file for perl command"); - my_close(fd, MYF(0)); - - str_to_file(temp_file_path, ds_script.str, ds_script.length); - - /* 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); - - while (fgets(buf, sizeof(buf), res_file)) - { - if (disable_result_log) - { - buf[strlen(buf)-1]=0; - DBUG_PRINT("exec_result",("%s", buf)); - } - else - { - replace_dynstr_append(&ds_res, buf); - } - } - error= pclose(res_file); - - /* Remove the temporary file */ - my_delete(temp_file_path, MYF(0)); - - handle_command_error(command, WEXITSTATUS(error)); - dynstr_free(&ds_script); - dynstr_free(&ds_delimiter); - DBUG_VOID_RETURN; -} - - -/* - Print the content between echo and <delimiter> to result file. - Evaluate all variables in the string before printing, allow - for variable names to be escaped using \ - - SYNOPSIS - do_echo() - command called command - - DESCRIPTION - echo text - Print the text after echo until end of command to result file - - echo $<var_name> - Print the content of the variable <var_name> to result file - - echo Some text $<var_name> - Print "Some text" plus the content of the variable <var_name> to - result file - - echo Some text \$<var_name> - Print "Some text" plus $<var_name> to result file -*/ - -int do_echo(struct st_command *command) -{ - DYNAMIC_STRING ds_echo; - DBUG_ENTER("do_echo"); - - init_dynamic_string(&ds_echo, "", command->query_len, 256); - do_eval(&ds_echo, command->first_argument, command->end, FALSE); - dynstr_append_mem(&ds_res, ds_echo.str, ds_echo.length); - dynstr_append_mem(&ds_res, "\n", 1); - dynstr_free(&ds_echo); - command->last_argument= command->end; - DBUG_RETURN(0); -} - - -void do_wait_for_slave_to_stop(struct st_command *c __attribute__((unused))) -{ - static int SLAVE_POLL_INTERVAL= 300000; - MYSQL* mysql = &cur_con->mysql; - for (;;) - { - MYSQL_RES *res; - MYSQL_ROW row; - int done; - LINT_INIT(res); - - if (mysql_query(mysql,"show status like 'Slave_running'") || - !(res=mysql_store_result(mysql))) - die("Query failed while probing slave for stop: %s", - mysql_error(mysql)); - if (!(row=mysql_fetch_row(res)) || !row[1]) - { - mysql_free_result(res); - die("Strange result from query while probing slave for stop"); - } - done = !strcmp(row[1],"OFF"); - mysql_free_result(res); - if (done) - break; - my_sleep(SLAVE_POLL_INTERVAL); - } - return; -} - - -void do_sync_with_master2(long offset) -{ - MYSQL_RES *res; - MYSQL_ROW row; - MYSQL *mysql= &cur_con->mysql; - char query_buf[FN_REFLEN+128]; - int tries= 0; - int rpl_parse; - - if (!master_pos.file[0]) - die("Calling 'sync_with_master' without calling 'save_master_pos'"); - rpl_parse= mysql_rpl_parse_enabled(mysql); - mysql_disable_rpl_parse(mysql); - - sprintf(query_buf, "select master_pos_wait('%s', %ld)", master_pos.file, - master_pos.pos + offset); - -wait_for_position: - - if (mysql_query(mysql, query_buf)) - die("failed in '%s': %d: %s", query_buf, mysql_errno(mysql), - mysql_error(mysql)); - - if (!(res= mysql_store_result(mysql))) - die("mysql_store_result() returned NULL for '%s'", query_buf); - if (!(row= mysql_fetch_row(res))) - { - mysql_free_result(res); - die("empty result in %s", query_buf); - } - if (!row[0]) - { - /* - It may be that the slave SQL thread has not started yet, though START - SLAVE has been issued ? - */ - mysql_free_result(res); - if (tries++ == 30) - { - show_query(mysql, "SHOW MASTER STATUS"); - show_query(mysql, "SHOW SLAVE STATUS"); - die("could not sync with master ('%s' returned NULL)", query_buf); - } - sleep(1); /* So at most we will wait 30 seconds and make 31 tries */ - goto wait_for_position; - } - mysql_free_result(res); - if (rpl_parse) - mysql_enable_rpl_parse(mysql); - - return; -} - - -void do_sync_with_master(struct st_command *command) -{ - long offset= 0; - char *p= command->first_argument; - const char *offset_start= p; - if (*offset_start) - { - for (; my_isdigit(charset_info, *p); p++) - offset = offset * 10 + *p - '0'; - - if(*p && !my_isspace(charset_info, *p)) - die("Invalid integer argument \"%s\"", offset_start); - command->last_argument= p; - } - do_sync_with_master2(offset); - return; -} - - -/* - when ndb binlog is on, this call will wait until last updated epoch - (locally in the mysqld) has been received into the binlog -*/ -int do_save_master_pos() -{ - MYSQL_RES *res; - MYSQL_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 - done on the local mysql server - */ - { - ulong have_ndbcluster; - if (mysql_query(mysql, query= "show variables like 'have_ndbcluster'")) - die("'%s' failed: %d %s", query, - mysql_errno(mysql), mysql_error(mysql)); - if (!(res= mysql_store_result(mysql))) - die("mysql_store_result() returned NULL for '%s'", query); - if (!(row= mysql_fetch_row(res))) - die("Query '%s' returned empty result", query); - - have_ndbcluster= strcmp("YES", row[1]) == 0; - mysql_free_result(res); - - if (have_ndbcluster) - { - ulonglong start_epoch= 0, handled_epoch= 0, - latest_epoch=0, latest_trans_epoch=0, - latest_handled_binlog_epoch= 0, latest_received_binlog_epoch= 0, - latest_applied_binlog_epoch= 0; - int count= 0; - int do_continue= 1; - while (do_continue) - { - const char binlog[]= "binlog"; - const char latest_epoch_str[]= - "latest_epoch="; - const char latest_trans_epoch_str[]= - "latest_trans_epoch="; - const char latest_received_binlog_epoch_str[]= - "latest_received_binlog_epoch"; - const char latest_handled_binlog_epoch_str[]= - "latest_handled_binlog_epoch="; - const char latest_applied_binlog_epoch_str[]= - "latest_applied_binlog_epoch="; - if (count) - sleep(1); - if (mysql_query(mysql, query= "show engine ndb status")) - die("failed in '%s': %d %s", query, - mysql_errno(mysql), mysql_error(mysql)); - if (!(res= mysql_store_result(mysql))) - die("mysql_store_result() returned NULL for '%s'", query); - while ((row= mysql_fetch_row(res))) - { - if (strcmp(row[1], binlog) == 0) - { - const char *status= row[2]; - - /* latest_epoch */ - while (*status && strncmp(status, latest_epoch_str, - sizeof(latest_epoch_str)-1)) - status++; - if (*status) - { - status+= sizeof(latest_epoch_str)-1; - latest_epoch= strtoull(status, (char**) 0, 10); - } - else - die("result does not contain '%s' in '%s'", - latest_epoch_str, query); - /* latest_trans_epoch */ - while (*status && strncmp(status, latest_trans_epoch_str, - sizeof(latest_trans_epoch_str)-1)) - status++; - if (*status) - { - status+= sizeof(latest_trans_epoch_str)-1; - latest_trans_epoch= strtoull(status, (char**) 0, 10); - } - else - die("result does not contain '%s' in '%s'", - latest_trans_epoch_str, query); - /* latest_received_binlog_epoch */ - while (*status && - strncmp(status, latest_received_binlog_epoch_str, - sizeof(latest_received_binlog_epoch_str)-1)) - status++; - if (*status) - { - status+= sizeof(latest_received_binlog_epoch_str)-1; - latest_received_binlog_epoch= strtoull(status, (char**) 0, 10); - } - else - die("result does not contain '%s' in '%s'", - latest_received_binlog_epoch_str, query); - /* latest_handled_binlog */ - while (*status && - strncmp(status, latest_handled_binlog_epoch_str, - sizeof(latest_handled_binlog_epoch_str)-1)) - status++; - if (*status) - { - status+= sizeof(latest_handled_binlog_epoch_str)-1; - latest_handled_binlog_epoch= strtoull(status, (char**) 0, 10); - } - else - die("result does not contain '%s' in '%s'", - latest_handled_binlog_epoch_str, query); - /* latest_applied_binlog_epoch */ - while (*status && - strncmp(status, latest_applied_binlog_epoch_str, - sizeof(latest_applied_binlog_epoch_str)-1)) - status++; - if (*status) - { - status+= sizeof(latest_applied_binlog_epoch_str)-1; - latest_applied_binlog_epoch= strtoull(status, (char**) 0, 10); - } - else - die("result does not contain '%s' in '%s'", - latest_applied_binlog_epoch_str, query); - if (count == 0) - start_epoch= latest_trans_epoch; - break; - } - } - if (!row) - die("result does not contain '%s' in '%s'", - binlog, query); - if (latest_handled_binlog_epoch > handled_epoch) - count= 0; - handled_epoch= latest_handled_binlog_epoch; - count++; - if (latest_handled_binlog_epoch >= start_epoch) - do_continue= 0; - else if (count > 30) - { - break; - } - mysql_free_result(res); - } - } - } -#endif - if (mysql_query(mysql, query= "show master status")) - die("failed in 'show master status': %d %s", - mysql_errno(mysql), mysql_error(mysql)); - - if (!(res = mysql_store_result(mysql))) - die("mysql_store_result() retuned NULL for '%s'", query); - if (!(row = mysql_fetch_row(res))) - die("empty result in show master status"); - strnmov(master_pos.file, row[0], sizeof(master_pos.file)-1); - master_pos.pos = strtoul(row[1], (char**) 0, 10); - mysql_free_result(res); - - if (rpl_parse) - mysql_enable_rpl_parse(mysql); - - DBUG_RETURN(0); -} - - -/* - Assign the variable <var_name> with <var_val> - - SYNOPSIS - do_let() - query called command - - DESCRIPTION - let $<var_name>=<var_val><delimiter> - - <var_name> - is the string string found between the $ and = - <var_val> - is the content between the = and <delimiter>, it may span - multiple line and contain any characters except <delimiter> - <delimiter> - is a string containing of one or more chars, default is ; - - RETURN VALUES - Program will die if error detected -*/ - -void do_let(struct st_command *command) -{ - char *p= command->first_argument; - char *var_name, *var_name_end; - DYNAMIC_STRING let_rhs_expr; - DBUG_ENTER("do_let"); - - init_dynamic_string(&let_rhs_expr, "", 512, 2048); - - /* Find <var_name> */ - if (!*p) - die("Missing arguments to let"); - var_name= p; - while (*p && (*p != '=') && !my_isspace(charset_info,*p)) - p++; - var_name_end= p; - if (var_name == var_name_end || - (var_name+1 == var_name_end && *var_name == '$')) - die("Missing variable name in let"); - while (my_isspace(charset_info,*p)) - p++; - if (*p++ != '=') - die("Missing assignment operator in let"); - - /* Find start of <var_val> */ - while (*p && my_isspace(charset_info,*p)) - p++; - - do_eval(&let_rhs_expr, p, command->end, FALSE); - - command->last_argument= command->end; - /* Assign var_val to var_name */ - var_set(var_name, var_name_end, let_rhs_expr.str, - (let_rhs_expr.str + let_rhs_expr.length)); - dynstr_free(&let_rhs_expr); - DBUG_VOID_RETURN; -} - - -int do_rpl_probe(struct st_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 - - SYNOPSIS - do_sleep() - q called command - real_sleep use the value from opt_sleep as number of seconds to sleep - if real_sleep is false - - DESCRIPTION - sleep <seconds> - real_sleep <seconds> - - The difference between the sleep and real_sleep commands is that sleep - uses the delay from the --sleep command-line option if there is one. - (If the --sleep option is not given, the sleep command uses the delay - specified by its argument.) The real_sleep command always uses the - delay specified by its argument. The logic is that sometimes delays are - cpu-dependent, and --sleep can be used to set this delay. real_sleep is - used for cpu-independent delays. -*/ - -int do_sleep(struct st_command *command, my_bool real_sleep) -{ - int error= 0; - char *p= command->first_argument; - char *sleep_start, *sleep_end= command->end; - double sleep_val; - - while (my_isspace(charset_info, *p)) - p++; - if (!*p) - die("Missing argument to %.*s", command->first_word_len, command->query); - sleep_start= p; - /* Check that arg starts with a digit, not handled by my_strtod */ - if (!my_isdigit(charset_info, *sleep_start)) - die("Invalid argument to %.*s \"%s\"", command->first_word_len, - command->query,command->first_argument); - sleep_val= my_strtod(sleep_start, &sleep_end, &error); - if (error) - die("Invalid argument to %.*s \"%s\"", command->first_word_len, - command->query, command->first_argument); - - /* Fixed sleep time selected by --sleep option */ - if (opt_sleep >= 0 && !real_sleep) - sleep_val= opt_sleep; - - DBUG_PRINT("info", ("sleep_val: %f", sleep_val)); - if (sleep_val) - my_sleep((ulong) (sleep_val * 1000000L)); - command->last_argument= sleep_end; - return 0; -} - - -void do_get_file_name(struct st_command *command, - char* dest, uint dest_max_len) -{ - char *p= command->first_argument, *name; - if (!*p) - die("Missing file name argument"); - name= p; - while (*p && !my_isspace(charset_info,*p)) - p++; - if (*p) - *p++= 0; - command->last_argument= p; - strmake(dest, name, dest_max_len - 1); -} - - -void do_set_charset(struct st_command *command) -{ - char *charset_name= command->first_argument; - char *p; - - if (!charset_name || !*charset_name) - die("Missing charset name in 'character_set'"); - /* Remove end space */ - p= charset_name; - while (*p && !my_isspace(charset_info,*p)) - p++; - if(*p) - *p++= 0; - command->last_argument= p; - charset_info= get_charset_by_csname(charset_name,MY_CS_PRIMARY,MYF(MY_WME)); - if (!charset_info) - abort_not_supported_test("Test requires charset '%s'", charset_name); -} - - -/* - Run query and return one field in the result set from the - first row and <column> -*/ - -int query_get_string(MYSQL* mysql, const char* query, - int column, DYNAMIC_STRING* ds) -{ - MYSQL_RES *res= NULL; - MYSQL_ROW row; - - if (mysql_query(mysql, query)) - die("'%s' failed: %d %s", query, - mysql_errno(mysql), mysql_error(mysql)); - if ((res= mysql_store_result(mysql)) == NULL) - die("Failed to store result: %d %s", - mysql_errno(mysql), mysql_error(mysql)); - - 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); - mysql_free_result(res); - return 0; -} - - -static int my_kill(int pid, int sig) -{ -#ifdef __WIN__ - HANDLE proc; - if ((proc= OpenProcess(PROCESS_TERMINATE, FALSE, pid)) == NULL) - return -1; - if (sig == 0) - { - CloseHandle(proc); - return 0; - } - (void)TerminateProcess(proc, 201); - CloseHandle(proc); - return 1; -#else - return kill(pid, sig); -#endif -} - - - -/* - Shutdown the server of current connection and - make sure it goes away within <timeout> seconds - - NOTE! Currently only works with local server - - SYNOPSIS - do_shutdown_server() - command called command - - DESCRIPTION - shutdown [<timeout>] - -*/ - -void do_shutdown_server(struct st_command *command) -{ - int timeout=60, pid; - DYNAMIC_STRING ds_pidfile_name; - MYSQL* mysql = &cur_con->mysql; - static DYNAMIC_STRING ds_timeout; - const struct command_arg shutdown_args[] = { - {"timeout", ARG_STRING, FALSE, &ds_timeout, "Timeout before killing server"} - }; - DBUG_ENTER("do_shutdown_server"); - - check_command_args(command, command->first_argument, shutdown_args, - sizeof(shutdown_args)/sizeof(struct command_arg), - ' '); - - if (ds_timeout.length) - { - timeout= atoi(ds_timeout.str); - if (timeout == 0) - die("Illegal argument for timeout: '%s'", ds_timeout.str); - } - dynstr_free(&ds_timeout); - - /* Get the servers pid_file name and use it to read pid */ - if (query_get_string(mysql, "SHOW VARIABLES LIKE 'pid_file'", 1, - &ds_pidfile_name)) - die("Failed to get pid_file from server"); - - /* Read the pid from the file */ - { - int fd; - char buff[32]; - - if ((fd= my_open(ds_pidfile_name.str, O_RDONLY, MYF(0))) < 0) - die("Failed to open file '%s'", ds_pidfile_name.str); - dynstr_free(&ds_pidfile_name); - - if (my_read(fd, (uchar*)&buff, - sizeof(buff), MYF(0)) <= 0){ - my_close(fd, MYF(0)); - die("pid file was empty"); - } - my_close(fd, MYF(0)); - - pid= atoi(buff); - if (pid == 0) - die("Pidfile didn't contain a valid number"); - } - DBUG_PRINT("info", ("Got pid %d", pid)); - - /* Tell server to shutdown if timeout > 0*/ - if (timeout && mysql_shutdown(mysql, SHUTDOWN_DEFAULT)) - die("mysql_shutdown failed"); - - /* Check that server dies */ - while(timeout--){ - if (my_kill(0, pid) < 0){ - DBUG_PRINT("info", ("Sleeping, timeout: %d", timeout)); - break; - } - DBUG_PRINT("info", ("Sleeping, timeout: %d", timeout)); - my_sleep(1); - } - - /* Kill the server */ - DBUG_PRINT("info", ("Killing server, pid: %d", pid)); - (void)my_kill(9, pid); - - DBUG_VOID_RETURN; - -} - - -#if MYSQL_VERSION_ID >= 50000 -/* List of error names to error codes, available from 5.0 */ -typedef struct -{ - const char *name; - uint code; -} st_error; - -static st_error global_error_names[] = -{ -#include <mysqld_ername.h> - { 0, 0 } -}; - -uint get_errcode_from_name(char *error_name, char *error_end) -{ - /* SQL error as string */ - st_error *e= global_error_names; - - DBUG_ENTER("get_errcode_from_name"); - DBUG_PRINT("enter", ("error_name: %s", error_name)); - - /* Loop through the array of known error names */ - for (; e->name; e++) - { - /* - If we get a match, we need to check the length of the name we - matched against in case it was longer than what we are checking - (as in ER_WRONG_VALUE vs. ER_WRONG_VALUE_COUNT). - */ - if (!strncmp(error_name, e->name, (int) (error_end - error_name)) && - (uint) strlen(e->name) == (uint) (error_end - error_name)) - { - DBUG_RETURN(e->code); - } - } - if (!e->name) - die("Unknown SQL error name '%s'", error_name); - DBUG_RETURN(0); -} -#else -uint get_errcode_from_name(char *error_name __attribute__((unused)), - char *error_end __attribute__((unused))) -{ - abort_not_in_this_version(); - return 0; /* Never reached */ -} -#endif - - - -void do_get_errcodes(struct st_command *command) -{ - struct st_match_err *to= saved_expected_errors.err; - char *p= command->first_argument; - uint count= 0; - - DBUG_ENTER("do_get_errcodes"); - - if (!*p) - die("Missing argument(s) to 'error'"); - - do - { - char *end; - - /* Skip leading spaces */ - while (*p && *p == ' ') - p++; - - /* Find end */ - end= p; - while (*end && *end != ',' && *end != ' ') - end++; - - if (*p == 'S') - { - char *to_ptr= to->code.sqlstate; - - /* - SQLSTATE string - - Must be SQLSTATE_LENGTH long - - May contain only digits[0-9] and _uppercase_ letters - */ - p++; /* Step past the S */ - if ((end - p) != SQLSTATE_LENGTH) - die("The sqlstate must be exactly %d chars long", SQLSTATE_LENGTH); - - /* Check sqlstate string validity */ - while (*p && p < end) - { - if (my_isdigit(charset_info, *p) || my_isupper(charset_info, *p)) - *to_ptr++= *p++; - else - die("The sqlstate may only consist of digits[0-9] " \ - "and _uppercase_ letters"); - } - - *to_ptr= 0; - to->type= ERR_SQLSTATE; - DBUG_PRINT("info", ("ERR_SQLSTATE: %s", to->code.sqlstate)); - } - else if (*p == 's') - { - die("The sqlstate definition must start with an uppercase S"); - } - else if (*p == 'E') - { - /* Error name string */ - - DBUG_PRINT("info", ("Error name: %s", p)); - to->code.errnum= get_errcode_from_name(p, end); - to->type= ERR_ERRNO; - DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum)); - } - else if (*p == 'e') - { - die("The error name definition must start with an uppercase E"); - } - else - { - long val; - char *start= p; - /* Check that the string passed to str2int only contain digits */ - while (*p && p != end) - { - if (!my_isdigit(charset_info, *p)) - die("Invalid argument to error: '%s' - "\ - "the errno may only consist of digits[0-9]", - command->first_argument); - p++; - } - - /* Convert the sting to int */ - if (!str2int(start, 10, (long) INT_MIN, (long) INT_MAX, &val)) - die("Invalid argument to error: '%s'", command->first_argument); - - to->code.errnum= (uint) val; - to->type= ERR_ERRNO; - DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum)); - } - to++; - count++; - - if (count >= (sizeof(saved_expected_errors.err) / - sizeof(struct st_match_err))) - die("Too many errorcodes specified"); - - /* Set pointer to the end of the last error code */ - p= end; - - /* Find next ',' */ - while (*p && *p != ',') - p++; - - if (*p) - p++; /* Step past ',' */ - - } while (*p); - - command->last_argument= p; - to->type= ERR_EMPTY; /* End of data */ - - DBUG_PRINT("info", ("Expected errors: %d", count)); - saved_expected_errors.count= count; - DBUG_VOID_RETURN; -} - - -/* - Get a string; Return ptr to end of string - Strings may be surrounded by " or ' - - If string is a '$variable', return the value of the variable. -*/ - -char *get_string(char **to_ptr, char **from_ptr, - struct st_command *command) -{ - char c, sep; - char *to= *to_ptr, *from= *from_ptr, *start=to; - DBUG_ENTER("get_string"); - - /* Find separator */ - if (*from == '"' || *from == '\'') - sep= *from++; - else - sep=' '; /* Separated with space */ - - for ( ; (c=*from) ; from++) - { - if (c == '\\' && from[1]) - { /* Escaped character */ - /* We can't translate \0 -> ASCII 0 as replace can't handle ASCII 0 */ - switch (*++from) { - case 'n': - *to++= '\n'; - break; - case 't': - *to++= '\t'; - break; - case 'r': - *to++ = '\r'; - break; - case 'b': - *to++ = '\b'; - break; - case 'Z': /* ^Z must be escaped on Win32 */ - *to++='\032'; - break; - default: - *to++ = *from; - break; - } - } - else if (c == sep) - { - if (c == ' ' || c != *++from) - break; /* Found end of string */ - *to++=c; /* Copy duplicated separator */ - } - else - *to++=c; - } - if (*from != ' ' && *from) - die("Wrong string argument in %s", command->query); - - while (my_isspace(charset_info,*from)) /* Point to next string */ - from++; - - *to =0; /* End of string marker */ - *to_ptr= to+1; /* Store pointer to end */ - *from_ptr= from; - - /* Check if this was a variable */ - if (*start == '$') - { - const char *end= to; - VAR *var=var_get(start, &end, 0, 1); - if (var && to == (char*) end+1) - { - DBUG_PRINT("info",("var: '%s' -> '%s'", start, var->str_val)); - DBUG_RETURN(var->str_val); /* return found variable value */ - } - } - DBUG_RETURN(start); -} - - -void set_reconnect(MYSQL* mysql, int val) -{ - my_bool reconnect= val; - DBUG_ENTER("set_reconnect"); - DBUG_PRINT("info", ("val: %d", val)); -#if MYSQL_VERSION_ID < 50000 - mysql->reconnect= reconnect; -#else - mysql_options(mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect); -#endif - DBUG_VOID_RETURN; -} - - -int select_connection_name(const char *name) -{ - DBUG_ENTER("select_connection_name"); - DBUG_PRINT("enter",("name: '%s'", name)); - - if (!(cur_con= find_connection_by_name(name))) - die("connection '%s' not found in connection pool", name); - - /* Update $mysql_get_server_version to that of current connection */ - var_set_mysql_get_server_version(&cur_con->mysql); - - DBUG_RETURN(0); -} - - -int select_connection(struct st_command *command) -{ - char *name; - char *p= command->first_argument; - DBUG_ENTER("select_connection"); - - if (!*p) - die("Missing connection name in connect"); - name= p; - while (*p && !my_isspace(charset_info,*p)) - p++; - if (*p) - *p++= 0; - command->last_argument= p; - DBUG_RETURN(select_connection_name(name)); -} - - -void do_close_connection(struct st_command *command) -{ - char *p= command->first_argument, *name; - struct st_connection *con; - - DBUG_ENTER("close_connection"); - DBUG_PRINT("enter",("name: '%s'",p)); - - if (!*p) - die("Missing connection name in disconnect"); - name= p; - while (*p && !my_isspace(charset_info,*p)) - p++; - - if (*p) - *p++= 0; - command->last_argument= p; - - if (!(con= find_connection_by_name(name))) - die("connection '%s' not found in connection pool", name); - - DBUG_PRINT("info", ("Closing connection %s", con->name)); -#ifndef EMBEDDED_LIBRARY - if (command->type == Q_DIRTY_CLOSE) - { - if (con->mysql.net.vio) - { - vio_delete(con->mysql.net.vio); - con->mysql.net.vio = 0; - } - } -#else - /* - As query could be still executed in a separate theread - we need to check if the query's thread was finished and probably wait - (embedded-server specific) - */ - wait_query_thread_end(con); -#endif /*EMBEDDED_LIBRARY*/ - if (con->stmt) - mysql_stmt_close(con->stmt); - con->stmt= 0; - - mysql_close(&con->mysql); - - if (con->util_mysql) - mysql_close(con->util_mysql); - con->util_mysql= 0; - - my_free(con->name, MYF(0)); - - /* - When the connection is closed set name to "-closed_connection-" - to make it possible to reuse the connection name. - */ - if (!(con->name = my_strdup("-closed_connection-", MYF(MY_WME)))) - die("Out of memory"); - - DBUG_VOID_RETURN; -} - - -/* - Connect to a server doing several retries if needed. - - SYNOPSIS - safe_connect() - con - connection structure to be used - host, user, pass, - connection parameters - db, port, sock - - NOTE - - Sometimes in a test the client starts before - the server - to solve the problem, we try again - after some sleep if connection fails the first - time - - This function will try to connect to the given server - "opt_max_connect_retries" times and sleep "connection_retry_sleep" - seconds between attempts before finally giving up. - This helps in situation when the client starts - before the server (which happens sometimes). - It will only ignore connection errors during these retries. - -*/ - -void safe_connect(MYSQL* mysql, const char *name, const char *host, - const char *user, const char *pass, const char *db, - int port, const char *sock) -{ - int failed_attempts= 0; - static ulong connection_retry_sleep= 100000; /* Microseconds */ - - DBUG_ENTER("safe_connect"); - while(!mysql_real_connect(mysql, host,user, pass, db, port, sock, - CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS)) - { - /* - Connect failed - - Only allow retry if this was an error indicating the server - could not be contacted. Error code differs depending - on protocol/connection type - */ - - if ((mysql_errno(mysql) == CR_CONN_HOST_ERROR || - mysql_errno(mysql) == CR_CONNECTION_ERROR) && - failed_attempts < opt_max_connect_retries) - { - verbose_msg("Connect attempt %d/%d failed: %d: %s", failed_attempts, - opt_max_connect_retries, mysql_errno(mysql), - mysql_error(mysql)); - my_sleep(connection_retry_sleep); - } - else - { - if (failed_attempts > 0) - die("Could not open connection '%s' after %d attempts: %d %s", name, - failed_attempts, mysql_errno(mysql), mysql_error(mysql)); - else - die("Could not open connection '%s': %d %s", name, - mysql_errno(mysql), mysql_error(mysql)); - } - failed_attempts++; - } - DBUG_VOID_RETURN; -} - - -/* - Connect to a server and handle connection errors in case they occur. - - SYNOPSIS - connect_n_handle_errors() - q - context of connect "query" (command) - con - connection structure to be used - host, user, pass, - connection parameters - db, port, sock - - DESCRIPTION - This function will try to establish a connection to server and handle - possible errors in the same manner as if "connect" was usual SQL-statement - (If error is expected it will ignore it once it occurs and log the - "statement" to the query log). - Unlike safe_connect() it won't do several attempts. - - RETURN VALUES - 1 - Connected - 0 - Not connected - -*/ - -int connect_n_handle_errors(struct st_command *command, - MYSQL* con, const char* host, - const char* user, const char* pass, - const char* db, int port, const char* sock) -{ - DYNAMIC_STRING *ds; - - ds= &ds_res; - - /* Only log if an error is expected */ - if (!command->abort_on_error && - !disable_query_log) - { - /* - Log the connect to result log - */ - dynstr_append_mem(ds, "connect(", 8); - replace_dynstr_append(ds, host); - dynstr_append_mem(ds, ",", 1); - replace_dynstr_append(ds, user); - dynstr_append_mem(ds, ",", 1); - replace_dynstr_append(ds, pass); - dynstr_append_mem(ds, ",", 1); - if (db) - replace_dynstr_append(ds, db); - dynstr_append_mem(ds, ",", 1); - replace_dynstr_append_uint(ds, port); - dynstr_append_mem(ds, ",", 1); - if (sock) - replace_dynstr_append(ds, sock); - dynstr_append_mem(ds, ")", 1); - dynstr_append_mem(ds, delimiter, delimiter_length); - dynstr_append_mem(ds, "\n", 1); - } - if (!mysql_real_connect(con, host, user, pass, db, port, sock ? sock: 0, - CLIENT_MULTI_STATEMENTS)) - { - var_set_errno(mysql_errno(con)); - handle_error(command, mysql_errno(con), mysql_error(con), - mysql_sqlstate(con), ds); - return 0; /* Not connected */ - } - - var_set_errno(0); - handle_no_error(command); - return 1; /* Connected */ -} - - -/* - Open a new connection to MySQL Server with the parameters - specified. Make the new connection the current connection. - - SYNOPSIS - do_connect() - q called command - - DESCRIPTION - connect(<name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]]); - connect <name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]]; - - <name> - name of the new connection - <host> - hostname of server - <user> - user to connect as - <pass> - password used when connecting - <db> - initial db when connected - <port> - server port - <sock> - server socket - <opts> - options to use for the connection - * SSL - use SSL if available - * COMPRESS - use compression if available - -*/ - -void do_connect(struct st_command *command) -{ - int con_port= opt_port; - char *con_options; - my_bool con_ssl= 0, con_compress= 0; - struct st_connection* con_slot; - - static DYNAMIC_STRING ds_connection_name; - static DYNAMIC_STRING ds_host; - static DYNAMIC_STRING ds_user; - static DYNAMIC_STRING ds_password; - static DYNAMIC_STRING ds_database; - static DYNAMIC_STRING ds_port; - static DYNAMIC_STRING ds_sock; - static DYNAMIC_STRING ds_options; - const struct command_arg connect_args[] = { - { "connection name", ARG_STRING, TRUE, &ds_connection_name, "Name of the connection" }, - { "host", ARG_STRING, TRUE, &ds_host, "Host to connect to" }, - { "user", ARG_STRING, FALSE, &ds_user, "User to connect as" }, - { "passsword", ARG_STRING, FALSE, &ds_password, "Password used when connecting" }, - { "database", ARG_STRING, FALSE, &ds_database, "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" } - }; - - DBUG_ENTER("do_connect"); - DBUG_PRINT("enter",("connect: %s", command->first_argument)); - - strip_parentheses(command); - check_command_args(command, command->first_argument, connect_args, - sizeof(connect_args)/sizeof(struct command_arg), - ','); - - /* Port */ - if (ds_port.length) - { - con_port= atoi(ds_port.str); - if (con_port == 0) - die("Illegal argument for port: '%s'", ds_port.str); - } - - /* Sock */ - if (ds_sock.length) - { - /* - If the socket is specified just as a name without path - append tmpdir in front - */ - if (*ds_sock.str != FN_LIBCHAR) - { - char buff[FN_REFLEN]; - fn_format(buff, ds_sock.str, TMPDIR, "", 0); - dynstr_set(&ds_sock, buff); - } - } - else - { - /* No socket specified, use default */ - dynstr_set(&ds_sock, unix_sock); - } - DBUG_PRINT("info", ("socket: %s", ds_sock.str)); - - - /* Options */ - con_options= ds_options.str; - while (*con_options) - { - char* end; - /* Step past any spaces in beginning of option*/ - while (*con_options && my_isspace(charset_info, *con_options)) - con_options++; - /* Find end of this option */ - end= con_options; - while (*end && !my_isspace(charset_info, *end)) - end++; - if (!strncmp(con_options, "SSL", 3)) - con_ssl= 1; - else if (!strncmp(con_options, "COMPRESS", 8)) - con_compress= 1; - else - die("Illegal option to connect: %.*s", - (int) (end - con_options), con_options); - /* Process next option */ - con_options= end; - } - - if (find_connection_by_name(ds_connection_name.str)) - die("Connection %s already exists", ds_connection_name.str); - - if (next_con != connections_end) - con_slot= next_con; - else - { - if (!(con_slot= find_connection_by_name("-closed_connection-"))) - die("Connection limit exhausted, you can have max %d connections", - (int) (sizeof(connections)/sizeof(struct st_connection))); - } - -#ifdef EMBEDDED_LIBRARY - con_slot->query_done= 1; -#endif - if (!mysql_init(&con_slot->mysql)) - die("Failed on mysql_init()"); - if (opt_compress || con_compress) - mysql_options(&con_slot->mysql, MYSQL_OPT_COMPRESS, NullS); - mysql_options(&con_slot->mysql, MYSQL_OPT_LOCAL_INFILE, 0); - mysql_options(&con_slot->mysql, MYSQL_SET_CHARSET_NAME, - charset_info->csname); - if (opt_charsets_dir) - mysql_options(&con_slot->mysql, MYSQL_SET_CHARSET_DIR, - opt_charsets_dir); - -#ifdef HAVE_OPENSSL - if (opt_use_ssl || con_ssl) - { - 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 - /* Turn on ssl_verify_server_cert only if host is "localhost" */ - opt_ssl_verify_server_cert= !strcmp(ds_host.str, "localhost"); - mysql_options(&con_slot->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, - &opt_ssl_verify_server_cert); -#endif - } -#endif - - /* Use default db name */ - if (ds_database.length == 0) - dynstr_set(&ds_database, opt_db); - - /* Special database to allow one to connect without a database name */ - if (ds_database.length && !strcmp(ds_database.str,"*NO-ONE*")) - dynstr_set(&ds_database, ""); - - if (connect_n_handle_errors(command, &con_slot->mysql, - ds_host.str,ds_user.str, - ds_password.str, ds_database.str, - con_port, ds_sock.str)) - { - DBUG_PRINT("info", ("Inserting connection %s in connection pool", - ds_connection_name.str)); - if (!(con_slot->name= my_strdup(ds_connection_name.str, MYF(MY_WME)))) - die("Out of memory"); - con_slot->name_len= strlen(con_slot->name); - cur_con= con_slot; - - if (con_slot == next_con) - next_con++; /* if we used the next_con slot, advance the pointer */ - } - - /* Update $mysql_get_server_version to that of current connection */ - var_set_mysql_get_server_version(&cur_con->mysql); - - dynstr_free(&ds_connection_name); - dynstr_free(&ds_host); - dynstr_free(&ds_user); - dynstr_free(&ds_password); - dynstr_free(&ds_database); - dynstr_free(&ds_port); - dynstr_free(&ds_sock); - dynstr_free(&ds_options); - DBUG_VOID_RETURN; -} - - -int do_done(struct st_command *command) -{ - /* Check if empty block stack */ - if (cur_block == block_stack) - { - if (*command->query != '}') - die("Stray 'end' command - end of block before beginning"); - die("Stray '}' - end of block before beginning"); - } - - /* Test if inner block has been executed */ - if (cur_block->ok && cur_block->cmd == cmd_while) - { - /* Pop block from stack, re-execute outer block */ - cur_block--; - parser.current_line = cur_block->line; - } - else - { - /* Pop block from stack, goto next line */ - cur_block--; - parser.current_line++; - } - return 0; -} - - -/* - Process start of a "if" or "while" statement - - SYNOPSIS - do_block() - cmd Type of block - q called command - - DESCRIPTION - if ([!]<expr>) - { - <block statements> - } - - while ([!]<expr>) - { - <block statements> - } - - Evaluates the <expr> and if it evaluates to - greater than zero executes the following code block. - A '!' can be used before the <expr> to indicate it should - be executed if it evaluates to zero. - -*/ - -void do_block(enum block_cmd cmd, struct st_command* command) -{ - char *p= command->first_argument; - const char *expr_start, *expr_end; - VAR v; - const char *cmd_name= (cmd == cmd_while ? "while" : "if"); - my_bool not_expr= FALSE; - DBUG_ENTER("do_block"); - DBUG_PRINT("enter", ("%s", cmd_name)); - - /* Check stack overflow */ - if (cur_block == block_stack_end) - die("Nesting too deeply"); - - /* Set way to find outer block again, increase line counter */ - cur_block->line= parser.current_line++; - - /* If this block is ignored */ - if (!cur_block->ok) - { - /* Inner block should be ignored too */ - cur_block++; - cur_block->cmd= cmd; - cur_block->ok= FALSE; - DBUG_VOID_RETURN; - } - - /* Parse and evaluate test expression */ - expr_start= strchr(p, '('); - if (!expr_start++) - die("missing '(' in %s", cmd_name); - - /* Check for !<expr> */ - if (*expr_start == '!') - { - not_expr= TRUE; - expr_start++; /* Step past the '!' */ - } - /* Find ending ')' */ - expr_end= strrchr(expr_start, ')'); - if (!expr_end) - die("missing ')' in %s", cmd_name); - p= (char*)expr_end+1; - - while (*p && my_isspace(charset_info, *p)) - p++; - if (*p && *p != '{') - die("Missing '{' after %s. Found \"%s\"", cmd_name, p); - - var_init(&v,0,0,0,0); - eval_expr(&v, expr_start, &expr_end); - - /* Define inner block */ - cur_block++; - cur_block->cmd= cmd; - cur_block->ok= (v.int_val ? TRUE : FALSE); - - if (not_expr) - cur_block->ok = !cur_block->ok; - - DBUG_PRINT("info", ("OK: %d", cur_block->ok)); - - var_free(&v); - DBUG_VOID_RETURN; -} - - -void do_delimiter(struct st_command* command) -{ - char* p= command->first_argument; - DBUG_ENTER("do_delimiter"); - DBUG_PRINT("enter", ("first_argument: %s", command->first_argument)); - - while (*p && my_isspace(charset_info, *p)) - p++; - - if (!(*p)) - die("Can't set empty delimiter"); - - strmake(delimiter, p, sizeof(delimiter) - 1); - delimiter_length= strlen(delimiter); - - DBUG_PRINT("exit", ("delimiter: %s", delimiter)); - command->last_argument= p + delimiter_length; - DBUG_VOID_RETURN; -} - - -my_bool match_delimiter(int c, const char *delim, uint length) -{ - uint i; - char tmp[MAX_DELIMITER_LENGTH]; - - if (c != *delim) - return 0; - - for (i= 1; i < length && - (c= my_getc(cur_file->file)) == *(delim + i); - i++) - tmp[i]= c; - - if (i == length) - return 1; /* Found delimiter */ - - /* didn't find delimiter, push back things that we read */ - my_ungetc(c); - while (i > 1) - my_ungetc(tmp[--i]); - return 0; -} - - -my_bool end_of_query(int c) -{ - return match_delimiter(c, delimiter, delimiter_length); -} - - -/* - Read one "line" from the file - - SYNOPSIS - read_line - buf buffer for the read line - size size of the buffer i.e max size to read - - DESCRIPTION - This function actually reads several lines and adds them to the - buffer buf. It continues to read until it finds what it believes - is a complete query. - - Normally that means it will read lines until it reaches the - "delimiter" that marks end of query. Default delimiter is ';' - The function should be smart enough not to detect delimiter's - found inside strings surrounded with '"' and '\'' escaped strings. - - If the first line in a query starts with '#' or '-' this line is treated - as a comment. A comment is always terminated when end of line '\n' is - reached. - -*/ - -int read_line(char *buf, int size) -{ - char c, last_quote; - char *p= buf, *buf_end= buf + size - 1; - int skip_char= 0; - enum {R_NORMAL, R_Q, R_SLASH_IN_Q, - R_COMMENT, R_LINE_START} state= R_LINE_START; - DBUG_ENTER("read_line"); - LINT_INIT(last_quote); - - start_lineno= cur_file->lineno; - DBUG_PRINT("info", ("Starting to read at lineno: %d", start_lineno)); - for (; p < buf_end ;) - { - skip_char= 0; - c= my_getc(cur_file->file); - if (feof(cur_file->file)) - { - found_eof: - if (cur_file->file != stdin) - { - my_fclose(cur_file->file, MYF(0)); - cur_file->file= 0; - } - my_free((uchar*) cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR)); - cur_file->file_name= 0; - if (cur_file == file_stack) - { - /* We're back at the first file, check if - all { have matching } - */ - if (cur_block != block_stack) - die("Missing end of block"); - - *p= 0; - DBUG_PRINT("info", ("end of file at line %d", cur_file->lineno)); - DBUG_RETURN(1); - } - cur_file--; - start_lineno= cur_file->lineno; - continue; - } - - if (c == '\n') - { - /* Line counting is independent of state */ - cur_file->lineno++; - - /* Convert cr/lf to lf */ - if (p != buf && *(p-1) == '\r') - p--; - } - - switch(state) { - case R_NORMAL: - if (end_of_query(c)) - { - *p= 0; - DBUG_PRINT("exit", ("Found delimiter '%s' at line %d", - delimiter, cur_file->lineno)); - DBUG_RETURN(0); - } - else if ((c == '{' && - (!my_strnncoll_simple(charset_info, (const uchar*) "while", 5, - (uchar*) buf, min(5, p - buf), 0) || - !my_strnncoll_simple(charset_info, (const uchar*) "if", 2, - (uchar*) buf, min(2, p - buf), 0)))) - { - /* Only if and while commands can be terminated by { */ - *p++= c; - *p= 0; - DBUG_PRINT("exit", ("Found '{' indicating start of block at line %d", - cur_file->lineno)); - DBUG_RETURN(0); - } - else if (c == '\'' || c == '"' || c == '`') - { - last_quote= c; - state= R_Q; - } - break; - - case R_COMMENT: - if (c == '\n') - { - /* Comments are terminated by newline */ - *p= 0; - DBUG_PRINT("exit", ("Found newline in comment at line: %d", - cur_file->lineno)); - DBUG_RETURN(0); - } - break; - - case R_LINE_START: - if (c == '#' || c == '-') - { - /* A # or - in the first position of the line - this is a comment */ - state = R_COMMENT; - } - else if (my_isspace(charset_info, c)) - { - /* Skip all space at begining of line */ - if (c == '\n') - { - /* Query hasn't started yet */ - start_lineno= cur_file->lineno; - DBUG_PRINT("info", ("Query hasn't started yet, start_lineno: %d", - start_lineno)); - } - skip_char= 1; - } - else if (end_of_query(c)) - { - *p= 0; - DBUG_PRINT("exit", ("Found delimiter '%s' at line: %d", - delimiter, cur_file->lineno)); - DBUG_RETURN(0); - } - else if (c == '}') - { - /* A "}" need to be by itself in the begining of a line to terminate */ - *p++= c; - *p= 0; - DBUG_PRINT("exit", ("Found '}' in begining of a line at line: %d", - cur_file->lineno)); - DBUG_RETURN(0); - } - else if (c == '\'' || c == '"' || c == '`') - { - last_quote= c; - state= R_Q; - } - else - state= R_NORMAL; - break; - - case R_Q: - if (c == last_quote) - state= R_NORMAL; - else if (c == '\\') - state= R_SLASH_IN_Q; - break; - - case R_SLASH_IN_Q: - state= R_Q; - break; - - } - - if (!skip_char) - { - /* Could be a multibyte character */ - /* This code is based on the code in "sql_load.cc" */ -#ifdef USE_MB - int charlen = my_mbcharlen(charset_info, c); - /* We give up if multibyte character is started but not */ - /* completed before we pass buf_end */ - if ((charlen > 1) && (p + charlen) <= buf_end) - { - int i; - char* mb_start = p; - - *p++ = c; - - for (i= 1; i < charlen; i++) - { - if (feof(cur_file->file)) - goto found_eof; - c= my_getc(cur_file->file); - *p++ = c; - } - if (! my_ismbchar(charset_info, mb_start, p)) - { - /* It was not a multiline char, push back the characters */ - /* We leave first 'c', i.e. pretend it was a normal char */ - while (p > mb_start) - my_ungetc(*--p); - } - } - else -#endif - *p++= c; - } - } - die("The input buffer is too small for this query.x\n" \ - "check your query or increase MAX_QUERY and recompile"); - DBUG_RETURN(0); -} - - -/* - Convert the read query to result format version 1 - - That is: After newline, all spaces need to be skipped - unless the previous char was a quote - - This is due to an old bug that has now been fixed, but the - version 1 output format is preserved by using this function - -*/ - -void convert_to_format_v1(char* query) -{ - int last_c_was_quote= 0; - char *p= query, *to= query; - char *end= strend(query); - char last_c; - - while (p <= end) - { - if (*p == '\n' && !last_c_was_quote) - { - *to++ = *p++; /* Save the newline */ - - /* Skip any spaces on next line */ - while (*p && my_isspace(charset_info, *p)) - p++; - - last_c_was_quote= 0; - } - else if (*p == '\'' || *p == '"' || *p == '`') - { - last_c= *p; - *to++ = *p++; - - /* Copy anything until the next quote of same type */ - while (*p && *p != last_c) - *to++ = *p++; - - *to++ = *p++; - - last_c_was_quote= 1; - } - else - { - *to++ = *p++; - last_c_was_quote= 0; - } - } -} - - -/* - Check a command that is about to be sent (or should have been - sent if parsing was enabled) to mysql server for - suspicious things and generate warnings. -*/ - -void scan_command_for_warnings(struct st_command *command) -{ - const char *ptr= command->query; - DBUG_ENTER("scan_command_for_warnings"); - DBUG_PRINT("enter", ("query: %s", command->query)); - - while(*ptr) - { - /* - Look for query's that lines that start with a -- comment - and has a mysqltest command - */ - if (ptr[0] == '\n' && - ptr[1] && ptr[1] == '-' && - ptr[2] && ptr[2] == '-' && - ptr[3]) - { - uint type; - char save; - char *end, *start= (char*)ptr+3; - /* Skip leading spaces */ - while (*start && my_isspace(charset_info, *start)) - start++; - end= start; - /* Find end of command(next space) */ - while (*end && !my_isspace(charset_info, *end)) - end++; - save= *end; - *end= 0; - DBUG_PRINT("info", ("Checking '%s'", start)); - type= find_type(start, &command_typelib, 1+2); - if (type) - warning_msg("Embedded mysqltest command '--%s' detected in " - "query '%s' was this intentional? ", - start, command->query); - *end= save; - } - - ptr++; - } - DBUG_VOID_RETURN; -} - -/* - Check for unexpected "junk" after the end of query - This is normally caused by missing delimiters or when - switching between different delimiters -*/ - -void check_eol_junk_line(const char *line) -{ - const char *p= line; - DBUG_ENTER("check_eol_junk_line"); - DBUG_PRINT("enter", ("line: %s", line)); - - /* Check for extra delimiter */ - if (*p && !strncmp(p, delimiter, delimiter_length)) - die("Extra delimiter \"%s\" found", delimiter); - - /* Allow trailing # comment */ - if (*p && *p != '#') - { - if (*p == '\n') - die("Missing delimiter"); - die("End of line junk detected: \"%s\"", p); - } - DBUG_VOID_RETURN; -} - -void check_eol_junk(const char *eol) -{ - const char *p= eol; - DBUG_ENTER("check_eol_junk"); - DBUG_PRINT("enter", ("eol: %s", eol)); - - /* Skip past all spacing chars and comments */ - while (*p && (my_isspace(charset_info, *p) || *p == '#' || *p == '\n')) - { - /* Skip past comments started with # and ended with newline */ - if (*p && *p == '#') - { - p++; - while (*p && *p != '\n') - p++; - } - - /* Check this line */ - if (*p && *p == '\n') - check_eol_junk_line(p); - - if (*p) - p++; - } - - check_eol_junk_line(p); - - DBUG_VOID_RETURN; -} - - - -/* - Create a command from a set of lines - - SYNOPSIS - read_command() - command_ptr pointer where to return the new query - - DESCRIPTION - Converts lines returned by read_line into a command, this involves - parsing the first word in the read line to find the command type. - - A -- comment may contain a valid query as the first word after the - comment start. Thus it's always checked to see if that is the case. - The advantage with this approach is to be able to execute commands - terminated by new line '\n' regardless how many "delimiter" it contain. -*/ - -#define MAX_QUERY (256*1024*2) /* 256K -- a test in sp-big is >128K */ -static char read_command_buf[MAX_QUERY]; - -int read_command(struct st_command** command_ptr) -{ - char *p= read_command_buf; - struct st_command* command; - DBUG_ENTER("read_command"); - - if (parser.current_line < parser.read_lines) - { - get_dynamic(&q_lines, (uchar*) command_ptr, parser.current_line) ; - DBUG_RETURN(0); - } - if (!(*command_ptr= command= - (struct st_command*) my_malloc(sizeof(*command), - MYF(MY_WME|MY_ZEROFILL))) || - insert_dynamic(&q_lines, (uchar*) &command)) - die(NullS); - command->type= Q_UNKNOWN; - - read_command_buf[0]= 0; - if (read_line(read_command_buf, sizeof(read_command_buf))) - { - check_eol_junk(read_command_buf); - DBUG_RETURN(1); - } - - convert_to_format_v1(read_command_buf); - - DBUG_PRINT("info", ("query: %s", read_command_buf)); - if (*p == '#') - { - command->type= Q_COMMENT; - } - else if (p[0] == '-' && p[1] == '-') - { - command->type= Q_COMMENT_WITH_COMMAND; - p+= 2; /* Skip past -- */ - } - - /* Skip leading spaces */ - while (*p && my_isspace(charset_info, *p)) - p++; - - if (!(command->query_buf= command->query= my_strdup(p, MYF(MY_WME)))) - die("Out of memory"); - - /* Calculate first word length(the command), terminated by space or ( */ - p= command->query; - while (*p && !my_isspace(charset_info, *p) && *p != '(') - p++; - command->first_word_len= (uint) (p - command->query); - DBUG_PRINT("info", ("first_word: %.*s", - command->first_word_len, command->query)); - - /* Skip spaces between command and first argument */ - while (*p && my_isspace(charset_info, *p)) - p++; - command->first_argument= p; - - command->end= strend(command->query); - command->query_len= (command->end - command->query); - parser.read_lines++; - DBUG_RETURN(0); -} - - -static struct my_option my_long_options[] = -{ - {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, - 0, 0, 0, 0, 0, 0}, - {"basedir", 'b', "Basedir for tests.", (uchar**) &opt_basedir, - (uchar**) &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"character-sets-dir", OPT_CHARSETS_DIR, - "Directory where character sets are.", (uchar**) &opt_charsets_dir, - (uchar**) &opt_charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"compress", 'C', "Use the compressed server/client protocol.", - (uchar**) &opt_compress, (uchar**) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0, - 0, 0, 0}, - {"cursor-protocol", OPT_CURSOR_PROTOCOL, "Use cursors for prepared statements.", - (uchar**) &cursor_protocol, (uchar**) &cursor_protocol, 0, - GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"database", 'D', "Database to use.", (uchar**) &opt_db, (uchar**) &opt_db, 0, - GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, -#ifdef DBUG_OFF - {"debug", '#', "This is a non-debug version. Catch this and exit", - 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, -#else - {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.", - 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, -#endif - {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.", - (uchar**) &debug_check_flag, (uchar**) &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.", - (uchar**) &debug_info_flag, (uchar**) &debug_info_flag, - 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"host", 'h', "Connect to host.", (uchar**) &opt_host, (uchar**) &opt_host, 0, - GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"include", 'i', "Include SQL before each test case.", (uchar**) &opt_include, - (uchar**) &opt_include, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"logdir", OPT_LOG_DIR, "Directory for log files", (uchar**) &opt_logdir, - (uchar**) &opt_logdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"mark-progress", OPT_MARK_PROGRESS, - "Write linenumber and elapsed time to <testname>.progress ", - (uchar**) &opt_mark_progress, (uchar**) &opt_mark_progress, 0, - GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"max-connect-retries", OPT_MAX_CONNECT_RETRIES, - "Max number of connection attempts when connecting to server", - (uchar**) &opt_max_connect_retries, (uchar**) &opt_max_connect_retries, 0, - GET_INT, REQUIRED_ARG, 500, 1, 10000, 0, 0, 0}, - {"password", 'p', "Password to use when connecting to server.", - 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, - {"port", 'P', "Port number to use for connection or 0 for default to, in " - "order of preference, my.cnf, $MYSQL_TCP_PORT, " -#if MYSQL_PORT_DEFAULT == 0 - "/etc/services, " -#endif - "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").", - (uchar**) &opt_port, - (uchar**) &opt_port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"ps-protocol", OPT_PS_PROTOCOL, "Use prepared statements protocol for communication", - (uchar**) &ps_protocol, (uchar**) &ps_protocol, 0, - GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"quiet", 's', "Suppress all normal output.", (uchar**) &silent, - (uchar**) &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"record", 'r', "Record output of test_file into result file.", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"result-file", 'R', "Read/Store result from/in this file.", - (uchar**) &result_file_name, (uchar**) &result_file_name, 0, - GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"server-arg", 'A', "Send option value to embedded server as a parameter.", - 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"server-file", 'F', "Read embedded server arguments from file.", - 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"silent", 's', "Suppress all normal output. Synonym for --quiet.", - (uchar**) &silent, (uchar**) &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', "Sleep always this many seconds on sleep commands.", - (uchar**) &opt_sleep, (uchar**) &opt_sleep, 0, GET_INT, REQUIRED_ARG, -1, -1, 0, - 0, 0, 0}, - {"socket", 'S', "Socket file to use for connection.", - (uchar**) &unix_sock, (uchar**) &unix_sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, - 0, 0, 0}, - {"sp-protocol", OPT_SP_PROTOCOL, "Use stored procedures for select", - (uchar**) &sp_protocol, (uchar**) &sp_protocol, 0, - GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"tail-lines", OPT_TAIL_LINES, - "Number of lines of the resul to include in a failure report", - (uchar**) &opt_tail_lines, (uchar**) &opt_tail_lines, 0, - GET_INT, REQUIRED_ARG, 0, 0, 10000, 0, 0, 0}, -#include "sslopt-longopts.h" - {"test-file", 'x', "Read test from/in this file (default stdin).", - 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"timer-file", 'm', "File where the timing in micro seconds is stored.", - 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"tmpdir", 't', "Temporary directory where sockets are put.", - 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"user", 'u', "User for login.", (uchar**) &opt_user, (uchar**) &opt_user, 0, - GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"verbose", 'v', "Write more.", (uchar**) &verbose, (uchar**) &verbose, 0, - 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", - (uchar**) &view_protocol, (uchar**) &view_protocol, 0, - GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} -}; - - -#include <help_start.h> - -void print_version(void) -{ - printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname,MTEST_VERSION, - MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE); -} - -void usage() -{ - print_version(); - printf("MySQL AB, by Sasha, Matt, Monty & Jani\n"); - printf("This software comes with ABSOLUTELY NO WARRANTY\n\n"); - printf("Runs a test against the mysql server and compares output with a results file.\n\n"); - printf("Usage: %s [OPTIONS] [database] < test_file\n", my_progname); - my_print_help(my_long_options); - printf(" --no-defaults Don't read default options from any options file.\n"); - my_print_variables(my_long_options); -} - -#include <help_end.h> - - -/* - Read arguments for embedded server and put them into - embedded_server_args[] -*/ - -void read_embedded_server_arguments(const char *name) -{ - char argument[1024],buff[FN_REFLEN], *str=0; - FILE *file; - - if (!test_if_hard_path(name)) - { - strxmov(buff, opt_basedir, name, NullS); - name=buff; - } - fn_format(buff, name, "", "", MY_UNPACK_FILENAME); - - if (!embedded_server_arg_count) - { - embedded_server_arg_count=1; - embedded_server_args[0]= (char*) ""; /* Progname */ - } - if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME)))) - die("Failed to open file '%s'", buff); - - while (embedded_server_arg_count < MAX_EMBEDDED_SERVER_ARGS && - (str=fgets(argument,sizeof(argument), file))) - { - *(strend(str)-1)=0; /* Remove end newline */ - if (!(embedded_server_args[embedded_server_arg_count]= - (char*) my_strdup(str,MYF(MY_WME)))) - { - my_fclose(file,MYF(0)); - die("Out of memory"); - - } - embedded_server_arg_count++; - } - my_fclose(file,MYF(0)); - if (str) - die("Too many arguments in option file: %s",name); - - return; -} - - -static my_bool -get_one_option(int optid, const struct my_option *opt __attribute__((unused)), - char *argument) -{ - switch(optid) { - case '#': -#ifndef DBUG_OFF - DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysqltest.trace"); - debug_check_flag= 1; -#endif - break; - case 'r': - record = 1; - break; - case 'x': - { - char buff[FN_REFLEN]; - if (!test_if_hard_path(argument)) - { - strxmov(buff, opt_basedir, argument, NullS); - argument= buff; - } - fn_format(buff, argument, "", "", MY_UNPACK_FILENAME); - DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0); - if (!(cur_file->file= - my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0)))) - die("Could not open '%s' for reading, errno: %d", buff, errno); - cur_file->file_name= my_strdup(buff, MYF(MY_FAE)); - cur_file->lineno= 1; - break; - } - case 'm': - { - static char buff[FN_REFLEN]; - if (!test_if_hard_path(argument)) - { - strxmov(buff, opt_basedir, argument, NullS); - argument= buff; - } - fn_format(buff, argument, "", "", MY_UNPACK_FILENAME); - timer_file= buff; - unlink(timer_file); /* Ignore error, may not exist */ - break; - } - case 'p': - if (argument) - { - my_free(opt_pass, MYF(MY_ALLOW_ZERO_PTR)); - opt_pass= my_strdup(argument, MYF(MY_FAE)); - while (*argument) *argument++= 'x'; /* Destroy argument */ - tty_password= 0; - } - else - tty_password= 1; - break; -#include <sslopt-case.h> - case 't': - strnmov(TMPDIR, argument, sizeof(TMPDIR)); - break; - case 'A': - if (!embedded_server_arg_count) - { - embedded_server_arg_count=1; - embedded_server_args[0]= (char*) ""; - } - if (embedded_server_arg_count == MAX_EMBEDDED_SERVER_ARGS-1 || - !(embedded_server_args[embedded_server_arg_count++]= - my_strdup(argument, MYF(MY_FAE)))) - { - die("Can't use server argument"); - } - break; - case 'F': - read_embedded_server_arguments(argument); - break; - case OPT_SKIP_SAFEMALLOC: -#ifdef SAFEMALLOC - sf_malloc_quick=1; -#endif - break; - case 'V': - print_version(); - exit(0); - case '?': - usage(); - exit(0); - } - return 0; -} - - -int parse_args(int argc, char **argv) -{ - load_defaults("my",load_default_groups,&argc,&argv); - default_argv= argv; - - if ((handle_options(&argc, &argv, my_long_options, get_one_option))) - exit(1); - - if (argc > 1) - { - usage(); - exit(1); - } - if (argc == 1) - opt_db= *argv; - if (tty_password) - opt_pass= get_tty_password(NullS); /* purify tested */ - if (debug_info_flag) - my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO; - if (debug_check_flag) - my_end_arg= MY_CHECK_ERROR; - - return 0; -} - -/* - Write the content of str into file - - SYNOPSIS - str_to_file2 - fname - name of file to truncate/create and write to - str - content to write to file - size - size of content witten to file - append - append to file instead of overwriting old file -*/ - -void str_to_file2(const char *fname, char *str, int size, my_bool append) -{ - int fd; - char buff[FN_REFLEN]; - int flags= O_WRONLY | O_CREAT; - if (!test_if_hard_path(fname)) - { - strxmov(buff, opt_basedir, fname, NullS); - fname= buff; - } - fn_format(buff, fname, "", "", MY_UNPACK_FILENAME); - - if (!append) - flags|= O_TRUNC; - if ((fd= my_open(buff, flags, - MYF(MY_WME | MY_FFNF))) < 0) - die("Could not open '%s' for writing, errno: %d", buff, errno); - if (append && my_seek(fd, 0, SEEK_END, MYF(0)) == MY_FILEPOS_ERROR) - die("Could not find end of file '%s', errno: %d", buff, errno); - if (my_write(fd, (uchar*)str, size, MYF(MY_WME|MY_FNABP))) - die("write failed, errno: %d", errno); - my_close(fd, MYF(0)); -} - -/* - Write the content of str into file - - SYNOPSIS - str_to_file - fname - name of file to truncate/create and write to - str - content to write to file - size - size of content witten to file -*/ - -void str_to_file(const char *fname, char *str, int size) -{ - str_to_file2(fname, str, size, FALSE); -} - - -void dump_result_to_log_file(char *buf, int size) -{ - char log_file[FN_REFLEN]; - str_to_file(fn_format(log_file, result_file_name, opt_logdir, ".log", - *opt_logdir ? MY_REPLACE_DIR | MY_REPLACE_EXT : - MY_REPLACE_EXT), - buf, size); - fprintf(stderr, "\nMore results from queries before failure can be found in %s\n", - log_file); -} - -void dump_progress(void) -{ - char progress_file[FN_REFLEN]; - str_to_file(fn_format(progress_file, result_file_name, - opt_logdir, ".progress", - *opt_logdir ? MY_REPLACE_DIR | MY_REPLACE_EXT : - MY_REPLACE_EXT), - ds_progress.str, ds_progress.length); -} - -void dump_warning_messages(void) -{ - char warn_file[FN_REFLEN]; - - str_to_file(fn_format(warn_file, result_file_name, opt_logdir, ".warnings", - *opt_logdir ? MY_REPLACE_DIR | MY_REPLACE_EXT : - MY_REPLACE_EXT), - ds_warning_messages.str, ds_warning_messages.length); -} - -void check_regerr(my_regex_t* r, int err) -{ - char err_buf[1024]; - - if (err) - { - my_regerror(err,r,err_buf,sizeof(err_buf)); - die("Regex error: %s\n", err_buf); - } -} - - -#ifdef __WIN__ - -DYNAMIC_ARRAY patterns; - -/* - init_win_path_patterns - - DESCRIPTION - Setup string patterns that will be used to detect filenames that - needs to be converted from Win to Unix format - -*/ - -void init_win_path_patterns() -{ - /* List of string patterns to match in order to find paths */ - const char* paths[] = { "$MYSQL_TEST_DIR", - "$MYSQL_TMP_DIR", - "$MYSQLTEST_VARDIR", - "$MASTER_MYSOCK", - "./test/" }; - int num_paths= sizeof(paths)/sizeof(char*); - int i; - char* p; - - DBUG_ENTER("init_win_path_patterns"); - - my_init_dynamic_array(&patterns, sizeof(const char*), 16, 16); - - /* Loop through all paths in the array */ - for (i= 0; i < num_paths; i++) - { - VAR* v; - if (*(paths[i]) == '$') - { - v= var_get(paths[i], 0, 0, 0); - p= my_strdup(v->str_val, MYF(MY_FAE)); - } - else - p= my_strdup(paths[i], MYF(MY_FAE)); - - /* Don't insert zero length strings in patterns array */ - if (strlen(p) == 0) - { - my_free(p, MYF(0)); - continue; - } - - if (insert_dynamic(&patterns, (uchar*) &p)) - die(NullS); - - DBUG_PRINT("info", ("p: %s", p)); - while (*p) - { - if (*p == '/') - *p='\\'; - p++; - } - } - DBUG_VOID_RETURN; -} - -void free_win_path_patterns() -{ - uint i= 0; - for (i=0 ; i < patterns.elements ; i++) - { - const char** pattern= dynamic_element(&patterns, i, const char**); - my_free((char*) *pattern, MYF(0)); - } - delete_dynamic(&patterns); -} - -/* - fix_win_paths - - DESCRIPTION - Search the string 'val' for the patterns that are known to be - strings that contain filenames. Convert all \ to / in the - filenames that are found. - - Ex: - val = 'Error "c:\mysql\mysql-test\var\test\t1.frm" didn't exist' - => $MYSQL_TEST_DIR is found by strstr - => all \ from c:\mysql\m... until next space is converted into / -*/ - -void fix_win_paths(const char *val, int len) -{ - uint i; - char *p; - - DBUG_ENTER("fix_win_paths"); - for (i= 0; i < patterns.elements; i++) - { - const char** pattern= dynamic_element(&patterns, i, const char**); - DBUG_PRINT("info", ("pattern: %s", *pattern)); - - /* Search for the path in string */ - while ((p= strstr(val, *pattern))) - { - DBUG_PRINT("info", ("Found %s in val p: %s", *pattern, p)); - - while (*p && !my_isspace(charset_info, *p)) - { - if (*p == '\\') - *p= '/'; - p++; - } - DBUG_PRINT("info", ("Converted \\ to /, p: %s", p)); - } - } - DBUG_PRINT("exit", (" val: %s, len: %d", val, len)); - DBUG_VOID_RETURN; -} -#endif - - - -/* - Append the result for one field to the dynamic string ds -*/ - -void append_field(DYNAMIC_STRING *ds, uint col_idx, MYSQL_FIELD* field, - char* val, ulonglong len, my_bool is_null) -{ - char null[]= "NULL"; - - if (col_idx < max_replace_column && replace_column[col_idx]) - { - val= replace_column[col_idx]; - len= strlen(val); - } - else if (is_null) - { - val= null; - len= 4; - } -#ifdef __WIN__ - else if ((field->type == MYSQL_TYPE_DOUBLE || - field->type == MYSQL_TYPE_FLOAT ) && - field->decimals >= 31) - { - /* Convert 1.2e+018 to 1.2e+18 and 1.2e-018 to 1.2e-18 */ - char *start= strchr(val, 'e'); - if (start && strlen(start) >= 5 && - (start[1] == '-' || start[1] == '+') && start[2] == '0') - { - start+=2; /* Now points at first '0' */ - if (field->flags & ZEROFILL_FLAG) - { - /* Move all chars before the first '0' one step right */ - memmove(val + 1, val, start - val); - *val= '0'; - } - else - { - /* Move all chars after the first '0' one step left */ - memmove(start, start + 1, strlen(start)); - len--; - } - } - } -#endif - - if (!display_result_vertically) - { - if (col_idx) - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_mem(ds, val, (int)len); - } - else - { - dynstr_append(ds, field->name); - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_mem(ds, val, (int)len); - dynstr_append_mem(ds, "\n", 1); - } -} - - -/* - Append all results to the dynamic string separated with '\t' - Values may be converted with 'replace_column' -*/ - -void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res) -{ - MYSQL_ROW row; - uint num_fields= mysql_num_fields(res); - MYSQL_FIELD *fields= mysql_fetch_fields(res); - ulong *lengths; - - while ((row = mysql_fetch_row(res))) - { - uint i; - lengths = mysql_fetch_lengths(res); - for (i = 0; i < num_fields; i++) - append_field(ds, i, &fields[i], - row[i], lengths[i], !row[i]); - if (!display_result_vertically) - dynstr_append_mem(ds, "\n", 1); - } -} - - -/* - Append all results from ps execution to the dynamic string separated - with '\t'. Values may be converted with 'replace_column' -*/ - -void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, - MYSQL_FIELD *fields, uint num_fields) -{ - MYSQL_BIND *my_bind; - my_bool *is_null; - ulong *length; - uint i; - - /* Allocate array with bind structs, lengths and NULL flags */ - my_bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND), - MYF(MY_WME | MY_FAE | MY_ZEROFILL)); - length= (ulong*) my_malloc(num_fields * sizeof(ulong), - MYF(MY_WME | MY_FAE)); - is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool), - MYF(MY_WME | MY_FAE)); - - /* Allocate data for the result of each field */ - for (i= 0; i < num_fields; i++) - { - uint max_length= fields[i].max_length + 1; - my_bind[i].buffer_type= MYSQL_TYPE_STRING; - my_bind[i].buffer= (char *)my_malloc(max_length, MYF(MY_WME | MY_FAE)); - my_bind[i].buffer_length= max_length; - my_bind[i].is_null= &is_null[i]; - my_bind[i].length= &length[i]; - - DBUG_PRINT("bind", ("col[%d]: buffer_type: %d, buffer_length: %lu", - i, my_bind[i].buffer_type, my_bind[i].buffer_length)); - } - - if (mysql_stmt_bind_result(stmt, my_bind)) - die("mysql_stmt_bind_result failed: %d: %s", - mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); - - while (mysql_stmt_fetch(stmt) == 0) - { - for (i= 0; i < num_fields; i++) - append_field(ds, i, &fields[i], my_bind[i].buffer, - *my_bind[i].length, *my_bind[i].is_null); - if (!display_result_vertically) - dynstr_append_mem(ds, "\n", 1); - } - - if (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) - die("fetch didn't end with MYSQL_NO_DATA from statement: %d %s", - mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); - - for (i= 0; i < num_fields; i++) - { - /* Free data for output */ - my_free(my_bind[i].buffer, MYF(MY_WME | MY_FAE)); - } - /* 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)); -} - - -/* - Append metadata for fields to output -*/ - -void append_metadata(DYNAMIC_STRING *ds, - MYSQL_FIELD *field, - uint num_fields) -{ - MYSQL_FIELD *field_end; - dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\t" - "Column_alias\tType\tLength\tMax length\tIs_null\t" - "Flags\tDecimals\tCharsetnr\n"); - - for (field_end= field+num_fields ; - field < field_end ; - field++) - { - dynstr_append_mem(ds, field->catalog, - field->catalog_length); - dynstr_append_mem(ds, "\t", 1); - dynstr_append_mem(ds, field->db, field->db_length); - dynstr_append_mem(ds, "\t", 1); - dynstr_append_mem(ds, field->org_table, - field->org_table_length); - dynstr_append_mem(ds, "\t", 1); - dynstr_append_mem(ds, field->table, - field->table_length); - dynstr_append_mem(ds, "\t", 1); - dynstr_append_mem(ds, field->org_name, - field->org_name_length); - dynstr_append_mem(ds, "\t", 1); - dynstr_append_mem(ds, field->name, field->name_length); - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_uint(ds, field->type); - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_uint(ds, field->length); - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_uint(ds, field->max_length); - dynstr_append_mem(ds, "\t", 1); - dynstr_append_mem(ds, (char*) (IS_NOT_NULL(field->flags) ? - "N" : "Y"), 1); - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_uint(ds, field->flags); - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_uint(ds, field->decimals); - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_uint(ds, field->charsetnr); - dynstr_append_mem(ds, "\n", 1); - } -} - - -/* - Append affected row count and other info to output -*/ - -void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows, - const char *info) -{ - char buf[40], buff2[21]; - sprintf(buf,"affected rows: %s\n", llstr(affected_rows, buff2)); - dynstr_append(ds, buf); - if (info) - { - dynstr_append(ds, "info: "); - dynstr_append(ds, info); - dynstr_append_mem(ds, "\n", 1); - } -} - - -/* - Display the table headings with the names tab separated -*/ - -void append_table_headings(DYNAMIC_STRING *ds, - MYSQL_FIELD *field, - uint num_fields) -{ - uint col_idx; - for (col_idx= 0; col_idx < num_fields; col_idx++) - { - if (col_idx) - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append(ds, field[col_idx].name); - } - dynstr_append_mem(ds, "\n", 1); -} - -/* - Fetch warnings from server and append to ds - - RETURN VALUE - Number of warnings appended to ds -*/ - -int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql) -{ - uint count; - MYSQL_RES *warn_res; - DBUG_ENTER("append_warnings"); - - if (!(count= mysql_warning_count(mysql))) - DBUG_RETURN(0); - - /* - If one day we will support execution of multi-statements - through PS API we should not issue SHOW WARNINGS until - we have not read all results... - */ - DBUG_ASSERT(!mysql_more_results(mysql)); - - if (mysql_real_query(mysql, "SHOW WARNINGS", 13)) - die("Error running query \"SHOW WARNINGS\": %s", mysql_error(mysql)); - - if (!(warn_res= mysql_store_result(mysql))) - die("Warning count is %u but didn't get any warnings", - count); - - append_result(ds, warn_res); - mysql_free_result(warn_res); - - DBUG_PRINT("warnings", ("%s", ds->str)); - - DBUG_RETURN(count); -} - - -/* - Run query using MySQL C API - - SYNOPSIS - run_query_normal() - mysql mysql handle - command current command pointer - flags flags indicating if we should SEND and/or REAP - query query string to execute - query_len length query string to execute - ds output buffer where to store result form query -*/ - -void run_query_normal(struct st_connection *cn, struct st_command *command, - int flags, char *query, int query_len, - DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings) -{ - MYSQL_RES *res= 0; - MYSQL *mysql= &cn->mysql; - int err= 0, counter= 0; - DBUG_ENTER("run_query_normal"); - DBUG_PRINT("enter",("flags: %d", flags)); - DBUG_PRINT("enter", ("query: '%-.60s'", query)); - - if (flags & QUERY_SEND_FLAG) - { - /* - Send the query - */ - if (do_send_query(cn, query, query_len, flags)) - { - 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)) - DBUG_VOID_RETURN; - - do - { - /* - When on first result set, call mysql_read_query_result to retrieve - answer to the query sent earlier - */ - if ((counter==0) && mysql_read_query_result(mysql)) - { - handle_error(command, mysql_errno(mysql), mysql_error(mysql), - mysql_sqlstate(mysql), ds); - goto end; - - } - - /* - Store the result of the query if it will return any fields - */ - if (mysql_field_count(mysql) && ((res= mysql_store_result(mysql)) == 0)) - { - handle_error(command, mysql_errno(mysql), mysql_error(mysql), - mysql_sqlstate(mysql), ds); - goto end; - } - - if (!disable_result_log) - { - ulonglong affected_rows; /* Ok to be undef if 'disable_info' is set */ - LINT_INIT(affected_rows); - - if (res) - { - MYSQL_FIELD *fields= mysql_fetch_fields(res); - uint num_fields= mysql_num_fields(res); - - if (display_metadata) - append_metadata(ds, fields, num_fields); - - if (!display_result_vertically) - append_table_headings(ds, fields, num_fields); - - append_result(ds, res); - } - - /* - Need to call mysql_affected_rows() before the "new" - query to find the warnings - */ - if (!disable_info) - affected_rows= mysql_affected_rows(mysql); - - /* - Add all warnings to the result. We can't do this if we are in - the middle of processing results from multi-statement, because - this will break protocol. - */ - if (!disable_warnings && !mysql_more_results(mysql)) - { - if (append_warnings(ds_warnings, mysql) || ds_warnings->length) - { - dynstr_append_mem(ds, "Warnings:\n", 10); - dynstr_append_mem(ds, ds_warnings->str, ds_warnings->length); - } - } - - if (!disable_info) - append_info(ds, affected_rows, mysql_info(mysql)); - } - - if (res) - { - mysql_free_result(res); - res= 0; - } - counter++; - } while (!(err= mysql_next_result(mysql))); - if (err > 0) - { - /* We got an error from mysql_next_result, maybe expected */ - handle_error(command, mysql_errno(mysql), mysql_error(mysql), - mysql_sqlstate(mysql), ds); - goto end; - } - DBUG_ASSERT(err == -1); /* Successful and there are no more results */ - - /* If we come here the query is both executed and read successfully */ - handle_no_error(command); - -end: - - /* - We save the return code (mysql_errno(mysql)) from the last call sent - to the server into the mysqltest builtin variable $mysql_errno. This - variable then can be used from the test case itself. - */ - var_set_errno(mysql_errno(mysql)); - DBUG_VOID_RETURN; -} - - -/* - Handle errors which occurred during execution - - SYNOPSIS - handle_error() - q - query context - err_errno - error number - err_error - error message - err_sqlstate - sql state - ds - dynamic string which is used for output buffer - - NOTE - If there is an unexpected error this function will abort mysqltest - immediately. -*/ - -void handle_error(struct st_command *command, - unsigned int err_errno, const char *err_error, - const char *err_sqlstate, DYNAMIC_STRING *ds) -{ - uint i; - - DBUG_ENTER("handle_error"); - - if (command->require_file[0]) - { - /* - The query after a "--require" failed. This is fine as long the server - returned a valid reponse. Don't allow 2013 or 2006 to trigger an - abort_not_supported_test - */ - if (err_errno == CR_SERVER_LOST || - err_errno == CR_SERVER_GONE_ERROR) - die("require query '%s' failed: %d: %s", command->query, - err_errno, err_error); - - /* Abort the run of this test, pass the failed query as reason */ - abort_not_supported_test("Query '%s' failed, required functionality " \ - "not supported", command->query); - } - - if (command->abort_on_error) - die("query '%s' failed: %d: %s", command->query, err_errno, err_error); - - DBUG_PRINT("info", ("expected_errors.count: %d", - command->expected_errors.count)); - for (i= 0 ; (uint) i < command->expected_errors.count ; i++) - { - if (((command->expected_errors.err[i].type == ERR_ERRNO) && - (command->expected_errors.err[i].code.errnum == err_errno)) || - ((command->expected_errors.err[i].type == ERR_SQLSTATE) && - (strncmp(command->expected_errors.err[i].code.sqlstate, - err_sqlstate, SQLSTATE_LENGTH) == 0))) - { - if (!disable_result_log) - { - if (command->expected_errors.count == 1) - { - /* Only log error if there is one possible error */ - dynstr_append_mem(ds, "ERROR ", 6); - replace_dynstr_append(ds, err_sqlstate); - dynstr_append_mem(ds, ": ", 2); - replace_dynstr_append(ds, err_error); - dynstr_append_mem(ds,"\n",1); - } - /* Don't log error if we may not get an error */ - else if (command->expected_errors.err[0].type == ERR_SQLSTATE || - (command->expected_errors.err[0].type == ERR_ERRNO && - command->expected_errors.err[0].code.errnum != 0)) - dynstr_append(ds,"Got one of the listed errors\n"); - } - /* OK */ - DBUG_VOID_RETURN; - } - } - - DBUG_PRINT("info",("i: %d expected_errors: %d", i, - command->expected_errors.count)); - - if (!disable_result_log) - { - dynstr_append_mem(ds, "ERROR ",6); - replace_dynstr_append(ds, err_sqlstate); - dynstr_append_mem(ds, ": ", 2); - replace_dynstr_append(ds, err_error); - dynstr_append_mem(ds, "\n", 1); - } - - if (i) - { - if (command->expected_errors.err[0].type == ERR_ERRNO) - die("query '%s' failed with wrong errno %d: '%s', instead of %d...", - command->query, err_errno, err_error, - command->expected_errors.err[0].code.errnum); - else - die("query '%s' failed with wrong sqlstate %s: '%s', instead of %s...", - command->query, err_sqlstate, err_error, - command->expected_errors.err[0].code.sqlstate); - } - - DBUG_VOID_RETURN; -} - - -/* - Handle absence of errors after execution - - SYNOPSIS - handle_no_error() - q - context of query - - RETURN VALUE - error - function will not return -*/ - -void handle_no_error(struct st_command *command) -{ - DBUG_ENTER("handle_no_error"); - - if (command->expected_errors.err[0].type == ERR_ERRNO && - command->expected_errors.err[0].code.errnum != 0) - { - /* Error code we wanted was != 0, i.e. not an expected success */ - die("query '%s' succeeded - should have failed with errno %d...", - 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); - } - - DBUG_VOID_RETURN; -} - - -/* - Run query using prepared statement C API - - SYNPOSIS - run_query_stmt - mysql - mysql handle - command - currrent command pointer - query - query string to execute - query_len - length query string to execute - ds - output buffer where to store result form query - - RETURN VALUE - error - function will not return -*/ - -void run_query_stmt(MYSQL *mysql, struct st_command *command, - char *query, int query_len, DYNAMIC_STRING *ds, - DYNAMIC_STRING *ds_warnings) -{ - MYSQL_RES *res= NULL; /* Note that here 'res' is meta data result set */ - MYSQL_STMT *stmt; - DYNAMIC_STRING ds_prepare_warnings; - DYNAMIC_STRING ds_execute_warnings; - DBUG_ENTER("run_query_stmt"); - DBUG_PRINT("query", ("'%-.60s'", query)); - - /* - Init a new stmt if it's not already one created for this connection - */ - if(!(stmt= cur_con->stmt)) - { - if (!(stmt= mysql_stmt_init(mysql))) - die("unable to init stmt structure"); - cur_con->stmt= stmt; - } - - /* Init dynamic strings for warnings */ - if (!disable_warnings) - { - init_dynamic_string(&ds_prepare_warnings, NULL, 0, 256); - init_dynamic_string(&ds_execute_warnings, NULL, 0, 256); - } - - /* - Prepare the query - */ - if (mysql_stmt_prepare(stmt, query, query_len)) - { - handle_error(command, mysql_stmt_errno(stmt), - mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); - goto end; - } - - /* - Get the warnings from mysql_stmt_prepare and keep them in a - separate string - */ - if (!disable_warnings) - append_warnings(&ds_prepare_warnings, mysql); - - /* - No need to call mysql_stmt_bind_param() because we have no - parameter markers. - */ - -#if MYSQL_VERSION_ID >= 50000 - if (cursor_protocol_enabled) - { - /* - Use cursor when retrieving result - */ - ulong type= CURSOR_TYPE_READ_ONLY; - if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type)) - die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s", - mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); - } -#endif - - /* - Execute the query - */ - if (mysql_stmt_execute(stmt)) - { - handle_error(command, mysql_stmt_errno(stmt), - mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); - goto end; - } - - /* - When running in cursor_protocol get the warnings from execute here - and keep them in a separate string for later. - */ - if (cursor_protocol_enabled && !disable_warnings) - append_warnings(&ds_execute_warnings, mysql); - - /* - We instruct that we want to update the "max_length" field in - mysql_stmt_store_result(), this is our only way to know how much - buffer to allocate for result data - */ - { - my_bool one= 1; - if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one)) - die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s", - mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); - } - - /* - If we got here the statement succeeded and was expected to do so, - get data. Note that this can still give errors found during execution! - Store the result of the query if if will return any fields - */ - if (mysql_stmt_field_count(stmt) && mysql_stmt_store_result(stmt)) - { - handle_error(command, mysql_stmt_errno(stmt), - mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); - goto end; - } - - /* If we got here the statement was both executed and read successfully */ - handle_no_error(command); - if (!disable_result_log) - { - /* - Not all statements creates a result set. If there is one we can - now create another normal result set that contains the meta - data. This set can be handled almost like any other non prepared - statement result set. - */ - if ((res= mysql_stmt_result_metadata(stmt)) != NULL) - { - /* Take the column count from meta info */ - MYSQL_FIELD *fields= mysql_fetch_fields(res); - uint num_fields= mysql_num_fields(res); - - if (display_metadata) - append_metadata(ds, fields, num_fields); - - if (!display_result_vertically) - append_table_headings(ds, fields, num_fields); - - append_stmt_result(ds, stmt, fields, num_fields); - - mysql_free_result(res); /* Free normal result set with meta data */ - - /* Clear prepare warnings */ - dynstr_set(&ds_prepare_warnings, NULL); - } - else - { - /* - This is a query without resultset - */ - } - - if (!disable_warnings) - { - /* Get the warnings from execute */ - - /* Append warnings to ds - if there are any */ - if (append_warnings(&ds_execute_warnings, mysql) || - ds_execute_warnings.length || - ds_prepare_warnings.length || - ds_warnings->length) - { - dynstr_append_mem(ds, "Warnings:\n", 10); - if (ds_warnings->length) - dynstr_append_mem(ds, ds_warnings->str, - ds_warnings->length); - if (ds_prepare_warnings.length) - dynstr_append_mem(ds, ds_prepare_warnings.str, - ds_prepare_warnings.length); - if (ds_execute_warnings.length) - dynstr_append_mem(ds, ds_execute_warnings.str, - ds_execute_warnings.length); - } - } - - if (!disable_info) - append_info(ds, mysql_affected_rows(mysql), mysql_info(mysql)); - - } - -end: - if (!disable_warnings) - { - dynstr_free(&ds_prepare_warnings); - dynstr_free(&ds_execute_warnings); - } - - - /* Close the statement if - no reconnect, need new prepare */ - if (mysql->reconnect) - { - mysql_stmt_close(stmt); - cur_con->stmt= NULL; - } - - /* - We save the return code (mysql_stmt_errno(stmt)) from the last call sent - to the server into the mysqltest builtin variable $mysql_errno. This - variable then can be used from the test case itself. - */ - - var_set_errno(mysql_stmt_errno(stmt)); - - DBUG_VOID_RETURN; -} - - - -/* - Create a util connection if one does not already exists - and use that to run the query - This is done to avoid implict commit when creating/dropping objects such - as view, sp etc. -*/ - -int util_query(MYSQL* org_mysql, const char* query){ - - MYSQL* mysql; - DBUG_ENTER("util_query"); - - if(!(mysql= cur_con->util_mysql)) - { - DBUG_PRINT("info", ("Creating util_mysql")); - if (!(mysql= mysql_init(mysql))) - die("Failed in mysql_init()"); - - /* enable local infile, in non-binary builds often disabled by default */ - mysql_options(mysql, MYSQL_OPT_LOCAL_INFILE, 0); - safe_connect(mysql, "util", org_mysql->host, org_mysql->user, - org_mysql->passwd, org_mysql->db, org_mysql->port, - org_mysql->unix_socket); - - cur_con->util_mysql= mysql; - } - - return mysql_query(mysql, query); -} - - - -/* - Run query - - SYNPOSIS - run_query() - mysql mysql handle - command currrent command pointer - - flags control the phased/stages of query execution to be performed - if QUERY_SEND_FLAG bit is on, the query will be sent. If QUERY_REAP_FLAG - is on the result will be read - for regular query, both bits must be on -*/ - -void run_query(struct st_connection *cn, struct st_command *command, int flags) -{ - MYSQL *mysql= &cn->mysql; - DYNAMIC_STRING *ds; - DYNAMIC_STRING *save_ds= NULL; - DYNAMIC_STRING ds_result; - DYNAMIC_STRING ds_sorted; - DYNAMIC_STRING ds_warnings; - DYNAMIC_STRING eval_query; - char *query; - int query_len; - my_bool view_created= 0, sp_created= 0; - my_bool complete_query= ((flags & QUERY_SEND_FLAG) && - (flags & QUERY_REAP_FLAG)); - DBUG_ENTER("run_query"); - - init_dynamic_string(&ds_warnings, NULL, 0, 256); - - /* Scan for warning before sending to server */ - scan_command_for_warnings(command); - - /* - Evaluate query if this is an eval command - */ - if (command->type == Q_EVAL) - { - init_dynamic_string(&eval_query, "", command->query_len+256, 1024); - do_eval(&eval_query, command->query, command->end, FALSE); - query = eval_query.str; - query_len = eval_query.length; - } - else - { - query = command->query; - query_len = strlen(query); - } - - /* - When command->require_file is set the output of _this_ query - should be compared with an already existing file - Create a temporary dynamic string to contain the output from - this query. - */ - if (command->require_file[0]) - { - init_dynamic_string(&ds_result, "", 1024, 1024); - ds= &ds_result; - } - else - ds= &ds_res; - - /* - Log the query into the output buffer - */ - if (!disable_query_log && (flags & QUERY_SEND_FLAG)) - { - replace_dynstr_append_mem(ds, query, query_len); - dynstr_append_mem(ds, delimiter, delimiter_length); - dynstr_append_mem(ds, "\n", 1); - } - - if (view_protocol_enabled && - complete_query && - match_re(&view_re, query)) - { - /* - Create the query as a view. - Use replace since view can exist from a failed mysqltest run - */ - DYNAMIC_STRING query_str; - init_dynamic_string(&query_str, - "CREATE OR REPLACE VIEW mysqltest_tmp_v AS ", - query_len+64, 256); - dynstr_append_mem(&query_str, query, query_len); - if (util_query(mysql, query_str.str)) - { - /* - Failed to create the view, this is not fatal - just run the query the normal way - */ - DBUG_PRINT("view_create_error", - ("Failed to create view '%s': %d: %s", query_str.str, - mysql_errno(mysql), mysql_error(mysql))); - - /* Log error to create view */ - verbose_msg("Failed to create view '%s' %d: %s", query_str.str, - mysql_errno(mysql), mysql_error(mysql)); - } - else - { - /* - Yes, it was possible to create this query as a view - */ - view_created= 1; - query= (char*)"SELECT * FROM mysqltest_tmp_v"; - query_len = strlen(query); - - /* - Collect warnings from create of the view that should otherwise - have been produced when the SELECT was executed - */ - append_warnings(&ds_warnings, cur_con->util_mysql); - } - - dynstr_free(&query_str); - - } - - if (sp_protocol_enabled && - complete_query && - match_re(&sp_re, query)) - { - /* - Create the query as a stored procedure - Drop first since sp can exist from a failed mysqltest run - */ - DYNAMIC_STRING query_str; - init_dynamic_string(&query_str, - "DROP PROCEDURE IF EXISTS mysqltest_tmp_sp;", - query_len+64, 256); - util_query(mysql, query_str.str); - dynstr_set(&query_str, "CREATE PROCEDURE mysqltest_tmp_sp()\n"); - dynstr_append_mem(&query_str, query, query_len); - if (util_query(mysql, query_str.str)) - { - /* - Failed to create the stored procedure for this query, - this is not fatal just run the query the normal way - */ - DBUG_PRINT("sp_create_error", - ("Failed to create sp '%s': %d: %s", query_str.str, - mysql_errno(mysql), mysql_error(mysql))); - - /* Log error to create sp */ - verbose_msg("Failed to create sp '%s' %d: %s", query_str.str, - mysql_errno(mysql), mysql_error(mysql)); - - } - else - { - sp_created= 1; - - query= (char*)"CALL mysqltest_tmp_sp()"; - query_len = strlen(query); - } - dynstr_free(&query_str); - } - - if (display_result_sorted) - { - /* - Collect the query output in a separate string - that can be sorted before it's added to the - global result string - */ - init_dynamic_string(&ds_sorted, "", 1024, 1024); - save_ds= ds; /* Remember original ds */ - ds= &ds_sorted; - } - - /* - Find out how to run this query - - Always run with normal C API if it's not a complete - SEND + REAP - - If it is a '?' in the query it may be a SQL level prepared - statement already and we can't do it twice - */ - if (ps_protocol_enabled && - complete_query && - match_re(&ps_re, query)) - run_query_stmt(mysql, command, query, query_len, ds, &ds_warnings); - else - run_query_normal(cn, command, flags, query, query_len, - ds, &ds_warnings); - - if (display_result_sorted) - { - /* Sort the result set and append it to result */ - dynstr_append_sorted(save_ds, &ds_sorted); - ds= save_ds; - dynstr_free(&ds_sorted); - } - - if (sp_created) - { - if (util_query(mysql, "DROP PROCEDURE mysqltest_tmp_sp ")) - die("Failed to drop sp: %d: %s", mysql_errno(mysql), mysql_error(mysql)); - } - - if (view_created) - { - if (util_query(mysql, "DROP VIEW mysqltest_tmp_v ")) - die("Failed to drop view: %d: %s", - mysql_errno(mysql), mysql_error(mysql)); - } - - if (command->require_file[0]) - { - /* A result file was specified for _this_ query - and the output should be checked against an already - existing file which has been specified using --require or --result - */ - check_require(ds, command->require_file); - } - - dynstr_free(&ds_warnings); - if (ds == &ds_result) - dynstr_free(&ds_result); - if (command->type == Q_EVAL) - dynstr_free(&eval_query); - DBUG_VOID_RETURN; -} - -/****************************************************************************/ -/* - Functions to detect different SQL statements -*/ - -char *re_eprint(int err) -{ - static char epbuf[100]; - size_t len= my_regerror(REG_ITOA|err, (my_regex_t *)NULL, - epbuf, sizeof(epbuf)); - assert(len <= sizeof(epbuf)); - return(epbuf); -} - -void init_re_comp(my_regex_t *re, const char* str) -{ - int err= my_regcomp(re, str, (REG_EXTENDED | REG_ICASE | REG_NOSUB), - &my_charset_latin1); - if (err) - { - char erbuf[100]; - int len= my_regerror(err, re, erbuf, sizeof(erbuf)); - die("error %s, %d/%d `%s'\n", - re_eprint(err), len, (int)sizeof(erbuf), erbuf); - } -} - -void init_re(void) -{ - /* - Filter for queries that can be run using the - MySQL Prepared Statements C API - */ - const char *ps_re_str = - "^(" - "[[:space:]]*REPLACE[[:space:]]|" - "[[:space:]]*INSERT[[:space:]]|" - "[[:space:]]*UPDATE[[:space:]]|" - "[[:space:]]*DELETE[[:space:]]|" - "[[:space:]]*SELECT[[:space:]]|" - "[[:space:]]*CREATE[[:space:]]+TABLE[[:space:]]|" - "[[:space:]]*DO[[:space:]]|" - "[[:space:]]*SET[[:space:]]+OPTION[[:space:]]|" - "[[:space:]]*DELETE[[:space:]]+MULTI[[:space:]]|" - "[[:space:]]*UPDATE[[:space:]]+MULTI[[:space:]]|" - "[[:space:]]*INSERT[[:space:]]+SELECT[[:space:]])"; - - /* - Filter for queries that can be run using the - Stored procedures - */ - const char *sp_re_str =ps_re_str; - - /* - Filter for queries that can be run as views - */ - const char *view_re_str = - "^(" - "[[:space:]]*SELECT[[:space:]])"; - - init_re_comp(&ps_re, ps_re_str); - init_re_comp(&sp_re, sp_re_str); - init_re_comp(&view_re, view_re_str); -} - - -int match_re(my_regex_t *re, char *str) -{ - int err= my_regexec(re, str, (size_t)0, NULL, 0); - - if (err == 0) - return 1; - else if (err == REG_NOMATCH) - return 0; - - { - char erbuf[100]; - int len= my_regerror(err, re, erbuf, sizeof(erbuf)); - die("error %s, %d/%d `%s'\n", - re_eprint(err), len, (int)sizeof(erbuf), erbuf); - } - return 0; -} - -void free_re(void) -{ - my_regfree(&ps_re); - my_regfree(&sp_re); - my_regfree(&view_re); - my_regex_end(); -} - -/****************************************************************************/ - -void get_command_type(struct st_command* command) -{ - char save; - uint type; - DBUG_ENTER("get_command_type"); - - if (*command->query == '}') - { - command->type = Q_END_BLOCK; - DBUG_VOID_RETURN; - } - - save= command->query[command->first_word_len]; - command->query[command->first_word_len]= 0; - type= find_type(command->query, &command_typelib, 1+2); - command->query[command->first_word_len]= save; - if (type > 0) - { - command->type=(enum enum_commands) type; /* Found command */ - - /* - Look for case where "query" was explicitly specified to - force command being sent to server - */ - if (type == Q_QUERY) - { - /* Skip the "query" part */ - command->query= command->first_argument; - } - } - else - { - /* No mysqltest command matched */ - - if (command->type != Q_COMMENT_WITH_COMMAND) - { - /* A query that will sent to mysqld */ - command->type= Q_QUERY; - } - else - { - /* -- comment that didn't contain a mysqltest command */ - command->type= Q_COMMENT; - warning_msg("Suspicious command '--%s' detected, was this intentional? "\ - "Use # instead of -- to avoid this warning", - command->query); - - if (command->first_word_len && - strcmp(command->query + command->first_word_len - 1, delimiter) == 0) - { - /* - Detect comment with command using extra delimiter - Ex --disable_query_log; - ^ Extra delimiter causing the command - to be skipped - */ - save= command->query[command->first_word_len-1]; - command->query[command->first_word_len-1]= 0; - if (find_type(command->query, &command_typelib, 1+2) > 0) - die("Extra delimiter \";\" found"); - command->query[command->first_word_len-1]= save; - - } - } - } - - /* Set expected error on command */ - memcpy(&command->expected_errors, &saved_expected_errors, - sizeof(saved_expected_errors)); - DBUG_PRINT("info", ("There are %d expected errors", - command->expected_errors.count)); - command->abort_on_error= (command->expected_errors.count == 0 && - abort_on_error); - - DBUG_VOID_RETURN; -} - - - -/* - Record how many milliseconds it took to execute the test file - up until the current line and save it in the dynamic string ds_progress. - - The ds_progress will be dumped to <test_name>.progress when - test run completes - -*/ - -void mark_progress(struct st_command* command __attribute__((unused)), - int line) -{ - char buf[32], *end; - ulonglong timer= timer_now(); - if (!progress_start) - progress_start= timer; - timer-= progress_start; - - /* Milliseconds since start */ - end= longlong2str(timer, buf, 10); - dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); - dynstr_append_mem(&ds_progress, "\t", 1); - - /* Parser line number */ - end= int10_to_str(line, buf, 10); - dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); - dynstr_append_mem(&ds_progress, "\t", 1); - - /* Filename */ - dynstr_append(&ds_progress, cur_file->file_name); - dynstr_append_mem(&ds_progress, ":", 1); - - /* Line in file */ - end= int10_to_str(cur_file->lineno, buf, 10); - dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); - - - dynstr_append_mem(&ds_progress, "\n", 1); - -} - -#ifdef HAVE_STACKTRACE - -static void dump_backtrace(void) -{ - struct st_connection *conn= cur_con; - - my_safe_print_str("read_command_buf", read_command_buf, - sizeof(read_command_buf)); - if (conn) - { - my_safe_print_str("conn->name", conn->name, conn->name_len); -#ifdef EMBEDDED_LIBRARY - my_safe_print_str("conn->cur_query", conn->cur_query, conn->cur_query_len); -#endif - } - fputs("Attempting backtrace...\n", stderr); - my_print_stacktrace(NULL, my_thread_stack_size); -} - -#else - -static void dump_backtrace(void) -{ - fputs("Backtrace not available.\n", stderr); -} - -#endif - -static sig_handler signal_handler(int sig) -{ - fprintf(stderr, "mysqltest got " SIGNAL_FMT "\n", sig); - dump_backtrace(); -} - -#ifdef __WIN__ - -LONG WINAPI exception_filter(EXCEPTION_POINTERS *exp) -{ - __try - { - my_set_exception_pointers(exp); - signal_handler(exp->ExceptionRecord->ExceptionCode); - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - fputs("Got exception in exception handler!\n", stderr); - } - - return EXCEPTION_CONTINUE_SEARCH; -} - - -static void init_signal_handling(void) -{ - UINT mode; - - /* Set output destination of messages to the standard error stream. */ - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); - _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); - _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); - - /* Do not not display the a error message box. */ - mode= SetErrorMode(0) | SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX; - SetErrorMode(mode); - - SetUnhandledExceptionFilter(exception_filter); -} - -#else /* __WIN__ */ - -static void init_signal_handling(void) -{ - struct sigaction sa; - DBUG_ENTER("init_signal_handling"); - -#ifdef HAVE_STACKTRACE - my_init_stacktrace(); -#endif - - sa.sa_flags = SA_RESETHAND | SA_NODEFER; - sigemptyset(&sa.sa_mask); - sigprocmask(SIG_SETMASK, &sa.sa_mask, NULL); - - sa.sa_handler= signal_handler; - - sigaction(SIGSEGV, &sa, NULL); - sigaction(SIGABRT, &sa, NULL); -#ifdef SIGBUS - sigaction(SIGBUS, &sa, NULL); -#endif - sigaction(SIGILL, &sa, NULL); - sigaction(SIGFPE, &sa, NULL); -} - -#endif /* !__WIN__ */ - -int main(int argc, char **argv) -{ - struct st_command *command; - my_bool q_send_flag= 0, abort_flag= 0; - uint command_executed= 0, last_command_executed= 0; - char save_file[FN_REFLEN]; - MY_STAT res_info; - MY_INIT(argv[0]); - - save_file[0]= 0; - TMPDIR[0]= 0; - - init_signal_handling(); - - /* Init expected errors */ - memset(&saved_expected_errors, 0, sizeof(saved_expected_errors)); - - /* Init connections */ - memset(connections, 0, sizeof(connections)); - connections_end= connections + - (sizeof(connections)/sizeof(struct st_connection)) - 1; - next_con= connections + 1; - -#ifdef EMBEDDED_LIBRARY - /* set appropriate stack for the 'query' threads */ - (void) pthread_attr_init(&cn_thd_attrib); - pthread_attr_setstacksize(&cn_thd_attrib, DEFAULT_THREAD_STACK); -#endif /*EMBEDDED_LIBRARY*/ - - /* Init file stack */ - memset(file_stack, 0, sizeof(file_stack)); - file_stack_end= - file_stack + (sizeof(file_stack)/sizeof(struct st_test_file)) - 1; - cur_file= file_stack; - - /* Init block stack */ - memset(block_stack, 0, sizeof(block_stack)); - block_stack_end= - block_stack + (sizeof(block_stack)/sizeof(struct st_block)) - 1; - cur_block= block_stack; - cur_block->ok= TRUE; /* Outer block should always be executed */ - cur_block->cmd= cmd_none; - - my_init_dynamic_array(&q_lines, sizeof(struct st_command*), 1024, 1024); - - if (hash_init(&var_hash, charset_info, - 1024, 0, 0, get_var_key, var_free, MYF(0))) - die("Variable hash initialization failed"); - - var_set_string("$MYSQL_SERVER_VERSION", MYSQL_SERVER_VERSION); - - memset(&master_pos, 0, sizeof(master_pos)); - - parser.current_line= parser.read_lines= 0; - memset(&var_reg, 0, sizeof(var_reg)); - - init_builtin_echo(); -#ifdef __WIN__ -#ifndef USE_CYGWIN - is_windows= 1; -#endif - init_tmp_sh_file(); - init_win_path_patterns(); -#endif - - init_dynamic_string(&ds_res, "", 65536, 65536); - init_dynamic_string(&ds_progress, "", 0, 2048); - init_dynamic_string(&ds_warning_messages, "", 0, 2048); - parse_args(argc, argv); - - var_set_int("$PS_PROTOCOL", ps_protocol); - var_set_int("$SP_PROTOCOL", sp_protocol); - var_set_int("$VIEW_PROTOCOL", view_protocol); - var_set_int("$CURSOR_PROTOCOL", cursor_protocol); - - DBUG_PRINT("info",("result_file: '%s'", - result_file_name ? result_file_name : "")); - if (mysql_server_init(embedded_server_arg_count, - embedded_server_args, - (char**) embedded_server_groups)) - die("Can't initialize MySQL server"); - server_initialized= 1; - if (cur_file == file_stack && cur_file->file == 0) - { - cur_file->file= stdin; - cur_file->file_name= my_strdup("<stdin>", MYF(MY_WME)); - cur_file->lineno= 1; - } - init_re(); - ps_protocol_enabled= ps_protocol; - sp_protocol_enabled= sp_protocol; - view_protocol_enabled= view_protocol; - cursor_protocol_enabled= cursor_protocol; - /* Cursor protcol implies ps protocol */ - if (cursor_protocol_enabled) - ps_protocol_enabled= 1; - - cur_con= connections; - if (!( mysql_init(&cur_con->mysql))) - die("Failed in mysql_init()"); - if (opt_compress) - mysql_options(&cur_con->mysql,MYSQL_OPT_COMPRESS,NullS); - mysql_options(&cur_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0); - mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME, - charset_info->csname); - if (opt_charsets_dir) - mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_DIR, - opt_charsets_dir); - -#ifdef HAVE_OPENSSL - - if (opt_use_ssl) - { - mysql_ssl_set(&cur_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, - opt_ssl_capath, opt_ssl_cipher); -#if MYSQL_VERSION_ID >= 50000 - /* Turn on ssl_verify_server_cert only if host is "localhost" */ - opt_ssl_verify_server_cert= opt_host && !strcmp(opt_host, "localhost"); - mysql_options(&cur_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, - &opt_ssl_verify_server_cert); -#endif - } -#endif - - if (!(cur_con->name = my_strdup("default", MYF(MY_WME)))) - die("Out of memory"); - - safe_connect(&cur_con->mysql, cur_con->name, opt_host, opt_user, opt_pass, - opt_db, opt_port, unix_sock); - - /* Use all time until exit if no explicit 'start_timer' */ - timer_start= timer_now(); - - /* - Initialize $mysql_errno with -1, so we can - - distinguish it from valid values ( >= 0 ) and - - detect if there was never a command sent to the server - */ - var_set_errno(-1); - - /* Update $mysql_get_server_version to that of current connection */ - var_set_mysql_get_server_version(&cur_con->mysql); - - if (opt_include) - { - open_file(opt_include); - } - - while (!read_command(&command) && !abort_flag) - { - int current_line_inc = 1, processed = 0; - if (command->type == Q_UNKNOWN || command->type == Q_COMMENT_WITH_COMMAND) - get_command_type(command); - - if (parsing_disabled && - command->type != Q_ENABLE_PARSING && - command->type != Q_DISABLE_PARSING) - { - command->type= Q_COMMENT; - scan_command_for_warnings(command); - } - - if (cur_block->ok) - { - command->last_argument= command->first_argument; - processed = 1; - switch (command->type) { - case Q_CONNECT: - do_connect(command); - break; - case Q_CONNECTION: select_connection(command); break; - 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_QUERY_LOG: disable_query_log=0; break; - case Q_DISABLE_QUERY_LOG: disable_query_log=1; break; - case Q_ENABLE_ABORT_ON_ERROR: abort_on_error=1; break; - case Q_DISABLE_ABORT_ON_ERROR: abort_on_error=0; break; - case Q_ENABLE_RESULT_LOG: disable_result_log=0; break; - case Q_DISABLE_RESULT_LOG: disable_result_log=1; break; - case Q_ENABLE_WARNINGS: disable_warnings=0; break; - case Q_DISABLE_WARNINGS: disable_warnings=1; break; - case Q_ENABLE_INFO: disable_info=0; break; - case Q_DISABLE_INFO: disable_info=1; break; - case Q_ENABLE_METADATA: display_metadata=1; break; - case Q_DISABLE_METADATA: display_metadata=0; break; - case Q_SOURCE: do_source(command); break; - case Q_SLEEP: do_sleep(command, 0); break; - case Q_REAL_SLEEP: do_sleep(command, 1); break; - case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(command); break; - case Q_INC: do_modify_var(command, DO_INC); break; - case Q_DEC: do_modify_var(command, DO_DEC); break; - case Q_ECHO: do_echo(command); command_executed++; break; - case Q_SYSTEM: do_system(command); break; - case Q_REMOVE_FILE: do_remove_file(command); break; - case Q_MKDIR: do_mkdir(command); break; - case Q_RMDIR: do_rmdir(command); break; - case Q_FILE_EXIST: do_file_exist(command); break; - case Q_WRITE_FILE: do_write_file(command); break; - case Q_APPEND_FILE: do_append_file(command); break; - case Q_DIFF_FILES: do_diff_files(command); break; - case Q_SEND_QUIT: do_send_quit(command); break; - case Q_CHANGE_USER: do_change_user(command); break; - case Q_CAT_FILE: do_cat_file(command); break; - case Q_COPY_FILE: do_copy_file(command); break; - case Q_CHMOD_FILE: do_chmod_file(command); break; - case Q_PERL: do_perl(command); break; - case Q_DELIMITER: - do_delimiter(command); - break; - case Q_DISPLAY_VERTICAL_RESULTS: - display_result_vertically= TRUE; - break; - case Q_DISPLAY_HORIZONTAL_RESULTS: - display_result_vertically= FALSE; - break; - case Q_SORTED_RESULT: - /* - Turn on sorting of result set, will be reset after next - command - */ - display_result_sorted= TRUE; - break; - case Q_LET: do_let(command); break; - case Q_EVAL_RESULT: - die("'eval_result' command is deprecated"); - case Q_EVAL: - case Q_QUERY_VERTICAL: - case Q_QUERY_HORIZONTAL: - if (command->query == command->query_buf) - { - /* Skip the first part of command, i.e query_xxx */ - command->query= command->first_argument; - command->first_word_len= 0; - } - /* fall through */ - case Q_QUERY: - case Q_REAP: - { - my_bool old_display_result_vertically= display_result_vertically; - /* Default is full query, both reap and send */ - int flags= QUERY_REAP_FLAG | QUERY_SEND_FLAG; - - if (q_send_flag) - { - /* Last command was an empty 'send' */ - flags= QUERY_SEND_FLAG; - q_send_flag= 0; - } - else if (command->type == Q_REAP) - { - flags= QUERY_REAP_FLAG; - } - - /* Check for special property for this query */ - display_result_vertically|= (command->type == Q_QUERY_VERTICAL); - - if (save_file[0]) - { - strmake(command->require_file, save_file, sizeof(save_file) - 1); - save_file[0]= 0; - } - run_query(cur_con, command, flags); - command_executed++; - command->last_argument= command->end; - - /* Restore settings */ - display_result_vertically= old_display_result_vertically; - - break; - } - case Q_SEND: - if (!*command->first_argument) - { - /* - This is a send without arguments, it indicates that _next_ query - should be send only - */ - q_send_flag= 1; - break; - } - - /* Remove "send" if this is first iteration */ - if (command->query == command->query_buf) - command->query= command->first_argument; - - /* - run_query() can execute a query partially, depending on the flags. - QUERY_SEND_FLAG flag without QUERY_REAP_FLAG tells it to just send - the query and read the result some time later when reap instruction - is given on this connection. - */ - run_query(cur_con, command, QUERY_SEND_FLAG); - command_executed++; - command->last_argument= command->end; - break; - case Q_REQUIRE: - do_get_file_name(command, save_file, sizeof(save_file)); - break; - case Q_ERROR: - do_get_errcodes(command); - break; - case Q_REPLACE: - do_get_replace(command); - break; - case Q_REPLACE_REGEX: - do_get_replace_regex(command); - break; - case Q_REPLACE_COLUMN: - do_get_replace_column(command); - break; - case Q_SAVE_MASTER_POS: do_save_master_pos(); break; - case Q_SYNC_WITH_MASTER: do_sync_with_master(command); break; - case Q_SYNC_SLAVE_WITH_MASTER: - { - do_save_master_pos(); - if (*command->first_argument) - select_connection(command); - else - select_connection_name("slave"); - do_sync_with_master2(0); - break; - } - case Q_COMMENT: /* Ignore row */ - command->last_argument= command->end; - break; - case Q_PING: - handle_command_error(command, mysql_ping(&cur_con->mysql)); - break; - case Q_SEND_SHUTDOWN: - handle_command_error(command, - mysql_shutdown(&cur_con->mysql, - SHUTDOWN_DEFAULT)); - break; - case Q_SHUTDOWN_SERVER: - do_shutdown_server(command); - break; - case Q_EXEC: - do_exec(command); - command_executed++; - break; - case Q_START_TIMER: - /* Overwrite possible earlier start of timer */ - timer_start= timer_now(); - break; - case Q_END_TIMER: - /* End timer before ending mysqltest */ - timer_output(); - break; - case Q_CHARACTER_SET: - do_set_charset(command); - break; - case Q_DISABLE_PS_PROTOCOL: - ps_protocol_enabled= 0; - /* Close any open statements */ - close_statements(); - break; - case Q_ENABLE_PS_PROTOCOL: - ps_protocol_enabled= ps_protocol; - break; - case Q_DISABLE_RECONNECT: - set_reconnect(&cur_con->mysql, 0); - break; - case Q_ENABLE_RECONNECT: - set_reconnect(&cur_con->mysql, 1); - /* Close any open statements - no reconnect, need new prepare */ - close_statements(); - break; - case Q_DISABLE_PARSING: - if (parsing_disabled == 0) - parsing_disabled= 1; - else - die("Parsing is already disabled"); - break; - case Q_ENABLE_PARSING: - /* - Ensure we don't get parsing_disabled < 0 as this would accidentally - disable code we don't want to have disabled - */ - if (parsing_disabled == 1) - parsing_disabled= 0; - else - die("Parsing is already enabled"); - break; - case Q_DIE: - /* Abort test with error code and error message */ - die("%s", command->first_argument); - break; - case Q_EXIT: - /* Stop processing any more commands */ - abort_flag= 1; - break; - case Q_SKIP: - abort_not_supported_test("%s", command->first_argument); - break; - - case Q_RESULT: - die("result, deprecated command"); - break; - - default: - processed= 0; - break; - } - } - - if (!processed) - { - current_line_inc= 0; - switch (command->type) { - case Q_WHILE: do_block(cmd_while, command); break; - case Q_IF: do_block(cmd_if, command); break; - case Q_END_BLOCK: do_done(command); break; - default: current_line_inc = 1; break; - } - } - else - check_eol_junk(command->last_argument); - - if (command->type != Q_ERROR && - command->type != Q_COMMENT) - { - /* - As soon as any non "error" command or comment has been executed, - the array with expected errors should be cleared - */ - memset(&saved_expected_errors, 0, sizeof(saved_expected_errors)); - } - - if (command_executed != last_command_executed) - { - /* - As soon as any command has been executed, - the replace structures should be cleared - */ - free_all_replace(); - - /* Also reset "sorted_result" */ - display_result_sorted= FALSE; - } - last_command_executed= command_executed; - - parser.current_line += current_line_inc; - if ( opt_mark_progress ) - mark_progress(command, parser.current_line); - } - - start_lineno= 0; - - if (parsing_disabled) - die("Test ended with parsing disabled"); - - /* - The whole test has been executed _sucessfully_. - Time to compare result or save it to record file. - The entire output from test is now kept in ds_res. - */ - if (ds_res.length) - { - if (result_file_name) - { - /* A result file has been specified */ - - if (record) - { - /* Recording - dump the output from test to result file */ - str_to_file(result_file_name, ds_res.str, ds_res.length); - } - else - { - /* Check that the output from test is equal to result file - - detect missing result file - - detect zero size result file - */ - check_result(&ds_res); - } - } - else - { - /* No result_file_name specified to compare with, print to stdout */ - printf("%s", ds_res.str); - } - } - else - { - die("The test didn't produce any output"); - } - - if (!command_executed && - result_file_name && my_stat(result_file_name, &res_info, 0)) - { - /* - my_stat() successful on result file. Check if we have not run a - single query, but we do have a result file that contains data. - Note that we don't care, if my_stat() fails. For example, for a - non-existing or non-readable file, we assume it's fine to have - no query output from the test file, e.g. regarded as no error. - */ - die("No queries executed but result file found!"); - } - - if ( opt_mark_progress && result_file_name ) - dump_progress(); - - /* Dump warning messages */ - if (result_file_name && ds_warning_messages.length) - dump_warning_messages(); - - timer_output(); - /* Yes, if we got this far the test has suceeded! Sakila smiles */ - cleanup_and_exit(0); - return 0; /* Keep compiler happy too */ -} - - -/* - A primitive timer that give results in milliseconds if the - --timer-file=<filename> is given. The timer result is written - to that file when the result is available. To not confuse - mysql-test-run with an old obsolete result, we remove the file - before executing any commands. The time we measure is - - - If no explicit 'start_timer' or 'end_timer' is given in the - test case, the timer measure how long we execute in mysqltest. - - - If only 'start_timer' is given we measure how long we execute - from that point until we terminate mysqltest. - - - If only 'end_timer' is given we measure how long we execute - from that we enter mysqltest to the 'end_timer' is command is - executed. - - - If both 'start_timer' and 'end_timer' are given we measure - the time between executing the two commands. -*/ - -void timer_output(void) -{ - if (timer_file) - { - char buf[32], *end; - ulonglong timer= timer_now() - timer_start; - end= longlong2str(timer, buf, 10); - str_to_file(timer_file,buf, (int) (end-buf)); - /* Timer has been written to the file, don't use it anymore */ - timer_file= 0; - } -} - - -ulonglong timer_now(void) -{ - return my_micro_time() / 1000; -} - - -/* - Get arguments for replace_columns. The syntax is: - replace-column column_number to_string [column_number to_string ...] - Where each argument may be quoted with ' or " - A argument may also be a variable, in which case the value of the - variable is replaced. -*/ - -void do_get_replace_column(struct st_command *command) -{ - char *from= command->first_argument; - char *buff, *start; - DBUG_ENTER("get_replace_columns"); - - free_replace_column(); - if (!*from) - die("Missing argument in %s", command->query); - - /* Allocate a buffer for results */ - start= buff= my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE)); - while (*from) - { - char *to; - uint column_number; - - to= get_string(&buff, &from, command); - if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS) - die("Wrong column number to replace_column in '%s'", command->query); - if (!*from) - die("Wrong number of arguments to replace_column in '%s'", command->query); - to= get_string(&buff, &from, command); - my_free(replace_column[column_number-1], MY_ALLOW_ZERO_PTR); - replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE)); - set_if_bigger(max_replace_column, column_number); - } - my_free(start, MYF(0)); - command->last_argument= command->end; -} - - -void free_replace_column() -{ - uint i; - for (i=0 ; i < max_replace_column ; i++) - { - if (replace_column[i]) - { - my_free(replace_column[i], 0); - replace_column[i]= 0; - } - } - max_replace_column= 0; -} - - -/****************************************************************************/ -/* - Replace functions -*/ - -/* Definitions for replace result */ - -typedef struct st_pointer_array { /* when using array-strings */ - TYPELIB typelib; /* Pointer to strings */ - uchar *str; /* Strings is here */ - int7 *flag; /* Flag about each var. */ - uint array_allocs,max_count,length,max_length; -} POINTER_ARRAY; - -struct st_replace; -struct st_replace *init_replace(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 ...] - Where each argument may be quoted with ' or " - A argument may also be a variable, in which case the value of the - variable is replaced. -*/ - -void do_get_replace(struct st_command *command) -{ - uint i; - char *from= command->first_argument; - char *buff, *start; - char word_end_chars[256], *pos; - POINTER_ARRAY to_array, from_array; - DBUG_ENTER("get_replace"); - - free_replace(); - - bzero((char*) &to_array,sizeof(to_array)); - bzero((char*) &from_array,sizeof(from_array)); - if (!*from) - die("Missing argument in %s", command->query); - start= buff= my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE)); - while (*from) - { - char *to= buff; - to= get_string(&buff, &from, command); - if (!*from) - die("Wrong number of arguments to replace_result in '%s'", - command->query); -#ifdef __WIN__ - fix_win_paths(to, from - to); -#endif - insert_pointer_name(&from_array,to); - to= get_string(&buff, &from, command); - insert_pointer_name(&to_array,to); - } - for (i= 1,pos= word_end_chars ; i < 256 ; i++) - if (my_isspace(charset_info,i)) - *pos++= i; - *pos=0; /* End pointer */ - if (!(glob_replace= init_replace((char**) from_array.typelib.type_names, - (char**) to_array.typelib.type_names, - (uint) from_array.typelib.count, - word_end_chars))) - die("Can't initialize replace from '%s'", command->query); - free_pointer_array(&from_array); - free_pointer_array(&to_array); - my_free(start, MYF(0)); - command->last_argument= command->end; - DBUG_VOID_RETURN; -} - - -void free_replace() -{ - DBUG_ENTER("free_replace"); - if (glob_replace) - { - my_free(glob_replace,MYF(0)); - glob_replace=0; - } - DBUG_VOID_RETURN; -} - - -typedef struct st_replace { - my_bool found; - struct st_replace *next[256]; -} REPLACE; - -typedef struct st_replace_found { - my_bool found; - char *replace_string; - uint to_offset; - int from_offset; -} REPLACE_STRING; - - -void replace_strings_append(REPLACE *rep, DYNAMIC_STRING* ds, - const char *str, - int len __attribute__((unused))) -{ - reg1 REPLACE *rep_pos; - reg2 REPLACE_STRING *rep_str; - const char *start, *from; - DBUG_ENTER("replace_strings_append"); - - start= from= str; - rep_pos=rep+1; - for (;;) - { - /* Loop through states */ - DBUG_PRINT("info", ("Looping through states")); - while (!rep_pos->found) - rep_pos= rep_pos->next[(uchar) *from++]; - - /* Does this state contain a string to be replaced */ - if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string) - { - /* No match found */ - dynstr_append_mem(ds, start, from - start - 1); - DBUG_PRINT("exit", ("Found no more string to replace, appended: %s", start)); - DBUG_VOID_RETURN; - } - - /* Found a string that needs to be replaced */ - DBUG_PRINT("info", ("found: %d, to_offset: %d, from_offset: %d, string: %s", - rep_str->found, rep_str->to_offset, - rep_str->from_offset, rep_str->replace_string)); - - /* Append part of original string before replace string */ - dynstr_append_mem(ds, start, (from - rep_str->to_offset) - start); - - /* Append replace string */ - dynstr_append_mem(ds, rep_str->replace_string, - strlen(rep_str->replace_string)); - - if (!*(from-=rep_str->from_offset) && rep_pos->found != 2) - { - /* End of from string */ - DBUG_PRINT("exit", ("Found end of from string")); - DBUG_VOID_RETURN; - } - DBUG_ASSERT(from <= str+len); - start= from; - rep_pos=rep; - } -} - - -/* - Regex replace functions -*/ - - -/* Stores regex substitutions */ - -struct st_regex -{ - char* pattern; /* Pattern to be replaced */ - char* replace; /* String or expression to replace the pattern with */ - int icase; /* true if the match is case insensitive */ -}; - -struct st_replace_regex -{ - DYNAMIC_ARRAY regex_arr; /* stores a list of st_regex subsitutions */ - - /* - Temporary storage areas for substitutions. To reduce unnessary copying - and memory freeing/allocation, we pre-allocate two buffers, and alternate - their use, one for input/one for output, the roles changing on the next - st_regex substition. At the end of substitutions buf points to the - one containing the final result. - */ - char* buf; - char* even_buf; - char* odd_buf; - int even_buf_len; - int odd_buf_len; -}; - -struct st_replace_regex *glob_replace_regex= 0; - -int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace, - char *string, int icase); - - - -/* - Finds the next (non-escaped) '/' in the expression. - (If the character '/' is needed, it can be escaped using '\'.) -*/ - -#define PARSE_REGEX_ARG \ - while (p < expr_end) \ - { \ - char c= *p; \ - if (c == '/') \ - { \ - if (last_c == '\\') \ - { \ - buf_p[-1]= '/'; \ - } \ - else \ - { \ - *buf_p++ = 0; \ - break; \ - } \ - } \ - else \ - *buf_p++ = c; \ - \ - last_c= c; \ - p++; \ - } \ - \ -/* - Initializes the regular substitution expression to be used in the - result output of test. - - Returns: st_replace_regex struct with pairs of substitutions -*/ - -struct st_replace_regex* init_replace_regex(char* expr) -{ - struct st_replace_regex* res; - char* buf,*expr_end; - char* p; - char* buf_p; - uint expr_len= strlen(expr); - char last_c = 0; - struct st_regex reg; - - /* my_malloc() will die on fail with MY_FAE */ - res=(struct st_replace_regex*)my_malloc( - sizeof(*res)+expr_len ,MYF(MY_FAE+MY_WME)); - my_init_dynamic_array(&res->regex_arr,sizeof(struct st_regex),128,128); - - buf= (char*)res + sizeof(*res); - expr_end= expr + expr_len; - p= expr; - buf_p= buf; - - /* for each regexp substitution statement */ - while (p < expr_end) - { - bzero(®,sizeof(reg)); - /* find the start of the statement */ - while (p < expr_end) - { - if (*p == '/') - break; - p++; - } - - if (p == expr_end || ++p == expr_end) - { - if (res->regex_arr.elements) - break; - else - goto err; - } - /* we found the start */ - reg.pattern= buf_p; - - /* Find first argument -- pattern string to be removed */ - PARSE_REGEX_ARG - - if (p == expr_end || ++p == expr_end) - goto err; - - /* buf_p now points to the replacement pattern terminated with \0 */ - reg.replace= buf_p; - - /* Find second argument -- replace string to replace pattern */ - PARSE_REGEX_ARG - - if (p == expr_end) - goto err; - - /* skip the ending '/' in the statement */ - p++; - - /* Check if we should do matching case insensitive */ - if (p < expr_end && *p == 'i') - reg.icase= 1; - - /* done parsing the statement, now place it in regex_arr */ - if (insert_dynamic(&res->regex_arr,(uchar*) ®)) - die("Out of memory"); - } - res->odd_buf_len= res->even_buf_len= 8192; - res->even_buf= (char*)my_malloc(res->even_buf_len,MYF(MY_WME+MY_FAE)); - res->odd_buf= (char*)my_malloc(res->odd_buf_len,MYF(MY_WME+MY_FAE)); - res->buf= res->even_buf; - - return res; - -err: - my_free(res,0); - die("Error parsing replace_regex \"%s\"", expr); - return 0; -} - -/* - Execute all substitutions on val. - - Returns: true if substituition was made, false otherwise - Side-effect: Sets r->buf to be the buffer with all substitutions done. - - IN: - struct st_replace_regex* r - char* val - Out: - struct st_replace_regex* r - r->buf points at the resulting buffer - r->even_buf and r->odd_buf might have been reallocated - r->even_buf_len and r->odd_buf_len might have been changed - - TODO: at some point figure out if there is a way to do everything - in one pass -*/ - -int multi_reg_replace(struct st_replace_regex* r,char* val) -{ - uint i; - char* in_buf, *out_buf; - int* buf_len_p; - - in_buf= val; - out_buf= r->even_buf; - buf_len_p= &r->even_buf_len; - r->buf= 0; - - /* For each substitution, do the replace */ - for (i= 0; i < r->regex_arr.elements; i++) - { - struct st_regex re; - char* save_out_buf= out_buf; - - get_dynamic(&r->regex_arr,(uchar*)&re,i); - - if (!reg_replace(&out_buf, buf_len_p, re.pattern, re.replace, - in_buf, re.icase)) - { - /* if the buffer has been reallocated, make adjustements */ - if (save_out_buf != out_buf) - { - if (save_out_buf == r->even_buf) - r->even_buf= out_buf; - else - r->odd_buf= out_buf; - } - - r->buf= out_buf; - if (in_buf == val) - in_buf= r->odd_buf; - - swap_variables(char*,in_buf,out_buf); - - buf_len_p= (out_buf == r->even_buf) ? &r->even_buf_len : - &r->odd_buf_len; - } - } - - return (r->buf == 0); -} - -/* - Parse the regular expression to be used in all result files - from now on. - - The syntax is --replace_regex /from/to/i /from/to/i ... - i means case-insensitive match. If omitted, the match is - case-sensitive - -*/ -void do_get_replace_regex(struct st_command *command) -{ - char *expr= command->first_argument; - free_replace_regex(); - if (!(glob_replace_regex=init_replace_regex(expr))) - die("Could not init replace_regex"); - command->last_argument= command->end; -} - -void free_replace_regex() -{ - if (glob_replace_regex) - { - delete_dynamic(&glob_replace_regex->regex_arr); - my_free(glob_replace_regex->even_buf,MYF(MY_ALLOW_ZERO_PTR)); - my_free(glob_replace_regex->odd_buf,MYF(MY_ALLOW_ZERO_PTR)); - my_free(glob_replace_regex,MYF(0)); - glob_replace_regex=0; - } -} - - - -/* - auxiluary macro used by reg_replace - makes sure the result buffer has sufficient length -*/ -#define SECURE_REG_BUF if (buf_len < need_buf_len) \ - { \ - int off= res_p - buf; \ - buf= (char*)my_realloc(buf,need_buf_len,MYF(MY_WME+MY_FAE)); \ - res_p= buf + off; \ - buf_len= need_buf_len; \ - } \ - \ -/* - Performs a regex substitution - - IN: - - buf_p - result buffer pointer. Will change if reallocated - buf_len_p - result buffer length. Will change if the buffer is reallocated - pattern - regexp pattern to match - replace - replacement expression - string - the string to perform substituions in - icase - flag, if set to 1 the match is case insensitive -*/ -int reg_replace(char** buf_p, int* buf_len_p, char *pattern, - char *replace, char *string, int icase) -{ - my_regex_t r; - my_regmatch_t *subs; - char *replace_end; - char *buf= *buf_p; - int len; - int buf_len, need_buf_len; - int cflags= REG_EXTENDED; - int err_code; - char *res_p,*str_p,*str_end; - - buf_len= *buf_len_p; - len= strlen(string); - str_end= string + len; - - /* start with a buffer of a reasonable size that hopefully will not - need to be reallocated - */ - need_buf_len= len * 2 + 1; - res_p= buf; - - SECURE_REG_BUF - - if (icase) - cflags|= REG_ICASE; - - if ((err_code= my_regcomp(&r,pattern,cflags,&my_charset_latin1))) - { - check_regerr(&r,err_code); - return 1; - } - - subs= (my_regmatch_t*)my_malloc(sizeof(my_regmatch_t) * (r.re_nsub+1), - MYF(MY_WME+MY_FAE)); - - *res_p= 0; - str_p= string; - replace_end= replace + strlen(replace); - - /* for each pattern match instance perform a replacement */ - while (!err_code) - { - /* find the match */ - err_code= my_regexec(&r,str_p, r.re_nsub+1, subs, - (str_p == string) ? REG_NOTBOL : 0); - - /* if regular expression error (eg. bad syntax, or out of memory) */ - if (err_code && err_code != REG_NOMATCH) - { - check_regerr(&r,err_code); - my_regfree(&r); - return 1; - } - - /* if match found */ - if (!err_code) - { - char* expr_p= replace; - int c; - - /* - we need at least what we have so far in the buffer + the part - before this match - */ - need_buf_len= (res_p - buf) + (int) subs[0].rm_so; - - /* on this pass, calculate the memory for the result buffer */ - while (expr_p < replace_end) - { - int back_ref_num= -1; - c= *expr_p; - - if (c == '\\' && expr_p + 1 < replace_end) - { - back_ref_num= (int) (expr_p[1] - '0'); - } - - /* found a valid back_ref (eg. \1)*/ - if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub) - { - regoff_t start_off, end_off; - if ((start_off=subs[back_ref_num].rm_so) > -1 && - (end_off=subs[back_ref_num].rm_eo) > -1) - { - need_buf_len += (int) (end_off - start_off); - } - expr_p += 2; - } - else - { - expr_p++; - need_buf_len++; - } - } - need_buf_len++; - /* - now that we know the size of the buffer, - make sure it is big enough - */ - SECURE_REG_BUF - - /* copy the pre-match part */ - if (subs[0].rm_so) - { - memcpy(res_p, str_p, (size_t) subs[0].rm_so); - res_p+= subs[0].rm_so; - } - - expr_p= replace; - - /* copy the match and expand back_refs */ - while (expr_p < replace_end) - { - int back_ref_num= -1; - c= *expr_p; - - if (c == '\\' && expr_p + 1 < replace_end) - { - back_ref_num= expr_p[1] - '0'; - } - - if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub) - { - regoff_t start_off, end_off; - if ((start_off=subs[back_ref_num].rm_so) > -1 && - (end_off=subs[back_ref_num].rm_eo) > -1) - { - int block_len= (int) (end_off - start_off); - memcpy(res_p,str_p + start_off, block_len); - res_p += block_len; - } - expr_p += 2; - } - else - { - *res_p++ = *expr_p++; - } - } - - /* handle the post-match part */ - if (subs[0].rm_so == subs[0].rm_eo) - { - if (str_p + subs[0].rm_so >= str_end) - break; - str_p += subs[0].rm_eo ; - *res_p++ = *str_p++; - } - else - { - str_p += subs[0].rm_eo; - } - } - else /* no match this time, just copy the string as is */ - { - int left_in_str= str_end-str_p; - need_buf_len= (res_p-buf) + left_in_str; - SECURE_REG_BUF - memcpy(res_p,str_p,left_in_str); - res_p += left_in_str; - str_p= str_end; - } - } - my_free(subs, MYF(0)); - my_regfree(&r); - *res_p= 0; - *buf_p= buf; - *buf_len_p= buf_len; - return 0; -} - - -#ifndef WORD_BIT -#define WORD_BIT (8*sizeof(uint)) -#endif - -#define SET_MALLOC_HUNC 64 -#define LAST_CHAR_CODE 259 - -typedef struct st_rep_set { - uint *bits; /* Pointer to used sets */ - short next[LAST_CHAR_CODE]; /* Pointer to next sets */ - uint found_len; /* Best match to date */ - int found_offset; - uint table_offset; - uint size_of_bits; /* For convinience */ -} REP_SET; - -typedef struct st_rep_sets { - uint count; /* Number of sets */ - uint extra; /* Extra sets in buffer */ - uint invisible; /* Sets not chown */ - uint size_of_bits; - REP_SET *set,*set_buffer; - uint *bit_buffer; -} REP_SETS; - -typedef struct st_found_set { - uint table_offset; - int found_offset; -} FOUND_SET; - -typedef struct st_follow { - int chr; - uint table_offset; - uint len; -} FOLLOWS; - - -int init_sets(REP_SETS *sets,uint states); -REP_SET *make_new_set(REP_SETS *sets); -void make_sets_invisible(REP_SETS *sets); -void free_last_set(REP_SETS *sets); -void free_sets(REP_SETS *sets); -void internal_set_bit(REP_SET *set, uint bit); -void internal_clear_bit(REP_SET *set, uint bit); -void or_bits(REP_SET *to,REP_SET *from); -void copy_bits(REP_SET *to,REP_SET *from); -int cmp_bits(REP_SET *set1,REP_SET *set2); -int get_next_bit(REP_SET *set,uint lastpos); -int find_set(REP_SETS *sets,REP_SET *find); -int find_found(FOUND_SET *found_set,uint table_offset, - int found_offset); -uint start_at_word(char * pos); -uint end_of_word(char * pos); - -static uint found_sets=0; - - -uint replace_len(char * str) -{ - uint len=0; - while (*str) - { - str++; - len++; - } - return len; -} - -/* Init a replace structure for further calls */ - -REPLACE *init_replace(char * *from, char * *to,uint count, - char * word_end_chars) -{ - static const int SPACE_CHAR= 256; - static const int END_OF_LINE= 258; - - uint i,j,states,set_nr,len,result_len,max_length,found_end,bits_set,bit_nr; - int used_sets,chr,default_state; - char used_chars[LAST_CHAR_CODE],is_word_end[256]; - char * pos, *to_pos, **to_array; - REP_SETS sets; - REP_SET *set,*start_states,*word_states,*new_set; - FOLLOWS *follow,*follow_ptr; - REPLACE *replace; - FOUND_SET *found_set; - REPLACE_STRING *rep_str; - DBUG_ENTER("init_replace"); - - /* Count number of states */ - for (i=result_len=max_length=0 , states=2 ; i < count ; i++) - { - len=replace_len(from[i]); - if (!len) - { - errno=EINVAL; - DBUG_RETURN(0); - } - states+=len+1; - result_len+=(uint) strlen(to[i])+1; - if (len > max_length) - max_length=len; - } - bzero((char*) is_word_end,sizeof(is_word_end)); - for (i=0 ; word_end_chars[i] ; i++) - is_word_end[(uchar) word_end_chars[i]]=1; - - if (init_sets(&sets,states)) - DBUG_RETURN(0); - found_sets=0; - if (!(found_set= (FOUND_SET*) my_malloc(sizeof(FOUND_SET)*max_length*count, - MYF(MY_WME)))) - { - free_sets(&sets); - DBUG_RETURN(0); - } - 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 */ - start_states=make_new_set(&sets); /* This is first state */ - if (!(follow=(FOLLOWS*) my_malloc((states+2)*sizeof(FOLLOWS),MYF(MY_WME)))) - { - free_sets(&sets); - my_free(found_set,MYF(0)); - DBUG_RETURN(0); - } - - /* Init follow_ptr[] */ - for (i=0, states=1, follow_ptr=follow+1 ; i < count ; i++) - { - if (from[i][0] == '\\' && from[i][1] == '^') - { - internal_set_bit(start_states,states+1); - if (!from[i][2]) - { - start_states->table_offset=i; - start_states->found_offset=1; - } - } - else if (from[i][0] == '\\' && from[i][1] == '$') - { - internal_set_bit(start_states,states); - internal_set_bit(word_states,states); - if (!from[i][2] && start_states->table_offset == (uint) ~0) - { - start_states->table_offset=i; - start_states->found_offset=0; - } - } - else - { - internal_set_bit(word_states,states); - if (from[i][0] == '\\' && (from[i][1] == 'b' && from[i][2])) - internal_set_bit(start_states,states+1); - else - internal_set_bit(start_states,states); - } - for (pos=from[i], len=0; *pos ; pos++) - { - follow_ptr->chr= (uchar) *pos; - follow_ptr->table_offset=i; - follow_ptr->len= ++len; - follow_ptr++; - } - follow_ptr->chr=0; - follow_ptr->table_offset=i; - follow_ptr->len=len; - follow_ptr++; - states+=(uint) len+1; - } - - - for (set_nr=0,pos=0 ; set_nr < sets.count ; set_nr++) - { - set=sets.set+set_nr; - default_state= 0; /* Start from beginning */ - - /* If end of found-string not found or start-set with current set */ - - for (i= (uint) ~0; (i=get_next_bit(set,i)) ;) - { - if (!follow[i].chr) - { - if (! default_state) - default_state= find_found(found_set,set->table_offset, - set->found_offset+1); - } - } - copy_bits(sets.set+used_sets,set); /* Save set for changes */ - if (!default_state) - or_bits(sets.set+used_sets,sets.set); /* Can restart from start */ - - /* Find all chars that follows current sets */ - bzero((char*) used_chars,sizeof(used_chars)); - for (i= (uint) ~0; (i=get_next_bit(sets.set+used_sets,i)) ;) - { - used_chars[follow[i].chr]=1; - if ((follow[i].chr == SPACE_CHAR && !follow[i+1].chr && - follow[i].len > 1) || follow[i].chr == END_OF_LINE) - used_chars[0]=1; - } - - /* Mark word_chars used if \b is in state */ - if (used_chars[SPACE_CHAR]) - for (pos= word_end_chars ; *pos ; pos++) - used_chars[(int) (uchar) *pos] = 1; - - /* Handle other used characters */ - for (chr= 0 ; chr < 256 ; chr++) - { - if (! used_chars[chr]) - set->next[chr]= chr ? default_state : -1; - else - { - new_set=make_new_set(&sets); - set=sets.set+set_nr; /* if realloc */ - new_set->table_offset=set->table_offset; - new_set->found_len=set->found_len; - new_set->found_offset=set->found_offset+1; - found_end=0; - - for (i= (uint) ~0 ; (i=get_next_bit(sets.set+used_sets,i)) ; ) - { - if (!follow[i].chr || follow[i].chr == chr || - (follow[i].chr == SPACE_CHAR && - (is_word_end[chr] || - (!chr && follow[i].len > 1 && ! follow[i+1].chr))) || - (follow[i].chr == END_OF_LINE && ! chr)) - { - if ((! chr || (follow[i].chr && !follow[i+1].chr)) && - follow[i].len > found_end) - found_end=follow[i].len; - if (chr && follow[i].chr) - internal_set_bit(new_set,i+1); /* To next set */ - else - internal_set_bit(new_set,i); - } - } - if (found_end) - { - new_set->found_len=0; /* Set for testing if first */ - bits_set=0; - for (i= (uint) ~0; (i=get_next_bit(new_set,i)) ;) - { - if ((follow[i].chr == SPACE_CHAR || - follow[i].chr == END_OF_LINE) && ! chr) - bit_nr=i+1; - else - bit_nr=i; - if (follow[bit_nr-1].len < found_end || - (new_set->found_len && - (chr == 0 || !follow[bit_nr].chr))) - internal_clear_bit(new_set,i); - else - { - if (chr == 0 || !follow[bit_nr].chr) - { /* best match */ - new_set->table_offset=follow[bit_nr].table_offset; - if (chr || (follow[i].chr == SPACE_CHAR || - follow[i].chr == END_OF_LINE)) - new_set->found_offset=found_end; /* New match */ - new_set->found_len=found_end; - } - bits_set++; - } - } - if (bits_set == 1) - { - set->next[chr] = find_found(found_set, - new_set->table_offset, - new_set->found_offset); - free_last_set(&sets); - } - else - set->next[chr] = find_set(&sets,new_set); - } - else - set->next[chr] = find_set(&sets,new_set); - } - } - } - - /* Alloc replace structure for the replace-state-machine */ - - if ((replace=(REPLACE*) my_malloc(sizeof(REPLACE)*(sets.count)+ - sizeof(REPLACE_STRING)*(found_sets+1)+ - sizeof(char *)*count+result_len, - MYF(MY_WME | MY_ZEROFILL)))) - { - rep_str=(REPLACE_STRING*) (replace+sets.count); - to_array= (char **) (rep_str+found_sets+1); - to_pos=(char *) (to_array+count); - for (i=0 ; i < count ; i++) - { - to_array[i]=to_pos; - to_pos=strmov(to_pos,to[i])+1; - } - rep_str[0].found=1; - rep_str[0].replace_string=0; - for (i=1 ; i <= found_sets ; i++) - { - pos=from[found_set[i-1].table_offset]; - rep_str[i].found= !bcmp((const uchar*) pos, - (const uchar*) "\\^", 3) ? 2 : 1; - rep_str[i].replace_string=to_array[found_set[i-1].table_offset]; - rep_str[i].to_offset=found_set[i-1].found_offset-start_at_word(pos); - rep_str[i].from_offset=found_set[i-1].found_offset-replace_len(pos)+ - end_of_word(pos); - } - for (i=0 ; i < sets.count ; i++) - { - for (j=0 ; j < 256 ; j++) - if (sets.set[i].next[j] >= 0) - replace[i].next[j]=replace+sets.set[i].next[j]; - else - replace[i].next[j]=(REPLACE*) (rep_str+(-sets.set[i].next[j]-1)); - } - } - my_free(follow,MYF(0)); - free_sets(&sets); - my_free(found_set,MYF(0)); - DBUG_PRINT("exit",("Replace table has %d states",sets.count)); - DBUG_RETURN(replace); -} - - -int init_sets(REP_SETS *sets,uint states) -{ - bzero((char*) sets,sizeof(*sets)); - sets->size_of_bits=((states+7)/8); - if (!(sets->set_buffer=(REP_SET*) my_malloc(sizeof(REP_SET)*SET_MALLOC_HUNC, - MYF(MY_WME)))) - return 1; - 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)); - return 1; - } - return 0; -} - -/* Make help sets invisible for nicer codeing */ - -void make_sets_invisible(REP_SETS *sets) -{ - sets->invisible=sets->count; - sets->set+=sets->count; - sets->count=0; -} - -REP_SET *make_new_set(REP_SETS *sets) -{ - uint i,count,*bit_buffer; - REP_SET *set; - if (sets->extra) - { - sets->extra--; - set=sets->set+ sets->count++; - bzero((char*) set->bits,sizeof(uint)*sets->size_of_bits); - bzero((char*) &set->next[0],sizeof(set->next[0])*LAST_CHAR_CODE); - set->found_offset=0; - set->found_len=0; - set->table_offset= (uint) ~0; - set->size_of_bits=sets->size_of_bits; - return set; - } - count=sets->count+sets->invisible+SET_MALLOC_HUNC; - if (!(set=(REP_SET*) my_realloc((uchar*) sets->set_buffer, - sizeof(REP_SET)*count, - MYF(MY_WME)))) - return 0; - sets->set_buffer=set; - sets->set=set+sets->invisible; - if (!(bit_buffer=(uint*) my_realloc((uchar*) sets->bit_buffer, - (sizeof(uint)*sets->size_of_bits)*count, - MYF(MY_WME)))) - return 0; - sets->bit_buffer=bit_buffer; - for (i=0 ; i < count ; i++) - { - sets->set_buffer[i].bits=bit_buffer; - bit_buffer+=sets->size_of_bits; - } - sets->extra=SET_MALLOC_HUNC; - return make_new_set(sets); -} - -void free_last_set(REP_SETS *sets) -{ - sets->count--; - sets->extra++; - return; -} - -void free_sets(REP_SETS *sets) -{ - my_free(sets->set_buffer,MYF(0)); - my_free(sets->bit_buffer,MYF(0)); - return; -} - -void internal_set_bit(REP_SET *set, uint bit) -{ - set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT); - return; -} - -void internal_clear_bit(REP_SET *set, uint bit) -{ - set->bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT)); - return; -} - - -void or_bits(REP_SET *to,REP_SET *from) -{ - reg1 uint i; - for (i=0 ; i < to->size_of_bits ; i++) - to->bits[i]|=from->bits[i]; - return; -} - -void copy_bits(REP_SET *to,REP_SET *from) -{ - memcpy((uchar*) to->bits,(uchar*) from->bits, - (size_t) (sizeof(uint) * to->size_of_bits)); -} - -int cmp_bits(REP_SET *set1,REP_SET *set2) -{ - return bcmp((uchar*) set1->bits,(uchar*) set2->bits, - sizeof(uint) * set1->size_of_bits); -} - - -/* Get next set bit from set. */ - -int get_next_bit(REP_SET *set,uint lastpos) -{ - uint pos,*start,*end,bits; - - start=set->bits+ ((lastpos+1) / WORD_BIT); - end=set->bits + set->size_of_bits; - bits=start[0] & ~((1 << ((lastpos+1) % WORD_BIT)) -1); - - while (! bits && ++start < end) - bits=start[0]; - if (!bits) - return 0; - pos=(uint) (start-set->bits)*WORD_BIT; - while (! (bits & 1)) - { - bits>>=1; - pos++; - } - return pos; -} - -/* find if there is a same set in sets. If there is, use it and - free given set, else put in given set in sets and return its - position */ - -int find_set(REP_SETS *sets,REP_SET *find) -{ - uint i; - for (i=0 ; i < sets->count-1 ; i++) - { - if (!cmp_bits(sets->set+i,find)) - { - free_last_set(sets); - return i; - } - } - return i; /* return new postion */ -} - -/* find if there is a found_set with same table_offset & found_offset - If there is return offset to it, else add new offset and return pos. - Pos returned is -offset-2 in found_set_structure because it is - saved in set->next and set->next[] >= 0 points to next set and - set->next[] == -1 is reserved for end without replaces. -*/ - -int find_found(FOUND_SET *found_set,uint table_offset, int found_offset) -{ - int i; - for (i=0 ; (uint) i < found_sets ; i++) - if (found_set[i].table_offset == table_offset && - found_set[i].found_offset == found_offset) - return -i-2; - found_set[i].table_offset=table_offset; - found_set[i].found_offset=found_offset; - found_sets++; - return -i-2; /* return new postion */ -} - -/* Return 1 if regexp starts with \b or ends with \b*/ - -uint start_at_word(char * pos) -{ - return (((!bcmp((const uchar*) pos, (const uchar*) "\\b",2) && pos[2]) || - !bcmp((const uchar*) pos, (const uchar*) "\\^", 2)) ? 1 : 0); -} - -uint end_of_word(char * pos) -{ - char * end=strend(pos); - return ((end > pos+2 && !bcmp((const uchar*) end-2, - (const uchar*) "\\b", 2)) || - (end >= pos+2 && !bcmp((const uchar*) end-2, - (const uchar*) "\\$",2))) ? 1 : 0; -} - -/**************************************************************************** - * Handle replacement of strings - ****************************************************************************/ - -#define PC_MALLOC 256 /* Bytes for pointers */ -#define PS_MALLOC 512 /* Bytes for data */ - -int insert_pointer_name(reg1 POINTER_ARRAY *pa,char * name) -{ - uint i,length,old_count; - uchar *new_pos; - const char **new_array; - DBUG_ENTER("insert_pointer_name"); - - if (! pa->typelib.count) - { - if (!(pa->typelib.type_names=(const char **) - my_malloc(((PC_MALLOC-MALLOC_OVERHEAD)/ - (sizeof(char *)+sizeof(*pa->flag))* - (sizeof(char *)+sizeof(*pa->flag))),MYF(MY_WME)))) - DBUG_RETURN(-1); - if (!(pa->str= (uchar*) my_malloc((uint) (PS_MALLOC-MALLOC_OVERHEAD), - MYF(MY_WME)))) - { - my_free((char*) pa->typelib.type_names,MYF(0)); - 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->length=0; - pa->max_length=PS_MALLOC-MALLOC_OVERHEAD; - pa->array_allocs=1; - } - length=(uint) strlen(name)+1; - if (pa->length+length >= pa->max_length) - { - if (!(new_pos= (uchar*) my_realloc((uchar*) pa->str, - (uint) (pa->max_length+PS_MALLOC), - MYF(MY_WME)))) - DBUG_RETURN(1); - if (new_pos != pa->str) - { - my_ptrdiff_t diff=PTR_BYTE_DIFF(new_pos,pa->str); - for (i=0 ; i < pa->typelib.count ; i++) - pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff, - char*); - pa->str=new_pos; - } - pa->max_length+=PS_MALLOC; - } - if (pa->typelib.count >= pa->max_count-1) - { - int len; - pa->array_allocs++; - len=(PC_MALLOC*pa->array_allocs - MALLOC_OVERHEAD); - if (!(new_array=(const char **) my_realloc((uchar*) pa->typelib.type_names, - (uint) len/ - (sizeof(uchar*)+sizeof(*pa->flag))* - (sizeof(uchar*)+sizeof(*pa->flag)), - MYF(MY_WME)))) - DBUG_RETURN(1); - pa->typelib.type_names=new_array; - old_count=pa->max_count; - pa->max_count=len/(sizeof(uchar*) + sizeof(*pa->flag)); - pa->flag= (int7*) (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)); - pa->length+=length; - DBUG_RETURN(0); -} /* insert_pointer_name */ - - -/* free pointer array */ - -void free_pointer_array(POINTER_ARRAY *pa) -{ - if (pa->typelib.count) - { - pa->typelib.count=0; - my_free((char*) pa->typelib.type_names,MYF(0)); - pa->typelib.type_names=0; - my_free(pa->str,MYF(0)); - } -} /* free_pointer_array */ - - -/* Functions that uses replace and replace_regex */ - -/* Append the string to ds, with optional replace */ -void replace_dynstr_append_mem(DYNAMIC_STRING *ds, - const char *val, int len) -{ -#ifdef __WIN__ - fix_win_paths(val, len); -#endif - - 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) - { - /* Normal replace */ - replace_strings_append(glob_replace, ds, val, len); - } - else - dynstr_append_mem(ds, val, len); -} - - -/* Append zero-terminated string to ds, with optional replace */ -void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val) -{ - replace_dynstr_append_mem(ds, val, strlen(val)); -} - -/* Append uint to ds, with optional replace */ -void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val) -{ - char buff[22]; /* This should be enough for any int */ - char *end= longlong10_to_str(val, buff, 10); - replace_dynstr_append_mem(ds, buff, end - buff); -} - - - -/* - Build a list of pointer to each line in ds_input, sort - the list and use the sorted list to append the strings - sorted to the output ds - - SYNOPSIS - dynstr_append_sorted - ds - string where the sorted output will be appended - ds_input - string to be sorted - -*/ - -static int comp_lines(const char **a, const char **b) -{ - return (strcmp(*a,*b)); -} - -void dynstr_append_sorted(DYNAMIC_STRING* ds, DYNAMIC_STRING *ds_input) -{ - unsigned i; - char *start= ds_input->str; - DYNAMIC_ARRAY lines; - DBUG_ENTER("dynstr_append_sorted"); - - if (!*start) - DBUG_VOID_RETURN; /* No input */ - - my_init_dynamic_array(&lines, sizeof(const char*), 32, 32); - - /* First line is result header, skip past it */ - while (*start && *start != '\n') - start++; - start++; /* Skip past \n */ - dynstr_append_mem(ds, ds_input->str, start - ds_input->str); - - /* Insert line(s) in array */ - while (*start) - { - char* line_end= (char*)start; - - /* Find end of line */ - while (*line_end && *line_end != '\n') - line_end++; - *line_end= 0; - - /* Insert pointer to the line in array */ - if (insert_dynamic(&lines, (uchar*) &start)) - die("Out of memory inserting lines to sort"); - - start= line_end+1; - } - - /* Sort array */ - qsort(lines.buffer, lines.elements, - sizeof(char**), (qsort_cmp)comp_lines); - - /* Create new result */ - for (i= 0; i < lines.elements ; i++) - { - const char **line= dynamic_element(&lines, i, const char**); - dynstr_append(ds, *line); - dynstr_append(ds, "\n"); - } - - delete_dynamic(&lines); - DBUG_VOID_RETURN; -} |