summaryrefslogtreecommitdiff
path: root/client/mysqltest.c
diff options
context:
space:
mode:
Diffstat (limited to 'client/mysqltest.c')
-rw-r--r--client/mysqltest.c1211
1 files changed, 865 insertions, 346 deletions
diff --git a/client/mysqltest.c b/client/mysqltest.c
index d5a03961364..f40aec2a40e 100644
--- a/client/mysqltest.c
+++ b/client/mysqltest.c
@@ -2,8 +2,7 @@
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; either version 2 of the License, or
- (at your option) any later version.
+ 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
@@ -75,26 +74,20 @@
#define QUERY_SEND_FLAG 1
#define QUERY_REAP_FLAG 2
- enum {
- RESULT_OK= 0,
- RESULT_CONTENT_MISMATCH= 1,
- RESULT_LENGTH_MISMATCH= 2
- };
-
enum {
OPT_SKIP_SAFEMALLOC=256, OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT,
OPT_SSL_CA, OPT_SSL_CAPATH, OPT_SSL_CIPHER, OPT_PS_PROTOCOL,
OPT_SP_PROTOCOL, OPT_CURSOR_PROTOCOL, OPT_VIEW_PROTOCOL,
OPT_SSL_VERIFY_SERVER_CERT, OPT_MAX_CONNECT_RETRIES,
- OPT_MARK_PROGRESS, OPT_CHARSETS_DIR, OPT_LOG_DIR
+ OPT_MARK_PROGRESS, OPT_CHARSETS_DIR, OPT_LOG_DIR, OPT_TAIL_LINES
};
static int record= 0, opt_sleep= -1;
-static char *db= 0, *pass= 0;
-const char *user= 0, *host= 0, *unix_sock= 0, *opt_basedir= "./";
+static char *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 port= 0;
+static int opt_port= 0;
static int opt_max_connect_retries;
static my_bool opt_compress= 0, silent= 0, verbose= 0;
static my_bool tty_password= 0;
@@ -110,6 +103,7 @@ 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 };
@@ -117,6 +111,9 @@ static char line_buffer[MAX_DELIMITER_LENGTH], *line_buffer_pos= line_buffer;
static uint start_lineno= 0; /* Start line of current command */
+/* 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;
@@ -238,7 +235,7 @@ struct st_connection
#endif /*EMBEDDED_LIBRARY*/
};
struct st_connection connections[128];
-struct st_connection* cur_con, *next_con, *connections_end;
+struct st_connection* cur_con= NULL, *next_con, *connections_end;
/*
List of commands in mysqltest
@@ -280,6 +277,7 @@ enum enum_commands {
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_UNKNOWN, /* Unknown command. */
Q_COMMENT, /* Comments, ignored. */
@@ -359,6 +357,7 @@ const char *command_names[]=
"copy_file",
"perl",
"die",
+
/* Don't execute any more commands, compare result */
"exit",
"skip",
@@ -366,6 +365,7 @@ const char *command_names[]=
"append_file",
"cat_file",
"diff_files",
+ "send_quit",
0
};
@@ -452,7 +452,6 @@ void free_tmp_sh_file();
void free_win_path_patterns();
#endif
-static int eval_result = 0;
/* For replace_column */
static char *replace_column[MAX_COLUMNS];
@@ -494,16 +493,19 @@ void handle_error(struct st_command*,
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 what is
+ 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.
+ I don't see where it is needed.
*/
-pthread_handler_decl(send_one_query, arg)
+pthread_handler_t send_one_query(void *arg)
{
struct st_connection *cn= (struct st_connection*)arg;
@@ -534,7 +536,7 @@ static int do_send_query(struct st_connection *cn, const char *q, int q_len,
cn->cur_query= q;
cn->cur_query_len= q_len;
cn->query_done= 0;
- if (pthread_create(&tid, NULL, send_one_query, (void*)cn))
+ if (pthread_create(&tid, &cn_thd_attrib, send_one_query, (void*)cn))
die("Cannot start new thread for query");
return 0;
@@ -602,6 +604,142 @@ void do_eval(DYNAMIC_STRING *query_eval, const char *query,
}
+/*
+ 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,
@@ -625,15 +763,14 @@ void check_command_args(struct st_command *command,
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)
- {
+ switch (arg->type) {
/* A string */
case ARG_STRING:
/* Skip leading spaces */
@@ -681,6 +818,15 @@ void check_command_args(struct st_command *command,
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;
}
@@ -740,6 +886,20 @@ void close_connections()
}
+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");
@@ -784,15 +944,20 @@ void free_used_memory()
dynstr_free(&ds_progress);
dynstr_free(&ds_warning_messages);
free_all_replace();
- my_free(pass,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(opt_pass,MYF(MY_ALLOW_ZERO_PTR));
free_defaults(default_argv);
- mysql_server_end();
free_re();
#ifdef __WIN__
free_tmp_sh_file();
free_win_path_patterns();
#endif
- DBUG_VOID_RETURN;
+
+ /* 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;
}
@@ -857,6 +1022,24 @@ void die(const char *fmt, ...)
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);
@@ -865,6 +1048,13 @@ void die(const char *fmt, ...)
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);
}
@@ -968,11 +1158,10 @@ void warning_msg(const char *fmt, ...)
void log_msg(const char *fmt, ...)
{
va_list args;
- char buff[512];
+ char buff[1024];
size_t len;
DBUG_ENTER("log_msg");
- memset(buff, 0, sizeof(buff));
va_start(args, fmt);
len= my_vsnprintf(buff, sizeof(buff)-1, fmt, args);
va_end(args);
@@ -985,77 +1174,372 @@ void log_msg(const char *fmt, ...)
/*
- Compare content of the string ds to content of file fname
+ 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
+
*/
-int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname)
+void cat_file(DYNAMIC_STRING* ds, const char* filename)
{
- MY_STAT stat_info;
- char *tmp, *res_ptr;
- char eval_file[FN_REFLEN];
- int res;
- uint res_len;
int fd;
- DYNAMIC_STRING res_ds;
- DBUG_ENTER("dyn_string_cmp");
+ uint len;
+ char buff[512];
- if (!test_if_hard_path(fname))
+ if ((fd= my_open(filename, O_RDONLY, MYF(0))) < 0)
+ die("Failed to open file '%s'", filename);
+ while((len= my_read(fd, (byte*)&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))
{
- strxmov(eval_file, opt_basedir, fname, NullS);
- fn_format(eval_file, eval_file, "", "", MY_UNPACK_FILENAME);
+ 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);
+ }
}
- else
- fn_format(eval_file, fname, "", "", MY_UNPACK_FILENAME);
- if (!my_stat(eval_file, &stat_info, MYF(MY_WME)))
- die(NullS);
- if (!eval_result && (uint) stat_info.st_size != ds->length)
+ 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 *)))
{
- DBUG_PRINT("info",("Size differs: result size: %u file size: %lu",
- ds->length, stat_info.st_size));
- DBUG_PRINT("info",("result: '%s'", ds->str));
- DBUG_RETURN(RESULT_LENGTH_MISMATCH);
+ /* 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, " ");
}
- if (!(tmp = (char*) my_malloc(stat_info.st_size + 1, MYF(MY_WME))))
+
+ 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)
+{
+
+ DYNAMIC_STRING ds_tmp;
+
+ if (init_dynamic_string(&ds_tmp, "", 256, 256))
die("Out of memory");
- if ((fd = my_open(eval_file, O_RDONLY, MYF(MY_WME))) < 0)
- die("Failed to open file %s", eval_file);
- if (my_read(fd, (byte*)tmp, stat_info.st_size, MYF(MY_WME|MY_NABP)))
- die("Failed to read from file %s, errno: %d", eval_file, errno);
- tmp[stat_info.st_size] = 0;
- init_dynamic_string(&res_ds, "", stat_info.st_size+256, 256);
- if (eval_result)
- {
- do_eval(&res_ds, tmp, tmp + stat_info.st_size, FALSE);
- res_ptr= res_ds.str;
- res_len= res_ds.length;
- if (res_len != ds->length)
+ /* 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 */
{
- res= RESULT_LENGTH_MISMATCH;
- goto err;
+ /*
+ Fallback to dump both files to result file and inform
+ about installing "diff"
+ */
+ dynstr_set(&ds_tmp, "");
+
+ 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, tried both 'diff -u' or 'diff -c'.\n"
+"Instead the whole content of the 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
{
- res_ptr = tmp;
- res_len = stat_info.st_size;
+ /* Print diff directly to stdout */
+ fprintf(stderr, "%s\n", ds_tmp.str);
}
+
+ dynstr_free(&ds_tmp);
- res= (memcmp(res_ptr, ds->str, res_len)) ?
- RESULT_CONTENT_MISMATCH : RESULT_OK;
+}
-err:
- if (res && eval_result)
- str_to_file(fn_format(eval_file, fname, "", ".eval",
- MY_REPLACE_EXT),
- res_ptr, res_len);
- dynstr_free(&res_ds);
- my_free((gptr) tmp, MYF(0));
- my_close(fd, MYF(MY_WME));
+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, (byte*)&buff,
+ sizeof(buff), MYF(0))) > 0)
+ {
+ if ((len2= my_read(fd2, (byte*)&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, (byte*)&buff2,
+ sizeof(buff2), MYF(0)) > 0)
+ {
+ /* File 1 was smaller */
+ error= RESULT_LENGTH_MISMATCH;
+ }
+
+ my_close(fd2, MYF(0));
+
+ return error;
+}
+
- DBUG_RETURN(res);
+/*
+ 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, NULL,
+ "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, 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);
}
@@ -1073,21 +1557,40 @@ err:
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:
- dump_result_to_reject_file(ds->str, ds->length);
- die("Result length mismatch");
- break;
+ mess= "Result length mismatch\n";
+ /* Fallthrough */
case RESULT_CONTENT_MISMATCH:
- dump_result_to_reject_file(ds->str, ds->length);
- die("Result content mismatch");
+ {
+ /*
+ Result mismatched, dump results to .reject file
+ and then show the diff
+ */
+ char reject_file[FN_REFLEN];
+ str_to_file(fn_format(reject_file, result_file_name, opt_logdir, ".reject",
+ *opt_logdir ? MY_REPLACE_DIR | MY_REPLACE_EXT :
+ MY_REPLACE_EXT),
+ 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()");
}
@@ -1102,7 +1605,7 @@ void check_result(DYNAMIC_STRING* ds)
indicating that test is not supported
SYNOPSIS
- check_result
+ check_require
ds - content to be checked
fname - name of file to check against
@@ -1405,7 +1908,7 @@ void var_set_errno(int sql_errno)
void var_query_set(VAR *var, const char *query, const char** query_end)
{
- char* end = (char*)((query_end && *query_end) ?
+ char *end = (char*)((query_end && *query_end) ?
*query_end : query + strlen(query));
MYSQL_RES *res;
MYSQL_ROW row;
@@ -1431,41 +1934,22 @@ void var_query_set(VAR *var, const char *query, const char** query_end)
die("Query '%s' didn't return a result set", ds_query.str);
dynstr_free(&ds_query);
- if ((row = mysql_fetch_row(res)) && row[0])
+ if ((row= mysql_fetch_row(res)) && row[0])
{
/*
- Concatenate all row results with tab in between to allow us to work
- with results from many columns (for example from SHOW VARIABLES)
+ 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;
- char *end;
-#ifdef NOT_YET
- MYSQL_FIELD *fields= mysql_fetch_fields(res);
-#endif
- init_dynamic_string(&result, "", 2048, 2048);
+ init_dynamic_string(&result, "", 512, 512);
lengths= mysql_fetch_lengths(res);
- for (i=0; i < mysql_num_fields(res); i++)
+ for (i= 0; i < mysql_num_fields(res); i++)
{
- if (row[0])
+ if (row[i])
{
-#ifdef NOT_YET
- /* Add to <var_name>_<col_name> */
- uint j;
- char var_col_name[MAX_VAR_NAME_LENGTH];
- uint length= snprintf(var_col_name, MAX_VAR_NAME_LENGTH,
- "$%s_%s", var->name, fields[i].name);
- /* Convert characters not allowed in variable names to '_' */
- for (j= 1; j < length; j++)
- {
- if (!my_isvar(charset_info,var_col_name[j]))
- var_col_name[j]= '_';
- }
- var_set(var_col_name, var_col_name + length,
- row[i], row[i] + lengths[i]);
-#endif
/* Add column to tab separated string */
dynstr_append_mem(&result, row[i], lengths[i]);
}
@@ -1507,7 +1991,7 @@ void var_query_set(VAR *var, const char *query, const char** query_end)
void var_set_query_get_value(struct st_command *command, VAR *var)
{
- ulong row_no;
+ long row_no;
int col_no= -1;
MYSQL_RES* res;
MYSQL* mysql= &cur_con->mysql;
@@ -1579,7 +2063,7 @@ void var_set_query_get_value(struct st_command *command, VAR *var)
{
/* Get the value */
MYSQL_ROW row;
- ulong rows= 0;
+ long rows= 0;
const char* value= "No such row";
while ((row= mysql_fetch_row(res)))
@@ -1708,7 +2192,7 @@ int open_file(const char *name)
if (!(cur_file->file = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0))))
{
cur_file--;
- die("Could not open file %s", buff);
+ die("Could not open file '%s'", buff);
}
cur_file->file_name= my_strdup(buff, MYF(MY_FAE));
cur_file->lineno=1;
@@ -1942,7 +2426,7 @@ void do_exec(struct st_command *command)
if (command->abort_on_error)
{
- log_msg("exec of '%s failed, error: %d, status: %d, errno: %d",
+ log_msg("exec of '%s' failed, error: %d, status: %d, errno: %d",
ds_cmd.str, error, status, errno);
die("command \"%s\" failed", command->first_argument);
}
@@ -2180,19 +2664,19 @@ void do_copy_file(struct st_command *command)
command command handle
DESCRIPTION
- chmod <octal> <file>
- Change file permission of <file>
+ chmod_file <octal> <file_name>
+ Change file permission of <file_name>
*/
void do_chmod_file(struct st_command *command)
{
- ulong mode= 0;
+ 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",
- "file", ARG_STRING, TRUE, &ds_file, "Filename of file to modify"
+ "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");
@@ -2287,8 +2771,22 @@ void read_until_delimiter(DYNAMIC_STRING *ds,
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);
@@ -2326,6 +2824,12 @@ void do_write_file_command(struct st_command *command, my_bool append)
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));
@@ -2358,7 +2862,7 @@ void do_write_file_command(struct st_command *command, my_bool append)
Write everything between the "write_file" command and 'delimiter'
to "file_name"
- NOTE! Overwrites existing file
+ NOTE! Will fail if <file_name> exists
Default <delimiter> is EOF
@@ -2415,9 +2919,6 @@ void do_append_file(struct st_command *command)
void do_cat_file(struct st_command *command)
{
- int fd;
- uint len;
- char buff[512];
static DYNAMIC_STRING ds_filename;
const struct command_arg cat_file_args[] = {
"filename", ARG_STRING, TRUE, &ds_filename, "File to read from"
@@ -2432,37 +2933,13 @@ void do_cat_file(struct st_command *command)
DBUG_PRINT("info", ("Reading from, file: %s", ds_filename.str));
- if ((fd= my_open(ds_filename.str, O_RDONLY, MYF(0))) < 0)
- die("Failed to open file %s", ds_filename.str);
- while((len= my_read(fd, (byte*)&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_res, start, p-start);
- p++; /* Step past the "fake" newline */
- start= p;
- }
- else
- p++;
- }
- /* Output any chars that migh be left */
- dynstr_append_mem(&ds_res, start, p-start);
- }
- my_close(fd, MYF(0));
+ cat_file(&ds_res, ds_filename.str);
+
dynstr_free(&ds_filename);
DBUG_VOID_RETURN;
}
-
/*
SYNOPSIS
do_diff_files
@@ -2478,9 +2955,6 @@ void do_cat_file(struct st_command *command)
void do_diff_files(struct st_command *command)
{
int error= 0;
- int fd, fd2;
- uint len, len2;
- char buff[512], buff2[512];
static DYNAMIC_STRING ds_filename;
static DYNAMIC_STRING ds_filename2;
const struct command_arg diff_file_args[] = {
@@ -2495,45 +2969,72 @@ void do_diff_files(struct st_command *command)
sizeof(diff_file_args)/sizeof(struct command_arg),
' ');
- if ((fd= my_open(ds_filename.str, O_RDONLY, MYF(0))) < 0)
- die("Failed to open first file %s", ds_filename.str);
- if ((fd2= my_open(ds_filename2.str, O_RDONLY, MYF(0))) < 0)
+ if ((error= compare_files(ds_filename.str, ds_filename2.str)))
{
- my_close(fd, MYF(0));
- die("Failed to open second file %s", 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);
}
- while((len= my_read(fd, (byte*)&buff,
- sizeof(buff), MYF(0))) > 0)
+
+ 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 ((len2= my_read(fd2, (byte*)&buff2,
- sizeof(buff2), MYF(0))) != len)
- {
- /* File 2 was smaller */
- error= 1;
- break;
- }
- if ((memcmp(buff, buff2, len)))
+ if (!strcmp(con->name, name))
{
- /* Content of this part differed */
- error= 1;
- break;
+ return con;
}
}
- if (my_read(fd2, (byte*)&buff2,
- sizeof(buff2), MYF(0)) > 0)
- {
- /* File 1 was smaller */
- error= 1;
- }
+ 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,NullS,0,1);
- my_close(fd, MYF(0));
- my_close(fd2, MYF(0));
- dynstr_free(&ds_filename);
- dynstr_free(&ds_filename2);
- handle_command_error(command, error);
DBUG_VOID_RETURN;
}
+
/*
SYNOPSIS
do_perl
@@ -2556,8 +3057,10 @@ void do_diff_files(struct st_command *command)
void do_perl(struct st_command *command)
{
int error;
- char buf[FN_REFLEN];
+ 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[] = {
@@ -2580,14 +3083,17 @@ void do_perl(struct st_command *command)
DBUG_PRINT("info", ("Executing perl: %s", ds_script.str));
- /* Format a name for a tmp .pl file that is unique for this process */
- my_snprintf(buf, sizeof(buf), "%s/tmp/tmp_%d.pl",
- getenv("MYSQLTEST_VARDIR"), getpid());
- str_to_file(buf, ds_script.str, ds_script.length);
+ /* 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/tmp/tmp_%d.pl",
- getenv("MYSQLTEST_VARDIR"), getpid());
+ /* 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);
@@ -2605,6 +3111,10 @@ void do_perl(struct st_command *command)
}
}
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);
@@ -2639,6 +3149,7 @@ void do_perl(struct st_command *command)
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);
@@ -2646,7 +3157,7 @@ int do_echo(struct st_command *command)
dynstr_append_mem(&ds_res, "\n", 1);
dynstr_free(&ds_echo);
command->last_argument= command->end;
- return(0);
+ DBUG_RETURN(0);
}
@@ -2714,7 +3225,11 @@ wait_for_position:
SLAVE has been issued ?
*/
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 */
mysql_free_result(res);
goto wait_for_position;
@@ -2942,7 +3457,6 @@ 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);
@@ -3354,20 +3868,6 @@ void set_reconnect(MYSQL* mysql, int val)
}
-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 */
-}
-
-
int select_connection_name(const char *name)
{
DBUG_ENTER("select_connection2");
@@ -3415,41 +3915,40 @@ void do_close_connection(struct st_command *command)
*p++= 0;
command->last_argument= p;
- /* Loop through connection pool for connection to close */
- for (con= connections; con < next_con; con++)
+ 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)
{
- DBUG_PRINT("info", ("con->name: %s", con->name));
- if (!strcmp(con->name, name))
+ if (con->mysql.net.vio)
{
- 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;
- }
- }
+ vio_delete(con->mysql.net.vio);
+ con->mysql.net.vio = 0;
+ }
+ }
#endif
- mysql_close(&con->mysql);
- if (con->util_mysql)
- mysql_close(con->util_mysql);
- con->util_mysql= 0;
- my_free(con->name, MYF(0));
+ if (con->stmt)
+ mysql_stmt_close(con->stmt);
+ con->stmt= 0;
- /*
- When the connection is closed set name to "closed_connection"
- to make it possible to reuse the connection name.
- The connection slot will not be reused
- */
- if (!(con->name = my_strdup("closed_connection", MYF(MY_WME))))
- die("Out of memory");
+ mysql_close(&con->mysql);
- DBUG_VOID_RETURN;
- }
- }
- die("connection '%s' not found in connection pool", name);
+ 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;
}
@@ -3618,9 +4117,10 @@ int connect_n_handle_errors(struct st_command *command,
void do_connect(struct st_command *command)
{
- int con_port= port;
+ int con_port= opt_port;
char *con_options;
bool con_ssl= 0, con_compress= 0;
+ struct st_connection* con_slot;
static DYNAMIC_STRING ds_connection_name;
static DYNAMIC_STRING ds_host;
@@ -3700,38 +4200,44 @@ void do_connect(struct st_command *command)
else if (!strncmp(con_options, "COMPRESS", 8))
con_compress= 1;
else
- die("Illegal option to connect: %.*s", end - con_options, con_options);
+ die("Illegal option to connect: %.*s",
+ (int) (end - con_options), con_options);
/* Process next option */
con_options= end;
}
- if (next_con == connections_end)
- die("Connection limit exhausted, you can have max %d connections",
- (sizeof(connections)/sizeof(struct st_connection)));
-
if (find_connection_by_name(ds_connection_name.str))
die("Connection %s already exists", ds_connection_name.str);
+
+ if (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)));
+ }
- if (!mysql_init(&next_con->mysql))
+ if (!mysql_init(&con_slot->mysql))
die("Failed on mysql_init()");
if (opt_compress || con_compress)
- mysql_options(&next_con->mysql, MYSQL_OPT_COMPRESS, NullS);
- mysql_options(&next_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
- mysql_options(&next_con->mysql, MYSQL_SET_CHARSET_NAME,
+ 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(&cur_con->mysql, MYSQL_SET_CHARSET_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(&next_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
+ 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_connection_name.str, "localhost");
- mysql_options(&next_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
+ 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
}
@@ -3739,23 +4245,25 @@ void do_connect(struct st_command *command)
/* Use default db name */
if (ds_database.length == 0)
- dynstr_set(&ds_database, db);
+ 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, &next_con->mysql,
+ 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 (!(next_con->name= my_strdup(ds_connection_name.str, MYF(MY_WME))))
+ if (!(con_slot->name= my_strdup(ds_connection_name.str, MYF(MY_WME))))
die("Out of memory");
- cur_con= next_con++;
+ cur_con= con_slot;
+
+ if (con_slot == next_con)
+ next_con++; /* if we used the next_con slot, advance the pointer */
}
dynstr_free(&ds_connection_name);
@@ -4028,10 +4536,10 @@ int read_line(char *buf, int size)
DBUG_RETURN(0);
}
else if ((c == '{' &&
- (!my_strnncoll_simple(charset_info, "while", 5,
- buf, min(5, p - buf), 0) ||
- !my_strnncoll_simple(charset_info, "if", 2,
- buf, min(2, p - buf), 0))))
+ (!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;
@@ -4169,7 +4677,7 @@ int read_line(char *buf, int size)
void convert_to_format_v1(char* query)
{
int last_c_was_quote= 0;
- char *p= query, *write= query;
+ char *p= query, *to= query;
char *end= strend(query);
char last_c;
@@ -4177,7 +4685,7 @@ void convert_to_format_v1(char* query)
{
if (*p == '\n' && !last_c_was_quote)
{
- *write++ = *p++; /* Save the newline */
+ *to++ = *p++; /* Save the newline */
/* Skip any spaces on next line */
while (*p && my_isspace(charset_info, *p))
@@ -4188,19 +4696,19 @@ void convert_to_format_v1(char* query)
else if (*p == '\'' || *p == '"' || *p == '`')
{
last_c= *p;
- *write++ = *p++;
+ *to++ = *p++;
/* Copy anything until the next quote of same type */
while (*p && *p != last_c)
- *write++ = *p++;
+ *to++ = *p++;
- *write++ = *p++;
+ *to++ = *p++;
last_c_was_quote= 1;
}
else
{
- *write++ = *p++;
+ *to++ = *p++;
last_c_was_quote= 0;
}
}
@@ -4318,22 +4826,20 @@ void check_eol_junk(const char *eol)
Create a command from a set of lines
SYNOPSIS
- read_command()
- command_ptr pointer where to return the new query
+ read_command()
+ command_ptr pointer where to return the new query
DESCRIPTION
- Converts lines returned by read_line into a command, this involves
- parsing the first word in the read line to find the command type.
-
+ Converts lines returned by read_line into a command, this involves
+ parsing the first word in the read line to find the command type.
A -- comment may contain a valid query as the first word after the
comment start. Thus it's always checked to see if that is the case.
The advantage with this approach is to be able to execute commands
terminated by new line '\n' regardless how many "delimiter" it contain.
-
*/
-#define MAX_QUERY (256*1024) /* 256K -- a test in sp-big is >128K */
+#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)
@@ -4381,9 +4887,13 @@ int read_command(struct st_command** command_ptr)
if (!(command->query_buf= command->query= my_strdup(p, MYF(MY_WME))))
die("Out of memory");
- /* Calculate first word and first argument */
- for (p= command->query; *p && !my_isspace(charset_info, *p) ; p++) ;
+ /* 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))
@@ -4412,7 +4922,7 @@ static struct my_option my_long_options[] =
{"cursor-protocol", OPT_CURSOR_PROTOCOL, "Use cursors for prepared statements.",
(gptr*) &cursor_protocol, (gptr*) &cursor_protocol, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"database", 'D', "Database to use.", (gptr*) &db, (gptr*) &db, 0,
+ {"database", 'D', "Database to use.", (gptr*) &opt_db, (gptr*) &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",
@@ -4421,7 +4931,7 @@ static struct my_option my_long_options[] =
{"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
#endif
- {"host", 'h', "Connect to host.", (gptr*) &host, (gptr*) &host, 0,
+ {"host", 'h', "Connect to host.", (gptr*) &opt_host, (gptr*) &opt_host, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"include", 'i', "Include SQL before each test case.", (gptr*) &opt_include,
(gptr*) &opt_include, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
@@ -4437,8 +4947,8 @@ static struct my_option my_long_options[] =
GET_INT, REQUIRED_ARG, 500, 1, 10000, 0, 0, 0},
{"password", 'p', "Password to use when connecting to server.",
0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"port", 'P', "Port number to use for connection.", (gptr*) &port,
- (gptr*) &port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"port", 'P', "Port number to use for connection.", (gptr*) &opt_port,
+ (gptr*) &opt_port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"ps-protocol", OPT_PS_PROTOCOL, "Use prepared statements protocol for communication",
(gptr*) &ps_protocol, (gptr*) &ps_protocol, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
@@ -4467,6 +4977,10 @@ static struct my_option my_long_options[] =
{"sp-protocol", OPT_SP_PROTOCOL, "Use stored procedures for select",
(gptr*) &sp_protocol, (gptr*) &sp_protocol, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"tail-lines", OPT_TAIL_LINES,
+ "Number of lines of the resul to include in a failure report",
+ (gptr*) &opt_tail_lines, (gptr*) &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},
@@ -4474,8 +4988,8 @@ static struct my_option my_long_options[] =
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.", (gptr*) &user, (gptr*) &user, 0, GET_STR,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"user", 'u', "User for login.", (gptr*) &opt_user, (gptr*) &opt_user, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"verbose", 'v', "Write more.", (gptr*) &verbose, (gptr*) &verbose, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"version", 'V', "Output version information and exit.",
@@ -4533,7 +5047,7 @@ void read_embedded_server_arguments(const char *name)
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);
+ die("Failed to open file '%s'", buff);
while (embedded_server_arg_count < MAX_EMBEDDED_SERVER_ARGS &&
(str=fgets(argument,sizeof(argument), file)))
@@ -4602,8 +5116,8 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
case 'p':
if (argument)
{
- my_free(pass, MYF(MY_ALLOW_ZERO_PTR));
- pass= my_strdup(argument, MYF(MY_FAE));
+ 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;
}
@@ -4640,7 +5154,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
exit(0);
case '?':
usage();
- exit(1);
+ exit(0);
}
return 0;
}
@@ -4660,9 +5174,9 @@ int parse_args(int argc, char **argv)
exit(1);
}
if (argc == 1)
- db= *argv;
+ opt_db= *argv;
if (tty_password)
- pass=get_tty_password(NullS);
+ opt_pass= get_tty_password(NullS); /* purify tested */
return 0;
}
@@ -4718,14 +5232,6 @@ void str_to_file(const char *fname, char *str, int size)
}
-void dump_result_to_reject_file(char *buf, int size)
-{
- char reject_file[FN_REFLEN];
- str_to_file(fn_format(reject_file, result_file_name, "", ".reject",
- MY_REPLACE_EXT),
- buf, size);
-}
-
void dump_result_to_log_file(char *buf, int size)
{
char log_file[FN_REFLEN];
@@ -4733,6 +5239,8 @@ void dump_result_to_log_file(char *buf, int size)
*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)
@@ -4969,13 +5477,13 @@ void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res)
void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt,
MYSQL_FIELD *fields, uint num_fields)
{
- MYSQL_BIND *bind;
+ MYSQL_BIND *my_bind;
my_bool *is_null;
ulong *length;
uint i;
/* Allocate array with bind structs, lengths and NULL flags */
- bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND),
+ 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));
@@ -4986,25 +5494,25 @@ void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt,
for (i= 0; i < num_fields; i++)
{
uint max_length= fields[i].max_length + 1;
- bind[i].buffer_type= MYSQL_TYPE_STRING;
- bind[i].buffer= (char *)my_malloc(max_length, MYF(MY_WME | MY_FAE));
- bind[i].buffer_length= max_length;
- bind[i].is_null= &is_null[i];
- bind[i].length= &length[i];
+ 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, bind[i].buffer_type, bind[i].buffer_length));
+ i, my_bind[i].buffer_type, my_bind[i].buffer_length));
}
- if (mysql_stmt_bind_result(stmt, bind))
+ 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], (const char *) bind[i].buffer,
- *bind[i].length, *bind[i].is_null);
+ append_field(ds, i, &fields[i], (const char *) my_bind[i].buffer,
+ *my_bind[i].length, *my_bind[i].is_null);
if (!display_result_vertically)
dynstr_append_mem(ds, "\n", 1);
}
@@ -5016,10 +5524,10 @@ void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt,
for (i= 0; i < num_fields; i++)
{
/* Free data for output */
- my_free((gptr)bind[i].buffer, MYF(MY_WME | MY_FAE));
+ my_free((gptr)my_bind[i].buffer, MYF(MY_WME | MY_FAE));
}
/* Free array with bind structs, lengths and NULL flags */
- my_free((gptr)bind , MYF(MY_WME | MY_FAE));
+ my_free((gptr)my_bind , MYF(MY_WME | MY_FAE));
my_free((gptr)length , MYF(MY_WME | MY_FAE));
my_free((gptr)is_null , MYF(MY_WME | MY_FAE));
}
@@ -5156,25 +5664,22 @@ int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql)
/*
Run query using MySQL C API
- SYNPOSIS
- run_query_normal
- mysql - mysql handle
- command - currrent command pointer
- flags -flags indicating wheter to SEND and/or REAP
- query - query string to execute
- query_len - length query string to execute
- ds - output buffer wherte to store result form query
-
- RETURN VALUE
- error - function will not return
+ 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 *mysql= &cn->mysql;
MYSQL_RES *res= 0;
+ MYSQL *mysql= &cn->mysql;
int err= 0, counter= 0;
DBUG_ENTER("run_query_normal");
DBUG_PRINT("enter",("flags: %d", flags));
@@ -5638,6 +6143,14 @@ end:
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
@@ -5645,10 +6158,7 @@ end:
*/
var_set_errno(mysql_stmt_errno(stmt));
-#ifndef BUG15518_FIXED
- mysql_stmt_close(stmt);
- cur_con->stmt= NULL;
-#endif
+
DBUG_VOID_RETURN;
}
@@ -5687,15 +6197,14 @@ int util_query(MYSQL* org_mysql, const char* 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
-
- SYNPOSIS
- run_query
- mysql - mysql handle
- command - currrent command pointer
-
*/
void run_query(struct st_connection *cn, struct st_command *command, int flags)
@@ -5712,10 +6221,11 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags)
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 sendign to server */
+ /* Scan for warning before sending to server */
scan_command_for_warnings(command);
/*
@@ -5897,7 +6407,6 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags)
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
@@ -5910,6 +6419,7 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags)
dynstr_free(&ds_result);
if (command->type == Q_EVAL)
dynstr_free(&eval_query);
+ DBUG_VOID_RETURN;
}
/****************************************************************************/
@@ -6147,7 +6657,12 @@ int main(int argc, char **argv)
connections_end= connections +
(sizeof(connections)/sizeof(struct st_connection)) - 1;
next_con= connections + 1;
- cur_con= connections;
+
+#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));
@@ -6201,6 +6716,7 @@ int main(int argc, char **argv)
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;
@@ -6216,6 +6732,7 @@ int main(int argc, char **argv)
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)
@@ -6229,15 +6746,13 @@ int main(int argc, char **argv)
#ifdef HAVE_OPENSSL
-#if MYSQL_VERSION_ID >= 50000
- opt_ssl_verify_server_cert= TRUE; /* Always on in mysqltest */
-#endif
-
if (opt_use_ssl)
{
mysql_ssl_set(&cur_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
opt_ssl_capath, opt_ssl_cipher);
#if MYSQL_VERSION_ID >= 50000
+ /* 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
@@ -6247,8 +6762,8 @@ int main(int argc, char **argv)
if (!(cur_con->name = my_strdup("default", MYF(MY_WME))))
die("Out of memory");
- safe_connect(&cur_con->mysql, cur_con->name, host, user, pass,
- db, port, unix_sock);
+ 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();
@@ -6319,6 +6834,7 @@ int main(int argc, char **argv)
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_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;
@@ -6341,7 +6857,7 @@ int main(int argc, char **argv)
break;
case Q_LET: do_let(command); break;
case Q_EVAL_RESULT:
- eval_result = 1; break;
+ die("'eval_result' command is deprecated");
case Q_EVAL:
case Q_QUERY_VERTICAL:
case Q_QUERY_HORIZONTAL:
@@ -6462,6 +6978,8 @@ int main(int argc, char **argv)
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;
@@ -6471,6 +6989,8 @@ int main(int argc, char **argv)
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)
@@ -6612,8 +7132,6 @@ int main(int argc, char **argv)
if (result_file_name && ds_warning_messages.length)
dump_warning_messages();
- dynstr_free(&ds_res);
-
timer_output();
/* Yes, if we got this far the test has suceeded! Sakila smiles */
cleanup_and_exit(0);
@@ -6819,7 +7337,8 @@ typedef struct st_replace_found {
void replace_strings_append(REPLACE *rep, DYNAMIC_STRING* ds,
- const char *str, int len)
+ const char *str,
+ int len __attribute__((unused)))
{
reg1 REPLACE *rep_pos;
reg2 REPLACE_STRING *rep_str;
@@ -7210,7 +7729,7 @@ int reg_replace(char** buf_p, int* buf_len_p, char *pattern,
we need at least what we have so far in the buffer + the part
before this match
*/
- need_buf_len= (res_p - buf) + subs[0].rm_so;
+ 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)
@@ -7220,17 +7739,17 @@ int reg_replace(char** buf_p, int* buf_len_p, char *pattern,
if (c == '\\' && expr_p + 1 < replace_end)
{
- back_ref_num= expr_p[1] - '0';
+ 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)
{
- int start_off,end_off;
+ 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 += (end_off - start_off);
+ need_buf_len += (int) (end_off - start_off);
}
expr_p += 2;
}
@@ -7250,7 +7769,7 @@ int reg_replace(char** buf_p, int* buf_len_p, char *pattern,
/* copy the pre-match part */
if (subs[0].rm_so)
{
- memcpy(res_p, str_p, subs[0].rm_so);
+ memcpy(res_p, str_p, (size_t) subs[0].rm_so);
res_p+= subs[0].rm_so;
}
@@ -7269,11 +7788,11 @@ int reg_replace(char** buf_p, int* buf_len_p, char *pattern,
if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub)
{
- int start_off,end_off;
+ 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= end_off - start_off;
+ int block_len= (int) (end_off - start_off);
memcpy(res_p,str_p + start_off, block_len);
res_p += block_len;
}