summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/Makefile.am3
-rw-r--r--client/mysqltest.c467
-rw-r--r--mysql-test/r/mysqltest.result11
-rw-r--r--mysql-test/t/mysqltest.test75
4 files changed, 500 insertions, 56 deletions
diff --git a/client/Makefile.am b/client/Makefile.am
index ff97243815a..94174614d30 100644
--- a/client/Makefile.am
+++ b/client/Makefile.am
@@ -46,7 +46,8 @@ mysqladmin_SOURCES = mysqladmin.cc
mysql_LDADD = @readline_link@ @TERMCAP_LIB@ $(LDADD) $(CXXLDFLAGS)
mysqltest_SOURCES= mysqltest.c $(top_srcdir)/mysys/my_getsystime.c \
$(yassl_dummy_link_fix)
-mysqltest_LDADD = $(top_builddir)/regex/libregex.a $(LDADD)
+mysqltest_LDADD = $(top_builddir)/regex/libregex.a $(LDADD) \
+ $(top_builddir)/mysys/libmysys.a
mysqlbinlog_SOURCES = mysqlbinlog.cc $(top_srcdir)/mysys/mf_tempdir.c \
$(top_srcdir)/mysys/my_new.cc \
$(top_srcdir)/mysys/my_bit.c \
diff --git a/client/mysqltest.c b/client/mysqltest.c
index c3db4cf9659..56c06a10f5b 100644
--- a/client/mysqltest.c
+++ b/client/mysqltest.c
@@ -24,7 +24,7 @@
* Jani
**/
-#define MTEST_VERSION "2.6"
+#define MTEST_VERSION "2.7"
#include <my_global.h>
#include <mysql_embed.h>
@@ -333,7 +333,8 @@ Q_EXIT,
Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT,
Q_IF,
Q_DISABLE_PARSING, Q_ENABLE_PARSING,
-Q_REPLACE_REGEX,
+Q_REPLACE_REGEX, Q_REMOVE_FILE, Q_FILE_EXIST,
+Q_WRITE_FILE, Q_COPY_FILE, Q_PERL,
Q_UNKNOWN, /* Unknown command. */
Q_COMMENT, /* Comments, ignored. */
@@ -423,6 +424,11 @@ const char *command_names[]=
"disable_parsing",
"enable_parsing",
"replace_regex",
+ "remove_file",
+ "file_exists",
+ "write_file",
+ "copy_file",
+ "perl",
0
};
@@ -468,7 +474,7 @@ static void replace_strings_append(struct st_replace *rep, DYNAMIC_STRING* ds,
const char *from, int len);
void free_pointer_array(POINTER_ARRAY *pa);
static void do_eval(DYNAMIC_STRING *query_eval, const char *query,
- my_bool pass_through_escape_chars);
+ const char* query_end, my_bool pass_through_escape_chars);
static void str_to_file(const char *fname, char *str, int size);
#ifdef __WIN__
@@ -501,8 +507,8 @@ static void handle_error(const char *query, struct st_query *q,
const char *err_sqlstate, DYNAMIC_STRING *ds);
static void handle_no_error(struct st_query *q);
-static void do_eval(DYNAMIC_STRING* query_eval, const char *query,
- my_bool pass_through_escape_chars)
+static 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;
@@ -510,7 +516,7 @@ static void do_eval(DYNAMIC_STRING* query_eval, const char *query,
VAR* v;
DBUG_ENTER("do_eval");
- for (p= query; (c = *p); ++p)
+ for (p= query; (c = *p) && p < query_end; ++p)
{
switch(c) {
case '$':
@@ -533,9 +539,9 @@ static void do_eval(DYNAMIC_STRING* query_eval, const char *query,
escaped = 0;
dynstr_append_mem(query_eval, p, 1);
}
- else if (next_c == '\\' || next_c == '$')
+ else if (next_c == '\\' || next_c == '$' || next_c == '"')
{
- /* Set escaped only if next char is \ or $ */
+ /* Set escaped only if next char is \, " or $ */
escaped = 1;
if (pass_through_escape_chars)
@@ -556,6 +562,108 @@ static void do_eval(DYNAMIC_STRING* query_eval, const char *query,
}
+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 string argument */
+ const char *description; /* Description of the argument */
+};
+
+static void check_command_args(struct st_query *command, const char *arguments,
+ const struct command_arg *args, int num_args)
+{
+ 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 surrounded by spaces */
+ case ARG_STRING:
+ start= ptr;
+ /* Find end of arg */
+ while (*ptr && !my_isspace(charset_info, *ptr))
+ ptr++;
+ init_dynamic_string(arg->ds, 0, 256, 256);
+ do_eval(arg->ds, start, ptr, FALSE);
+ command->last_argument= (char*)ptr;
+ if (*ptr)
+ ptr++;
+ 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;
+ break;
+
+ default:
+ DBUG_ASSERT("Unknown argument type");
+ break;
+ }
+
+ /* Check required arg */
+ if (arg->ds->length == 0 && arg->required)
+ die("Missing required argument '%s' to command '%.*s'", arg->argname,
+ command->first_word_len, command->query);
+
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+static void handle_command_error(struct st_query *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", command->first_word_len, command->query);
+ for (i= 0; i < command->expected_errors; i++)
+ {
+ DBUG_PRINT("info", ("expected error: %d",
+ command->expected_errno[i].code.errnum));
+ if ((command->expected_errno[i].type == ERR_ERRNO) &&
+ (command->expected_errno[i].code.errnum == error))
+ {
+ 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_errno[0].type == ERR_ERRNO &&
+ command->expected_errno[0].code.errnum != 0)
+ {
+ /* Error code we wanted was != 0, i.e. not an expected success */
+ die("command \"%.*s\" succeeded - should have failed with errno %d...",
+ command->first_word_len, command->query,
+ command->expected_errno[0].code.errnum);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
static void close_cons()
{
DBUG_ENTER("close_cons");
@@ -743,7 +851,7 @@ static int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname)
init_dynamic_string(&res_ds, "", stat_info.st_size+256, 256);
if (eval_result)
{
- do_eval(&res_ds, tmp, FALSE);
+ 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)
@@ -1168,7 +1276,7 @@ static void do_exec(struct st_query *query)
init_dynamic_string(&ds_cmd, 0, query->query_len+256, 256);
/* Eval the command, thus replacing all environment variables */
- do_eval(&ds_cmd, cmd, TRUE);
+ do_eval(&ds_cmd, cmd, query->end, TRUE);
cmd= ds_cmd.str;
DBUG_PRINT("info", ("Executing '%s' as '%s'",
@@ -1484,7 +1592,7 @@ void do_system(struct st_query *command)
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, TRUE);
+ do_eval(&ds_cmd, command->first_argument, command->end, TRUE);
DBUG_PRINT("info", ("running system command '%s' as '%s'",
command->first_argument, ds_cmd.str));
@@ -1506,6 +1614,291 @@ void do_system(struct st_query *command)
/*
+ SYNOPSIS
+ do_remove_file
+ command called command
+
+ DESCRIPTION
+ remove_file <file_name>
+ Remove the file <file_name>
+*/
+
+static void do_remove_file(struct st_query *command)
+{
+ int error;
+ 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
+*/
+
+static void do_copy_file(struct st_query *command)
+{
+ int error;
+ DYNAMIC_STRING ds_from_file;
+ 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_file_exists
+ command called command
+
+ DESCRIPTION
+ fiile_exist <file_name>
+ Check if file <file_name> exists
+*/
+
+static void do_file_exist(struct st_query *command)
+{
+ int error;
+ DYNAMIC_STRING ds_filename;
+ const struct command_arg file_exist_args[] = {
+ "filename", ARG_STRING, TRUE, &ds_filename, "File to check if it exist"
+ };
+ DBUG_ENTER("do_file_exist");
+
+ check_command_args(command, command->first_argument,
+ file_exist_args, sizeof(file_exist_args)/sizeof(struct command_arg));
+
+ DBUG_PRINT("info", ("Checking for existence of file: %s", ds_filename.str));
+ error= (access(ds_filename.str, F_OK) != 0);
+ handle_command_error(command, error);
+ dynstr_free(&ds_filename);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Read characters from line buffer or file. This is needed to allow
+ my_ungetc() to buffer MAX_DELIMITER 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.
+*/
+
+static int my_getc(FILE *file)
+{
+ if (line_buffer_pos == line_buffer)
+ return fgetc(file);
+ return *--line_buffer_pos;
+}
+
+
+static void my_ungetc(int c)
+{
+ *line_buffer_pos++= (char) c;
+}
+
+
+static my_bool match_delimiter(int c, const char* delim, uint length)
+{
+ uint i;
+ char tmp[MAX_DELIMITER];
+
+ if (c != *delim)
+ return 0;
+
+ for (i= 1; i < length &&
+ (c= my_getc(cur_file->file)) == *(delim + i);
+ i++)
+ tmp[i]= c;
+
+ if (i == length)
+ return 1; /* Found delimiter */
+
+ /* didn't find delimiter, push back things that we read */
+ my_ungetc(c);
+ while (i > 1)
+ my_ungetc(tmp[--i]);
+ return 0;
+}
+
+
+static void read_until_EOF(DYNAMIC_STRING* ds)
+{
+ int c;
+ DBUG_ENTER("read_until_EOF");
+
+ /* Read from file until delimiter EOF is found */
+ while (1)
+ {
+ c= my_getc(cur_file->file);
+
+ if (feof(cur_file->file))
+ die("End of file encountered before 'EOF' delimiter was found");
+
+ if (match_delimiter(c, "EOF", 3))
+ {
+ DBUG_PRINT("exit", ("Found EOF"));
+ break;
+ }
+ dynstr_append_mem(ds, (const char*)&c, 1);
+ }
+ DBUG_PRINT("exit", ("ds: %s", ds->str));
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ SYNOPSIS
+ do_write_file
+ command called command
+
+ DESCRIPTION
+ write_file <file_name>;
+ <what to write line 1>
+ <...>
+ < what to write line n>
+ EOF
+
+ --write_file <file_name>;
+ <what to write line 1>
+ <...>
+ < what to write line n>
+ EOF
+
+ Write everything between the "write_file" command and EOF to "file_name"
+
+ NOTE! Overwrites existing file
+
+*/
+
+static void do_write_file(struct st_query *command)
+{
+ DYNAMIC_STRING ds_content;
+ DYNAMIC_STRING ds_filename;
+ const struct command_arg write_file_args[] = {
+ "filename", ARG_STRING, TRUE, &ds_filename, "File to write to",
+ };
+ DBUG_ENTER("do_write_file");
+
+ check_command_args(command,
+ command->first_argument,
+ write_file_args,
+ sizeof(write_file_args)/sizeof(struct command_arg));
+
+ init_dynamic_string(&ds_content, "", 1024, 1024);
+ read_until_EOF(&ds_content);
+ DBUG_PRINT("info", ("Writing to file: %s", ds_filename.str));
+ str_to_file(ds_filename.str, ds_content.str, ds_content.length);
+ dynstr_free(&ds_content);
+ dynstr_free(&ds_filename);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ SYNOPSIS
+ do_perl
+ command command handle
+
+ DESCRIPTION
+ perl;
+ <perlscript line 1>
+ <...>
+ <perlscript line n>
+ EOF
+
+ Execute everything after "perl" until EOF as perl.
+ Useful for doing more advanced things
+ but still being able to execute it on all platforms.
+
+ The function sets delimiter to EOF and remembers that this
+ is a perl command by setting "perl mode". The following lines
+ will then be parsed as any normal query, but when searching
+ for command in get_query_type, this function will be called
+ again since "perl mode" is on and the perl script can be
+ executed.
+*/
+
+static void do_perl(struct st_query *command)
+{
+ int error;
+ char buf[FN_REFLEN];
+ FILE *res_file;
+ DYNAMIC_STRING ds_script;
+ DBUG_ENTER("do_perl");
+
+ init_dynamic_string(&ds_script, "", 1024, 1024);
+ read_until_EOF(&ds_script);
+
+ DBUG_PRINT("info", ("Executing perl: %s", ds_script.str));
+
+ /* Format a name for a tmp .pl file that is unique for this process */
+ my_snprintf(buf, sizeof(buf), "%s/tmp/tmp_%d.pl",
+ getenv("MYSQLTEST_VARDIR"), getpid());
+ str_to_file(buf, ds_script.str, ds_script.length);
+
+ /* Format the perl <filename> command */
+ my_snprintf(buf, sizeof(buf), "perl %s/tmp/tmp_%d.pl",
+ getenv("MYSQLTEST_VARDIR"), getpid());
+
+ if (!(res_file= popen(buf, "r")) && command->abort_on_error)
+ die("popen(\"%s\", \"r\") failed", buf);
+
+ while (fgets(buf, sizeof(buf), res_file))
+ {
+ if (disable_result_log)
+ {
+ buf[strlen(buf)-1]=0;
+ DBUG_PRINT("exec_result",("%s", buf));
+ }
+ else
+ {
+ replace_dynstr_append(&ds_res, buf);
+ }
+ }
+ error= pclose(res_file);
+ handle_command_error(command, WEXITSTATUS(error));
+ dynstr_free(&ds_script);
+ 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 \
@@ -1534,7 +1927,7 @@ int do_echo(struct st_query *command)
DYNAMIC_STRING ds_echo;
init_dynamic_string(&ds_echo, "", command->query_len, 256);
- do_eval(&ds_echo, command->first_argument, FALSE);
+ 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);
@@ -2900,50 +3293,9 @@ void do_block(enum block_cmd cmd, struct st_query* q)
}
-/*
- Read characters from line buffer or file. This is needed to allow
- my_ungetc() to buffer MAX_DELIMITER 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;
-}
-
-
my_bool end_of_query(int c)
{
- uint i;
- char tmp[MAX_DELIMITER];
-
- if (c != *delimiter)
- return 0;
-
- for (i= 1; i < delimiter_length &&
- (c= my_getc(cur_file->file)) == *(delimiter + i);
- i++)
- tmp[i]= c;
-
- if (i == delimiter_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;
+ return match_delimiter(c, delimiter, delimiter_length);
}
@@ -4786,7 +5138,7 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags)
if (command->type == Q_EVAL)
{
init_dynamic_string(&eval_query, "", command->query_len+256, 1024);
- do_eval(&eval_query, command->query, FALSE);
+ do_eval(&eval_query, command->query, command->end, FALSE);
query = eval_query.str;
query_len = eval_query.length;
}
@@ -5390,6 +5742,11 @@ int main(int argc, char **argv)
case Q_DEC: do_modify_var(q, DO_DEC); break;
case Q_ECHO: do_echo(q); query_executed= 1; break;
case Q_SYSTEM: do_system(q); break;
+ case Q_REMOVE_FILE: do_remove_file(q); break;
+ case Q_FILE_EXIST: do_file_exist(q); break;
+ case Q_WRITE_FILE: do_write_file(q); break;
+ case Q_COPY_FILE: do_copy_file(q); break;
+ case Q_PERL: do_perl(q); break;
case Q_DELIMITER:
do_delimiter(q);
break;
diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result
index 4c2695e5caf..fdc43e8852b 100644
--- a/mysql-test/r/mysqltest.result
+++ b/mysql-test/r/mysqltest.result
@@ -467,3 +467,14 @@ a D
1 1
1 4
drop table t1;
+mysqltest: At line 1: Missing required argument 'filename' to command 'remove_file'
+mysqltest: At line 1: Missing required argument 'filename' to command 'write_file'
+mysqltest: At line 1: End of file encountered before 'EOF' delimiter was found
+mysqltest: At line 1: End of line junk detected: "write_file filename ";
+"
+mysqltest: At line 1: Missing required argument 'filename' to command 'file_exists'
+mysqltest: At line 1: Missing required argument 'from_file' to command 'copy_file'
+mysqltest: At line 1: Missing required argument 'to_file' to command 'copy_file'
+hello
+hello
+hello
diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test
index 86cfd66ae2b..8aa85937c17 100644
--- a/mysql-test/t/mysqltest.test
+++ b/mysql-test/t/mysqltest.test
@@ -1159,3 +1159,78 @@ insert into t1 values (2,4);
--replace_regex /A/C/ /B/D/i /3/2/ /2/1/
select * from t1;
drop table t1;
+
+# ----------------------------------------------------------------------------
+# test for remove_file
+# ----------------------------------------------------------------------------
+
+--error 1
+--exec echo "remove_file ;" | $MYSQL_TEST 2>&1
+
+--error 1
+remove_file non_existing_file;
+
+# ----------------------------------------------------------------------------
+# test for write_file
+# ----------------------------------------------------------------------------
+--error 1
+--exec echo "write_file ;" | $MYSQL_TEST 2>&1
+
+--error 1
+--exec echo "write_file filename ;" | $MYSQL_TEST 2>&1
+
+--error 1
+--exec echo "write_file filename \";" | $MYSQL_TEST 2>&1
+
+# ----------------------------------------------------------------------------
+# test for file_exist
+# ----------------------------------------------------------------------------
+--error 1
+--exec echo "file_exists ;" | $MYSQL_TEST 2>&1
+
+--error 0,1
+remove_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp;
+--error 1
+file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp;
+write_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp;
+Content for test_file1
+EOF
+file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp;
+remove_file $MYSQLTEST_VARDIR/tmp/test_file1.tmp;
+--error 1
+file_exists $MYSQLTEST_VARDIR/tmp/test_file1.tmp;
+
+
+# ----------------------------------------------------------------------------
+# test for copy_file
+# ----------------------------------------------------------------------------
+--write_file $MYSQLTEST_VARDIR/tmp/file1.tmp
+file1
+EOF
+
+copy_file $MYSQLTEST_VARDIR/tmp/file1.tmp $MYSQLTEST_VARDIR/tmp/file2.tmp;
+file_exists $MYSQLTEST_VARDIR/tmp/file2.tmp;
+remove_file $MYSQLTEST_VARDIR/tmp/file1.tmp;
+remove_file $MYSQLTEST_VARDIR/tmp/file2.tmp;
+
+--error 1
+--exec echo "copy_file ;" | $MYSQL_TEST 2>&1
+
+--error 1
+--exec echo "copy_file from_file;" | $MYSQL_TEST 2>&1
+
+# ----------------------------------------------------------------------------
+# test for perl
+# ----------------------------------------------------------------------------
+--perl
+print "hello\n";
+EOF
+
+perl;
+print "hello\n";
+EOF
+
+perl;
+ # Print "hello"
+ print "hello\n";
+EOF