summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authorunknown <cmiller@zippy.cornsilk.net>2006-11-02 17:51:59 -0500
committerunknown <cmiller@zippy.cornsilk.net>2006-11-02 17:51:59 -0500
commit75ed6f1a3ba3e908d7feaf5f1fd658ff7e4309f3 (patch)
treedc9dc6a9c5046129bcdf2523c94969503fb1a0cc /client
parent74a15e2f8e73ec23c69d667e7c36b03822ba3fb4 (diff)
parent6afaa993e4142527a0f6cc8b9d853e976105a716 (diff)
downloadmariadb-git-75ed6f1a3ba3e908d7feaf5f1fd658ff7e4309f3.tar.gz
Merge bk-internal.mysql.com:/home/bk/mysql-5.1
into zippy.cornsilk.net:/home/cmiller/work/mysql/mysql-5.1-maint BitKeeper/deleted/.del-CMakeLists.txt~1: Auto merged BitKeeper/deleted/.del-make_win_bin_dist: Auto merged configure.in: Auto merged include/my_global.h: Auto merged include/mysql.h: Auto merged libmysqld/lib_sql.cc: Auto merged mysql-test/include/mix1.inc: Auto merged mysql-test/r/ctype_utf8.result: Auto merged mysql-test/r/innodb_mysql.result: Auto merged mysql-test/r/log_tables.result: Auto merged mysql-test/r/trigger.result: Auto merged mysql-test/r/view.result: Auto merged mysql-test/t/ctype_utf8.test: Auto merged mysql-test/t/im_daemon_life_cycle.imtest: Auto merged mysql-test/t/sp-error.test: Auto merged mysql-test/t/sp.test: Auto merged mysql-test/t/trigger.test: Auto merged mysql-test/t/view.test: Auto merged netware/BUILD/mwenv: Auto merged scripts/make_binary_distribution.sh: Auto merged sql/Makefile.am: Auto merged sql/handler.cc: Auto merged sql/item_func.cc: Auto merged sql/item_func.h: Auto merged sql/log.cc: Auto merged sql/mysql_priv.h: Auto merged sql/mysqld.cc: Auto merged sql/set_var.cc: Auto merged sql/sp.cc: Auto merged sql/sql_class.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_delete.cc: Auto merged sql/sql_insert.cc: Auto merged sql/sql_lex.cc: Auto merged sql/sql_lex.h: Auto merged sql/sql_parse.cc: Auto merged sql/sql_prepare.cc: Auto merged sql/sql_select.cc: Auto merged sql/sql_show.cc: Auto merged sql/sql_table.cc: Auto merged sql/sql_trigger.cc: Auto merged sql/sql_union.cc: Auto merged sql/sql_update.cc: Auto merged sql/sql_view.cc: Auto merged sql/sql_yacc.yy: Auto merged sql/table.cc: Auto merged storage/innobase/handler/ha_innodb.cc: Auto merged tests/mysql_client_test.c: Auto merged include/my_time.h: manual merge. mysql-test/mysql-test-run.pl: manual merge. mysql-test/r/ps.result: manual merge. mysql-test/t/disabled.def: manual merge. mysql-test/t/ps.test: manual merge.
Diffstat (limited to 'client')
-rw-r--r--client/Makefile.am3
-rw-r--r--client/mysql.cc23
-rw-r--r--client/mysqldump.c142
-rw-r--r--client/mysqltest.c5858
4 files changed, 3467 insertions, 2559 deletions
diff --git a/client/Makefile.am b/client/Makefile.am
index d3e96dd126f..7d48e34b37b 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/mysql.cc b/client/mysql.cc
index 1dbdeb8be97..486e5bc113c 100644
--- a/client/mysql.cc
+++ b/client/mysql.cc
@@ -386,6 +386,21 @@ int main(int argc,char *argv[])
else
status.add_to_history=1;
status.exit_status=1;
+
+ {
+ /*
+ The file descriptor-layer may be out-of-sync with the file-number layer,
+ so we make sure that "stdout" is really open. If its file is closed then
+ explicitly close the FD layer.
+ */
+ int stdout_fileno_copy;
+ stdout_fileno_copy= dup(fileno(stdout)); /* Okay if fileno fails. */
+ if (stdout_fileno_copy == -1)
+ fclose(stdout);
+ else
+ close(stdout_fileno_copy); /* Clean up dup(). */
+ }
+
load_defaults("my",load_default_groups,&argc,&argv);
defaults_argv=argv;
if (get_options(argc, (char **) argv))
@@ -2091,6 +2106,8 @@ com_go(String *buffer,char *line __attribute__((unused)))
(long) mysql_num_rows(result),
(long) mysql_num_rows(result) == 1 ? "row" : "rows");
end_pager();
+ if (mysql_errno(&mysql))
+ error= put_error(&mysql);
}
}
else if (mysql_affected_rows(&mysql) == ~(ulonglong) 0)
@@ -2905,7 +2922,11 @@ com_connect(String *buffer, char *line)
bzero(buff, sizeof(buff));
if (buffer)
{
- strmake(buff, line, sizeof(buff) - 1);
+ /*
+ Two null bytes are needed in the end of buff to allow
+ get_arg to find end of string the second time it's called.
+ */
+ strmake(buff, line, sizeof(buff)-2);
tmp= get_arg(buff, 0);
if (tmp && *tmp)
{
diff --git a/client/mysqldump.c b/client/mysqldump.c
index 2d2961e465c..992f20db6b6 100644
--- a/client/mysqldump.c
+++ b/client/mysqldump.c
@@ -45,6 +45,7 @@
#include <m_string.h>
#include <m_ctype.h>
#include <hash.h>
+#include <stdarg.h>
#include "client_priv.h"
#include "mysql.h"
@@ -536,6 +537,8 @@ static void write_header(FILE *sql_file, char *db_name)
if (opt_xml)
{
fputs("<?xml version=\"1.0\"?>\n", sql_file);
+ /* Schema reference. Allows use of xsi:nil for NULL values and
+ xsi:type to define an element's data type. */
fputs("<mysqldump ", sql_file);
fputs("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"",
sql_file);
@@ -1136,7 +1139,7 @@ static char *quote_for_like(const char *name, char *buff)
SYNOPSIS
print_quoted_xml()
- output - output file
+ xml_file - output file
str - string to print
len - its length
@@ -1173,34 +1176,63 @@ static void print_quoted_xml(FILE *xml_file, const char *str, ulong len)
/*
- Print xml tag with one attribute.
+ Print xml tag. Optionally add attribute(s).
SYNOPSIS
- print_xml_tag1()
- xml_file - output file
- sbeg - line beginning
- stag_atr - tag and attribute
- sval - value of attribute
- send - line ending
+ print_xml_tag(xml_file, sbeg, send, tag_name, first_attribute_name,
+ ..., attribute_name_n, attribute_value_n, NullS)
+ xml_file - output file
+ sbeg - line beginning
+ send - line ending
+ tag_name - XML tag name.
+ first_attribute_name - tag and first attribute
+ first_attribute_value - (Implied) value of first attribute
+ attribute_name_n - attribute n
+ attribute_value_n - value of attribute n
DESCRIPTION
- Print tag with one attribute to the xml_file. Format is:
- sbeg<stag_atr="sval">send
+ Print XML tag with any number of attribute="value" pairs to the xml_file.
+
+ Format is:
+ sbeg<tag_name first_attribute_name="first_attribute_value" ...
+ attribute_name_n="attribute_value_n">send
NOTE
- sval MUST be a NULL terminated string.
- sval string will be qouted before output.
+ Additional arguments must be present in attribute/value pairs.
+ The last argument should be the null character pointer.
+ All attribute_value arguments MUST be NULL terminated strings.
+ All attribute_value arguments will be quoted before output.
*/
-static void print_xml_tag1(FILE * xml_file, const char* sbeg,
- const char* stag_atr, const char* sval,
- const char* send)
+static void print_xml_tag(FILE * xml_file, const char* sbeg, const char* send,
+ const char* tag_name,
+ const char* first_attribute_name, ...)
{
+ va_list arg_list;
+ const char *attribute_name, *attribute_value;
+
fputs(sbeg, xml_file);
- fputs("<", xml_file);
- fputs(stag_atr, xml_file);
- fputs("\"", xml_file);
- print_quoted_xml(xml_file, sval, strlen(sval));
- fputs("\">", xml_file);
+ fputc('<', xml_file);
+ fputs(tag_name, xml_file);
+
+ va_start(arg_list, first_attribute_name);
+ attribute_name= first_attribute_name;
+ while (attribute_name != NullS)
+ {
+ attribute_value= va_arg(arg_list, char *);
+ DBUG_ASSERT(attribute_value != NullS);
+
+ fputc(' ', xml_file);
+ fputs(attribute_name, xml_file);
+ fputc('\"', xml_file);
+
+ print_quoted_xml(xml_file, attribute_value, strlen(attribute_value));
+ fputc('\"', xml_file);
+
+ attribute_name= va_arg(arg_list, char *);
+ }
+ va_end(arg_list);
+
+ fputc('>', xml_file);
fputs(send, xml_file);
check_io(xml_file);
}
@@ -1411,6 +1443,28 @@ static uint dump_events_for_db(char *db)
/*
+ Print hex value for blob data.
+
+ SYNOPSIS
+ print_blob_as_hex()
+ output_file - output file
+ str - string to print
+ len - its length
+
+ DESCRIPTION
+ Print hex value for blob data.
+*/
+
+static void print_blob_as_hex(FILE *output_file, const char *str, ulong len)
+{
+ /* sakaik got the idea to to provide blob's in hex notation. */
+ const char *ptr= str, *end= ptr + len;
+ for (; ptr < end ; ptr++)
+ fprintf(output_file, "%02X", *((uchar *)ptr));
+ check_io(output_file);
+}
+
+/*
dump_routines_for_db
-- retrieves list of routines for a given db, and prints out
the CREATE PROCEDURE definition into the output (the dump).
@@ -1857,7 +1911,8 @@ static uint get_table_structure(char *table, char *db, char *table_type,
if (!opt_xml)
fprintf(sql_file, "CREATE TABLE %s (\n", result_table);
else
- print_xml_tag1(sql_file, "\t", "table_structure name=", table, "\n");
+ print_xml_tag(sql_file, "\t", "\n", "table_structure", "name=", table,
+ NullS);
check_io(sql_file);
}
@@ -2429,8 +2484,8 @@ static void dump_table(char *table, char *db)
rownr=0;
init_length=(uint) insert_pat.length+4;
if (opt_xml)
- print_xml_tag1(md_result_file, "\t", "table_data name=", table, "\n");
-
+ print_xml_tag(md_result_file, "\t", "\n", "table_data", "name=", table,
+ NullS);
if (opt_autocommit)
{
fprintf(md_result_file, "set autocommit=0;\n");
@@ -2484,7 +2539,7 @@ static void dump_table(char *table, char *db)
field->type == MYSQL_TYPE_LONG_BLOB ||
field->type == MYSQL_TYPE_MEDIUM_BLOB ||
field->type == MYSQL_TYPE_TINY_BLOB)) ? 1 : 0;
- if (extended_insert)
+ if (extended_insert && !opt_xml)
{
if (i == 0)
dynstr_set(&extended_row,"(");
@@ -2573,18 +2628,25 @@ static void dump_table(char *table, char *db)
{
if (opt_xml)
{
- print_xml_tag1(md_result_file, "\t\t", "field name=",
- field->name, "");
- print_quoted_xml(md_result_file, row[i], length);
+ if (opt_hex_blob && is_blob && length)
+ {
+ /* Define xsi:type="xs:hexBinary" for hex encoded data */
+ print_xml_tag(md_result_file, "\t\t", "", "field", "name=",
+ field->name, "xsi:type=", "xs:hexBinary", NullS);
+ print_blob_as_hex(md_result_file, row[i], length);
+ }
+ else
+ {
+ print_xml_tag(md_result_file, "\t\t", "", "field", "name=",
+ field->name, NullS);
+ print_quoted_xml(md_result_file, row[i], length);
+ }
fputs("</field>\n", md_result_file);
}
else if (opt_hex_blob && is_blob && length)
{
- /* sakaik got the idea to to provide blob's in hex notation. */
- char *ptr= row[i], *end= ptr + length;
fputs("0x", md_result_file);
- for (; ptr < end ; ptr++)
- fprintf(md_result_file, "%02X", *((uchar *)ptr));
+ print_blob_as_hex(md_result_file, row[i], length);
}
else
unescape(md_result_file, row[i], length);
@@ -2595,8 +2657,8 @@ static void dump_table(char *table, char *db)
char *ptr= row[i];
if (opt_xml)
{
- print_xml_tag1(md_result_file, "\t\t", "field name=",
- field->name, "");
+ print_xml_tag(md_result_file, "\t\t", "", "field", "name=",
+ field->name, NullS);
fputs(!my_isalpha(charset_info, *ptr) ? ptr: "NULL",
md_result_file);
fputs("</field>\n", md_result_file);
@@ -3070,7 +3132,7 @@ static int dump_all_tables_in_db(char *database)
if (init_dumping(database, init_dumping_tables))
return 1;
if (opt_xml)
- print_xml_tag1(md_result_file, "", "database name=", database, "\n");
+ print_xml_tag(md_result_file, "", "\n", "database", "name=", database, NullS);
if (lock_tables)
{
DYNAMIC_STRING query;
@@ -3153,7 +3215,7 @@ static my_bool dump_all_views_in_db(char *database)
if (init_dumping(database, init_dumping_views))
return 1;
if (opt_xml)
- print_xml_tag1(md_result_file, "", "database name=", database, "\n");
+ print_xml_tag(md_result_file, "", "\n", "database", "name=", database, NullS);
if (lock_tables)
{
DYNAMIC_STRING query;
@@ -3292,7 +3354,7 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
/* We shall countinue here, if --force was given */
}
if (opt_xml)
- print_xml_tag1(md_result_file, "", "database name=", db, "\n");
+ print_xml_tag(md_result_file, "", "\n", "database", "name=", db, NullS);
/* Dump each selected table */
for (pos= dump_tables; pos < end; pos++)
@@ -3405,7 +3467,7 @@ static int do_reset_master(MYSQL *mysql_con)
}
-static int start_transaction(MYSQL *mysql_con, my_bool consistent_read_now)
+static int start_transaction(MYSQL *mysql_con)
{
/*
We use BEGIN for old servers. --single-transaction --master-data will fail
@@ -3420,10 +3482,8 @@ static int start_transaction(MYSQL *mysql_con, my_bool consistent_read_now)
"SET SESSION TRANSACTION ISOLATION "
"LEVEL REPEATABLE READ") ||
mysql_query_with_error_report(mysql_con, 0,
- consistent_read_now ?
"START TRANSACTION "
- "WITH CONSISTENT SNAPSHOT" :
- "BEGIN"));
+ "/*!40100 WITH CONSISTENT SNAPSHOT */"));
}
@@ -3913,7 +3973,7 @@ int main(int argc, char **argv)
if ((opt_lock_all_tables || opt_master_data) &&
do_flush_tables_read_lock(mysql))
goto err;
- if (opt_single_transaction && start_transaction(mysql, test(opt_master_data)))
+ if (opt_single_transaction && start_transaction(mysql))
goto err;
if (opt_delete_master_logs && do_reset_master(mysql))
goto err;
diff --git a/client/mysqltest.c b/client/mysqltest.c
index f9c4ae617fd..7c1ce19283f 100644
--- a/client/mysqltest.c
+++ b/client/mysqltest.c
@@ -14,35 +14,24 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* mysqltest test tool
- * See the manual for more information
- * TODO: document better how mysqltest works
- *
- * Written by:
- * Sasha Pachev <sasha@mysql.com>
- * Matt Wagner <matt@mysql.com>
- * Monty
- * Jani
- **/
-
-/**********************************************************************
- TODO:
+/*
+ mysqltest
-- Do comparison line by line, instead of doing a full comparison of
- the text file. This will save space as we don't need to keep many
- results in memory. It will also make it possible to do simple
- 'comparison' fixes like accepting the result even if a float differed
- in the last decimals.
+ Tool used for executing a .test file
-- Don't buffer lines from the test that you don't expect to need
- again.
+ See the "MySQL Test framework manual" for more information
+ http://dev.mysql.com/doc/mysqltest/en/index.html
-- Change 'read_line' to be faster by using the readline.cc code;
- We can do better than calling feof() for each character!
+ Please keep the test framework tools identical in all versions!
-**********************************************************************/
+ Written by:
+ Sasha Pachev <sasha@mysql.com>
+ Matt Wagner <matt@mysql.com>
+ Monty
+ Jani
+*/
-#define MTEST_VERSION "2.6"
+#define MTEST_VERSION "3.0"
#include <my_global.h>
#include <mysql_embed.h>
@@ -51,18 +40,18 @@
#include <mysql.h>
#include <mysql_version.h>
#include <mysqld_error.h>
+#include <errmsg.h>
#include <m_ctype.h>
#include <my_dir.h>
-#include <errmsg.h> /* Error codes */
#include <hash.h>
#include <my_getopt.h>
#include <stdarg.h>
-#include <sys/stat.h>
#include <violite.h>
-#include "my_regex.h" /* Our own version of lib */
+#include "my_regex.h" /* Our own version of regex */
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
+
#ifndef WEXITSTATUS
# ifdef __WIN__
# define WEXITSTATUS(stat_val) (stat_val)
@@ -70,169 +59,116 @@
# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
# endif
#endif
-/* MAX_QUERY is 256K -- there is a test in sp-big that is >128K */
-#define MAX_QUERY (256*1024)
-#define MAX_VAR_NAME 256
-#define MAX_COLUMNS 256
-#define MAX_CONS 128
-#define MAX_INCLUDE_DEPTH 16
-#define INIT_Q_LINES 1024
-#define MIN_VAR_ALLOC 32
-#define BLOCK_STACK_DEPTH 32
-#define MAX_EXPECTED_ERRORS 10
-#define QUERY_SEND 1
-#define QUERY_REAP 2
-#define MAX_SERVER_ARGS 64
-
-
-#define SLAVE_POLL_INTERVAL 300000 /* 0.3 of a sec */
-#define DEFAULT_DELIMITER ";"
-#define MAX_DELIMITER 16
-
-#define RESULT_OK 0
-#define RESULT_CONTENT_MISMATCH 1
-#define 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};
-
-/* ************************************************************************ */
-/*
- The list of error codes to --error are stored in an internal array of
- structs. This struct can hold numeric SQL error codes or SQLSTATE codes
- as strings. The element next to the last active element in the list is
- set to type ERR_EMPTY. When an SQL statement returns an error, we use
- this list to check if this is an expected error.
-*/
-
-enum match_err_type
-{
- ERR_EMPTY= 0,
- ERR_ERRNO,
- ERR_SQLSTATE
-};
-
-typedef struct
-{
- enum match_err_type type;
- union
- {
- uint errnum;
- char sqlstate[SQLSTATE_LENGTH+1]; /* \0 terminated string */
- } code;
-} match_err;
-
-typedef struct
-{
- const char *name;
- long code;
-} st_error;
-static st_error global_error[] =
-{
-#include <mysqld_ername.h>
- { 0, 0 }
+#define MAX_VAR_NAME_LENGTH 256
+#define MAX_COLUMNS 256
+#define MAX_EMBEDDED_SERVER_ARGS 64
+#define MAX_DELIMITER_LENGTH 16
+
+/* Flags controlling send and reap */
+#define QUERY_SEND_FLAG 1
+#define QUERY_REAP_FLAG 2
+
+ enum {
+ RESULT_OK= 0,
+ RESULT_CONTENT_MISMATCH= 1,
+ RESULT_LENGTH_MISMATCH= 2
+ };
+
+enum {
+ OPT_SKIP_SAFEMALLOC=256, OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT,
+ OPT_SSL_CA, OPT_SSL_CAPATH, OPT_SSL_CIPHER, OPT_PS_PROTOCOL,
+ OPT_SP_PROTOCOL, OPT_CURSOR_PROTOCOL, OPT_VIEW_PROTOCOL,
+ OPT_SSL_VERIFY_SERVER_CERT, OPT_MAX_CONNECT_RETRIES,
+ OPT_MARK_PROGRESS
};
-static match_err global_expected_errno[MAX_EXPECTED_ERRORS];
-static uint global_expected_errors;
-
-/* ************************************************************************ */
-
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 *db= 0, *pass= 0;
+const char *user= 0, *host= 0, *unix_sock= 0, *opt_basedir= "./";
const char *opt_include= 0;
-static int port = 0;
+static int port= 0;
static int opt_max_connect_retries;
-static my_bool opt_big_test= 0, opt_compress= 0, silent= 0, verbose = 0;
+static my_bool opt_compress= 0, silent= 0, verbose= 0;
static my_bool tty_password= 0;
static my_bool opt_mark_progress= 0;
static my_bool ps_protocol= 0, ps_protocol_enabled= 0;
static my_bool sp_protocol= 0, sp_protocol_enabled= 0;
static my_bool view_protocol= 0, view_protocol_enabled= 0;
static my_bool cursor_protocol= 0, cursor_protocol_enabled= 0;
-static my_bool opt_valgrind_test= 0;
-static int parsing_disabled= 0;
+static my_bool parsing_disabled= 0;
+static my_bool display_result_vertically= FALSE, display_metadata= FALSE;
+static my_bool disable_query_log= 0, disable_result_log= 0;
+static my_bool disable_warnings= 0, disable_ps_warnings= 0;
+static my_bool disable_info= 1;
+static my_bool abort_on_error= 1;
static char **default_argv;
-static const char *load_default_groups[]= { "mysqltest","client",0 };
-static char line_buffer[MAX_DELIMITER], *line_buffer_pos= line_buffer;
+static const char *load_default_groups[]= { "mysqltest", "client", 0 };
+static char line_buffer[MAX_DELIMITER_LENGTH], *line_buffer_pos= line_buffer;
-typedef struct
-{
- FILE* file;
- const char *file_name;
- uint lineno; /* Current line in file */
-} test_file;
+static uint start_lineno= 0; /* Start line of current command */
+
+static char delimiter[MAX_DELIMITER_LENGTH]= ";";
+static uint delimiter_length= 1;
-static test_file file_stack[MAX_INCLUDE_DEPTH];
-static test_file* cur_file;
-static test_file* file_stack_end;
-uint start_lineno= 0; /* Start line of query */
+static char TMPDIR[FN_REFLEN];
-/* Stores regex substitutions */
+/* Block stack */
+enum block_cmd {
+ cmd_none,
+ cmd_if,
+ cmd_while
+};
-struct st_regex
+struct st_block
{
- 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 */
+ int line; /* Start line of block */
+ my_bool ok; /* Should block be executed */
+ enum block_cmd cmd; /* Command owning the block */
};
-struct st_replace_regex
+static struct st_block block_stack[32];
+static struct st_block *cur_block, *block_stack_end;
+
+/* Open file stack */
+struct st_test_file
{
- 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;
+ FILE* file;
+ const char *file_name;
+ uint lineno; /* Current line in file */
};
-struct st_replace_regex *glob_replace_regex= 0;
-
-static char TMPDIR[FN_REFLEN];
-static char delimiter[MAX_DELIMITER]= DEFAULT_DELIMITER;
-static uint delimiter_length= 1;
+static struct st_test_file file_stack[16];
+static struct st_test_file* cur_file;
+static struct st_test_file* file_stack_end;
-/* Block stack */
-enum block_cmd { cmd_none, cmd_if, cmd_while };
-typedef struct
-{
- int line; /* Start line of block */
- my_bool ok; /* Should block be executed */
- enum block_cmd cmd; /* Command owning the block */
-} BLOCK;
-static BLOCK block_stack[BLOCK_STACK_DEPTH];
-static BLOCK *cur_block, *block_stack_end;
static CHARSET_INFO *charset_info= &my_charset_latin1; /* Default charset */
static const char *charset_name= "latin1"; /* Default character set name */
-static int embedded_server_arg_count=0;
-static char *embedded_server_args[MAX_SERVER_ARGS];
+static const char *embedded_server_groups[]=
+{
+ "server",
+ "embedded",
+ "mysqltest_SERVER",
+ NullS
+};
-static my_bool display_result_vertically= FALSE, display_metadata= FALSE;
+static int embedded_server_arg_count=0;
+static char *embedded_server_args[MAX_EMBEDDED_SERVER_ARGS];
-/* See the timer_output() definition for details */
+/*
+ Timer related variables
+ See the timer_output() definition for details
+*/
static char *timer_file = NULL;
-static ulonglong timer_start, progress_start= 0;
-static int got_end_timer= FALSE;
+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 */
@@ -242,47 +178,25 @@ static void init_re(void);
static int match_re(my_regex_t *, char *);
static void free_re(void);
-static int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace,
- char *string, int icase);
-
-static const char *embedded_server_groups[]=
-{
- "server",
- "embedded",
- "mysqltest_SERVER",
- NullS
-};
-
DYNAMIC_ARRAY q_lines;
#include "sslopt-vars.h"
-typedef struct
-{
- char file[FN_REFLEN];
- ulong pos;
-} MASTER_POS ;
-
-struct connection
+struct
{
- MYSQL mysql;
- /* Used when creating views and sp, to avoid implicit commit */
- MYSQL* util_mysql;
- char *name;
- MYSQL_STMT* stmt;
-};
+ int read_lines,current_line;
+} parser;
-typedef struct
+struct
{
- int read_lines,current_line;
-} PARSER;
+ char file[FN_REFLEN];
+ ulong pos;
+} master_pos;
-PARSER parser;
-MASTER_POS master_pos;
/* if set, all results are concated and compared against this file */
-const char *result_file = 0;
+const char *result_file_name= 0;
-typedef struct
+typedef struct st_var
{
char *name;
int name_len;
@@ -295,70 +209,68 @@ typedef struct
char *env_s;
} VAR;
-VAR var_reg[10];
/*Perl/shell-like variable registers */
-HASH var_hash;
-my_bool disable_query_log=0, disable_result_log=0, disable_warnings=0;
-my_bool disable_ps_warnings= 0;
-my_bool disable_info= 1; /* By default off */
-my_bool abort_on_error= 1;
+VAR var_reg[10];
-struct connection cons[MAX_CONS];
-struct connection* cur_con, *next_con, *cons_end;
+HASH var_hash;
- /* Add new commands before Q_UNKNOWN !*/
+struct st_connection
+{
+ MYSQL mysql;
+ /* Used when creating views and sp, to avoid implicit commit */
+ MYSQL* util_mysql;
+ char *name;
+ MYSQL_STMT* stmt;
+};
+struct st_connection connections[128];
+struct st_connection* cur_con, *next_con, *connections_end;
+/*
+ List of commands in mysqltest
+ Must match the "command_names" array
+ Add new commands before Q_UNKNOWN!
+*/
enum enum_commands {
-Q_CONNECTION=1, Q_QUERY,
-Q_CONNECT, Q_SLEEP, Q_REAL_SLEEP,
-Q_INC, Q_DEC,
-Q_SOURCE, Q_DISCONNECT,
-Q_LET, Q_ECHO,
-Q_WHILE, Q_END_BLOCK,
-Q_SYSTEM, Q_RESULT,
-Q_REQUIRE, Q_SAVE_MASTER_POS,
-Q_SYNC_WITH_MASTER,
-Q_SYNC_SLAVE_WITH_MASTER,
-Q_ERROR,
-Q_SEND, Q_REAP,
-Q_DIRTY_CLOSE, Q_REPLACE, Q_REPLACE_COLUMN,
-Q_PING, Q_EVAL,
-Q_RPL_PROBE, Q_ENABLE_RPL_PARSE,
-Q_DISABLE_RPL_PARSE, Q_EVAL_RESULT,
-Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
-Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
-Q_WAIT_FOR_SLAVE_TO_STOP,
-Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
-Q_ENABLE_PS_WARNINGS, Q_DISABLE_PS_WARNINGS,
-Q_ENABLE_INFO, Q_DISABLE_INFO,
-Q_ENABLE_METADATA, Q_DISABLE_METADATA,
-Q_EXEC, Q_DELIMITER,
-Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR,
-Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS,
-Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL,
-Q_START_TIMER, Q_END_TIMER,
-Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL,
-Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT,
-Q_IF,
-Q_DISABLE_PARSING, Q_ENABLE_PARSING,
-Q_REPLACE_REGEX, Q_DIE,
-
-Q_UNKNOWN, /* Unknown command. */
-Q_COMMENT, /* Comments, ignored. */
-Q_COMMENT_WITH_COMMAND
+ Q_CONNECTION=1, Q_QUERY,
+ Q_CONNECT, Q_SLEEP, Q_REAL_SLEEP,
+ Q_INC, Q_DEC,
+ Q_SOURCE, Q_DISCONNECT,
+ Q_LET, Q_ECHO,
+ Q_WHILE, Q_END_BLOCK,
+ Q_SYSTEM, Q_RESULT,
+ Q_REQUIRE, Q_SAVE_MASTER_POS,
+ Q_SYNC_WITH_MASTER,
+ Q_SYNC_SLAVE_WITH_MASTER,
+ Q_ERROR,
+ Q_SEND, Q_REAP,
+ Q_DIRTY_CLOSE, Q_REPLACE, Q_REPLACE_COLUMN,
+ Q_PING, Q_EVAL,
+ Q_RPL_PROBE, Q_ENABLE_RPL_PARSE,
+ Q_DISABLE_RPL_PARSE, Q_EVAL_RESULT,
+ Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
+ Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
+ Q_WAIT_FOR_SLAVE_TO_STOP,
+ Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
+ Q_ENABLE_PS_WARNINGS, Q_DISABLE_PS_WARNINGS,
+ Q_ENABLE_INFO, Q_DISABLE_INFO,
+ Q_ENABLE_METADATA, Q_DISABLE_METADATA,
+ Q_EXEC, Q_DELIMITER,
+ Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR,
+ Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS,
+ Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL,
+ Q_START_TIMER, Q_END_TIMER,
+ Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL,
+ Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT,
+ Q_IF,
+ Q_DISABLE_PARSING, Q_ENABLE_PARSING,
+ Q_REPLACE_REGEX, Q_REMOVE_FILE, Q_FILE_EXIST,
+ Q_WRITE_FILE, Q_COPY_FILE, Q_PERL, Q_DIE, Q_EXIT,
+
+ Q_UNKNOWN, /* Unknown command. */
+ Q_COMMENT, /* Comments, ignored. */
+ Q_COMMENT_WITH_COMMAND
};
-/* this should really be called command */
-struct st_query
-{
- char *query, *query_buf,*first_argument,*last_argument,*end;
- int first_word_len;
- my_bool abort_on_error, require_file;
- match_err expected_errno[MAX_EXPECTED_ERRORS];
- uint expected_errors;
- char record_file[FN_REFLEN];
- enum enum_commands type;
-};
const char *command_names[]=
{
@@ -427,69 +339,118 @@ const char *command_names[]=
"disable_parsing",
"enable_parsing",
"replace_regex",
+ "remove_file",
+ "file_exists",
+ "write_file",
+ "copy_file",
+ "perl",
"die",
+ /* Don't execute any more commands, compare result */
+ "exit",
0
};
-TYPELIB command_typelib= {array_elements(command_names),"",
- command_names, 0};
-
-DYNAMIC_STRING ds_res, ds_progress;
-static void die(const char *fmt, ...);
-static void init_var_hash();
-static VAR* var_from_env(const char *, const char *);
-static byte* get_var_key(const byte* rec, uint* len, my_bool t);
-static VAR* var_init(VAR* v, const char *name, int name_len, const char *val,
- int val_len);
-static void var_free(void* v);
+/*
+ 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
+};
-void dump_result_to_reject_file(const char *record_file, char *buf, int size);
-void dump_result_to_log_file(const char *record_file, char *buf, int size);
+struct st_match_err
+{
+ enum match_err_type type;
+ union
+ {
+ uint errnum;
+ char sqlstate[SQLSTATE_LENGTH+1]; /* \0 terminated string */
+ } code;
+};
-int close_connection(struct st_query*);
-static void set_charset(struct st_query*);
-VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw,
- my_bool ignore_not_existing);
-int eval_expr(VAR* v, const char *p, const char** p_end);
-static int read_server_arguments(const char *name);
+struct st_expected_errors
+{
+ struct st_match_err err[10];
+ uint count;
+};
+static struct st_expected_errors saved_expected_errors;
-/* Definitions for replace result */
+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;
+};
-typedef struct st_pointer_array { /* when using array-strings */
- TYPELIB typelib; /* Pointer to strings */
- byte *str; /* Strings is here */
- int7 *flag; /* Flag about each var. */
- uint array_allocs,max_count,length,max_length;
-} POINTER_ARRAY;
+TYPELIB command_typelib= {array_elements(command_names),"",
+ command_names, 0};
-struct st_replace;
-struct st_replace *init_replace(my_string *from, my_string *to, uint count,
- my_string word_end_chars);
-void free_replace();
-static void free_replace_regex();
-static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name);
-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);
-static void str_to_file(const char *fname, char *str, int size);
+DYNAMIC_STRING ds_res, ds_progress, ds_warning_messages;
+
+void die(const char *fmt, ...)
+ ATTRIBUTE_FORMAT(printf, 1, 2);
+void abort_not_supported_test(const char *fmt, ...)
+ ATTRIBUTE_FORMAT(printf, 1, 2);
+void verbose_msg(const char *fmt, ...)
+ ATTRIBUTE_FORMAT(printf, 1, 2);
+void warning_msg(const char *fmt, ...)
+ ATTRIBUTE_FORMAT(printf, 1, 2);
+
+VAR* var_from_env(const char *, const char *);
+VAR* var_init(VAR* v, const char *name, int name_len, const char *val,
+ int val_len);
+void var_free(void* v);
+VAR* var_get(const char *var_name, const char** var_name_end,
+ my_bool raw, my_bool ignore_not_existing);
+void eval_expr(VAR* v, const char *p, const char** p_end);
+my_bool match_delimiter(int c, const char *delim, uint length);
+void dump_result_to_reject_file(char *buf, int size);
+void dump_result_to_log_file(char *buf, int size);
+void dump_warning_messages();
+void dump_progress();
+
+void do_eval(DYNAMIC_STRING *query_eval, const char *query,
+ const char *query_end, my_bool pass_through_escape_chars);
+void str_to_file(const char *fname, char *str, int size);
#ifdef __WIN__
-static void free_tmp_sh_file();
-static void free_win_path_patterns();
+void free_tmp_sh_file();
+void free_win_path_patterns();
#endif
-struct st_replace *glob_replace;
static int eval_result = 0;
-/* For column replace */
-char *replace_column[MAX_COLUMNS];
-uint max_replace_column= 0;
+/* 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();
+}
-static void get_replace_column(struct st_query *q);
-static void free_replace_column();
/* Disable functions that only exist in MySQL 4.0 */
#if MYSQL_VERSION_ID < 40000
@@ -498,35 +459,40 @@ void mysql_disable_rpl_parse(MYSQL* mysql __attribute__((unused))) {}
int mysql_rpl_parse_enabled(MYSQL* mysql __attribute__((unused))) { return 1; }
my_bool mysql_rpl_probe(MYSQL *mysql __attribute__((unused))) { return 1; }
#endif
-static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
- int len);
-static void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val);
-static void handle_error(const char *query, struct st_query *q,
- unsigned int err_errno, const char *err_error,
- 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)
+void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
+ int len);
+void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val);
+void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val);
+
+void handle_error(struct st_command*,
+ unsigned int err_errno, const char *err_error,
+ const char *err_sqlstate, DYNAMIC_STRING *ds);
+void handle_no_error(struct st_command*);
+
+
+
+void do_eval(DYNAMIC_STRING *query_eval, const char *query,
+ const char *query_end, my_bool pass_through_escape_chars)
{
const char *p;
register char c, next_c;
register int escaped = 0;
- VAR* v;
+ VAR *v;
DBUG_ENTER("do_eval");
- for (p= query; (c = *p); ++p)
+ for (p= query; (c= *p) && p < query_end; ++p)
{
switch(c) {
case '$':
if (escaped)
{
- escaped = 0;
+ escaped= 0;
dynstr_append_mem(query_eval, p, 1);
}
else
{
- if (!(v = var_get(p, &p, 0, 0)))
+ if (!(v= var_get(p, &p, 0, 0)))
die("Bad variable in eval");
dynstr_append_mem(query_eval, v->str_val, v->str_val_len);
}
@@ -535,13 +501,13 @@ static void do_eval(DYNAMIC_STRING* query_eval, const char *query,
next_c= *(p+1);
if (escaped)
{
- escaped = 0;
+ 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 $ */
- escaped = 1;
+ /* Set escaped only if next char is \, " or $ */
+ escaped= 1;
if (pass_through_escape_chars)
{
@@ -561,10 +527,131 @@ static void do_eval(DYNAMIC_STRING* query_eval, const char *query,
}
-static void close_cons()
+enum arg_type
+{
+ ARG_STRING,
+ ARG_REST
+};
+
+struct command_arg {
+ const char *argname; /* Name of argument */
+ enum arg_type type; /* Type of argument */
+ my_bool required; /* Argument required */
+ DYNAMIC_STRING *ds; /* Storage for argument */
+ const char *description; /* Description of the argument */
+};
+
+
+void check_command_args(struct st_command *command,
+ const char *arguments,
+ const struct command_arg *args,
+ int num_args, const char delimiter_arg)
+{
+ int i;
+ const char *ptr= arguments;
+ const char *start;
+
+ DBUG_ENTER("check_command_args");
+ DBUG_PRINT("enter", ("num_args: %d", num_args));
+ for (i= 0; i < num_args; i++)
+ {
+ const struct command_arg *arg= &args[i];
+
+ switch (arg->type)
+ {
+ /* A string */
+ case ARG_STRING:
+ /* Skip leading spaces */
+ while (*ptr && *ptr == ' ')
+ ptr++;
+ start= ptr;
+ /* Find end of arg, terminated by "delimiter_arg" */
+ while (*ptr && *ptr != delimiter_arg)
+ ptr++;
+ if (ptr > start)
+ {
+ init_dynamic_string(arg->ds, 0, ptr-start, 32);
+ do_eval(arg->ds, start, ptr, FALSE);
+ }
+ else
+ {
+ /* Empty string */
+ init_dynamic_string(arg->ds, "", 0, 0);
+ }
+ command->last_argument= (char*)ptr;
+
+ /* Step past the delimiter */
+ if (*ptr && *ptr == delimiter_arg)
+ ptr++;
+ DBUG_PRINT("info", ("val: %s", arg->ds->str));
+ break;
+
+ /* Rest of line */
+ case ARG_REST:
+ start= ptr;
+ init_dynamic_string(arg->ds, 0, command->query_len, 256);
+ do_eval(arg->ds, start, command->end, FALSE);
+ command->last_argument= command->end;
+ DBUG_PRINT("info", ("val: %s", arg->ds->str));
+ break;
+
+ default:
+ DBUG_ASSERT("Unknown argument type");
+ break;
+ }
+
+ /* Check required arg */
+ if (arg->ds->length == 0 && arg->required)
+ die("Missing required argument '%s' to command '%.*s'", arg->argname,
+ command->first_word_len, command->query);
+
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+void handle_command_error(struct st_command *command, uint error)
+{
+ DBUG_ENTER("handle_command_error");
+ DBUG_PRINT("enter", ("error: %d", error));
+ if (error != 0)
+ {
+ uint i;
+
+ if (command->abort_on_error)
+ die("command \"%.*s\" failed with error %d",
+ command->first_word_len, command->query, error);
+ for (i= 0; i < command->expected_errors.count; i++)
+ {
+ DBUG_PRINT("info", ("expected error: %d",
+ command->expected_errors.err[i].code.errnum));
+ if ((command->expected_errors.err[i].type == ERR_ERRNO) &&
+ (command->expected_errors.err[i].code.errnum == error))
+ {
+ DBUG_PRINT("info", ("command \"%.*s\" failed with expected error: %d",
+ command->first_word_len, command->query, error));
+ DBUG_VOID_RETURN;
+ }
+ }
+ die("command \"%.*s\" failed with wrong error: %d",
+ command->first_word_len, command->query, error);
+ }
+ else if (command->expected_errors.err[0].type == ERR_ERRNO &&
+ command->expected_errors.err[0].code.errnum != 0)
+ {
+ /* Error code we wanted was != 0, i.e. not an expected success */
+ die("command \"%.*s\" succeeded - should have failed with errno %d...",
+ command->first_word_len, command->query,
+ command->expected_errors.err[0].code.errnum);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+void close_connections()
{
- DBUG_ENTER("close_cons");
- for (--next_con; next_con >= cons; --next_con)
+ DBUG_ENTER("close_connections");
+ for (--next_con; next_con >= connections; --next_con)
{
if (next_con->stmt)
mysql_stmt_close(next_con->stmt);
@@ -578,14 +665,16 @@ static void close_cons()
}
-static void close_files()
+void close_files()
{
DBUG_ENTER("close_files");
for (; cur_file >= file_stack; cur_file--)
{
- DBUG_PRINT("info", ("file_name: %s", cur_file->file_name));
if (cur_file->file && cur_file->file != stdin)
+ {
+ DBUG_PRINT("info", ("closing file: %s", cur_file->file_name));
my_fclose(cur_file->file, MYF(0));
+ }
my_free((gptr)cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR));
cur_file->file_name= 0;
}
@@ -593,22 +682,22 @@ static void close_files()
}
-static void free_used_memory()
+void free_used_memory()
{
uint i;
DBUG_ENTER("free_used_memory");
- close_cons();
+ close_connections();
close_files();
hash_free(&var_hash);
- for (i=0 ; i < q_lines.elements ; i++)
+ for (i= 0 ; i < q_lines.elements ; i++)
{
- struct st_query **q= dynamic_element(&q_lines, i, struct st_query**);
+ struct st_command **q= dynamic_element(&q_lines, i, struct st_command**);
my_free((gptr) (*q)->query_buf,MYF(MY_ALLOW_ZERO_PTR));
my_free((gptr) (*q),MYF(0));
}
- for (i=0; i < 10; i++)
+ for (i= 0; i < 10; i++)
{
if (var_reg[i].alloced_len)
my_free(var_reg[i].str_val, MYF(MY_WME));
@@ -618,8 +707,8 @@ static void free_used_memory()
delete_dynamic(&q_lines);
dynstr_free(&ds_res);
dynstr_free(&ds_progress);
- free_replace();
- free_replace_column();
+ dynstr_free(&ds_warning_messages);
+ free_all_replace();
my_free(pass,MYF(MY_ALLOW_ZERO_PTR));
free_defaults(default_argv);
mysql_server_end();
@@ -631,10 +720,12 @@ static void free_used_memory()
DBUG_VOID_RETURN;
}
-static void die(const char *fmt, ...)
+
+void die(const char *fmt, ...)
{
va_list args;
DBUG_ENTER("die");
+ DBUG_PRINT("enter", ("start_lineno: %d", start_lineno));
/* Print the error message */
va_start(args, fmt);
@@ -653,8 +744,12 @@ static void die(const char *fmt, ...)
va_end(args);
/* Dump the result that has been accumulated so far to .log file */
- if (result_file && ds_res.length)
- dump_result_to_log_file(result_file, ds_res.str, ds_res.length);
+ if (result_file_name && ds_res.length)
+ dump_result_to_log_file(ds_res.str, ds_res.length);
+
+ /* Dump warning messages */
+ if (result_file_name && ds_warning_messages.length)
+ dump_warning_messages();
/* Clean up and exit */
free_used_memory();
@@ -667,10 +762,10 @@ static void die(const char *fmt, ...)
}
-static void abort_not_supported_test(const char *fmt, ...)
+void abort_not_supported_test(const char *fmt, ...)
{
va_list args;
- test_file* err_file= cur_file;
+ struct st_test_file* err_file= cur_file;
DBUG_ENTER("abort_not_supported_test");
/* Print include filestack */
@@ -706,7 +801,14 @@ static void abort_not_supported_test(const char *fmt, ...)
exit(62);
}
-static void verbose_msg(const char *fmt, ...)
+
+void abort_not_in_this_version()
+{
+ die("Not available in this version of mysqltest");
+}
+
+
+void verbose_msg(const char *fmt, ...)
{
va_list args;
DBUG_ENTER("verbose_msg");
@@ -715,6 +817,9 @@ static void verbose_msg(const char *fmt, ...)
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);
@@ -725,14 +830,46 @@ static void verbose_msg(const char *fmt, ...)
}
-void init_parser()
+void warning_msg(const char *fmt, ...)
{
- parser.current_line= parser.read_lines= 0;
- memset(&var_reg, 0, sizeof(var_reg));
+ va_list args;
+ char buff[512];
+ size_t len;
+ DBUG_ENTER("warning_msg");
+
+ va_start(args, fmt);
+ dynstr_append(&ds_warning_messages, "mysqltest: ");
+ if (start_lineno != 0)
+ {
+ dynstr_append(&ds_warning_messages, "Warning detected ");
+ if (cur_file && cur_file != file_stack)
+ {
+ len= my_snprintf(buff, sizeof(buff), "in included file %s ",
+ cur_file->file_name);
+ dynstr_append_mem(&ds_warning_messages,
+ buff, len);
+ }
+ len= my_snprintf(buff, sizeof(buff), "at line %d: ",
+ start_lineno);
+ dynstr_append_mem(&ds_warning_messages,
+ buff, len);
+ }
+#ifndef __WIN__
+ len= vsnprintf(buff, sizeof(buff), fmt, args);
+ dynstr_append_mem(&ds_warning_messages, buff, len);
+#endif
+ dynstr_append(&ds_warning_messages, "\n");
+ va_end(args);
+
+ DBUG_VOID_RETURN;
}
-static int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname)
+/*
+ Compare content of the string ds to content of file fname
+*/
+
+int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname)
{
MY_STAT stat_info;
char *tmp, *res_ptr;
@@ -755,25 +892,26 @@ static int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname)
die(NullS);
if (!eval_result && (uint) stat_info.st_size != ds->length)
{
- DBUG_PRINT("info",("Size differs: result size: %u file size: %u",
+ DBUG_PRINT("info",("Size differs: result size: %u file size: %llu",
ds->length, stat_info.st_size));
DBUG_PRINT("info",("result: '%s'", ds->str));
DBUG_RETURN(RESULT_LENGTH_MISMATCH);
}
if (!(tmp = (char*) my_malloc(stat_info.st_size + 1, MYF(MY_WME))))
- die(NullS);
+ die("Out of memory");
if ((fd = my_open(eval_file, O_RDONLY, MYF(MY_WME))) < 0)
- die(NullS);
+ die("Failed to open file %s", eval_file);
if (my_read(fd, (byte*)tmp, stat_info.st_size, MYF(MY_WME|MY_NABP)))
- die(NullS);
+ die("Failed to read from file %s, errno: %d", eval_file, errno);
tmp[stat_info.st_size] = 0;
- init_dynamic_string(&res_ds, "", 0, 65536);
+ init_dynamic_string(&res_ds, "", stat_info.st_size+256, 256);
if (eval_result)
{
- do_eval(&res_ds, tmp, FALSE);
- res_ptr = res_ds.str;
- if ((res_len = res_ds.length) != ds->length)
+ do_eval(&res_ds, tmp, tmp + stat_info.st_size, FALSE);
+ res_ptr= res_ds.str;
+ res_len= res_ds.length;
+ if (res_len != ds->length)
{
res= RESULT_LENGTH_MISMATCH;
goto err;
@@ -794,48 +932,41 @@ err:
MY_REPLACE_EXT),
res_ptr, res_len);
+ dynstr_free(&res_ds);
my_free((gptr) tmp, MYF(0));
my_close(fd, MYF(MY_WME));
- dynstr_free(&res_ds);
DBUG_RETURN(res);
}
+
/*
- Check the content of ds against content of file fname
+ Check the content of ds against result file
SYNOPSIS
check_result
ds - content to be checked
- fname - name of file to check against
- require_option - if set and check fails, the test will be aborted
- with the special exit code "not supported test"
RETURN VALUES
- error - the function will not return
+ error - the function will not return
*/
-static void check_result(DYNAMIC_STRING* ds, const char *fname,
- my_bool require_option)
+
+void check_result(DYNAMIC_STRING* ds)
{
- int res= dyn_string_cmp(ds, fname);
DBUG_ENTER("check_result");
+ DBUG_ASSERT(result_file_name);
- if (res && require_option)
+ switch (dyn_string_cmp(ds, result_file_name))
{
- char reason[FN_REFLEN];
- fn_format(reason, fname, "", "", MY_REPLACE_EXT | MY_REPLACE_DIR);
- abort_not_supported_test("Test requires: '%s'", reason);
- }
- switch (res) {
case RESULT_OK:
break; /* ok */
case RESULT_LENGTH_MISMATCH:
- dump_result_to_reject_file(fname, ds->str, ds->length);
+ dump_result_to_reject_file(ds->str, ds->length);
die("Result length mismatch");
break;
case RESULT_CONTENT_MISMATCH:
- dump_result_to_reject_file(fname, ds->str, ds->length);
+ dump_result_to_reject_file(ds->str, ds->length);
die("Result content mismatch");
break;
default: /* impossible */
@@ -846,13 +977,109 @@ static void check_result(DYNAMIC_STRING* ds, const char *fname,
}
-VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw,
+/*
+ Check the content of ds against a require file
+ If match fails, abort the test with special error code
+ indicating that test is not supported
+
+ SYNOPSIS
+ check_result
+ ds - content to be checked
+ fname - name of file to check against
+
+ RETURN VALUES
+ error - the function will not return
+
+*/
+
+void check_require(DYNAMIC_STRING* ds, const char *fname)
+{
+ DBUG_ENTER("check_require");
+
+ if (dyn_string_cmp(ds, fname))
+ {
+ char reason[FN_REFLEN];
+ fn_format(reason, fname, "", "", MY_REPLACE_EXT | MY_REPLACE_DIR);
+ abort_not_supported_test("Test requires: '%s'", reason);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+static byte *get_var_key(const byte* var, uint* len,
+ my_bool __attribute__((unused)) t)
+{
+ register char* key;
+ key = ((VAR*)var)->name;
+ *len = ((VAR*)var)->name_len;
+ return (byte*)key;
+}
+
+
+VAR *var_init(VAR *v, const char *name, int name_len, const char *val,
+ int val_len)
+{
+ int val_alloc_len;
+ VAR *tmp_var;
+ if (!name_len && name)
+ name_len = strlen(name);
+ if (!val_len && val)
+ val_len = strlen(val) ;
+ val_alloc_len = val_len + 16; /* room to grow */
+ if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var)
+ + name_len+1, MYF(MY_WME))))
+ die("Out of memory");
+
+ tmp_var->name = (name) ? (char*) tmp_var + sizeof(*tmp_var) : 0;
+ tmp_var->alloced = (v == 0);
+
+ if (!(tmp_var->str_val = my_malloc(val_alloc_len+1, MYF(MY_WME))))
+ die("Out of memory");
+
+ memcpy(tmp_var->name, name, name_len);
+ if (val)
+ {
+ memcpy(tmp_var->str_val, val, val_len);
+ tmp_var->str_val[val_len]= 0;
+ }
+ tmp_var->name_len = name_len;
+ tmp_var->str_val_len = val_len;
+ tmp_var->alloced_len = val_alloc_len;
+ tmp_var->int_val = (val) ? atoi(val) : 0;
+ tmp_var->int_dirty = 0;
+ tmp_var->env_s = 0;
+ return tmp_var;
+}
+
+
+void var_free(void *v)
+{
+ my_free(((VAR*) v)->str_val, MYF(MY_WME));
+ if (((VAR*)v)->alloced)
+ my_free((char*) v, MYF(MY_WME));
+}
+
+
+VAR* var_from_env(const char *name, const char *def_val)
+{
+ const char *tmp;
+ VAR *v;
+ if (!(tmp = getenv(name)))
+ tmp = def_val;
+
+ v = var_init(0, name, strlen(name), tmp, strlen(tmp));
+ my_hash_insert(&var_hash, (byte*)v);
+ return v;
+}
+
+
+VAR* var_get(const char *var_name, const char **var_name_end, my_bool raw,
my_bool ignore_not_existing)
{
int digit;
- VAR* v;
+ VAR *v;
DBUG_ENTER("var_get");
- DBUG_PRINT("enter",("var_name: %s",var_name));
+ DBUG_PRINT("enter", ("var_name: %s",var_name));
if (*var_name != '$')
goto err;
@@ -871,16 +1098,16 @@ VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw,
die("Empty variable");
}
length= (uint) (var_name - save_var_name);
- if (length >= MAX_VAR_NAME)
+ if (length >= MAX_VAR_NAME_LENGTH)
die("Too long variable name: %s", save_var_name);
if (!(v = (VAR*) hash_search(&var_hash, save_var_name, length)))
{
- char buff[MAX_VAR_NAME+1];
+ char buff[MAX_VAR_NAME_LENGTH+1];
strmake(buff, save_var_name, length);
v= var_from_env(buff, "");
}
- var_name--; /* Point at last character */
+ var_name--; /* Point at last character */
}
else
v = var_reg + digit;
@@ -901,7 +1128,8 @@ err:
DBUG_RETURN(0);
}
-static VAR *var_obtain(const char *name, int len)
+
+VAR *var_obtain(const char *name, int len)
{
VAR* v;
if ((v = (VAR*)hash_search(&var_hash, name, len)))
@@ -911,16 +1139,18 @@ static VAR *var_obtain(const char *name, int len)
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
*/
-int var_set(const char *var_name, const char *var_name_end,
- const char *var_val, const char *var_val_end)
+
+void var_set(const char *var_name, const char *var_name_end,
+ const char *var_val, const char *var_val_end)
{
- int digit, result, env_var= 0;
- VAR* v;
+ int digit, env_var= 0;
+ VAR *v;
DBUG_ENTER("var_set");
DBUG_PRINT("enter", ("var_name: '%.*s' = '%.*s' (length: %d)",
(int) (var_name_end - var_name), var_name,
@@ -932,15 +1162,15 @@ int var_set(const char *var_name, const char *var_name_end,
else
var_name++;
- digit = *var_name - '0';
+ digit= *var_name - '0';
if (!(digit < 10 && digit >= 0))
{
- v = var_obtain(var_name, (uint) (var_name_end - var_name));
+ v= var_obtain(var_name, (uint) (var_name_end - var_name));
}
else
- v = var_reg + digit;
+ v= var_reg + digit;
- result= eval_expr(v, var_val, (const char**) &var_val_end);
+ eval_expr(v, var_val, (const char**) &var_val_end);
if (env_var)
{
@@ -957,7 +1187,183 @@ int var_set(const char *var_name, const char *var_name_end,
putenv(v->env_s);
my_free((gptr)old_env_s, MYF(MY_ALLOW_ZERO_PTR));
}
- DBUG_RETURN(result);
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Store an integer (typically the returncode of the last SQL)
+ statement in the mysqltest builtin variable $mysql_errno, by
+ simulating of a user statement "let $mysql_errno= <integer>"
+*/
+
+void var_set_errno(int sql_errno)
+{
+ /* TODO MASV make easier */
+ const char *var_name= "$mysql_errno";
+ char var_val[21];
+ uint length= my_sprintf(var_val, (var_val, "%d", sql_errno));
+ var_set(var_name, var_name + 12, var_val, var_val + length);
+ return;
+}
+
+
+/*
+ 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;
+ DBUG_ENTER("var_query_set");
+ LINT_INIT(res);
+
+ while (end > query && *end != '`')
+ --end;
+ if (query == end)
+ die("Syntax error in query, missing '`'");
+ ++query;
+
+ if (mysql_real_query(mysql, query, (int)(end - query)) ||
+ !(res = mysql_store_result(mysql)))
+ {
+ *end = 0;
+ die("Error running query '%s': %d %s", query,
+ mysql_errno(mysql), mysql_error(mysql));
+ }
+
+ 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)
+ */
+ 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);
+ lengths= mysql_fetch_lengths(res);
+ for (i=0; i < mysql_num_fields(res); i++)
+ {
+ if (row[0])
+ {
+#ifdef NOT_YET
+ /* Add to <var_name>_<col_name> */
+ uint j;
+ char var_col_name[MAX_VAR_NAME_LENGTH];
+ uint length= snprintf(var_col_name, MAX_VAR_NAME_LENGTH,
+ "$%s_%s", var->name, fields[i].name);
+ /* Convert characters not allowed in variable names to '_' */
+ for (j= 1; j < length; j++)
+ {
+ if (!my_isvar(charset_info,var_col_name[j]))
+ var_col_name[j]= '_';
+ }
+ var_set(var_col_name, var_col_name + length,
+ row[i], row[i] + lengths[i]);
+#endif
+ /* Add column to tab separated string */
+ dynstr_append_mem(&result, row[i], lengths[i]);
+ }
+ dynstr_append_mem(&result, "\t", 1);
+ }
+ 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;
+}
+
+
+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)
+{
+ static int MIN_VAR_ALLOC= 32; /* MASV why 32? */
+ VAR *vp;
+ if (*p == '$')
+ {
+ if ((vp= var_get(p, p_end, 0, 0)))
+ {
+ var_copy(v, vp);
+ return;
+ }
+ }
+ else if (*p == '`')
+ {
+ var_query_set(v, p, p_end);
+ }
+ else
+ {
+ int new_val_len = (p_end && *p_end) ?
+ (int) (*p_end - p) : (int) strlen(p);
+ if (new_val_len + 1 >= v->alloced_len)
+ {
+ v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ?
+ MIN_VAR_ALLOC : new_val_len + 1;
+ if (!(v->str_val =
+ v->str_val ? my_realloc(v->str_val, v->alloced_len+1,
+ MYF(MY_WME)) :
+ my_malloc(v->alloced_len+1, MYF(MY_WME))))
+ die("Out of memory");
+ }
+ v->str_val_len = new_val_len;
+ memcpy(v->str_val, p, new_val_len);
+ v->str_val[new_val_len] = 0;
+ v->int_val=atoi(p);
+ v->int_dirty=0;
+ }
+ return;
}
@@ -988,103 +1394,53 @@ int open_file(const char *name)
/*
- Check for unexpected "junk" after the end of query
- This is normally caused by missing delimiters
-*/
-
-int check_eol_junk(const char *eol)
-{
- const char *p= eol;
- DBUG_ENTER("check_eol_junk");
- DBUG_PRINT("enter", ("eol: %s", eol));
- /* Remove all spacing chars except new line */
- while (*p && my_isspace(charset_info, *p) && (*p != '\n'))
- p++;
-
- /* 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_RETURN(0);
-}
-
-
-/* ugly long name, but we are following the convention */
-int do_wait_for_slave_to_stop(struct st_query *q __attribute__((unused)))
-{
- MYSQL* mysql = &cur_con->mysql;
- for (;;)
- {
- 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 0;
-}
-
-
-/*
Source and execute the given file
SYNOPSIS
- do_source()
- query called command
+ do_source()
+ query called command
DESCRIPTION
- source <file_name>
+ source <file_name>
- Open the file <file_name> and execute it
+ Open the file <file_name> and execute it
*/
-int do_source(struct st_query *query)
+void do_source(struct st_command *command)
{
- char *p= query->first_argument, *name;
- if (!*p)
- die("Missing file name in source");
- name= p;
- while (*p && !my_isspace(charset_info,*p))
- p++;
- if (*p)
- *p++= 0;
- query->last_argument= p;
+ static DYNAMIC_STRING ds_filename;
+ const struct command_arg source_args[] = {
+ "filename", ARG_STRING, TRUE, &ds_filename, "File to source"
+ };
+ DBUG_ENTER("do_source");
+
+ check_command_args(command, command->first_argument, source_args,
+ sizeof(source_args)/sizeof(struct command_arg),
+ ' ');
+
/*
- If this file has already been sourced, don't source it again.
- It's already available in the q_lines cache.
+ If this file has already been sourced, don't source it again.
+ It's already available in the q_lines cache.
*/
if (parser.current_line < (parser.read_lines - 1))
- return 0;
- return open_file(name);
+ ; /* Do nothing */
+ else
+ {
+ DBUG_PRINT("info", ("sourcing file: %s", ds_filename.str));
+ open_file(ds_filename.str);
+ }
+
+ dynstr_free(&ds_filename);
+ return;
}
+
#ifdef __WIN__
-/* Variables used for temuprary sh files used for emulating Unix on Windows */
+/* Variables used for temporary sh files used for emulating Unix on Windows */
char tmp_sh_name[64], tmp_sh_cmd[70];
-static void init_tmp_sh_file()
+void init_tmp_sh_file()
{
/* Format a name for the tmp sh file that is unique for this process */
my_snprintf(tmp_sh_name, sizeof(tmp_sh_name), "tmp_%d.sh", getpid());
@@ -1092,13 +1448,15 @@ static void init_tmp_sh_file()
my_snprintf(tmp_sh_cmd, sizeof(tmp_sh_cmd), "sh %s", tmp_sh_name);
}
-static void free_tmp_sh_file()
+
+void free_tmp_sh_file()
{
my_delete(tmp_sh_name, MYF(0));
}
#endif
-FILE* my_popen(DYNAMIC_STRING* ds_cmd, const char* mode)
+
+FILE* my_popen(DYNAMIC_STRING *ds_cmd, const char *mode)
{
#ifdef __WIN__
/* Dump the command into a sh script file and execute with popen */
@@ -1114,49 +1472,49 @@ FILE* my_popen(DYNAMIC_STRING* ds_cmd, const char* mode)
Execute given command.
SYNOPSIS
- do_exec()
- query called command
+ do_exec()
+ query called command
DESCRIPTION
- exec <command>
+ exec <command>
- Execute the text between exec and end of line in a subprocess.
- The error code returned from the subprocess is checked against the
- expected error array, previously set with the --error command.
- It can thus be used to execute a command that shall fail.
+ Execute the text between exec and end of line in a subprocess.
+ The error code returned from the subprocess is checked against the
+ expected error array, previously set with the --error command.
+ It can thus be used to execute a command that shall fail.
NOTE
- Although mysqltest is executed from cygwin shell, the command will be
- executed in "cmd.exe". Thus commands like "rm" etc can NOT be used, use
- system for those commands.
+ Although mysqltest is executed from cygwin shell, the command will be
+ executed in "cmd.exe". Thus commands like "rm" etc can NOT be used, use
+ system for those commands.
*/
-static void do_exec(struct st_query *query)
+void do_exec(struct st_command *command)
{
int error;
char buf[1024];
FILE *res_file;
- char *cmd= query->first_argument;
+ char *cmd= command->first_argument;
DYNAMIC_STRING ds_cmd;
DBUG_ENTER("do_exec");
DBUG_PRINT("enter", ("cmd: '%s'", cmd));
+ /* Skip leading space */
while (*cmd && my_isspace(charset_info, *cmd))
cmd++;
if (!*cmd)
die("Missing argument in exec");
- query->last_argument= query->end;
+ command->last_argument= command->end;
- init_dynamic_string(&ds_cmd, 0, strlen(cmd)+256, 256);
+ init_dynamic_string(&ds_cmd, 0, command->query_len+256, 256);
/* Eval the command, thus replacing all environment variables */
- do_eval(&ds_cmd, cmd, TRUE);
- cmd= ds_cmd.str;
+ do_eval(&ds_cmd, cmd, command->end, TRUE);
DBUG_PRINT("info", ("Executing '%s' as '%s'",
- query->first_argument, cmd));
+ command->first_argument, cmd));
- if (!(res_file= my_popen(&ds_cmd, "r")) && query->abort_on_error)
- die("popen(\"%s\", \"r\") failed", query->first_argument);
+ if (!(res_file= my_popen(&ds_cmd, "r")) && command->abort_on_error)
+ die("popen(\"%s\", \"r\") failed", command->first_argument);
while (fgets(buf, sizeof(buf), res_file))
{
@@ -1176,229 +1534,70 @@ static void do_exec(struct st_query *query)
uint status= WEXITSTATUS(error), i;
my_bool ok= 0;
- if (query->abort_on_error)
- die("command \"%s\" failed", query->first_argument);
+ if (command->abort_on_error)
+ die("command \"%s\" failed", command->first_argument);
DBUG_PRINT("info",
("error: %d, status: %d", error, status));
- for (i= 0; i < query->expected_errors; i++)
+ for (i= 0; i < command->expected_errors.count; i++)
{
DBUG_PRINT("info", ("expected error: %d",
- query->expected_errno[i].code.errnum));
- if ((query->expected_errno[i].type == ERR_ERRNO) &&
- (query->expected_errno[i].code.errnum == status))
+ command->expected_errors.err[i].code.errnum));
+ if ((command->expected_errors.err[i].type == ERR_ERRNO) &&
+ (command->expected_errors.err[i].code.errnum == status))
{
ok= 1;
DBUG_PRINT("info", ("command \"%s\" failed with expected error: %d",
- query->first_argument, status));
+ command->first_argument, status));
}
}
if (!ok)
die("command \"%s\" failed with wrong error: %d",
- query->first_argument, status);
+ command->first_argument, status);
}
- else if (query->expected_errno[0].type == ERR_ERRNO &&
- query->expected_errno[0].code.errnum != 0)
+ else if (command->expected_errors.err[0].type == ERR_ERRNO &&
+ command->expected_errors.err[0].code.errnum != 0)
{
/* Error code we wanted was != 0, i.e. not an expected success */
die("command \"%s\" succeeded - should have failed with errno %d...",
- query->first_argument, query->expected_errno[0].code.errnum);
+ command->first_argument, command->expected_errors.err[0].code.errnum);
}
- free_replace();
+ dynstr_free(&ds_cmd);
DBUG_VOID_RETURN;
}
-/*
- Set variable from the result of a query
-
- SYNOPSIS
- var_query_set()
- var variable to set from query
- query start of query string to execute
- query_end end of the query string to execute
-
-
- DESCRIPTION
- let @<var_name> = `<query>`
-
- Execute the query and assign the first row of result to var as
- a tab separated strings
-
- Also assign each column of the result set to
- variable "$<var_name>_<column_name>"
- Thus the tab separated output can be read from $<var_name> and
- and each individual column can be read as $<var_name>_<col_name>
-
-*/
-
-int 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;
- DBUG_ENTER("var_query_set");
- LINT_INIT(res);
-
- while (end > query && *end != '`')
- --end;
- if (query == end)
- die("Syntax error in query, missing '`'");
- ++query;
-
- if (mysql_real_query(mysql, query, (int)(end - query)) ||
- !(res = mysql_store_result(mysql)))
- {
- *end = 0;
- die("Error running query '%s': %d: %s", query,
- mysql_errno(mysql) ,mysql_error(mysql));
- }
-
- 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)
- */
- DYNAMIC_STRING result;
- uint i;
- ulong *lengths;
- char *end;
-#ifdef NOT_YET
- MYSQL_FIELD *fields= mysql_fetch_fields(res);
-#endif
-
- init_dynamic_string(&result, "", 16384, 65536);
- lengths= mysql_fetch_lengths(res);
- for (i=0; i < mysql_num_fields(res); i++)
- {
- if (row[0])
- {
-#ifdef NOT_YET
- /* Add to <var_name>_<col_name> */
- uint j;
- char var_col_name[MAX_VAR_NAME];
- uint length= snprintf(var_col_name, MAX_VAR_NAME,
- "$%s_%s", var->name, fields[i].name);
- /* Convert characters not allowed in variable names to '_' */
- for (j= 1; j < length; j++)
- {
- if (!my_isvar(charset_info,var_col_name[j]))
- var_col_name[j]= '_';
- }
- var_set(var_col_name, var_col_name + length,
- row[i], row[i] + lengths[i]);
-#endif
- /* Add column to tab separated string */
- dynstr_append_mem(&result, row[i], lengths[i]);
- }
- dynstr_append_mem(&result, "\t", 1);
- }
- 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_RETURN(0);
-}
-
-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);
-}
-
-int eval_expr(VAR* v, const char *p, const char** p_end)
-{
- VAR* vp;
- if (*p == '$')
- {
- if ((vp = var_get(p,p_end,0,0)))
- {
- var_copy(v, vp);
- return 0;
- }
- }
- else if (*p == '`')
- {
- return var_query_set(v, p, p_end);
- }
- else
- {
- int new_val_len = (p_end && *p_end) ?
- (int) (*p_end - p) : (int) strlen(p);
- if (new_val_len + 1 >= v->alloced_len)
- {
- v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ?
- MIN_VAR_ALLOC : new_val_len + 1;
- if (!(v->str_val =
- v->str_val ? my_realloc(v->str_val, v->alloced_len+1,
- MYF(MY_WME)) :
- my_malloc(v->alloced_len+1, MYF(MY_WME))))
- die("Out of memory");
- }
- v->str_val_len = new_val_len;
- memcpy(v->str_val, p, new_val_len);
- v->str_val[new_val_len] = 0;
- v->int_val=atoi(p);
- v->int_dirty=0;
- return 0;
- }
-
- die("Invalid expr: %s", p);
- return 1;
-}
-
-
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
+ do_modify_var()
+ query called command
+ operator operation to perform on the var
DESCRIPTION
- dec $var_name
- inc $var_name
+ dec $var_name
+ inc $var_name
*/
-int do_modify_var(struct st_query *query,
+int do_modify_var(struct st_command *command,
enum enum_operator operator)
{
- const char *p= query->first_argument;
+ const char *p= command->first_argument;
VAR* v;
if (!*p)
- die("Missing argument to %.*s", query->first_word_len, query->query);
+ die("Missing argument to %.*s", command->first_word_len, command->query);
if (*p != '$')
die("The argument to %.*s must be a variable (start with $)",
- query->first_word_len, query->query);
+ command->first_word_len, command->query);
v= var_get(p, &p, 1, 0);
switch (operator) {
case DO_DEC:
@@ -1412,7 +1611,7 @@ int do_modify_var(struct st_query *query,
break;
}
v->int_dirty= 1;
- query->last_argument= (char*)++p;
+ command->last_argument= (char*)++p;
return 0;
}
@@ -1421,10 +1620,10 @@ int do_modify_var(struct st_query *query,
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.
+ 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.
*/
@@ -1441,20 +1640,19 @@ int my_system(DYNAMIC_STRING* ds_cmd)
/*
-
SYNOPSIS
do_system
- command called command
+ command called command
DESCRIPTION
- system <command>
+ system <command>
- Eval the query to expand any $variables in the command.
- Execute the command with the "system" command.
+ Eval the query to expand any $variables in the command.
+ Execute the command with the "system" command.
*/
-void do_system(struct st_query *command)
+void do_system(struct st_command *command)
{
DYNAMIC_STRING ds_cmd;
DBUG_ENTER("do_system");
@@ -1462,10 +1660,10 @@ void do_system(struct st_query *command)
if (strlen(command->first_argument) == 0)
die("Missing arguments to system, nothing to do!");
- init_dynamic_string(&ds_cmd, 0, strlen(command->first_argument) + 64, 256);
+ 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));
@@ -1487,50 +1685,375 @@ void do_system(struct st_query *command)
/*
+ 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_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;
+}
+
+
+/*
+ Read characters from line buffer or file. This is needed to allow
+ my_ungetc() to buffer MAX_DELIMITER_LENGTH characters for a file
+
+ NOTE:
+ This works as long as one doesn't change files (with 'source file_name')
+ when there is things pushed into the buffer. This should however not
+ happen for any tests in the test suite.
+*/
+
+int my_getc(FILE *file)
+{
+ if (line_buffer_pos == line_buffer)
+ return fgetc(file);
+ return *--line_buffer_pos;
+}
+
+
+void my_ungetc(int c)
+{
+ *line_buffer_pos++= (char) c;
+}
+
+
+void read_until_delimiter(DYNAMIC_STRING *ds,
+ DYNAMIC_STRING *ds_delimiter)
+{
+ char c;
+ DBUG_ENTER("read_until_delimiter");
+ DBUG_PRINT("enter", ("delimiter: %s, length: %d",
+ ds_delimiter->str, ds_delimiter->length));
+
+ if (ds_delimiter->length > MAX_DELIMITER_LENGTH)
+ die("Max delimiter length(%d) exceeded", MAX_DELIMITER_LENGTH);
+
+ /* Read from file until delimiter is found */
+ while (1)
+ {
+ c= my_getc(cur_file->file);
+
+ if (c == '\n')
+ cur_file->lineno++;
+
+ if (feof(cur_file->file))
+ die("End of file encountered before '%s' delimiter was found",
+ ds_delimiter->str);
+
+ if (match_delimiter(c, ds_delimiter->str, ds_delimiter->length))
+ {
+ 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;
+}
+
+
+/*
+ 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! Overwrites existing file
+
+ Default <delimiter> is EOF
+
+*/
+
+void do_write_file(struct st_command *command)
+{
+ static DYNAMIC_STRING ds_content;
+ static DYNAMIC_STRING ds_filename;
+ static DYNAMIC_STRING ds_delimiter;
+ const struct command_arg write_file_args[] = {
+ "filename", ARG_STRING, TRUE, &ds_filename, "File to write to",
+ "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until"
+ };
+ DBUG_ENTER("do_write_file");
+
+ check_command_args(command,
+ command->first_argument,
+ write_file_args,
+ sizeof(write_file_args)/sizeof(struct command_arg),
+ ' ');
+
+ /* If no delimiter was provided, use EOF */
+ if (ds_delimiter.length == 0)
+ dynstr_set(&ds_delimiter, "EOF");
+
+ init_dynamic_string(&ds_content, "", 1024, 1024);
+ read_until_delimiter(&ds_content, &ds_delimiter);
+ DBUG_PRINT("info", ("Writing to file: %s", ds_filename.str));
+ str_to_file(ds_filename.str, ds_content.str, ds_content.length);
+ dynstr_free(&ds_content);
+ dynstr_free(&ds_filename);
+ dynstr_free(&ds_delimiter);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ 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;
+ char buf[FN_REFLEN];
+ FILE *res_file;
+ static DYNAMIC_STRING ds_script;
+ static DYNAMIC_STRING ds_delimiter;
+ const struct command_arg perl_args[] = {
+ "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until"
+ };
+ DBUG_ENTER("do_perl");
+
+ check_command_args(command,
+ command->first_argument,
+ perl_args,
+ sizeof(perl_args)/sizeof(struct command_arg),
+ ' ');
+
+ /* If no delimiter was provided, use EOF */
+ if (ds_delimiter.length == 0)
+ dynstr_set(&ds_delimiter, "EOF");
+
+ init_dynamic_string(&ds_script, "", 1024, 1024);
+ read_until_delimiter(&ds_script, &ds_delimiter);
+
+ DBUG_PRINT("info", ("Executing perl: %s", ds_script.str));
+
+ /* Format a name for a tmp .pl file that is unique for this process */
+ my_snprintf(buf, sizeof(buf), "%s/tmp/tmp_%d.pl",
+ getenv("MYSQLTEST_VARDIR"), getpid());
+ str_to_file(buf, ds_script.str, ds_script.length);
+
+ /* Format the perl <filename> command */
+ my_snprintf(buf, sizeof(buf), "perl %s/tmp/tmp_%d.pl",
+ getenv("MYSQLTEST_VARDIR"), getpid());
+
+ if (!(res_file= popen(buf, "r")) && command->abort_on_error)
+ die("popen(\"%s\", \"r\") failed", buf);
+
+ while (fgets(buf, sizeof(buf), res_file))
+ {
+ if (disable_result_log)
+ {
+ buf[strlen(buf)-1]=0;
+ DBUG_PRINT("exec_result",("%s", buf));
+ }
+ else
+ {
+ replace_dynstr_append(&ds_res, buf);
+ }
+ }
+ error= pclose(res_file);
+ handle_command_error(command, WEXITSTATUS(error));
+ dynstr_free(&ds_script);
+ dynstr_free(&ds_delimiter);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
Print the content between echo and <delimiter> to result file.
Evaluate all variables in the string before printing, allow
for variable names to be escaped using \
SYNOPSIS
- do_echo()
- q called command
+ do_echo()
+ command called command
DESCRIPTION
- echo text
- Print the text after echo until end of command to result file
+ echo text
+ Print the text after echo until end of command to result file
- echo $<var_name>
- Print the content of the variable <var_name> to result file
+ 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 the content of the variable <var_name> to
+ result file
- echo Some text \$<var_name>
- Print "Some text" plus $<var_name> to result file
+ echo Some text \$<var_name>
+ Print "Some text" plus $<var_name> to result file
*/
-int do_echo(struct st_query *command)
+int do_echo(struct st_command *command)
{
- DYNAMIC_STRING *ds, ds_echo;
+ DYNAMIC_STRING ds_echo;
- ds= &ds_res;
-
- init_dynamic_string(&ds_echo, "", 256, 256);
- do_eval(&ds_echo, command->first_argument, FALSE);
- dynstr_append_mem(ds, ds_echo.str, ds_echo.length);
- dynstr_append_mem(ds, "\n", 1);
+ init_dynamic_string(&ds_echo, "", command->query_len, 256);
+ do_eval(&ds_echo, command->first_argument, command->end, FALSE);
+ dynstr_append_mem(&ds_res, ds_echo.str, ds_echo.length);
+ dynstr_append_mem(&ds_res, "\n", 1);
dynstr_free(&ds_echo);
command->last_argument= command->end;
return(0);
}
-int do_sync_with_master2(long offset)
+void do_wait_for_slave_to_stop(struct st_command *c __attribute__((unused)))
{
- MYSQL_RES* res;
+ 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;
+ MYSQL *mysql= &cur_con->mysql;
char query_buf[FN_REFLEN+128];
int tries= 0;
int rpl_parse;
@@ -1546,7 +2069,7 @@ int do_sync_with_master2(long offset)
wait_for_position:
if (mysql_query(mysql, query_buf))
- die("failed in %s: %d: %s", query_buf, mysql_errno(mysql),
+ die("failed in '%s': %d: %s", query_buf, mysql_errno(mysql),
mysql_error(mysql));
if (!(res= mysql_store_result(mysql)))
@@ -1569,13 +2092,14 @@ wait_for_position:
if (rpl_parse)
mysql_enable_rpl_parse(mysql);
- return 0;
+ return;
}
-int do_sync_with_master(struct st_query *query)
+
+void do_sync_with_master(struct st_command *command)
{
long offset= 0;
- char *p= query->first_argument;
+ char *p= command->first_argument;
const char *offset_start= p;
if (*offset_start)
{
@@ -1584,20 +2108,22 @@ int do_sync_with_master(struct st_query *query)
if(*p && !my_isspace(charset_info, *p))
die("Invalid integer argument \"%s\"", offset_start);
- query->last_argument= p;
+ command->last_argument= p;
}
- return do_sync_with_master2(offset);
+ do_sync_with_master2(offset);
+ return;
}
+
/*
when ndb binlog is on, this call will wait until last updated epoch
(locally in the mysqld) has been received into the binlog
*/
int do_save_master_pos()
{
- MYSQL_RES* res;
+ MYSQL_RES *res;
MYSQL_ROW row;
- MYSQL* mysql = &cur_con->mysql;
+ MYSQL *mysql = &cur_con->mysql;
const char *query;
int rpl_parse;
@@ -1606,19 +2132,18 @@ int do_save_master_pos()
#ifdef HAVE_NDB_BINLOG
/*
- Wait for ndb binlog to be up-to-date with all changes
- done on the local mysql server
+ 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("At line %u: failed in %s: %d: %s", start_lineno, query,
+ die("'%s' failed: %d %s", query,
mysql_errno(mysql), mysql_error(mysql));
if (!(res= mysql_store_result(mysql)))
- die("line %u: mysql_store_result() retuned NULL for '%s'", start_lineno,
- query);
+ die("mysql_store_result() returned NULL for '%s'", query);
if (!(row= mysql_fetch_row(res)))
- die("line %u: empty result in %s", start_lineno, query);
+ die("Query '%s' returned empty result", query);
have_ndbcluster= strcmp("YES", row[1]) == 0;
mysql_free_result(res);
@@ -1626,7 +2151,7 @@ int do_save_master_pos()
if (have_ndbcluster)
{
ulonglong start_epoch= 0, applied_epoch= 0,
- latest_epoch=0, latest_trans_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;
@@ -1647,11 +2172,10 @@ int do_save_master_pos()
if (count)
sleep(1);
if (mysql_query(mysql, query= "show engine ndb status"))
- die("At line %u: failed in '%s': %d: %s", start_lineno, query,
+ die("failed in '%s': %d %s", query,
mysql_errno(mysql), mysql_error(mysql));
if (!(res= mysql_store_result(mysql)))
- die("line %u: mysql_store_result() retuned NULL for '%s'",
- start_lineno, query);
+ die("mysql_store_result() returned NULL for '%s'", query);
while ((row= mysql_fetch_row(res)))
{
if (strcmp(row[1], binlog) == 0)
@@ -1668,8 +2192,8 @@ int do_save_master_pos()
latest_epoch= strtoull(status, (char**) 0, 10);
}
else
- die("line %u: result does not contain '%s' in '%s'",
- start_lineno, latest_epoch_str, query);
+ 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))
@@ -1680,10 +2204,10 @@ int do_save_master_pos()
latest_trans_epoch= strtoull(status, (char**) 0, 10);
}
else
- die("line %u: result does not contain '%s' in '%s'",
- start_lineno, latest_trans_epoch_str, query);
+ die("result does not contain '%s' in '%s'",
+ latest_trans_epoch_str, query);
/* latest_received_binlog_epoch */
- while (*status &&
+ while (*status &&
strncmp(status, latest_received_binlog_epoch_str,
sizeof(latest_received_binlog_epoch_str)-1))
status++;
@@ -1693,10 +2217,10 @@ int do_save_master_pos()
latest_received_binlog_epoch= strtoull(status, (char**) 0, 10);
}
else
- die("line %u: result does not contain '%s' in '%s'",
- start_lineno, latest_received_binlog_epoch_str, query);
+ die("result does not contain '%s' in '%s'",
+ latest_received_binlog_epoch_str, query);
/* latest_handled_binlog */
- while (*status &&
+ while (*status &&
strncmp(status, latest_handled_binlog_epoch_str,
sizeof(latest_handled_binlog_epoch_str)-1))
status++;
@@ -1706,10 +2230,10 @@ int do_save_master_pos()
latest_handled_binlog_epoch= strtoull(status, (char**) 0, 10);
}
else
- die("line %u: result does not contain '%s' in '%s'",
- start_lineno, latest_handled_binlog_epoch_str, query);
+ die("result does not contain '%s' in '%s'",
+ latest_handled_binlog_epoch_str, query);
/* latest_applied_binlog_epoch */
- while (*status &&
+ while (*status &&
strncmp(status, latest_applied_binlog_epoch_str,
sizeof(latest_applied_binlog_epoch_str)-1))
status++;
@@ -1719,16 +2243,16 @@ int do_save_master_pos()
latest_applied_binlog_epoch= strtoull(status, (char**) 0, 10);
}
else
- die("line %u: result does not contain '%s' in '%s'",
- start_lineno, latest_applied_binlog_epoch_str, query);
+ 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("line %u: result does not contain '%s' in '%s'",
- start_lineno, binlog, query);
+ die("result does not contain '%s' in '%s'",
+ binlog, query);
if (latest_applied_binlog_epoch > applied_epoch)
count= 0;
applied_epoch= latest_applied_binlog_epoch;
@@ -1745,7 +2269,7 @@ int do_save_master_pos()
}
#endif
if (mysql_query(mysql, query= "show master status"))
- die("failed in show master status: %d: %s",
+ die("failed in 'show master status': %d %s",
mysql_errno(mysql), mysql_error(mysql));
if (!(res = mysql_store_result(mysql)))
@@ -1767,28 +2291,29 @@ int do_save_master_pos()
Assign the variable <var_name> with <var_val>
SYNOPSIS
- do_let()
- query called command
+ do_let()
+ query called command
DESCRIPTION
- let $<var_name>=<var_val><delimiter>
+ let $<var_name>=<var_val><delimiter>
- <var_name> - is the string string found between the $ and =
- <var_val> - is the content between the = and <delimiter>, it may span
- multiple line and contain any characters except <delimiter>
- <delimiter> - is a string containing of one or more chars, default is ;
+ <var_name> - is the string string found between the $ and =
+ <var_val> - is the content between the = and <delimiter>, it may span
+ multiple line and contain any characters except <delimiter>
+ <delimiter> - is a string containing of one or more chars, default is ;
RETURN VALUES
- Program will die if error detected
+ Program will die if error detected
*/
-int do_let(struct st_query *query)
+void do_let(struct st_command *command)
{
- int ret;
- char *p= query->first_argument;
+ 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> */
@@ -1810,34 +2335,18 @@ int do_let(struct st_query *query)
while (*p && my_isspace(charset_info,*p))
p++;
- do_eval(&let_rhs_expr, p, FALSE);
+ do_eval(&let_rhs_expr, p, command->end, FALSE);
- query->last_argument= query->end;
+ command->last_argument= command->end;
/* Assign var_val to var_name */
- ret= var_set(var_name, var_name_end, let_rhs_expr.str,
- (let_rhs_expr.str + let_rhs_expr.length));
+ var_set(var_name, var_name_end, let_rhs_expr.str,
+ (let_rhs_expr.str + let_rhs_expr.length));
dynstr_free(&let_rhs_expr);
-
- return(ret);
-}
-
-
-/*
- Store an integer (typically the returncode of the last SQL)
- statement in the mysqltest builtin variable $mysql_errno, by
- simulating of a user statement "let $mysql_errno= <integer>"
-*/
-
-int var_set_errno(int sql_errno)
-{
- const char *var_name= "$mysql_errno";
- char var_val[21];
- uint length= my_sprintf(var_val, (var_val, "%d", sql_errno));
- return var_set(var_name, var_name + 12, var_val, var_val + length);
+ DBUG_VOID_RETURN;
}
-int do_rpl_probe(struct st_query *query __attribute__((unused)))
+int do_rpl_probe(struct st_command *command __attribute__((unused)))
{
DBUG_ENTER("do_rpl_probe");
if (mysql_rpl_probe(&cur_con->mysql))
@@ -1846,14 +2355,14 @@ int do_rpl_probe(struct st_query *query __attribute__((unused)))
}
-int do_enable_rpl_parse(struct st_query *query __attribute__((unused)))
+int do_enable_rpl_parse(struct st_command *command __attribute__((unused)))
{
mysql_enable_rpl_parse(&cur_con->mysql);
return 0;
}
-int do_disable_rpl_parse(struct st_query *query __attribute__((unused)))
+int do_disable_rpl_parse(struct st_command *command __attribute__((unused)))
{
mysql_disable_rpl_parse(&cur_con->mysql);
return 0;
@@ -1861,17 +2370,17 @@ int do_disable_rpl_parse(struct st_query *query __attribute__((unused)))
/*
- Sleep the number of specifed seconds
+ Sleep the number of specified seconds
SYNOPSIS
- do_sleep()
- q called command
- real_sleep use the value from opt_sleep as number of seconds to sleep
- if real_sleep is false
+ do_sleep()
+ q called command
+ real_sleep use the value from opt_sleep as number of seconds to sleep
+ if real_sleep is false
DESCRIPTION
- sleep <seconds>
- real_sleep <seconds>
+ sleep <seconds>
+ real_sleep <seconds>
The difference between the sleep and real_sleep commands is that sleep
uses the delay from the --sleep command-line option if there is one.
@@ -1882,26 +2391,26 @@ int do_disable_rpl_parse(struct st_query *query __attribute__((unused)))
used for cpu-independent delays.
*/
-int do_sleep(struct st_query *query, my_bool real_sleep)
+int do_sleep(struct st_command *command, my_bool real_sleep)
{
int error= 0;
- char *p= query->first_argument;
- char *sleep_start, *sleep_end= query->end;
+ char *p= command->first_argument;
+ char *sleep_start, *sleep_end= command->end;
double sleep_val;
while (my_isspace(charset_info, *p))
p++;
if (!*p)
- die("Missing argument to %.*s", query->first_word_len, query->query);
+ die("Missing argument to %.*s", command->first_word_len, command->query);
sleep_start= p;
/* Check that arg starts with a digit, not handled by my_strtod */
if (!my_isdigit(charset_info, *sleep_start))
- die("Invalid argument to %.*s \"%s\"", query->first_word_len, query->query,
- query->first_argument);
+ die("Invalid argument to %.*s \"%s\"", command->first_word_len,
+ command->query,command->first_argument);
sleep_val= my_strtod(sleep_start, &sleep_end, &error);
if (error)
- die("Invalid argument to %.*s \"%s\"", query->first_word_len, query->query,
- query->first_argument);
+ die("Invalid argument to %.*s \"%s\"", command->first_word_len,
+ command->query, command->first_argument);
/* Fixed sleep time selected by --sleep option */
if (opt_sleep >= 0 && !real_sleep)
@@ -1910,13 +2419,15 @@ int do_sleep(struct st_query *query, my_bool real_sleep)
DBUG_PRINT("info", ("sleep_val: %f", sleep_val));
if (sleep_val)
my_sleep((ulong) (sleep_val * 1000000L));
- query->last_argument= sleep_end;
+ command->last_argument= sleep_end;
return 0;
}
-static void get_file_name(char *filename, struct st_query *q)
+
+void do_get_file_name(struct st_command *command,
+ char* dest, uint dest_max_len)
{
- char *p= q->first_argument, *name;
+ char *p= command->first_argument, *name;
if (!*p)
die("Missing file name argument");
name= p;
@@ -1924,13 +2435,14 @@ static void get_file_name(char *filename, struct st_query *q)
p++;
if (*p)
*p++= 0;
- q->last_argument= p;
- strmake(filename, name, FN_REFLEN);
+ command->last_argument= p;
+ strmake(dest, name, dest_max_len);
}
-static void set_charset(struct st_query *q)
+
+void do_set_charset(struct st_command *command)
{
- char *charset_name= q->first_argument;
+ char *charset_name= command->first_argument;
char *p;
if (!charset_name || !*charset_name)
@@ -1941,78 +2453,182 @@ static void set_charset(struct st_query *q)
p++;
if(*p)
*p++= 0;
- q->last_argument= p;
+ command->last_argument= p;
charset_info= get_charset_by_csname(charset_name,MY_CS_PRIMARY,MYF(MY_WME));
if (!charset_info)
abort_not_supported_test("Test requires charset '%s'", charset_name);
}
-static uint get_errcodes(match_err *to,struct st_query *q)
+
+#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)
{
- char *p= q->first_argument;
+ /* 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("get_errcodes");
+ DBUG_ENTER("do_get_errcodes");
if (!*p)
- die("Missing argument in %s", q->query);
+ die("Missing argument(s) to 'error'");
do
{
+ char *end;
+
+ /* Skip leading spaces */
+ while (*p && *p == ' ')
+ p++;
+
+ /* Find end */
+ end= p;
+ while (*end && *end != ',' && *end != ' ')
+ end++;
+
if (*p == 'S')
{
- /* SQLSTATE string */
- char *end= ++p + SQLSTATE_LENGTH;
- char *to_ptr= to[count].code.sqlstate;
+ char *to_ptr= to->code.sqlstate;
- for (; my_isalnum(charset_info, *p) && p != end; p++)
- *to_ptr++= *p;
- *to_ptr= 0;
+ /*
+ 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);
- to[count].type= ERR_SQLSTATE;
+ /* 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')
{
- /* SQL error as string */
- st_error *e= global_error;
- char *start= p++;
+ /* Error name string */
- for (; *p == '_' || my_isalnum(charset_info, *p); p++)
- ;
- 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(start, e->name, (int) (p - start)) &&
- (uint) strlen(e->name) == (uint) (p - start))
- {
- to[count].code.errnum= (uint) e->code;
- to[count].type= ERR_ERRNO;
- break;
- }
- }
- if (!e->name)
- die("Unknown SQL error '%s'", start);
+ 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);
- if (!(p= str2int(p,10,(long) INT_MIN, (long) INT_MAX, &val)))
- die("Invalid argument in %s", q->query);
- to[count].code.errnum= (uint) val;
- to[count].type= ERR_ERRNO;
+ to->code.errnum= (uint) val;
+ to->type= ERR_ERRNO;
+ DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum));
}
+ to++;
count++;
- } while (*(p++) == ',');
- q->last_argument= (p - 1);
- to[count].type= ERR_EMPTY; /* End of data */
- DBUG_RETURN(count);
+
+ if (count >= (sizeof(saved_expected_errors.err) /
+ sizeof(struct st_match_err)))
+ die("Too many errorcodes specified");
+
+ /* Set pointer to the end of the last error code */
+ p= end;
+
+ /* Find next ',' */
+ while (*p && *p != ',')
+ p++;
+
+ if (*p)
+ p++; /* Step past ',' */
+
+ } while (*p);
+
+ command->last_argument= p;
+ to->type= ERR_EMPTY; /* End of data */
+
+ DBUG_PRINT("info", ("Expected errors: %d", count));
+ saved_expected_errors.count= count;
+ DBUG_VOID_RETURN;
}
+
/*
Get a string; Return ptr to end of string
Strings may be surrounded by " or '
@@ -2020,11 +2636,10 @@ static uint get_errcodes(match_err *to,struct st_query *q)
If string is a '$variable', return the value of the variable.
*/
-
-static char *get_string(char **to_ptr, char **from_ptr,
- struct st_query *q)
+char *get_string(char **to_ptr, char **from_ptr,
+ struct st_command *command)
{
- reg1 char c,sep;
+ char c, sep;
char *to= *to_ptr, *from= *from_ptr, *start=to;
DBUG_ENTER("get_string");
@@ -2070,7 +2685,7 @@ static char *get_string(char **to_ptr, char **from_ptr,
*to++=c;
}
if (*from != ' ' && *from)
- die("Wrong string argument in %s", q->query);
+ die("Wrong string argument in %s", command->query);
while (my_isspace(charset_info,*from)) /* Point to next string */
from++;
@@ -2093,282 +2708,25 @@ static char *get_string(char **to_ptr, char **from_ptr,
DBUG_RETURN(start);
}
-/*
- 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
-*/
-
-static 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(&reg,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,(gptr) &reg))
- 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((gptr)res,0);
- die("Error parsing replace_regex \"%s\"", expr);
- return 0;
-}
-
-/*
- Execute all substitutions on val.
-
- Returns: true if substituition was made, false otherwise
- Side-effect: Sets r->buf to be the buffer with all substitutions done.
-
- IN:
- struct st_replace_regex* r
- char* val
- Out:
- struct st_replace_regex* r
- r->buf points at the resulting buffer
- r->even_buf and r->odd_buf might have been reallocated
- r->even_buf_len and r->odd_buf_len might have been changed
-
- TODO: at some point figure out if there is a way to do everything
- in one pass
-*/
-
-static 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,(gptr)&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
-
-*/
-static void get_replace_regex(struct st_query *q)
-{
- char *expr= q->first_argument;
- free_replace_regex();
- if (!(glob_replace_regex=init_replace_regex(expr)))
- die("Could not init replace_regex");
- q->last_argument= q->end;
-}
-
-/*
- Get arguments for replace. The syntax is:
- replace from to [from to ...]
- Where each argument may be quoted with ' or "
- A argument may also be a variable, in which case the value of the
- variable is replaced.
-*/
-
-static void get_replace(struct st_query *q)
+void set_reconnect(MYSQL* mysql, int val)
{
- uint i;
- char *from= q->first_argument;
- char *buff,*start;
- char word_end_chars[256],*pos;
- POINTER_ARRAY to_array,from_array;
- DBUG_ENTER("get_replace");
-
- free_replace();
-
- bzero((char*) &to_array,sizeof(to_array));
- bzero((char*) &from_array,sizeof(from_array));
- if (!*from)
- die("Missing argument in %s", q->query);
- start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
- while (*from)
- {
- char *to=buff;
- to=get_string(&buff, &from, q);
- if (!*from)
- die("Wrong number of arguments to replace_result in '%s'", q->query);
- insert_pointer_name(&from_array,to);
- to=get_string(&buff, &from, q);
- insert_pointer_name(&to_array,to);
- }
- for (i=1,pos=word_end_chars ; i < 256 ; i++)
- if (my_isspace(charset_info,i))
- *pos++= i;
- *pos=0; /* End pointer */
- if (!(glob_replace=init_replace((char**) from_array.typelib.type_names,
- (char**) to_array.typelib.type_names,
- (uint) from_array.typelib.count,
- word_end_chars)))
- die("Can't initialize replace from '%s'", q->query);
- free_pointer_array(&from_array);
- free_pointer_array(&to_array);
- my_free(start, MYF(0));
- q->last_argument= q->end;
+ my_bool reconnect= val;
+ DBUG_ENTER("set_reconnect");
+ DBUG_PRINT("info", ("val: %d", val));
+#if MYSQL_VERSION_ID < 50000
+ mysql->reconnect= reconnect;
+#else
+ mysql_options(mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
+#endif
DBUG_VOID_RETURN;
}
-static void free_replace_regex()
-{
- if (glob_replace_regex)
- {
- my_free(glob_replace_regex->even_buf,MYF(MY_ALLOW_ZERO_PTR));
- my_free(glob_replace_regex->odd_buf,MYF(MY_ALLOW_ZERO_PTR));
- my_free((char*) glob_replace_regex,MYF(0));
- glob_replace_regex=0;
- }
-}
-
-
-void free_replace()
-{
- DBUG_ENTER("free_replace");
- if (glob_replace)
- {
- my_free((char*) glob_replace,MYF(0));
- glob_replace=0;
- }
- DBUG_VOID_RETURN;
-}
-struct connection * find_connection_by_name(const char *name)
+struct st_connection * find_connection_by_name(const char *name)
{
- struct connection *con;
- for (con= cons; con < next_con; con++)
+ struct st_connection *con;
+ for (con= connections; con < next_con; con++)
{
if (!strcmp(con->name, name))
{
@@ -2390,10 +2748,10 @@ int select_connection_name(const char *name)
}
-int select_connection(struct st_query *query)
+int select_connection(struct st_command *command)
{
char *name;
- char *p= query->first_argument;
+ char *p= command->first_argument;
DBUG_ENTER("select_connection");
if (!*p)
@@ -2403,15 +2761,16 @@ int select_connection(struct st_query *query)
p++;
if (*p)
*p++= 0;
- query->last_argument= p;
+ command->last_argument= p;
return select_connection_name(name);
}
-int close_connection(struct st_query *q)
+void do_close_connection(struct st_command *command)
{
- char *p= q->first_argument, *name;
- struct connection *con;
+ char *p= command->first_argument, *name;
+ struct st_connection *con;
+
DBUG_ENTER("close_connection");
DBUG_PRINT("enter",("name: '%s'",p));
@@ -2423,13 +2782,17 @@ int close_connection(struct st_query *q)
if (*p)
*p++= 0;
- q->last_argument= p;
- for (con= cons; con < next_con; con++)
+ command->last_argument= p;
+
+ /* Loop through connection pool for connection to close */
+ for (con= connections; con < next_con; con++)
{
+ DBUG_PRINT("info", ("con->name: %s", con->name));
if (!strcmp(con->name, name))
{
+ DBUG_PRINT("info", ("Closing connection %s", con->name));
#ifndef EMBEDDED_LIBRARY
- if (q->type == Q_DIRTY_CLOSE)
+ if (command->type == Q_DIRTY_CLOSE)
{
if (con->mysql.net.vio)
{
@@ -2443,59 +2806,19 @@ int close_connection(struct st_query *q)
mysql_close(con->util_mysql);
con->util_mysql= 0;
my_free(con->name, MYF(0));
+
/*
- When the connection is closed set name to "closed_connection"
- to make it possible to reuse the connection name.
- The connection slot will not be reused
- */
+ When the connection is closed set name to "closed_connection"
+ to make it possible to reuse the connection name.
+ The connection slot will not be reused
+ */
if (!(con->name = my_strdup("closed_connection", MYF(MY_WME))))
die("Out of memory");
- DBUG_RETURN(0);
+
+ DBUG_VOID_RETURN;
}
}
die("connection '%s' not found in connection pool", name);
- DBUG_RETURN(1); /* Never reached */
-}
-
-
-/*
- This one now is a hack - we may want to improve in in the
- future to handle quotes. For now we assume that anything that is not
- a comma, a space or ) belongs to the argument. space is a chopper, comma or
- ) are delimiters/terminators
-
- SYNOPSIS
- safe_get_param
- str - string to get param from
- arg - pointer to string where result will be stored
- msg - Message to display if param is not found
- if msg is 0 this param is not required and param may be empty
-
- RETURNS
- pointer to str after param
-
-*/
-
-char* safe_get_param(char *str, char** arg, const char *msg)
-{
- DBUG_ENTER("safe_get_param");
- if(!*str)
- {
- if (msg)
- die(msg);
- *arg= str;
- DBUG_RETURN(str);
- }
- while (*str && my_isspace(charset_info,*str))
- str++;
- *arg= str;
- while (*str && *str != ',' && *str != ')')
- str++;
- if (msg && !*arg)
- die(msg);
-
- *str++= 0;
- DBUG_RETURN(str);
}
@@ -2503,54 +2826,60 @@ char* safe_get_param(char *str, char** arg, const char *msg)
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
+ 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
+ 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 ignore any errors during these retries. One should use
- connect_n_handle_errors() if he expects a connection error and wants
- handle as if it was an error from a usual statement.
+ 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.
- RETURN VALUE
- 0 - success, non-0 - failure
*/
-int safe_connect(MYSQL* mysql, const char *host, const char *user,
- const char *pass, const char *db, int port, const char *sock)
+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 con_error= 1;
- my_bool reconnect= 1;
+ int failed_attempts= 0;
static ulong connection_retry_sleep= 100000; /* Microseconds */
- int i;
- for (i= 0; i < opt_max_connect_retries; i++)
+
+ DBUG_ENTER("safe_connect");
+ while(!mysql_real_connect(mysql, host,user, pass, db, port, sock,
+ CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS))
{
- if (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
+ */
+
+ if (mysql_errno(mysql) == CR_CONNECTION_ERROR &&
+ failed_attempts < opt_max_connect_retries)
+ my_sleep(connection_retry_sleep);
+ else
{
- con_error= 0;
- break;
+ 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));
}
- my_sleep(connection_retry_sleep);
+ failed_attempts++;
}
- /*
- TODO: change this to 0 in future versions, but the 'kill' test relies on
- existing behavior
- */
- mysql_options(mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
- return con_error;
+ DBUG_VOID_RETURN;
}
@@ -2558,45 +2887,41 @@ int safe_connect(MYSQL* mysql, const char *host, const char *user,
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
- create_conn - out parameter, set to zero if connection was
- not established and is not touched otherwise
+ 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.
+ 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
- RETURN VALUE
- 0 - success, non-0 - failure
*/
-int connect_n_handle_errors(struct st_query *q, MYSQL* con, const char* host,
+int connect_n_handle_errors(struct st_command *command,
+ MYSQL* con, const char* host,
const char* user, const char* pass,
- const char* db, int port, const char* sock,
- int* create_conn)
+ const char* db, int port, const char* sock)
{
DYNAMIC_STRING *ds;
- my_bool reconnect= 1;
- int error= 0;
ds= &ds_res;
- if (!disable_query_log)
+ /* Only log if an error is expected */
+ if (!command->abort_on_error &&
+ !disable_query_log)
{
/*
- It is nice to have connect() statement logged in result file
- in this case.
- QQ: Should we do this only if we are expecting an error ?
+ Log the connect to result log
*/
- char port_buff[22]; /* This should be enough for any int */
- char *port_end;
dynstr_append_mem(ds, "connect(", 8);
replace_dynstr_append(ds, host);
dynstr_append_mem(ds, ",", 1);
@@ -2607,8 +2932,7 @@ int connect_n_handle_errors(struct st_query *q, MYSQL* con, const char* host,
if (db)
replace_dynstr_append(ds, db);
dynstr_append_mem(ds, ",", 1);
- port_end= int10_to_str(port, port_buff, 10);
- replace_dynstr_append_mem(ds, port_buff, port_end - port_buff);
+ replace_dynstr_append_uint(ds, port);
dynstr_append_mem(ds, ",", 1);
if (sock)
replace_dynstr_append(ds, sock);
@@ -2619,148 +2943,150 @@ int connect_n_handle_errors(struct st_query *q, MYSQL* con, const char* host,
if (!mysql_real_connect(con, host, user, pass, db, port, sock ? sock: 0,
CLIENT_MULTI_STATEMENTS))
{
- handle_error("connect", q, mysql_errno(con), mysql_error(con),
+ handle_error(command, mysql_errno(con), mysql_error(con),
mysql_sqlstate(con), ds);
- *create_conn= 0;
- goto err;
+ return 0; /* Not connected */
}
- handle_no_error(q);
-
- /*
- TODO: change this to 0 in future versions, but the 'kill' test relies on
- existing behavior
- */
- mysql_options(con, MYSQL_OPT_RECONNECT, (char *)&reconnect);
-
-err:
- free_replace();
- free_replace_regex();
- return error;
+ handle_no_error(command);
+ return 1; /* Connected */
}
/*
Open a new connection to MySQL Server with the parameters
- specified
+ specified. Make the new connection the current connection.
SYNOPSIS
- do_connect()
- q called command
+ do_connect()
+ q called command
DESCRIPTION
- 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
-
- */
-
-int do_connect(struct st_query *q)
-{
- char *con_name, *con_user,*con_pass, *con_host, *con_port_str,
- *con_db, *con_sock, *con_options;
- char *con_buf, *p;
- char buff[FN_REFLEN];
- int con_port;
- bool con_ssl= 0;
- bool con_compress= 0;
- int free_con_sock= 0;
- int error= 0;
- int create_conn= 1;
- VAR *var_port, *var_sock;
+ 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= port;
+ char *con_options;
+ bool con_ssl= 0, con_compress= 0;
+ char *ptr;
+
+ static DYNAMIC_STRING ds_connection_name;
+ static DYNAMIC_STRING ds_host;
+ static DYNAMIC_STRING ds_user;
+ static DYNAMIC_STRING ds_password;
+ static DYNAMIC_STRING ds_database;
+ static DYNAMIC_STRING ds_port;
+ static DYNAMIC_STRING ds_sock;
+ static DYNAMIC_STRING ds_options;
+ const struct command_arg connect_args[] = {
+ "connection name", ARG_STRING, TRUE, &ds_connection_name,
+ "Name of the connection",
+ "host", ARG_STRING, TRUE, &ds_host, "Host to connect to",
+ "user", ARG_STRING, FALSE, &ds_user, "User to connect as",
+ "passsword", ARG_STRING, FALSE, &ds_password,
+ "Password used when connecting",
+ "database", ARG_STRING, FALSE, &ds_database,
+ "Dtabase to select after connect",
+ "port", ARG_STRING, FALSE, &ds_port, "Port to connect to",
+ "socket", ARG_STRING, FALSE, &ds_sock, "Socket to connect with",
+ "options", ARG_STRING, FALSE, &ds_options,
+ "Options to use while connecting"
+ };
DBUG_ENTER("do_connect");
- DBUG_PRINT("enter",("connect: %s", q->first_argument));
-
- /* Make a copy of query before parsing, safe_get_param will modify */
- if (!(con_buf= my_strdup(q->first_argument, MYF(MY_WME))))
- die("Could not allocate con_buf");
- p= con_buf;
-
- if (*p != '(')
- die("Syntax error in connect - expected '(' found '%c'", *p);
- p++;
- p= safe_get_param(p, &con_name, "Missing connection name");
- p= safe_get_param(p, &con_host, "Missing connection host");
- p= safe_get_param(p, &con_user, "Missing connection user");
- p= safe_get_param(p, &con_pass, "Missing connection password");
- p= safe_get_param(p, &con_db, "Missing connection db");
+ DBUG_PRINT("enter",("connect: %s", command->first_argument));
- /* Port */
- p= safe_get_param(p, &con_port_str, 0);
- if (*con_port_str)
+ /* Remove parenteses around connect arguments */
+ if ((ptr= strstr(command->first_argument, "(")))
{
- if (*con_port_str == '$')
+ /* Replace it with a space */
+ *ptr= ' ';
+ if ((ptr= strstr(command->first_argument, ")")))
{
- if (!(var_port= var_get(con_port_str, 0, 0, 0)))
- die("Unknown variable '%s'", con_port_str+1);
- con_port= var_port->int_val;
+ /* Replace it with \0 */
+ *ptr= 0;
}
else
- {
- con_port= atoi(con_port_str);
- if (con_port == 0)
- die("Illegal argument for port: '%s'", con_port_str);
- }
+ die("connect - argument list started with '(' must be ended with ')'");
}
- else
+
+ check_command_args(command, command->first_argument, connect_args,
+ sizeof(connect_args)/sizeof(struct command_arg),
+ ',');
+
+ /* Port */
+ if (ds_port.length)
{
- con_port= port;
+ con_port= atoi(ds_port.str);
+ if (con_port == 0)
+ die("Illegal argument for port: '%s'", ds_port.str);
}
/* Sock */
- p= safe_get_param(p, &con_sock, 0);
- if (*con_sock)
+ if (ds_sock.length)
{
- if (*con_sock == '$')
+ /*
+ If the socket is specified just as a name without path
+ append tmpdir in front
+ */
+ if (*ds_sock.str != FN_LIBCHAR)
{
- if (!(var_sock= var_get(con_sock, 0, 0, 0)))
- die("Unknown variable '%s'", con_sock+1);
- if (!(con_sock= (char*)my_malloc(var_sock->str_val_len+1, MYF(0))))
- die("Out of memory");
- free_con_sock= 1;
- memcpy(con_sock, var_sock->str_val, var_sock->str_val_len);
- con_sock[var_sock->str_val_len]= 0;
+ char buff[FN_REFLEN];
+ fn_format(buff, ds_sock.str, TMPDIR, "", 0);
+ dynstr_set(&ds_sock, buff);
}
}
else
{
- con_sock= (char*) unix_sock;
+ /* No socket specified, use default */
+ dynstr_set(&ds_sock, unix_sock);
}
+ DBUG_PRINT("info", ("socket: %s", ds_sock.str));
+
/* Options */
- p= safe_get_param(p, &con_options, 0);
+ con_options= ds_options.str;
while (*con_options)
{
- char* str= con_options;
- while (*str && !my_isspace(charset_info, *str))
- str++;
- *str++= 0;
- if (!strcmp(con_options, "SSL"))
+ 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 (!strcmp(con_options, "COMPRESS"))
+ else if (!strncmp(con_options, "COMPRESS", 8))
con_compress= 1;
else
- die("Illegal option to connect: %s", con_options);
- con_options= str;
+ die("Illegal option to connect: %.*s", end - con_options, con_options);
+ /* Process next option */
+ con_options= end;
}
- /* Note: 'p' is pointing into the copy 'con_buf' */
- q->last_argument= q->first_argument + (p - con_buf);
- if (next_con == cons_end)
- die("Connection limit exhausted - increase MAX_CONS in mysqltest.c");
+ 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(con_name))
- die("Connection %s already exists", con_name);
+ if (find_connection_by_name(ds_connection_name.str))
+ die("Connection %s already exists", ds_connection_name.str);
if (!mysql_init(&next_con->mysql))
die("Failed on mysql_init()");
@@ -2772,53 +3098,56 @@ int do_connect(struct st_query *q)
#ifdef HAVE_OPENSSL
if (opt_use_ssl || con_ssl)
{
- /* Turn on ssl_verify_server_cert only if host is "localhost" */
- opt_ssl_verify_server_cert= !strcmp(con_host, "localhost");
-
mysql_ssl_set(&next_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
opt_ssl_capath, opt_ssl_cipher);
+#if MYSQL_VERSION_ID >= 50000
+ /* Turn on ssl_verify_server_cert only if host is "localhost" */
+ opt_ssl_verify_server_cert= !strcmp(ds_connection_name.str, "localhost");
mysql_options(&next_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
&opt_ssl_verify_server_cert);
+#endif
}
#endif
- if (con_sock && !free_con_sock && *con_sock && *con_sock != FN_LIBCHAR)
- con_sock=fn_format(buff, con_sock, TMPDIR, "", 0);
- if (!con_db[0])
- con_db= db;
+
+ /* Use default db name */
+ if (ds_database.length == 0)
+ dynstr_set(&ds_database, db);
+
/* Special database to allow one to connect without a database name */
- if (con_db && !strcmp(con_db,"*NO-ONE*"))
- con_db= 0;
- if (q->abort_on_error)
- {
- if (safe_connect(&next_con->mysql, con_host, con_user, con_pass,
- con_db, con_port, con_sock ? con_sock: 0))
- die("Could not open connection '%s': %d %s", con_name,
- mysql_errno(&next_con->mysql), mysql_error(&next_con->mysql));
- }
- else
- error= connect_n_handle_errors(q, &next_con->mysql, con_host, con_user,
- con_pass, con_db, con_port, con_sock,
- &create_conn);
+ if (ds_database.length && !strcmp(ds_database.str,"*NO-ONE*"))
+ dynstr_set(&ds_database, "");
- if (create_conn)
+
+ if (connect_n_handle_errors(command, &next_con->mysql,
+ ds_host.str,ds_user.str,
+ ds_password.str, ds_database.str,
+ con_port, ds_sock.str))
{
- if (!(next_con->name= my_strdup(con_name, MYF(MY_WME))))
- die(NullS);
+ DBUG_PRINT("info", ("Inserting connection %s in connection pool",
+ ds_connection_name.str));
+ if (!(next_con->name= my_strdup(ds_connection_name.str, MYF(MY_WME))))
+ die("Out of memory");
cur_con= next_con++;
}
- if (free_con_sock)
- my_free(con_sock, MYF(MY_WME));
- my_free(con_buf, MYF(MY_WME));
- DBUG_RETURN(error);
+
+ dynstr_free(&ds_connection_name);
+ dynstr_free(&ds_host);
+ dynstr_free(&ds_user);
+ dynstr_free(&ds_password);
+ dynstr_free(&ds_database);
+ dynstr_free(&ds_port);
+ dynstr_free(&ds_sock);
+ dynstr_free(&ds_options);
+ DBUG_VOID_RETURN;
}
-int do_done(struct st_query *q)
+int do_done(struct st_command *command)
{
/* Check if empty block stack */
if (cur_block == block_stack)
{
- if (*q->query != '}')
+ if (*command->query != '}')
die("Stray 'end' command - end of block before beginning");
die("Stray '}' - end of block before beginning");
}
@@ -2844,31 +3173,31 @@ int do_done(struct st_query *q)
Process start of a "if" or "while" statement
SYNOPSIS
- do_block()
- cmd Type of block
- q called command
+ do_block()
+ cmd Type of block
+ q called command
DESCRIPTION
- if ([!]<expr>)
- {
- <block statements>
- }
+ if ([!]<expr>)
+ {
+ <block statements>
+ }
- while ([!]<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.
+ 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_query* q)
+void do_block(enum block_cmd cmd, struct st_command* command)
{
- char *p= q->first_argument;
+ char *p= command->first_argument;
const char *expr_start, *expr_end;
VAR v;
const char *cmd_name= (cmd == cmd_while ? "while" : "if");
@@ -2912,9 +3241,7 @@ void do_block(enum block_cmd cmd, struct st_query* q)
while (*p && my_isspace(charset_info, *p))
p++;
- if (*p == '{')
- die("Missing newline between %s and '{'", cmd_name);
- if (*p)
+ if (*p && *p != '{')
die("Missing '{' after %s. Found \"%s\"", cmd_name, p);
var_init(&v,0,0,0,0);
@@ -2935,43 +3262,41 @@ 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
+void do_delimiter(struct st_command* command)
+{
+ char* p= command->first_argument;
+ DBUG_ENTER("do_delimiter");
+ DBUG_PRINT("enter", ("first_argument: %s", command->first_argument));
- NOTE:
- This works as long as one doesn't change files (with 'source file_name')
- when there is things pushed into the buffer. This should however not
- happen for any tests in the test suite.
-*/
+ while (*p && my_isspace(charset_info, *p))
+ p++;
-int my_getc(FILE *file)
-{
- if (line_buffer_pos == line_buffer)
- return fgetc(file);
- return *--line_buffer_pos;
-}
+ if (!(*p))
+ die("Can't set empty delimiter");
-void my_ungetc(int c)
-{
- *line_buffer_pos++= (char) c;
+ strmake(delimiter, p, sizeof(delimiter) - 1);
+ delimiter_length= strlen(delimiter);
+
+ DBUG_PRINT("exit", ("delimiter: %s", delimiter));
+ command->last_argument= p + delimiter_length;
+ DBUG_VOID_RETURN;
}
-my_bool end_of_query(int c)
+my_bool match_delimiter(int c, const char *delim, uint length)
{
uint i;
- char tmp[MAX_DELIMITER];
+ char tmp[MAX_DELIMITER_LENGTH];
- if (c != *delimiter)
+ if (c != *delim)
return 0;
- for (i= 1; i < delimiter_length &&
- (c= my_getc(cur_file->file)) == *(delimiter + i);
+ for (i= 1; i < length &&
+ (c= my_getc(cur_file->file)) == *(delim + i);
i++)
tmp[i]= c;
- if (i == delimiter_length)
+ if (i == length)
return 1; /* Found delimiter */
/* didn't find delimiter, push back things that we read */
@@ -2982,45 +3307,51 @@ my_bool end_of_query(int c)
}
+my_bool end_of_query(int c)
+{
+ return match_delimiter(c, delimiter, delimiter_length);
+}
+
+
/*
Read one "line" from the file
SYNOPSIS
- read_line
- buf buffer for the read line
- size size of the buffer i.e max size to read
+ read_line
+ buf buffer for the read line
+ size size of the buffer i.e max size to read
DESCRIPTION
- This function actually reads several lines and adds them to the
- buffer buf. It continues to read until it finds what it believes
- is a complete query.
+ This function actually reads several lines and adds them to the
+ buffer buf. It continues to read until it finds what it believes
+ is a complete query.
- Normally that means it will read lines until it reaches the
- "delimiter" that marks end of query. Default delimiter is ';'
- The function should be smart enough not to detect delimiter's
- found inside strings surrounded with '"' and '\'' escaped strings.
+ Normally that means it will read lines until it reaches the
+ "delimiter" that marks end of query. Default delimiter is ';'
+ The function should be smart enough not to detect delimiter's
+ found inside strings surrounded with '"' and '\'' escaped strings.
- If the first line in a query starts with '#' or '-' this line is treated
- as a comment. A comment is always terminated when end of line '\n' is
- reached.
+ If the first line in a query starts with '#' or '-' this line is treated
+ as a comment. A comment is always terminated when end of line '\n' is
+ reached.
*/
int read_line(char *buf, int size)
{
- int c;
- char quote;
+ char c, last_quote;
char *p= buf, *buf_end= buf + size - 1;
- int no_save= 0;
- enum {R_NORMAL, R_Q, R_Q_IN_Q, R_SLASH_IN_Q,
- R_COMMENT, R_LINE_START} state= R_LINE_START;
+ int skip_char= 0;
+ enum {R_NORMAL, R_Q, R_SLASH_IN_Q,
+ R_COMMENT, R_LINE_START} state= R_LINE_START;
DBUG_ENTER("read_line");
- LINT_INIT(quote);
+ LINT_INIT(last_quote);
start_lineno= cur_file->lineno;
+ DBUG_PRINT("info", ("Starting to read at lineno: %d", start_lineno));
for (; p < buf_end ;)
{
- no_save= 0;
+ skip_char= 0;
c= my_getc(cur_file->file);
if (feof(cur_file->file))
{
@@ -3036,12 +3367,13 @@ int read_line(char *buf, int size)
{
/* We're back at the first file, check if
all { have matching }
- */
+ */
if (cur_block != block_stack)
die("Missing end of block");
- DBUG_PRINT("info", ("end of file"));
- DBUG_RETURN(1);
+ *p= 0;
+ DBUG_PRINT("info", ("end of file at line %d", cur_file->lineno));
+ DBUG_RETURN(1);
}
cur_file--;
start_lineno= cur_file->lineno;
@@ -3055,61 +3387,86 @@ int read_line(char *buf, int size)
/* Convert cr/lf to lf */
if (p != buf && *(p-1) == '\r')
- *(p-1)= 0;
+ p--;
}
switch(state) {
case R_NORMAL:
- /* Only accept '{' in the beginning of a line */
if (end_of_query(c))
{
*p= 0;
+ DBUG_PRINT("exit", ("Found delimiter '%s' at line %d",
+ delimiter, cur_file->lineno));
DBUG_RETURN(0);
}
- else if (c == '\'' || c == '"' || c == '`')
+ 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))))
{
- quote= c;
- state= R_Q;
+ /* 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 == '\n')
+ else if (c == '\'' || c == '"' || c == '`')
{
- state = R_LINE_START;
+ last_quote= c;
+ state= R_Q;
}
break;
+
case R_COMMENT:
if (c == '\n')
{
+ /* Comments are terminated by newline */
*p= 0;
+ DBUG_PRINT("exit", ("Found newline in comment at line: %d",
+ cur_file->lineno));
DBUG_RETURN(0);
}
break;
+
case R_LINE_START:
- /* Only accept start of comment if this is the first line in query */
- if ((cur_file->lineno == start_lineno) &&
- (c == '#' || c == '-' || parsing_disabled))
+ if (c == '#' || c == '-')
{
+ /* A # or - in the first position of the line - this is a comment */
state = R_COMMENT;
}
else if (my_isspace(charset_info, c))
{
+ /* Skip all space at begining of line */
if (c == '\n')
- start_lineno= cur_file->lineno; /* Query hasn't started yet */
- no_save= 1;
+ {
+ /* Query hasn't started yet */
+ start_lineno= cur_file->lineno;
+ DBUG_PRINT("info", ("Query hasn't started yet, start_lineno: %d",
+ start_lineno));
+ }
+ skip_char= 1;
}
- else if (c == '}')
+ else if (end_of_query(c))
{
- *buf++= '}';
- *buf= 0;
+ *p= 0;
+ DBUG_PRINT("exit", ("Found delimiter '%s' at line: %d",
+ delimiter, cur_file->lineno));
DBUG_RETURN(0);
}
- else if (end_of_query(c) || c == '{')
+ else if (c == '}')
{
+ /* A "}" need to be by itself in the begining of a line to terminate */
+ *p++= c;
*p= 0;
+ DBUG_PRINT("exit", ("Found '}' in begining of a line at line: %d",
+ cur_file->lineno));
DBUG_RETURN(0);
}
else if (c == '\'' || c == '"' || c == '`')
{
- quote= c;
+ last_quote= c;
state= R_Q;
}
else
@@ -3117,29 +3474,19 @@ int read_line(char *buf, int size)
break;
case R_Q:
- if (c == quote)
- state= R_Q_IN_Q;
+ if (c == last_quote)
+ state= R_NORMAL;
else if (c == '\\')
state= R_SLASH_IN_Q;
break;
- case R_Q_IN_Q:
- if (end_of_query(c))
- {
- *p= 0;
- DBUG_RETURN(0);
- }
- if (c != quote)
- state= R_NORMAL;
- else
- state= R_Q;
- break;
+
case R_SLASH_IN_Q:
state= R_Q;
break;
}
- if (!no_save)
+ if (!skip_char)
{
/* Could be a multibyte character */
/* This code is based on the code in "sql_load.cc" */
@@ -3157,7 +3504,7 @@ int read_line(char *buf, int size)
for (i= 1; i < charlen; i++)
{
if (feof(cur_file->file))
- goto found_eof; /* FIXME: could we just break here?! */
+ goto found_eof;
c= my_getc(cur_file->file);
*p++ = c;
}
@@ -3174,99 +3521,253 @@ int read_line(char *buf, int size)
*p++= c;
}
}
- *p= 0; /* Always end with \0 */
- DBUG_RETURN(feof(cur_file->file));
+ die("The input buffer is too small for this query.x\n" \
+ "check your query or increase MAX_QUERY and recompile");
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Convert the read query to result format version 1
+
+ That is: After newline, all spaces need to be skipped
+ unless the previous char was a quote
+
+ This is due to an old bug that has now been fixed, but the
+ version 1 output format is preserved by using this function
+
+*/
+
+void convert_to_format_v1(char* query)
+{
+ int last_c_was_quote= 0;
+ char *p= query, *write= query;
+ char *end= strend(query);
+ char last_c;
+
+ while (p <= end)
+ {
+ if (*p == '\n' && !last_c_was_quote)
+ {
+ *write++ = *p++; /* Save the newline */
+
+ /* Skip any spaces on next line */
+ while (*p && my_isspace(charset_info, *p))
+ p++;
+
+ last_c_was_quote= 0;
+ }
+ else if (*p == '\'' || *p == '"' || *p == '`')
+ {
+ last_c= *p;
+ *write++ = *p++;
+
+ /* Copy anything until the next quote of same type */
+ while (*p && *p != last_c)
+ *write++ = *p++;
+
+ *write++ = *p++;
+
+ last_c_was_quote= 1;
+ }
+ else
+ {
+ *write++ = *p++;
+ last_c_was_quote= 0;
+ }
+ }
+}
+
+
+/*
+ Check a command that is about to be sent (or should have been
+ sent if parsing was enabled) to mysql server for
+ suspicious things and generate warnings.
+*/
+
+void scan_command_for_warnings(struct st_command *command)
+{
+ const char *ptr= command->query;
+ DBUG_ENTER("scan_command_for_warnings");
+ DBUG_PRINT("enter", ("query: %s", command->query));
+
+ while(*ptr)
+ {
+ /*
+ Look for query's that lines that start with a -- comment
+ and has a mysqltest command
+ */
+ if (ptr[0] == '\n' &&
+ ptr[1] && ptr[1] == '-' &&
+ ptr[2] && ptr[2] == '-' &&
+ ptr[3])
+ {
+ uint type;
+ char save;
+ char *end, *start= (char*)ptr+3;
+ /* Skip leading spaces */
+ while (*start && my_isspace(charset_info, *start))
+ start++;
+ end= start;
+ /* Find end of command(next space) */
+ while (*end && !my_isspace(charset_info, *end))
+ end++;
+ save= *end;
+ *end= 0;
+ DBUG_PRINT("info", ("Checking '%s'", start));
+ type= find_type(start, &command_typelib, 1+2);
+ if (type)
+ warning_msg("Embedded mysqltest command '--%s' detected in "
+ "query '%s' was this intentional? ",
+ start, command->query);
+ *end= save;
+ }
+
+ ptr++;
+ }
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Check for unexpected "junk" after the end of query
+ This is normally caused by missing delimiters or when
+ switching between different delimiters
+*/
+
+void check_eol_junk_line(const char *line)
+{
+ const char *p= line;
+ DBUG_ENTER("check_eol_junk_line");
+ DBUG_PRINT("enter", ("line: %s", line));
+
+ /* Check for extra delimiter */
+ if (*p && !strncmp(p, delimiter, delimiter_length))
+ die("Extra delimiter \"%s\" found", delimiter);
+
+ /* Allow trailing # comment */
+ if (*p && *p != '#')
+ {
+ if (*p == '\n')
+ die("Missing delimiter");
+ die("End of line junk detected: \"%s\"", p);
+ }
+ DBUG_VOID_RETURN;
+}
+
+void check_eol_junk(const char *eol)
+{
+ const char *p= eol;
+ DBUG_ENTER("check_eol_junk");
+ DBUG_PRINT("enter", ("eol: %s", eol));
+
+ /* Skip past all spacing chars and comments */
+ while (*p && (my_isspace(charset_info, *p) || *p == '#' || *p == '\n'))
+ {
+ /* Skip past comments started with # and ended with newline */
+ if (*p && *p == '#')
+ {
+ p++;
+ while (*p && *p != '\n')
+ p++;
+ }
+
+ /* Check this line */
+ if (*p && *p == '\n')
+ check_eol_junk_line(p);
+
+ if (*p)
+ p++;
+ }
+
+ check_eol_junk_line(p);
+
+ DBUG_VOID_RETURN;
}
+
+
/*
- Create a query from a set of lines
+ Create a command from a set of lines
SYNOPSIS
- read_query()
- q_ptr pointer where to return the new query
+ read_command()
+ command_ptr pointer where to return the new query
DESCRIPTION
- Converts lines returned by read_line into a query, this involves
- parsing the first word in the read line to find the query type.
+ Converts lines returned by read_line into a command, this involves
+ parsing the first word in the read line to find the command type.
- A -- comment may contain a valid query as the first word after the
- comment start. Thus it's always checked to see if that is the case.
- The advantage with this approach is to be able to execute commands
- terminated by new line '\n' regardless how many "delimiter" it contain.
+ A -- comment may contain a valid query as the first word after the
+ comment start. Thus it's always checked to see if that is the case.
+ The advantage with this approach is to be able to execute commands
+ terminated by new line '\n' regardless how many "delimiter" it contain.
*/
-static char read_query_buf[MAX_QUERY];
+#define MAX_QUERY (256*1024) /* 256K -- a test in sp-big is >128K */
+static char read_command_buf[MAX_QUERY];
-int read_query(struct st_query** q_ptr)
+int read_command(struct st_command** command_ptr)
{
- char *p= read_query_buf;
- struct st_query* q;
- DBUG_ENTER("read_query");
+ char *p= read_command_buf;
+ struct st_command* command;
+ DBUG_ENTER("read_command");
if (parser.current_line < parser.read_lines)
{
- get_dynamic(&q_lines, (gptr) q_ptr, parser.current_line) ;
+ get_dynamic(&q_lines, (gptr) command_ptr, parser.current_line) ;
DBUG_RETURN(0);
}
- if (!(*q_ptr= q= (struct st_query*) my_malloc(sizeof(*q), MYF(MY_WME))) ||
- insert_dynamic(&q_lines, (gptr) &q))
+ if (!(*command_ptr= command=
+ (struct st_command*) my_malloc(sizeof(*command), MYF(MY_WME))) ||
+ insert_dynamic(&q_lines, (gptr) &command))
die(NullS);
- q->record_file[0]= 0;
- q->require_file= 0;
- q->first_word_len= 0;
+ command->require_file[0]= 0;
+ command->first_word_len= 0;
+ command->query_len= 0;
- q->type= Q_UNKNOWN;
- q->query_buf= q->query= 0;
- read_query_buf[0]= 0;
- if (read_line(read_query_buf, sizeof(read_query_buf)))
+ command->type= Q_UNKNOWN;
+ command->query_buf= command->query= 0;
+ read_command_buf[0]= 0;
+ if (read_line(read_command_buf, sizeof(read_command_buf)))
{
- check_eol_junk(read_query_buf);
+ check_eol_junk(read_command_buf);
DBUG_RETURN(1);
}
-
- DBUG_PRINT("info", ("query: %s", read_query_buf));
- if (*p == '#')
- {
- q->type= Q_COMMENT;
- /* This goto is to avoid losing the "expected error" info. */
- goto end;
- }
- if (!parsing_disabled)
- {
- memcpy((gptr) q->expected_errno, (gptr) global_expected_errno,
- sizeof(global_expected_errno));
- q->expected_errors= global_expected_errors;
- q->abort_on_error= (global_expected_errors == 0 && abort_on_error);
- }
- if (p[0] == '-' && p[1] == '-')
+ convert_to_format_v1(read_command_buf);
+
+ DBUG_PRINT("info", ("query: %s", read_command_buf));
+ if (*p == '#')
{
- q->type= Q_COMMENT_WITH_COMMAND;
- p+= 2; /* To calculate first word */
+ command->type= Q_COMMENT;
}
- else if (!parsing_disabled)
+ else if (p[0] == '-' && p[1] == '-')
{
- while (*p && my_isspace(charset_info, *p))
- p++ ;
+ command->type= Q_COMMENT_WITH_COMMAND;
+ p+= 2; /* Skip past -- */
}
-end:
+ /* Skip leading spaces */
while (*p && my_isspace(charset_info, *p))
p++;
- if (!(q->query_buf= q->query= my_strdup(p, MYF(MY_WME))))
- die(NullS);
+ if (!(command->query_buf= command->query= my_strdup(p, MYF(MY_WME))))
+ die("Out of memory");
/* Calculate first word and first argument */
- for (p= q->query; *p && !my_isspace(charset_info, *p) ; p++) ;
- q->first_word_len= (uint) (p - q->query);
+ for (p= command->query; *p && !my_isspace(charset_info, *p) ; p++) ;
+ command->first_word_len= (uint) (p - command->query);
+
+ /* Skip spaces between command and first argument */
while (*p && my_isspace(charset_info, *p))
p++;
- q->first_argument= p;
- q->end= strend(q->query);
+ command->first_argument= p;
+
+ command->end= strend(command->query);
+ command->query_len= (command->end - command->query);
parser.read_lines++;
DBUG_RETURN(0);
}
@@ -3278,8 +3779,6 @@ static struct my_option my_long_options[] =
0, 0, 0, 0, 0, 0},
{"basedir", 'b', "Basedir for tests.", (gptr*) &opt_basedir,
(gptr*) &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"big-test", 'B', "Define BIG_TEST to 1.", (gptr*) &opt_big_test,
- (gptr*) &opt_big_test, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"compress", 'C', "Use the compressed server/client protocol.",
(gptr*) &opt_compress, (gptr*) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
0, 0, 0},
@@ -3319,8 +3818,8 @@ static struct my_option my_long_options[] =
{"record", 'r', "Record output of test_file into result file.",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"result-file", 'R', "Read/Store result from/in this file.",
- (gptr*) &result_file, (gptr*) &result_file, 0, GET_STR, REQUIRED_ARG,
- 0, 0, 0, 0, 0, 0},
+ (gptr*) &result_file_name, (gptr*) &result_file_name, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"server-arg", 'A', "Send option value to embedded server as a parameter.",
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"server-file", 'F', "Read embedded server arguments from file.",
@@ -3348,8 +3847,6 @@ static struct my_option my_long_options[] =
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},
- {"valgrind", 'N', "Define VALGRIND_TEST to 1.", (gptr*) &opt_valgrind_test,
- (gptr*) &opt_valgrind_test, 0, GET_BOOL, NO_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.",
@@ -3363,7 +3860,7 @@ static struct my_option my_long_options[] =
#include <help_start.h>
-static void print_version(void)
+void print_version(void)
{
printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname,MTEST_VERSION,
MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE);
@@ -3384,6 +3881,52 @@ void usage()
#include <help_end.h>
+/*
+ Read arguments for embedded server and put them into
+ embedded_server_args[]
+*/
+
+void read_embedded_server_arguments(const char *name)
+{
+ char argument[1024],buff[FN_REFLEN], *str=0;
+ FILE *file;
+
+ if (!test_if_hard_path(name))
+ {
+ strxmov(buff, opt_basedir, name, NullS);
+ name=buff;
+ }
+ fn_format(buff, name, "", "", MY_UNPACK_FILENAME);
+
+ if (!embedded_server_arg_count)
+ {
+ embedded_server_arg_count=1;
+ embedded_server_args[0]= (char*) ""; /* Progname */
+ }
+ if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
+ die("Failed to open file %s", buff);
+
+ while (embedded_server_arg_count < MAX_EMBEDDED_SERVER_ARGS &&
+ (str=fgets(argument,sizeof(argument), file)))
+ {
+ *(strend(str)-1)=0; /* Remove end newline */
+ if (!(embedded_server_args[embedded_server_arg_count]=
+ (char*) my_strdup(str,MYF(MY_WME))))
+ {
+ my_fclose(file,MYF(0));
+ die("Out of memory");
+
+ }
+ embedded_server_arg_count++;
+ }
+ my_fclose(file,MYF(0));
+ if (str)
+ die("Too many arguments in option file: %s",name);
+
+ return;
+}
+
+
static my_bool
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
char *argument)
@@ -3398,35 +3941,35 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
record = 1;
break;
case 'x':
+ {
+ char buff[FN_REFLEN];
+ if (!test_if_hard_path(argument))
{
- char buff[FN_REFLEN];
- if (!test_if_hard_path(argument))
- {
- strxmov(buff, opt_basedir, argument, NullS);
- argument= buff;
- }
- fn_format(buff, argument, "", "", MY_UNPACK_FILENAME);
- DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0);
- if (!(cur_file->file=
- my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0))))
- die("Could not open %s: errno = %d", buff, errno);
- cur_file->file_name= my_strdup(buff, MYF(MY_FAE));
- cur_file->lineno= 1;
- break;
+ strxmov(buff, opt_basedir, argument, NullS);
+ argument= buff;
}
+ fn_format(buff, argument, "", "", MY_UNPACK_FILENAME);
+ DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0);
+ if (!(cur_file->file=
+ my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0))))
+ die("Could not open %s: errno = %d", buff, errno);
+ cur_file->file_name= my_strdup(buff, MYF(MY_FAE));
+ cur_file->lineno= 1;
+ break;
+ }
case 'm':
+ {
+ static char buff[FN_REFLEN];
+ if (!test_if_hard_path(argument))
{
- static char buff[FN_REFLEN];
- if (!test_if_hard_path(argument))
- {
- strxmov(buff, opt_basedir, argument, NullS);
- argument= buff;
- }
- fn_format(buff, argument, "", "", MY_UNPACK_FILENAME);
- timer_file= buff;
- unlink(timer_file); /* Ignore error, may not exist */
- break;
+ strxmov(buff, opt_basedir, argument, NullS);
+ argument= buff;
}
+ fn_format(buff, argument, "", "", MY_UNPACK_FILENAME);
+ timer_file= buff;
+ unlink(timer_file); /* Ignore error, may not exist */
+ break;
+ }
case 'p':
if (argument)
{
@@ -3448,7 +3991,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
embedded_server_arg_count=1;
embedded_server_args[0]= (char*) "";
}
- if (embedded_server_arg_count == MAX_SERVER_ARGS-1 ||
+ if (embedded_server_arg_count == MAX_EMBEDDED_SERVER_ARGS-1 ||
!(embedded_server_args[embedded_server_arg_count++]=
my_strdup(argument, MYF(MY_FAE))))
{
@@ -3456,8 +3999,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
}
break;
case 'F':
- if (read_server_arguments(argument))
- die(NullS);
+ read_embedded_server_arguments(argument);
break;
case OPT_SKIP_SAFEMALLOC:
#ifdef SAFEMALLOC
@@ -3498,16 +4040,16 @@ int parse_args(int argc, char **argv)
/*
- Write the content of str into file
+ 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
+ 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
*/
-static void str_to_file(const char *fname, char *str, int size)
+void str_to_file(const char *fname, char *str, int size)
{
int fd;
char buff[FN_REFLEN];
@@ -3519,7 +4061,7 @@ static void str_to_file(const char *fname, char *str, int size)
fn_format(buff, fname, "", "", MY_UNPACK_FILENAME);
if ((fd= my_open(buff, O_WRONLY | O_CREAT | O_TRUNC,
- MYF(MY_WME | MY_FFNF))) < 0)
+ MYF(MY_WME | MY_FFNF))) < 0)
die("Could not open %s: errno = %d", buff, errno);
if (my_write(fd, (byte*)str, size, MYF(MY_WME|MY_FNABP)))
die("write failed");
@@ -3527,236 +4069,48 @@ static void str_to_file(const char *fname, char *str, int size)
}
-void dump_result_to_reject_file(const char *record_file, char *buf, int size)
+void dump_result_to_reject_file(char *buf, int size)
{
char reject_file[FN_REFLEN];
- str_to_file(fn_format(reject_file, record_file, "", ".reject",
+ str_to_file(fn_format(reject_file, result_file_name, "", ".reject",
MY_REPLACE_EXT),
buf, size);
}
-void dump_result_to_log_file(const char *record_file, char *buf, int size)
+void dump_result_to_log_file(char *buf, int size)
{
char log_file[FN_REFLEN];
- str_to_file(fn_format(log_file, record_file, "", ".log",
+ str_to_file(fn_format(log_file, result_file_name, "", ".log",
MY_REPLACE_EXT),
buf, size);
}
-void dump_progress(const char *record_file)
+void dump_progress(void)
{
char log_file[FN_REFLEN];
- str_to_file(fn_format(log_file, record_file, "", ".progress",
+ str_to_file(fn_format(log_file, result_file_name, "", ".progress",
MY_REPLACE_EXT),
ds_progress.str, ds_progress.length);
}
-static void check_regerr(my_regex_t* r, int err)
+void dump_warning_messages(void)
{
- char err_buf[1024];
+ char warn_file[FN_REFLEN];
- if (err)
- {
- my_regerror(err,r,err_buf,sizeof(err_buf));
- die("Regex error: %s\n", err_buf);
- }
+ str_to_file(fn_format(warn_file, result_file_name, "", ".warnings",
+ MY_REPLACE_EXT),
+ ds_warning_messages.str, ds_warning_messages.length);
}
-/*
- 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
- */
-static int reg_replace(char** buf_p, int* buf_len_p, char *pattern,
- char *replace, char *string, int icase)
+void check_regerr(my_regex_t* r, int err)
{
- my_regex_t r;
- my_regmatch_t* subs;
- char* buf_end, *replace_end;
- char* buf= *buf_p;
- int len;
- int buf_len,need_buf_len;
- int cflags= REG_EXTENDED;
- int err_code;
- char* res_p,*str_p,*str_end;
-
- buf_len= *buf_len_p;
- len= strlen(string);
- str_end= string + len;
-
- /* start with a buffer of a reasonable size that hopefully will not
- need to be reallocated
- */
- need_buf_len= len * 2 + 1;
- res_p= buf;
+ char err_buf[1024];
- SECURE_REG_BUF
-
- buf_end = buf + buf_len;
-
- 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)
+ if (err)
{
- /* find the match */
- err_code= my_regexec(&r,str_p, r.re_nsub+1, subs,
- (str_p == string) ? REG_NOTBOL : 0);
-
- /* if regular expression error (eg. bad syntax, or out of memory) */
- if (err_code && err_code != REG_NOMATCH)
- {
- check_regerr(&r,err_code);
- my_regfree(&r);
- return 1;
- }
-
- /* if match found */
- if (!err_code)
- {
- char* expr_p= replace;
- int c;
-
- /*
- we need at least what we have so far in the buffer + the part
- before this match
- */
- need_buf_len= (res_p - buf) + subs[0].rm_so;
-
- /* on this pass, calculate the memory for the result buffer */
- while (expr_p < replace_end)
- {
- int back_ref_num= -1;
- c= *expr_p;
-
- if (c == '\\' && expr_p + 1 < replace_end)
- {
- back_ref_num= expr_p[1] - '0';
- }
-
- /* found a valid back_ref (eg. \1)*/
- if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub)
- {
- int start_off,end_off;
- if ((start_off=subs[back_ref_num].rm_so) > -1 &&
- (end_off=subs[back_ref_num].rm_eo) > -1)
- {
- need_buf_len += (end_off - start_off);
- }
- expr_p += 2;
- }
- else
- {
- expr_p++;
- need_buf_len++;
- }
- }
- need_buf_len++;
- /*
- now that we know the size of the buffer,
- make sure it is big enough
- */
- SECURE_REG_BUF
-
- /* copy the pre-match part */
- if (subs[0].rm_so)
- {
- memcpy(res_p,str_p,subs[0].rm_so);
- res_p += subs[0].rm_so;
- }
-
- expr_p= replace;
-
- /* copy the match and expand back_refs */
- while (expr_p < replace_end)
- {
- int back_ref_num= -1;
- c= *expr_p;
-
- if (c == '\\' && expr_p + 1 < replace_end)
- {
- back_ref_num= expr_p[1] - '0';
- }
-
- if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub)
- {
- int start_off,end_off;
- if ((start_off=subs[back_ref_num].rm_so) > -1 &&
- (end_off=subs[back_ref_num].rm_eo) > -1)
- {
- int block_len= end_off - start_off;
- memcpy(res_p,str_p + start_off, block_len);
- res_p += block_len;
- }
- expr_p += 2;
- }
- else
- {
- *res_p++ = *expr_p++;
- }
- }
-
- /* handle the post-match part */
- if (subs[0].rm_so == subs[0].rm_eo)
- {
- if (str_p + subs[0].rm_so >= str_end)
- break;
- str_p += subs[0].rm_eo ;
- *res_p++ = *str_p++;
- }
- else
- {
- str_p += subs[0].rm_eo;
- }
- }
- else /* no match this time, just copy the string as is */
- {
- int left_in_str= str_end-str_p;
- need_buf_len= (res_p-buf) + left_in_str;
- SECURE_REG_BUF
- memcpy(res_p,str_p,left_in_str);
- res_p += left_in_str;
- str_p= str_end;
- }
- }
- my_regfree(&r);
- *res_p= 0;
- *buf_p= buf;
- *buf_len_p= buf_len;
- return 0;
+ my_regerror(err,r,err_buf,sizeof(err_buf));
+ die("Regex error: %s\n", err_buf);
+ }
}
@@ -3768,12 +4122,12 @@ 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
+ Setup string patterns that will be used to detect filenames that
+ needs to be converted from Win to Unix format
*/
-static void init_win_path_patterns()
+void init_win_path_patterns()
{
/* List of string patterns to match in order to find paths */
const char* paths[] = { "$MYSQL_TEST_DIR",
@@ -3800,7 +4154,7 @@ static void init_win_path_patterns()
p= my_strdup(paths[i], MYF(MY_FAE));
if (insert_dynamic(&patterns, (gptr) &p))
- die(NullS);
+ die(NullS);
DBUG_PRINT("info", ("p: %s", p));
while (*p)
@@ -3813,7 +4167,7 @@ static void init_win_path_patterns()
DBUG_VOID_RETURN;
}
-static void free_win_path_patterns()
+void free_win_path_patterns()
{
uint i= 0;
for (i=0 ; i < patterns.elements ; i++)
@@ -3828,17 +4182,17 @@ static void free_win_path_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 /
+ 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 /
*/
-static void fix_win_paths(const char* val, int len)
+void fix_win_paths(const char *val, int len)
{
uint i;
char *p;
@@ -3868,43 +4222,14 @@ static void fix_win_paths(const char* val, int len)
}
#endif
-/* Append the string to ds, with optional replace */
-static 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)
- {
- if (!multi_reg_replace(glob_replace_regex, (char*)val))
- {
- val= glob_replace_regex->buf;
- len= strlen(val);
- }
- }
-
- if (glob_replace)
- replace_strings_append(glob_replace, ds, val, len);
- else
- dynstr_append_mem(ds, val, len);
-}
-
-
-/* Append zero-terminated string to ds, with optional replace */
-static void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val)
-{
- replace_dynstr_append_mem(ds, val, strlen(val));
-}
/*
Append the result for one field to the dynamic string ds
*/
-static void append_field(DYNAMIC_STRING *ds, uint col_idx, MYSQL_FIELD* field,
- const char* val, ulonglong len, bool is_null)
+void append_field(DYNAMIC_STRING *ds, uint col_idx, MYSQL_FIELD* field,
+ const char* val, ulonglong len, bool is_null)
{
if (col_idx < max_replace_column && replace_column[col_idx])
{
@@ -3955,7 +4280,7 @@ static void append_field(DYNAMIC_STRING *ds, uint col_idx, MYSQL_FIELD* field,
Values may be converted with 'replace_column'
*/
-static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res)
+void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res)
{
MYSQL_ROW row;
uint num_fields= mysql_num_fields(res);
@@ -3972,7 +4297,6 @@ static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res)
if (!display_result_vertically)
dynstr_append_mem(ds, "\n", 1);
}
- free_replace_column();
}
@@ -3981,8 +4305,8 @@ static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res)
with '\t'. Values may be converted with 'replace_column'
*/
-static void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt,
- MYSQL_FIELD *fields, uint num_fields)
+void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt,
+ MYSQL_FIELD *fields, uint num_fields)
{
MYSQL_BIND *bind;
my_bool *is_null;
@@ -4007,7 +4331,7 @@ static void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt,
bind[i].is_null= &is_null[i];
bind[i].length= &length[i];
- DBUG_PRINT("bind", ("col[%d]: buffer_type: %d, buffer_length: %d",
+ DBUG_PRINT("bind", ("col[%d]: buffer_type: %d, buffer_length: %lu",
i, bind[i].buffer_type, bind[i].buffer_length));
}
@@ -4028,8 +4352,6 @@ static void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt,
die("fetch didn't end with MYSQL_NO_DATA from statement: %d %s",
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
- free_replace_column();
-
for (i= 0; i < num_fields; i++)
{
/* Free data for output */
@@ -4046,9 +4368,9 @@ static void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt,
Append metadata for fields to output
*/
-static void append_metadata(DYNAMIC_STRING *ds,
- MYSQL_FIELD *field,
- uint num_fields)
+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"
@@ -4059,44 +4381,36 @@ static void append_metadata(DYNAMIC_STRING *ds,
field < field_end ;
field++)
{
- char buff[22];
dynstr_append_mem(ds, field->catalog,
- field->catalog_length);
+ 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);
+ field->org_table_length);
dynstr_append_mem(ds, "\t", 1);
dynstr_append_mem(ds, field->table,
- field->table_length);
+ field->table_length);
dynstr_append_mem(ds, "\t", 1);
dynstr_append_mem(ds, field->org_name,
- field->org_name_length);
+ field->org_name_length);
dynstr_append_mem(ds, "\t", 1);
dynstr_append_mem(ds, field->name, field->name_length);
dynstr_append_mem(ds, "\t", 1);
- int10_to_str((int) field->type, buff, 10);
- dynstr_append(ds, buff);
+ replace_dynstr_append_uint(ds, field->type);
dynstr_append_mem(ds, "\t", 1);
- longlong10_to_str((unsigned int) field->length, buff, 10);
- dynstr_append(ds, buff);
+ replace_dynstr_append_uint(ds, field->length);
dynstr_append_mem(ds, "\t", 1);
- longlong10_to_str((unsigned int) field->max_length, buff, 10);
- dynstr_append(ds, buff);
+ 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);
-
- int10_to_str((int) field->flags, buff, 10);
- dynstr_append(ds, buff);
+ replace_dynstr_append_uint(ds, field->flags);
dynstr_append_mem(ds, "\t", 1);
- int10_to_str((int) field->decimals, buff, 10);
- dynstr_append(ds, buff);
+ replace_dynstr_append_uint(ds, field->decimals);
dynstr_append_mem(ds, "\t", 1);
- int10_to_str((int) field->charsetnr, buff, 10);
- dynstr_append(ds, buff);
+ replace_dynstr_append_uint(ds, field->charsetnr);
dynstr_append_mem(ds, "\n", 1);
}
}
@@ -4106,8 +4420,8 @@ static void append_metadata(DYNAMIC_STRING *ds,
Append affected row count and other info to output
*/
-static void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows,
- const char *info)
+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));
@@ -4122,12 +4436,12 @@ static void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows,
/*
- Display the table headings with the names tab separated
+ Display the table headings with the names tab separated
*/
-static void append_table_headings(DYNAMIC_STRING *ds,
- MYSQL_FIELD *field,
- uint num_fields)
+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++)
@@ -4143,10 +4457,10 @@ static void append_table_headings(DYNAMIC_STRING *ds,
Fetch warnings from server and append to ds
RETURN VALUE
- Number of warnings appended to ds
+ Number of warnings appended to ds
*/
-static int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql)
+int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql)
{
uint count;
MYSQL_RES *warn_res;
@@ -4195,9 +4509,9 @@ static int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql)
error - function will not return
*/
-static void run_query_normal(MYSQL *mysql, struct st_query *command,
- int flags, char *query, int query_len,
- DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings)
+void run_query_normal(MYSQL *mysql, struct st_command *command,
+ int flags, char *query, int query_len,
+ DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings)
{
MYSQL_RES *res= 0;
int err= 0, counter= 0;
@@ -4205,20 +4519,20 @@ static void run_query_normal(MYSQL *mysql, struct st_query *command,
DBUG_PRINT("enter",("flags: %d", flags));
DBUG_PRINT("enter", ("query: '%-.60s'", query));
- if (flags & QUERY_SEND)
+ if (flags & QUERY_SEND_FLAG)
{
/*
- Send the query
- */
+ Send the query
+ */
if (mysql_send_query(mysql, query, query_len))
{
- handle_error(query, command, mysql_errno(mysql), mysql_error(mysql),
+ handle_error(command, mysql_errno(mysql), mysql_error(mysql),
mysql_sqlstate(mysql), ds);
goto end;
}
}
- if (!(flags & QUERY_REAP))
+ if (!(flags & QUERY_REAP_FLAG))
DBUG_VOID_RETURN;
do
@@ -4226,22 +4540,22 @@ static void run_query_normal(MYSQL *mysql, struct st_query *command,
/*
When on first result set, call mysql_read_query_result to retrieve
answer to the query sent earlier
- */
+ */
if ((counter==0) && mysql_read_query_result(mysql))
{
- handle_error(query, command, mysql_errno(mysql), mysql_error(mysql),
+ handle_error(command, mysql_errno(mysql), mysql_error(mysql),
mysql_sqlstate(mysql), ds);
goto end;
}
/*
- Store the result. If res is NULL, use mysql_field_count to
- determine if that was expected
- */
+ Store the result. If res is NULL, use mysql_field_count to
+ determine if that was expected
+ */
if (!(res= mysql_store_result(mysql)) && mysql_field_count(mysql))
{
- handle_error(query, command, mysql_errno(mysql), mysql_error(mysql),
+ handle_error(command, mysql_errno(mysql), mysql_error(mysql),
mysql_sqlstate(mysql), ds);
goto end;
}
@@ -4297,7 +4611,7 @@ static void run_query_normal(MYSQL *mysql, struct st_query *command,
if (err > 0)
{
/* We got an error from mysql_next_result, maybe expected */
- handle_error(query, command, mysql_errno(mysql), mysql_error(mysql),
+ handle_error(command, mysql_errno(mysql), mysql_error(mysql),
mysql_sqlstate(mysql), ds);
goto end;
}
@@ -4307,8 +4621,6 @@ static void run_query_normal(MYSQL *mysql, struct st_query *command,
handle_no_error(command);
end:
- free_replace();
- free_replace_regex();
/*
We save the return code (mysql_errno(mysql)) from the last call sent
@@ -4324,58 +4636,62 @@ end:
Handle errors which occurred during execution
SYNOPSIS
- handle_error()
- query - query string
- q - query context
- err_errno - error number
- err_error - error message
- err_sqlstate - sql state
- ds - dynamic string which is used for output buffer
+ 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.
+ If there is an unexpected error this function will abort mysqltest
+ immediately.
RETURN VALUE
- error - function will not return
+ error - function will not return
*/
-static void handle_error(const char *query, struct st_query *q,
- unsigned int err_errno, const char *err_error,
- const char *err_sqlstate, DYNAMIC_STRING *ds)
+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 (q->require_file)
+ 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", query, err_errno, err_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", query);
+ abort_not_supported_test("Query '%s' failed, required functionality" \
+ "not supported", command->query);
}
- if (q->abort_on_error)
- die("query '%s' failed: %d: %s", query, err_errno, err_error);
+ if (command->abort_on_error)
+ die("query '%s' failed: %d: %s", command->query, err_errno, err_error);
- for (i= 0 ; (uint) i < q->expected_errors ; i++)
+ DBUG_PRINT("info", ("expected_errors.count: %d",
+ command->expected_errors.count));
+ for (i= 0 ; (uint) i < command->expected_errors.count ; i++)
{
- if (((q->expected_errno[i].type == ERR_ERRNO) &&
- (q->expected_errno[i].code.errnum == err_errno)) ||
- ((q->expected_errno[i].type == ERR_SQLSTATE) &&
- (strcmp(q->expected_errno[i].code.sqlstate, err_sqlstate) == 0)))
+ 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 (q->expected_errors == 1)
+ if (command->expected_errors.count == 1)
{
/* Only log error if there is one possible error */
dynstr_append_mem(ds, "ERROR ", 6);
@@ -4385,9 +4701,9 @@ static void handle_error(const char *query, struct st_query *q,
dynstr_append_mem(ds,"\n",1);
}
/* Don't log error if we may not get an error */
- else if (q->expected_errno[0].type == ERR_SQLSTATE ||
- (q->expected_errno[0].type == ERR_ERRNO &&
- q->expected_errno[0].code.errnum != 0))
+ 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 */
@@ -4395,7 +4711,8 @@ static void handle_error(const char *query, struct st_query *q,
}
}
- DBUG_PRINT("info",("i: %d expected_errors: %d", i, q->expected_errors));
+ DBUG_PRINT("info",("i: %d expected_errors: %d", i,
+ command->expected_errors.count));
if (!disable_result_log)
{
@@ -4408,13 +4725,14 @@ static void handle_error(const char *query, struct st_query *q,
if (i)
{
- if (q->expected_errno[0].type == ERR_ERRNO)
+ if (command->expected_errors.err[0].type == ERR_ERRNO)
die("query '%s' failed with wrong errno %d: '%s', instead of %d...",
- q->query, err_errno, err_error, q->expected_errno[0].code.errnum);
+ 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...",
- q->query, err_sqlstate, err_error,
- q->expected_errno[0].code.sqlstate);
+ command->query, err_sqlstate, err_error,
+ command->expected_errors.err[0].code.sqlstate);
}
DBUG_VOID_RETURN;
@@ -4425,30 +4743,30 @@ static void handle_error(const char *query, struct st_query *q,
Handle absence of errors after execution
SYNOPSIS
- handle_no_error()
- q - context of query
+ handle_no_error()
+ q - context of query
RETURN VALUE
- error - function will not return
+ error - function will not return
*/
-static void handle_no_error(struct st_query *q)
+void handle_no_error(struct st_command *command)
{
DBUG_ENTER("handle_no_error");
- if (q->expected_errno[0].type == ERR_ERRNO &&
- q->expected_errno[0].code.errnum != 0)
+ 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...",
- q->query, q->expected_errno[0].code.errnum);
+ command->query, command->expected_errors.err[0].code.errnum);
}
- else if (q->expected_errno[0].type == ERR_SQLSTATE &&
- strcmp(q->expected_errno[0].code.sqlstate,"00000") != 0)
+ 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...",
- q->query, q->expected_errno[0].code.sqlstate);
+ command->query, command->expected_errors.err[0].code.sqlstate);
}
DBUG_VOID_RETURN;
@@ -4470,9 +4788,9 @@ static void handle_no_error(struct st_query *q)
error - function will not return
*/
-static void run_query_stmt(MYSQL *mysql, struct st_query *command,
- char *query, int query_len, DYNAMIC_STRING *ds,
- DYNAMIC_STRING *ds_warnings)
+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;
@@ -4503,7 +4821,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command,
*/
if (mysql_stmt_prepare(stmt, query, query_len))
{
- handle_error(query, command, mysql_stmt_errno(stmt),
+ handle_error(command, mysql_stmt_errno(stmt),
mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
goto end;
}
@@ -4520,6 +4838,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command,
parameter markers.
*/
+#if MYSQL_VERSION_ID >= 50000
if (cursor_protocol_enabled)
{
/*
@@ -4530,13 +4849,14 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command,
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(query, command, mysql_stmt_errno(stmt),
+ handle_error(command, mysql_stmt_errno(stmt),
mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
goto end;
}
@@ -4550,8 +4870,8 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command,
/*
We instruct that we want to update the "max_length" field in
- mysql_stmt_store_result(), this is our only way to know how much
- buffer to allocate for result data
+ mysql_stmt_store_result(), this is our only way to know how much
+ buffer to allocate for result data
*/
{
my_bool one= 1;
@@ -4566,7 +4886,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command,
*/
if (mysql_stmt_store_result(stmt))
{
- handle_error(query, command, mysql_stmt_errno(stmt),
+ handle_error(command, mysql_stmt_errno(stmt),
mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
goto end;
}
@@ -4636,9 +4956,6 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command,
}
end:
- free_replace();
- free_replace_regex();
-
if (!disable_warnings)
{
dynstr_free(&ds_prepare_warnings);
@@ -4650,7 +4967,7 @@ end:
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));
#ifndef BUG15518_FIXED
mysql_stmt_close(stmt);
@@ -4668,7 +4985,7 @@ end:
as view, sp etc.
*/
-static int util_query(MYSQL* org_mysql, const char* query){
+int util_query(MYSQL* org_mysql, const char* query){
MYSQL* mysql;
DBUG_ENTER("util_query");
@@ -4679,11 +4996,9 @@ static int util_query(MYSQL* org_mysql, const char* query){
if (!(mysql= mysql_init(mysql)))
die("Failed in mysql_init()");
- if (safe_connect(mysql, org_mysql->host, org_mysql->user,
- org_mysql->passwd, org_mysql->db, org_mysql->port,
- org_mysql->unix_socket))
- die("Could not open util connection: %d %s",
- mysql_errno(mysql), mysql_error(mysql));
+ 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;
}
@@ -4697,8 +5012,8 @@ static int util_query(MYSQL* org_mysql, const char* query){
Run query
flags control the phased/stages of query execution to be performed
- if QUERY_SEND bit is on, the query will be sent. If QUERY_REAP is on
- the result will be read - for regular query, both bits must be on
+ 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
@@ -4707,7 +5022,7 @@ static int util_query(MYSQL* org_mysql, const char* query){
*/
-static void run_query(MYSQL *mysql, struct st_query *command, int flags)
+void run_query(MYSQL *mysql, struct st_command *command, int flags)
{
DYNAMIC_STRING *ds;
DYNAMIC_STRING ds_result;
@@ -4716,17 +5031,21 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags)
char *query;
int query_len;
my_bool view_created= 0, sp_created= 0;
- my_bool complete_query= ((flags & QUERY_SEND) && (flags & QUERY_REAP));
+ my_bool complete_query= ((flags & QUERY_SEND_FLAG) &&
+ (flags & QUERY_REAP_FLAG));
init_dynamic_string(&ds_warnings, NULL, 0, 256);
+ /* Scan for warning before sendign to server */
+ scan_command_for_warnings(command);
+
/*
Evaluate query if this is an eval command
- */
+ */
if (command->type == Q_EVAL)
{
- init_dynamic_string(&eval_query, "", 16384, 65536);
- do_eval(&eval_query, command->query, FALSE);
+ 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;
}
@@ -4737,23 +5056,23 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags)
}
/*
- When command->record_file is set the output of _this_ 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->record_file[0])
+ */
+ if (command->require_file[0])
{
- init_dynamic_string(&ds_result, "", 16384, 65536);
+ init_dynamic_string(&ds_result, "", 1024, 1024);
ds= &ds_result;
}
else
ds= &ds_res;
/*
- Log the query into the output buffer
+ Log the query into the output buffer
*/
- if (!disable_query_log && (flags & QUERY_SEND))
+ if (!disable_query_log && (flags & QUERY_SEND_FLAG))
{
replace_dynstr_append_mem(ds, query, query_len);
dynstr_append_mem(ds, delimiter, delimiter_length);
@@ -4765,8 +5084,8 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags)
match_re(&view_re, query))
{
/*
- Create the query as a view.
- Use replace since view can exist from a failed mysqltest run
+ 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,
@@ -4778,7 +5097,7 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags)
/*
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)));
@@ -4791,14 +5110,14 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags)
{
/*
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
+ 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);
}
@@ -4877,26 +5196,14 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags)
mysql_errno(mysql), mysql_error(mysql));
}
- if (command->record_file[0])
+ if (command->require_file[0])
{
- /* A result file was specified for _this_ query */
- if (record)
- {
- /*
- Recording in progress
- Dump the output from _this_ query to the specified record_file
- */
- str_to_file(command->record_file, ds->str, ds->length);
-
- } else {
-
- /*
- The output from _this_ query should be checked against an already
- existing file which has been specified using --require or --result
- */
- check_result(ds, command->record_file, command->require_file);
- }
+ /* 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);
@@ -4906,12 +5213,12 @@ static void run_query(MYSQL *mysql, struct st_query *command, int flags)
dynstr_free(&eval_query);
}
+/****************************************************************************/
+/*
+ Functions to detect different SQL statements
+*/
-/****************************************************************************\
- * Functions to detect different SQL statements
-\****************************************************************************/
-
-static char *re_eprint(int err)
+char *re_eprint(int err)
{
static char epbuf[100];
size_t len= my_regerror(REG_ITOA|err, (my_regex_t *)NULL,
@@ -4920,7 +5227,7 @@ static char *re_eprint(int err)
return(epbuf);
}
-static void init_re_comp(my_regex_t *re, const char* str)
+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);
@@ -4933,11 +5240,11 @@ static void init_re_comp(my_regex_t *re, const char* str)
}
}
-static void init_re(void)
+void init_re(void)
{
/*
- Filter for queries that can be run using the
- MySQL Prepared Statements C API
+ Filter for queries that can be run using the
+ MySQL Prepared Statements C API
*/
const char *ps_re_str =
"^("
@@ -4954,13 +5261,13 @@ static void init_re(void)
"[[:space:]]*INSERT[[:space:]]+SELECT[[:space:]])";
/*
- Filter for queries that can be run using the
- Stored procedures
+ 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
+ Filter for queries that can be run as views
*/
const char *view_re_str =
"^("
@@ -4972,7 +5279,7 @@ static void init_re(void)
}
-static int match_re(my_regex_t *re, char *str)
+int match_re(my_regex_t *re, char *str)
{
int err= my_regexec(re, str, (size_t)0, NULL, 0);
@@ -4990,7 +5297,7 @@ static int match_re(my_regex_t *re, char *str)
return 0;
}
-static void free_re(void)
+void free_re(void)
{
my_regfree(&ps_re);
my_regfree(&sp_re);
@@ -5000,142 +5307,85 @@ static void free_re(void)
/****************************************************************************/
-void get_query_type(struct st_query* q)
+void get_command_type(struct st_command* command)
{
char save;
uint type;
- DBUG_ENTER("get_query_type");
+ DBUG_ENTER("get_command_type");
- if (!parsing_disabled && *q->query == '}')
+ if (*command->query == '}')
{
- q->type = Q_END_BLOCK;
+ command->type = Q_END_BLOCK;
DBUG_VOID_RETURN;
}
- if (q->type != Q_COMMENT_WITH_COMMAND)
- q->type= parsing_disabled ? Q_COMMENT : Q_QUERY;
- save=q->query[q->first_word_len];
- q->query[q->first_word_len]=0;
- type=find_type(q->query, &command_typelib, 1+2);
- q->query[q->first_word_len]=save;
+ save= command->query[command->first_word_len];
+ command->query[command->first_word_len]= 0;
+ type= find_type(command->query, &command_typelib, 1+2);
+ command->query[command->first_word_len]= save;
if (type > 0)
{
- q->type=(enum enum_commands) type; /* Found command */
+ command->type=(enum enum_commands) type; /* Found command */
+
/*
- If queries are disabled, only recognize
- --enable_parsing and --disable_parsing
+ Look for case where "query" was explicitly specified to
+ force command being sent to server
*/
- if (parsing_disabled && q->type != Q_ENABLE_PARSING &&
- q->type != Q_DISABLE_PARSING)
- q->type= Q_COMMENT;
+ if (type == Q_QUERY)
+ {
+ /* Skip the "query" part */
+ command->query= command->first_argument;
+ }
}
- else if (q->type == Q_COMMENT_WITH_COMMAND &&
- q->first_word_len &&
- q->query[q->first_word_len-1] == ';')
+ else
{
- /*
- Detect comment with command using extra delimiter
- Ex --disable_query_log;
- ^ Extra delimiter causing the command
- to be skipped
- */
- save= q->query[q->first_word_len-1];
- q->query[q->first_word_len-1]= 0;
- type= find_type(q->query, &command_typelib, 1+2);
- q->query[q->first_word_len-1]= save;
- if (type > 0)
- die("Extra delimiter \";\" found");
- }
- DBUG_VOID_RETURN;
-}
-
-
-static byte *get_var_key(const byte* var, uint* len,
- my_bool __attribute__((unused)) t)
-{
- register char* key;
- key = ((VAR*)var)->name;
- *len = ((VAR*)var)->name_len;
- return (byte*)key;
-}
+ /* No mysqltest command matched */
-static VAR *var_init(VAR *v, const char *name, int name_len, const char *val,
- int val_len)
-{
- int val_alloc_len;
- VAR *tmp_var;
- if (!name_len && name)
- name_len = strlen(name);
- if (!val_len && val)
- val_len = strlen(val) ;
- val_alloc_len = val_len + 16; /* room to grow */
- if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var)
- + name_len+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");
+ 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;
- 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;
-}
-
-static void var_free(void *v)
-{
- my_free(((VAR*) v)->str_val, MYF(MY_WME));
- if (((VAR*)v)->alloced)
- my_free((char*) v, MYF(MY_WME));
-}
-
-
-static VAR* var_from_env(const char *name, const char *def_val)
-{
- const char *tmp;
- VAR *v;
- if (!(tmp = getenv(name)))
- tmp = def_val;
-
- v = var_init(0, name, strlen(name), tmp, strlen(tmp));
- my_hash_insert(&var_hash, (byte*)v);
- return v;
-}
+ /* 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);
-static void init_var_hash(MYSQL *mysql)
-{
- VAR *v;
- DBUG_ENTER("init_var_hash");
- if (hash_init(&var_hash, charset_info,
- 1024, 0, 0, get_var_key, var_free, MYF(0)))
- die("Variable hash initialization failed");
- my_hash_insert(&var_hash, (byte*) var_init(0,"BIG_TEST", 0,
- (opt_big_test) ? "1" : "0", 0));
- my_hash_insert(&var_hash, (byte*) var_init(0,"VALGRIND_TEST", 0,
- (opt_valgrind_test) ? "1" : "0",
- 0));
- v= var_init(0,"MAX_TABLES", 0, (sizeof(ulong) == 4) ? "31" : "62",0);
- my_hash_insert(&var_hash, (byte*) v);
- v= var_init(0,"SERVER_VERSION", 0, mysql_get_server_info(mysql), 0);
- my_hash_insert(&var_hash, (byte*) v); v= var_init(0,"DB", 2, db, 0);
- my_hash_insert(&var_hash, (byte*) v);
DBUG_VOID_RETURN;
}
+
/*
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.
@@ -5144,7 +5394,9 @@ static void init_var_hash(MYSQL *mysql)
test run completes
*/
-static void mark_progress(struct st_query* q __attribute__((unused)), int line)
+
+void mark_progress(struct st_command* command __attribute__((unused)),
+ int line)
{
char buf[32], *end;
ulonglong timer= timer_now();
@@ -5178,47 +5430,63 @@ static void mark_progress(struct st_query* q __attribute__((unused)), int line)
int main(int argc, char **argv)
{
- struct st_query *q;
- my_bool require_file=0, q_send_flag=0,
- query_executed= 0;
+ struct st_command *command;
+ my_bool q_send_flag= 0, abort_flag= 0;
+ uint command_executed= 0, last_command_executed= 0;
char save_file[FN_REFLEN];
MY_STAT res_info;
MY_INIT(argv[0]);
- /* Use all time until exit if no explicit 'start_timer' */
- timer_start= timer_now();
+ save_file[0]= 0;
+ TMPDIR[0]= 0;
- save_file[0]=0;
- TMPDIR[0]=0;
+ /* Init expected errors */
+ memset(&saved_expected_errors, 0, sizeof(saved_expected_errors));
- /* Init cons */
- memset(cons, 0, sizeof(cons));
- cons_end = cons + MAX_CONS;
- next_con = cons + 1;
- cur_con = cons;
+ /* Init connections */
+ memset(connections, 0, sizeof(connections));
+ connections_end= connections +
+ (sizeof(connections)/sizeof(struct st_connection)) - 1;
+ next_con= connections + 1;
+ cur_con= connections;
/* Init file stack */
memset(file_stack, 0, sizeof(file_stack));
- file_stack_end= file_stack + MAX_INCLUDE_DEPTH - 1;
+ file_stack_end=
+ file_stack + (sizeof(file_stack)/sizeof(struct st_test_file)) - 1;
cur_file= file_stack;
/* Init block stack */
memset(block_stack, 0, sizeof(block_stack));
- block_stack_end= block_stack + BLOCK_STACK_DEPTH - 1;
+ block_stack_end=
+ block_stack + (sizeof(block_stack)/sizeof(struct st_block)) - 1;
cur_block= block_stack;
cur_block->ok= TRUE; /* Outer block should always be executed */
cur_block->cmd= cmd_none;
- my_init_dynamic_array(&q_lines, sizeof(struct st_query*), INIT_Q_LINES,
- INIT_Q_LINES);
+ my_init_dynamic_array(&q_lines, sizeof(struct st_command*), 1024, 1024);
+
+ if (hash_init(&var_hash, charset_info,
+ 1024, 0, 0, get_var_key, var_free, MYF(0)))
+ die("Variable hash initialization failed");
memset(&master_pos, 0, sizeof(master_pos));
- init_dynamic_string(&ds_res, "", 0, 65536);
+ parser.current_line= parser.read_lines= 0;
+ memset(&var_reg, 0, sizeof(var_reg));
+
+#ifdef __WIN__
+ init_tmp_sh_file();
+ init_win_path_patterns();
+#endif
+
+ init_dynamic_string(&ds_res, "", 65536, 65536);
init_dynamic_string(&ds_progress, "", 0, 2048);
+ init_dynamic_string(&ds_warning_messages, "", 0, 2048);
parse_args(argc, argv);
- DBUG_PRINT("info",("result_file: '%s'", result_file ? result_file : ""));
+ 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))
@@ -5246,29 +5514,30 @@ int main(int argc, char **argv)
mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name);
#ifdef HAVE_OPENSSL
+
+#if MYSQL_VERSION_ID >= 50000
opt_ssl_verify_server_cert= TRUE; /* Always on in mysqltest */
+#endif
+
if (opt_use_ssl)
{
mysql_ssl_set(&cur_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
opt_ssl_capath, opt_ssl_cipher);
+#if MYSQL_VERSION_ID >= 50000
mysql_options(&cur_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
&opt_ssl_verify_server_cert);
+#endif
}
#endif
if (!(cur_con->name = my_strdup("default", MYF(MY_WME))))
die("Out of memory");
- if (safe_connect(&cur_con->mysql, host, user, pass, db, port, unix_sock))
- die("Could not open connection '%s': %d %s", cur_con->name,
- mysql_errno(&cur_con->mysql), mysql_error(&cur_con->mysql));
+ safe_connect(&cur_con->mysql, cur_con->name, host, user, pass,
+ db, port, unix_sock);
- init_var_hash(&cur_con->mysql);
-
-#ifdef __WIN__
- init_tmp_sh_file();
- init_win_path_patterns();
-#endif
+ /* Use all time until exit if no explicit 'start_timer' */
+ timer_start= timer_now();
/*
Initialize $mysql_errno with -1, so we can
@@ -5282,26 +5551,35 @@ int main(int argc, char **argv)
open_file(opt_include);
}
- while (!read_query(&q))
+ while (!read_command(&command) && !abort_flag)
{
int current_line_inc = 1, processed = 0;
- if (q->type == Q_UNKNOWN || q->type == Q_COMMENT_WITH_COMMAND)
- get_query_type(q);
+ if (command->type == Q_UNKNOWN || command->type == Q_COMMENT_WITH_COMMAND)
+ get_command_type(command);
+
+ if (parsing_disabled &&
+ command->type != Q_ENABLE_PARSING &&
+ command->type != Q_DISABLE_PARSING)
+ {
+ command->type= Q_COMMENT;
+ scan_command_for_warnings(command);
+ }
+
if (cur_block->ok)
{
- q->last_argument= q->first_argument;
+ command->last_argument= command->first_argument;
processed = 1;
- switch (q->type) {
+ switch (command->type) {
case Q_CONNECT:
- do_connect(q);
+ do_connect(command);
break;
- case Q_CONNECTION: select_connection(q); break;
+ case Q_CONNECTION: select_connection(command); break;
case Q_DISCONNECT:
case Q_DIRTY_CLOSE:
- close_connection(q); break;
- case Q_RPL_PROBE: do_rpl_probe(q); break;
- case Q_ENABLE_RPL_PARSE: do_enable_rpl_parse(q); break;
- case Q_DISABLE_RPL_PARSE: do_disable_rpl_parse(q); break;
+ do_close_connection(command); break;
+ case Q_RPL_PROBE: do_rpl_probe(command); break;
+ case Q_ENABLE_RPL_PARSE: do_enable_rpl_parse(command); break;
+ case Q_DISABLE_RPL_PARSE: do_disable_rpl_parse(command); break;
case Q_ENABLE_QUERY_LOG: disable_query_log=0; break;
case Q_DISABLE_QUERY_LOG: disable_query_log=1; break;
case Q_ENABLE_ABORT_ON_ERROR: abort_on_error=1; break;
@@ -5316,18 +5594,21 @@ int main(int argc, char **argv)
case Q_DISABLE_INFO: disable_info=1; break;
case Q_ENABLE_METADATA: display_metadata=1; break;
case Q_DISABLE_METADATA: display_metadata=0; break;
- case Q_SOURCE: do_source(q); break;
- case Q_SLEEP: do_sleep(q, 0); break;
- case Q_REAL_SLEEP: do_sleep(q, 1); break;
- case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(q); break;
- case Q_INC: do_modify_var(q, DO_INC); break;
- 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_SOURCE: do_source(command); break;
+ case Q_SLEEP: do_sleep(command, 0); break;
+ case Q_REAL_SLEEP: do_sleep(command, 1); break;
+ case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(command); break;
+ case Q_INC: do_modify_var(command, DO_INC); break;
+ case Q_DEC: do_modify_var(command, DO_DEC); break;
+ case Q_ECHO: do_echo(command); command_executed++; break;
+ case Q_SYSTEM: do_system(command); break;
+ case Q_REMOVE_FILE: do_remove_file(command); break;
+ case Q_FILE_EXIST: do_file_exist(command); break;
+ case Q_WRITE_FILE: do_write_file(command); break;
+ case Q_COPY_FILE: do_copy_file(command); break;
+ case Q_PERL: do_perl(command); break;
case Q_DELIMITER:
- strmake(delimiter, q->first_argument, sizeof(delimiter) - 1);
- delimiter_length= strlen(delimiter);
- q->last_argument= q->first_argument+delimiter_length;
+ do_delimiter(command);
break;
case Q_DISPLAY_VERTICAL_RESULTS:
display_result_vertically= TRUE;
@@ -5335,133 +5616,128 @@ int main(int argc, char **argv)
case Q_DISPLAY_HORIZONTAL_RESULTS:
display_result_vertically= FALSE;
break;
- case Q_LET: do_let(q); break;
+ case Q_LET: do_let(command); break;
case Q_EVAL_RESULT:
eval_result = 1; break;
case Q_EVAL:
- if (q->query == q->query_buf)
+ if (command->query == command->query_buf)
{
- q->query= q->first_argument;
- q->first_word_len= 0;
+ command->query= command->first_argument;
+ command->first_word_len= 0;
}
/* fall through */
case Q_QUERY_VERTICAL:
case Q_QUERY_HORIZONTAL:
{
my_bool old_display_result_vertically= display_result_vertically;
- /* fix up query pointer if this is first iteration for this line */
- if (q->query == q->query_buf)
- q->query += q->first_word_len + 1;
- display_result_vertically= (q->type==Q_QUERY_VERTICAL);
+
+ /* Remove "query_*" if this is first iteration */
+ if (command->query == command->query_buf)
+ command->query= command->first_argument;
+
+ display_result_vertically= (command->type == Q_QUERY_VERTICAL);
if (save_file[0])
{
- strmov(q->record_file,save_file);
- q->require_file=require_file;
- save_file[0]=0;
+ strmake(command->require_file, save_file, sizeof(save_file));
+ save_file[0]= 0;
}
- run_query(&cur_con->mysql, q, QUERY_REAP|QUERY_SEND);
+ run_query(&cur_con->mysql, command, QUERY_REAP_FLAG|QUERY_SEND_FLAG);
display_result_vertically= old_display_result_vertically;
- q->last_argument= q->end;
- query_executed= 1;
+ command->last_argument= command->end;
+ command_executed++;
break;
}
case Q_QUERY:
case Q_REAP:
{
- /*
- We read the result always regardless of the mode for both full
- query and read-result only (reap)
- */
- int flags = QUERY_REAP;
- if (q->type != Q_REAP) /* for a full query, enable the send stage */
- flags |= QUERY_SEND;
- if (q_send_flag)
- {
- flags= QUERY_SEND;
- q_send_flag=0;
- }
+ int flags;
+ if (q_send_flag)
+ {
+ /* Last command was an empty 'send' */
+ flags= QUERY_SEND_FLAG;
+ q_send_flag= 0;
+ }
+ else if (command->type == Q_REAP)
+ {
+ flags= QUERY_REAP_FLAG;
+ }
+ else
+ {
+ /* full query, both reap and send */
+ flags= QUERY_REAP_FLAG | QUERY_SEND_FLAG;
+ }
+
if (save_file[0])
{
- strmov(q->record_file,save_file);
- q->require_file=require_file;
- save_file[0]=0;
+ strmake(command->require_file, save_file, sizeof(save_file));
+ save_file[0]= 0;
}
- /*
- To force something being sent as a query to the mysqld one can
- use the prefix "query". Remove "query" from string before executing
- */
- if (strncmp(q->query, "query ", 6) == 0)
- {
- q->query= q->first_argument;
- }
- run_query(&cur_con->mysql, q, flags);
- query_executed= 1;
- q->last_argument= q->end;
+ run_query(&cur_con->mysql, command, flags);
+ command_executed++;
+ command->last_argument= command->end;
break;
}
case Q_SEND:
- if (!q->query[q->first_word_len])
- {
- /* This happens when we use 'send' on its own line */
- q_send_flag=1;
- break;
- }
- /* fix up query pointer if this is first iteration for this line */
- if (q->query == q->query_buf)
- q->query += q->first_word_len;
+ if (!*command->first_argument)
+ {
+ /*
+ This is a send without arguments, it indicates that _next_ query
+ should be send only
+ */
+ q_send_flag= 1;
+ break;
+ }
+
+ /* Remove "send" if this is first iteration */
+ if (command->query == command->query_buf)
+ command->query= command->first_argument;
+
/*
run_query() can execute a query partially, depending on the flags.
- QUERY_SEND flag without QUERY_REAP tells it to just send the
- query and read the result some time later when reap instruction
+ 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->mysql, q, QUERY_SEND);
- query_executed= 1;
- q->last_argument= q->end;
+ */
+ run_query(&cur_con->mysql, command, QUERY_SEND_FLAG);
+ command_executed++;
+ command->last_argument= command->end;
break;
- case Q_RESULT:
- get_file_name(save_file,q);
- require_file=0;
+ case Q_REQUIRE:
+ do_get_file_name(command, save_file, sizeof(save_file));
break;
case Q_ERROR:
- global_expected_errors=get_errcodes(global_expected_errno,q);
- break;
- case Q_REQUIRE:
- get_file_name(save_file,q);
- require_file=1;
+ do_get_errcodes(command);
break;
case Q_REPLACE:
- get_replace(q);
+ do_get_replace(command);
break;
case Q_REPLACE_REGEX:
- get_replace_regex(q);
+ do_get_replace_regex(command);
break;
-
case Q_REPLACE_COLUMN:
- get_replace_column(q);
+ do_get_replace_column(command);
break;
case Q_SAVE_MASTER_POS: do_save_master_pos(); break;
- case Q_SYNC_WITH_MASTER: do_sync_with_master(q); break;
+ case Q_SYNC_WITH_MASTER: do_sync_with_master(command); break;
case Q_SYNC_SLAVE_WITH_MASTER:
{
do_save_master_pos();
- if (*q->first_argument)
- select_connection(q);
+ if (*command->first_argument)
+ select_connection(command);
else
select_connection_name("slave");
do_sync_with_master2(0);
break;
}
case Q_COMMENT: /* Ignore row */
- case Q_COMMENT_WITH_COMMAND:
- q->last_argument= q->end;
+ command->last_argument= command->end;
break;
case Q_PING:
(void) mysql_ping(&cur_con->mysql);
break;
case Q_EXEC:
- do_exec(q);
- query_executed= 1;
+ do_exec(command);
+ command_executed++;
break;
case Q_START_TIMER:
/* Overwrite possible earlier start of timer */
@@ -5470,10 +5746,9 @@ int main(int argc, char **argv)
case Q_END_TIMER:
/* End timer before ending mysqltest */
timer_output();
- got_end_timer= TRUE;
break;
case Q_CHARACTER_SET:
- set_charset(q);
+ do_set_charset(command);
break;
case Q_DISABLE_PS_PROTOCOL:
ps_protocol_enabled= 0;
@@ -5482,31 +5757,38 @@ int main(int argc, char **argv)
ps_protocol_enabled= ps_protocol;
break;
case Q_DISABLE_RECONNECT:
- {
- my_bool reconnect= 0;
- mysql_options(&cur_con->mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
+ set_reconnect(&cur_con->mysql, 0);
break;
- }
case Q_ENABLE_RECONNECT:
- {
- my_bool reconnect= 1;
- mysql_options(&cur_con->mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
+ set_reconnect(&cur_con->mysql, 1);
break;
- }
case Q_DISABLE_PARSING:
- parsing_disabled++;
+ 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 > 0)
- parsing_disabled--;
+ if (parsing_disabled == 1)
+ parsing_disabled= 0;
+ else
+ die("Parsing is already enabled");
break;
-
case Q_DIE:
- die("%s", q->first_argument);
+ /* 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_RESULT:
+ die("result, deprecated command");
break;
default:
@@ -5518,33 +5800,46 @@ int main(int argc, char **argv)
if (!processed)
{
current_line_inc= 0;
- switch (q->type) {
- case Q_WHILE: do_block(cmd_while, q); break;
- case Q_IF: do_block(cmd_if, q); break;
- case Q_END_BLOCK: do_done(q); break;
+ switch (command->type) {
+ case Q_WHILE: do_block(cmd_while, command); break;
+ case Q_IF: do_block(cmd_if, command); break;
+ case Q_END_BLOCK: do_done(command); break;
default: current_line_inc = 1; break;
}
}
else
- check_eol_junk(q->last_argument);
+ check_eol_junk(command->last_argument);
- if (q->type != Q_ERROR)
+ if (command->type != Q_ERROR &&
+ command->type != Q_COMMENT)
{
/*
- As soon as any non "error" command has been executed,
+ As soon as any non "error" command or comment has been executed,
the array with expected errors should be cleared
*/
- global_expected_errors= 0;
- bzero((gptr) global_expected_errno, sizeof(global_expected_errno));
+ memset(&saved_expected_errors, 0, sizeof(saved_expected_errors));
+ }
+
+ if (command_executed != last_command_executed)
+ {
+ /*
+ As soon as any command has been executed,
+ the replace structures should be cleared
+ */
+ free_all_replace();
}
+ last_command_executed= command_executed;
parser.current_line += current_line_inc;
if ( opt_mark_progress )
- mark_progress(q, parser.current_line);
+ 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.
@@ -5552,25 +5847,27 @@ int main(int argc, char **argv)
*/
if (ds_res.length)
{
- if (result_file)
+ if (result_file_name)
{
+ /* A result file has been specified */
+
if (record)
{
- /* Dump the output from test to result file */
- str_to_file(result_file, ds_res.str, ds_res.length);
+ /* 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, result_file, 0);
+ */
+ check_result(&ds_res);
}
}
else
{
- /* No result_file specified to compare with, print to stdout */
+ /* No result_file_name specified to compare with, print to stdout */
printf("%s", ds_res.str);
}
}
@@ -5579,7 +5876,8 @@ int main(int argc, char **argv)
die("The test didn't produce any output");
}
- if (!query_executed && result_file && my_stat(result_file, &res_info, 0))
+ 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
@@ -5591,14 +5889,16 @@ int main(int argc, char **argv)
die("No queries executed but result file found!");
}
- if ( opt_mark_progress )
- dump_progress(result_file);
- dynstr_free(&ds_progress);
+ if ( opt_mark_progress && result_file_name )
+ dump_progress();
+
+ /* Dump warning messages */
+ if (result_file_name && ds_warning_messages.length)
+ dump_warning_messages();
dynstr_free(&ds_res);
- if (!got_end_timer)
- timer_output(); /* No end_timer cmd, end it */
+ timer_output();
free_used_memory();
my_end(MY_CHECK_ERROR);
@@ -5611,101 +5911,188 @@ int main(int argc, char **argv)
/*
- Read arguments for embedded server and put them into
- embedded_server_args_count and embedded_server_args[]
-*/
+ A primitive timer that give results in milliseconds if the
+ --timer-file=<filename> is given. The timer result is written
+ to that file when the result is available. To not confuse
+ mysql-test-run with an old obsolete result, we remove the file
+ before executing any commands. The time we measure is
+ - If no explicit 'start_timer' or 'end_timer' is given in the
+ test case, the timer measure how long we execute in mysqltest.
-static int read_server_arguments(const char *name)
-{
- char argument[1024],buff[FN_REFLEN], *str=0;
- FILE *file;
+ - If only 'start_timer' is given we measure how long we execute
+ from that point until we terminate mysqltest.
- if (!test_if_hard_path(name))
+ - If only 'end_timer' is given we measure how long we execute
+ from that we enter mysqltest to the 'end_timer' is command is
+ executed.
+
+ - If both 'start_timer' and 'end_timer' are given we measure
+ the time between executing the two commands.
+*/
+
+void timer_output(void)
+{
+ if (timer_file)
{
- strxmov(buff, opt_basedir, name, NullS);
- name=buff;
+ char buf[32], *end;
+ ulonglong timer= timer_now() - timer_start;
+ end= longlong2str(timer, buf, 10);
+ str_to_file(timer_file,buf, (int) (end-buf));
+ /* Timer has been written to the file, don't use it anymore */
+ timer_file= 0;
}
- fn_format(buff, name, "", "", MY_UNPACK_FILENAME);
+}
- if (!embedded_server_arg_count)
+
+ulonglong timer_now(void)
+{
+ return my_getsystime() / 10000;
+}
+
+
+/*
+ Get arguments for replace_columns. The syntax is:
+ replace-column column_number to_string [column_number to_string ...]
+ Where each argument may be quoted with ' or "
+ A argument may also be a variable, in which case the value of the
+ variable is replaced.
+*/
+
+void do_get_replace_column(struct st_command *command)
+{
+ char *from= command->first_argument;
+ char *buff, *start;
+ DBUG_ENTER("get_replace_columns");
+
+ free_replace_column();
+ if (!*from)
+ die("Missing argument in %s", command->query);
+
+ /* Allocate a buffer for results */
+ start= buff= my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
+ while (*from)
{
- embedded_server_arg_count=1;
- embedded_server_args[0]= (char*) ""; /* Progname */
+ char *to;
+ uint column_number;
+
+ to= get_string(&buff, &from, command);
+ if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS)
+ die("Wrong column number to replace_column in '%s'", command->query);
+ if (!*from)
+ die("Wrong number of arguments to replace_column in '%s'", command->query);
+ to= get_string(&buff, &from, command);
+ my_free(replace_column[column_number-1], MY_ALLOW_ZERO_PTR);
+ replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE));
+ set_if_bigger(max_replace_column, column_number);
}
- if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
- return 1;
- while (embedded_server_arg_count < MAX_SERVER_ARGS &&
- (str=fgets(argument,sizeof(argument), file)))
+ my_free(start, MYF(0));
+ command->last_argument= command->end;
+}
+
+
+void free_replace_column()
+{
+ uint i;
+ for (i=0 ; i < max_replace_column ; i++)
{
- *(strend(str)-1)=0; /* Remove end newline */
- if (!(embedded_server_args[embedded_server_arg_count]=
- (char*) my_strdup(str,MYF(MY_WME))))
+ if (replace_column[i])
{
- my_fclose(file,MYF(0));
- return 1;
+ my_free(replace_column[i], 0);
+ replace_column[i]= 0;
}
- embedded_server_arg_count++;
}
- my_fclose(file,MYF(0));
- if (str)
- {
- fprintf(stderr,"Too many arguments in option file: %s\n",name);
- return 1;
- }
- return 0;
+ max_replace_column= 0;
}
-/****************************************************************************\
- *
- * A primitive timer that give results in milliseconds if the
- * --timer-file=<filename> is given. The timer result is written
- * to that file when the result is available. To not confuse
- * mysql-test-run with an old obsolete result, we remove the file
- * before executing any commands. The time we measure is
- *
- * - If no explicit 'start_timer' or 'end_timer' is given in the
- * test case, the timer measure how long we execute in mysqltest.
- *
- * - If only 'start_timer' is given we measure how long we execute
- * from that point until we terminate mysqltest.
- *
- * - If only 'end_timer' is given we measure how long we execute
- * from that we enter mysqltest to the 'end_timer' is command is
- * executed.
- *
- * - If both 'start_timer' and 'end_timer' are given we measure
- * the time between executing the two commands.
- *
-\****************************************************************************/
-
-static void timer_output(void)
+
+/****************************************************************************/
+/*
+ Replace functions
+*/
+
+/* Definitions for replace result */
+
+typedef struct st_pointer_array { /* when using array-strings */
+ TYPELIB typelib; /* Pointer to strings */
+ byte *str; /* Strings is here */
+ int7 *flag; /* Flag about each var. */
+ uint array_allocs,max_count,length,max_length;
+} POINTER_ARRAY;
+
+struct st_replace;
+struct st_replace *init_replace(my_string *from, my_string *to, uint count,
+ my_string word_end_chars);
+int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name);
+void replace_strings_append(struct st_replace *rep, DYNAMIC_STRING* ds,
+ const char *from, int len);
+void free_pointer_array(POINTER_ARRAY *pa);
+
+struct st_replace *glob_replace;
+
+/*
+ Get arguments for replace. The syntax is:
+ replace from to [from to ...]
+ Where each argument may be quoted with ' or "
+ A argument may also be a variable, in which case the value of the
+ variable is replaced.
+*/
+
+void do_get_replace(struct st_command *command)
{
- if (timer_file)
+ uint i;
+ char *from= command->first_argument;
+ char *buff, *start;
+ char word_end_chars[256], *pos;
+ POINTER_ARRAY to_array, from_array;
+ DBUG_ENTER("get_replace");
+
+ free_replace();
+
+ bzero((char*) &to_array,sizeof(to_array));
+ bzero((char*) &from_array,sizeof(from_array));
+ if (!*from)
+ die("Missing argument in %s", command->query);
+ start= buff= my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
+ while (*from)
{
- char buf[32], *end;
- ulonglong timer= timer_now() - timer_start;
- end= longlong2str(timer, buf, 10);
- str_to_file(timer_file,buf, (int) (end-buf));
+ char *to= buff;
+ to= get_string(&buff, &from, command);
+ if (!*from)
+ die("Wrong number of arguments to replace_result in '%s'",
+ command->query);
+ insert_pointer_name(&from_array,to);
+ to= get_string(&buff, &from, command);
+ insert_pointer_name(&to_array,to);
}
+ for (i= 1,pos= word_end_chars ; i < 256 ; i++)
+ if (my_isspace(charset_info,i))
+ *pos++= i;
+ *pos=0; /* End pointer */
+ if (!(glob_replace= init_replace((char**) from_array.typelib.type_names,
+ (char**) to_array.typelib.type_names,
+ (uint) from_array.typelib.count,
+ word_end_chars)))
+ die("Can't initialize replace from '%s'", command->query);
+ free_pointer_array(&from_array);
+ free_pointer_array(&to_array);
+ my_free(start, MYF(0));
+ command->last_argument= command->end;
+ DBUG_VOID_RETURN;
}
-static ulonglong timer_now(void)
+
+void free_replace()
{
- return my_getsystime() / 10000;
+ DBUG_ENTER("free_replace");
+ if (glob_replace)
+ {
+ my_free((char*) glob_replace,MYF(0));
+ glob_replace=0;
+ }
+ DBUG_VOID_RETURN;
}
-/****************************************************************************
-* Handle replacement of strings
-****************************************************************************/
-
-#define PC_MALLOC 256 /* Bytes for pointers */
-#define PS_MALLOC 512 /* Bytes for data */
-
-#define SPACE_CHAR 256
-#define START_OF_LINE 257
-#define END_OF_LINE 258
-#define LAST_CHAR_CODE 259
typedef struct st_replace {
bool found;
@@ -5719,99 +6106,514 @@ typedef struct st_replace_found {
int from_offset;
} REPLACE_STRING;
-#ifndef WORD_BIT
-#define WORD_BIT (8*sizeof(uint))
-#endif
+void replace_strings_append(REPLACE *rep, DYNAMIC_STRING* ds,
+ const char *str, int len)
+{
+ reg1 REPLACE *rep_pos;
+ reg2 REPLACE_STRING *rep_str;
+ const char *start, *from;
+ DBUG_ENTER("replace_strings_append");
+
+ start= from= str;
+ rep_pos=rep+1;
+ for (;;)
+ {
+ /* Loop through states */
+ DBUG_PRINT("info", ("Looping through states"));
+ while (!rep_pos->found)
+ rep_pos= rep_pos->next[(uchar) *from++];
+
+ /* Does this state contain a string to be replaced */
+ if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string)
+ {
+ /* No match found */
+ dynstr_append_mem(ds, start, from - start - 1);
+ DBUG_PRINT("exit", ("Found no more string to replace, appended: %s", start));
+ DBUG_VOID_RETURN;
+ }
-static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name)
+ /* 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
{
- uint i,length,old_count;
- byte *new_pos;
- const char **new_array;
- DBUG_ENTER("insert_pointer_name");
+ char* pattern; /* Pattern to be replaced */
+ char* replace; /* String or expression to replace the pattern with */
+ int icase; /* true if the match is case insensitive */
+};
- if (! pa->typelib.count)
+struct st_replace_regex
+{
+ DYNAMIC_ARRAY regex_arr; /* stores a list of st_regex subsitutions */
+
+ /*
+ Temporary storage areas for substitutions. To reduce unnessary copying
+ and memory freeing/allocation, we pre-allocate two buffers, and alternate
+ their use, one for input/one for output, the roles changing on the next
+ st_regex substition. At the end of substitutions buf points to the
+ one containing the final result.
+ */
+ char* buf;
+ char* even_buf;
+ char* odd_buf;
+ int even_buf_len;
+ int odd_buf_len;
+};
+
+struct st_replace_regex *glob_replace_regex= 0;
+
+int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace,
+ char *string, int icase);
+
+
+
+/*
+ Finds the next (non-escaped) '/' in the expression.
+ (If the character '/' is needed, it can be escaped using '\'.)
+*/
+
+#define PARSE_REGEX_ARG \
+ while (p < expr_end) \
+ { \
+ char c= *p; \
+ if (c == '/') \
+ { \
+ if (last_c == '\\') \
+ { \
+ buf_p[-1]= '/'; \
+ } \
+ else \
+ { \
+ *buf_p++ = 0; \
+ break; \
+ } \
+ } \
+ else \
+ *buf_p++ = c; \
+ \
+ last_c= c; \
+ p++; \
+ } \
+ \
+/*
+ Initializes the regular substitution expression to be used in the
+ result output of test.
+
+ Returns: st_replace_regex struct with pairs of substitutions
+*/
+
+struct st_replace_regex* init_replace_regex(char* expr)
+{
+ struct st_replace_regex* res;
+ char* buf,*expr_end;
+ char* p;
+ char* buf_p;
+ uint expr_len= strlen(expr);
+ char last_c = 0;
+ struct st_regex reg;
+
+ /* my_malloc() will die on fail with MY_FAE */
+ res=(struct st_replace_regex*)my_malloc(
+ sizeof(*res)+expr_len ,MYF(MY_FAE+MY_WME));
+ my_init_dynamic_array(&res->regex_arr,sizeof(struct st_regex),128,128);
+
+ buf= (char*)res + sizeof(*res);
+ expr_end= expr + expr_len;
+ p= expr;
+ buf_p= buf;
+
+ /* for each regexp substitution statement */
+ while (p < expr_end)
{
- if (!(pa->typelib.type_names=(const char **)
- my_malloc(((PC_MALLOC-MALLOC_OVERHEAD)/
- (sizeof(my_string)+sizeof(*pa->flag))*
- (sizeof(my_string)+sizeof(*pa->flag))),MYF(MY_WME))))
- DBUG_RETURN(-1);
- if (!(pa->str= (byte*) my_malloc((uint) (PS_MALLOC-MALLOC_OVERHEAD),
- MYF(MY_WME))))
+ bzero(&reg,sizeof(reg));
+ /* find the start of the statement */
+ while (p < expr_end)
{
- my_free((gptr) pa->typelib.type_names,MYF(0));
- DBUG_RETURN (-1);
+ if (*p == '/')
+ break;
+ p++;
}
- pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(byte*)+
- sizeof(*pa->flag));
- pa->flag= (int7*) (pa->typelib.type_names+pa->max_count);
- pa->length=0;
- pa->max_length=PS_MALLOC-MALLOC_OVERHEAD;
- pa->array_allocs=1;
+
+ if (p == expr_end || ++p == expr_end)
+ {
+ if (res->regex_arr.elements)
+ break;
+ else
+ goto err;
+ }
+ /* we found the start */
+ reg.pattern= buf_p;
+
+ /* Find first argument -- pattern string to be removed */
+ PARSE_REGEX_ARG
+
+ if (p == expr_end || ++p == expr_end)
+ goto err;
+
+ /* buf_p now points to the replacement pattern terminated with \0 */
+ reg.replace= buf_p;
+
+ /* Find second argument -- replace string to replace pattern */
+ PARSE_REGEX_ARG
+
+ if (p == expr_end)
+ goto err;
+
+ /* skip the ending '/' in the statement */
+ p++;
+
+ /* Check if we should do matching case insensitive */
+ if (p < expr_end && *p == 'i')
+ reg.icase= 1;
+
+ /* done parsing the statement, now place it in regex_arr */
+ if (insert_dynamic(&res->regex_arr,(gptr) &reg))
+ die("Out of memory");
}
- length=(uint) strlen(name)+1;
- if (pa->length+length >= pa->max_length)
+ res->odd_buf_len= res->even_buf_len= 8192;
+ res->even_buf= (char*)my_malloc(res->even_buf_len,MYF(MY_WME+MY_FAE));
+ res->odd_buf= (char*)my_malloc(res->odd_buf_len,MYF(MY_WME+MY_FAE));
+ res->buf= res->even_buf;
+
+ return res;
+
+err:
+ my_free((gptr)res,0);
+ die("Error parsing replace_regex \"%s\"", expr);
+ return 0;
+}
+
+/*
+ Execute all substitutions on val.
+
+ Returns: true if substituition was made, false otherwise
+ Side-effect: Sets r->buf to be the buffer with all substitutions done.
+
+ IN:
+ struct st_replace_regex* r
+ char* val
+ Out:
+ struct st_replace_regex* r
+ r->buf points at the resulting buffer
+ r->even_buf and r->odd_buf might have been reallocated
+ r->even_buf_len and r->odd_buf_len might have been changed
+
+ TODO: at some point figure out if there is a way to do everything
+ in one pass
+*/
+
+int multi_reg_replace(struct st_replace_regex* r,char* val)
+{
+ uint i;
+ char* in_buf, *out_buf;
+ int* buf_len_p;
+
+ in_buf= val;
+ out_buf= r->even_buf;
+ buf_len_p= &r->even_buf_len;
+ r->buf= 0;
+
+ /* For each substitution, do the replace */
+ for (i= 0; i < r->regex_arr.elements; i++)
{
- if (!(new_pos= (byte*) my_realloc((gptr) pa->str,
- (uint) (pa->max_length+PS_MALLOC),
- MYF(MY_WME))))
- DBUG_RETURN(1);
- if (new_pos != pa->str)
+ struct st_regex re;
+ char* save_out_buf= out_buf;
+
+ get_dynamic(&r->regex_arr,(gptr)&re,i);
+
+ if (!reg_replace(&out_buf, buf_len_p, re.pattern, re.replace,
+ in_buf, re.icase))
{
- my_ptrdiff_t diff=PTR_BYTE_DIFF(new_pos,pa->str);
- for (i=0 ; i < pa->typelib.count ; i++)
- pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff,
- char*);
- pa->str=new_pos;
+ /* if the buffer has been reallocated, make adjustements */
+ if (save_out_buf != out_buf)
+ {
+ if (save_out_buf == r->even_buf)
+ r->even_buf= out_buf;
+ else
+ r->odd_buf= out_buf;
+ }
+
+ r->buf= out_buf;
+ if (in_buf == val)
+ in_buf= r->odd_buf;
+
+ swap_variables(char*,in_buf,out_buf);
+
+ buf_len_p= (out_buf == r->even_buf) ? &r->even_buf_len :
+ &r->odd_buf_len;
}
- pa->max_length+=PS_MALLOC;
}
- if (pa->typelib.count >= pa->max_count-1)
+
+ return (r->buf == 0);
+}
+
+/*
+ Parse the regular expression to be used in all result files
+ from now on.
+
+ The syntax is --replace_regex /from/to/i /from/to/i ...
+ i means case-insensitive match. If omitted, the match is
+ case-sensitive
+
+*/
+void do_get_replace_regex(struct st_command *command)
+{
+ char *expr= command->first_argument;
+ free_replace_regex();
+ if (!(glob_replace_regex=init_replace_regex(expr)))
+ die("Could not init replace_regex");
+ command->last_argument= command->end;
+}
+
+void free_replace_regex()
+{
+ if (glob_replace_regex)
{
- int len;
- pa->array_allocs++;
- len=(PC_MALLOC*pa->array_allocs - MALLOC_OVERHEAD);
- if (!(new_array=(const char **) my_realloc((gptr) pa->typelib.type_names,
- (uint) len/
- (sizeof(byte*)+sizeof(*pa->flag))*
- (sizeof(byte*)+sizeof(*pa->flag)),
- MYF(MY_WME))))
- DBUG_RETURN(1);
- pa->typelib.type_names=new_array;
- old_count=pa->max_count;
- pa->max_count=len/(sizeof(byte*) + sizeof(*pa->flag));
- pa->flag= (int7*) (pa->typelib.type_names+pa->max_count);
- memcpy((byte*) pa->flag,(my_string) (pa->typelib.type_names+old_count),
- old_count*sizeof(*pa->flag));
+ delete_dynamic(&glob_replace_regex->regex_arr);
+ my_free(glob_replace_regex->even_buf,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(glob_replace_regex->odd_buf,MYF(MY_ALLOW_ZERO_PTR));
+ my_free((char*) glob_replace_regex,MYF(0));
+ glob_replace_regex=0;
}
- pa->flag[pa->typelib.count]=0; /* Reset flag */
- pa->typelib.type_names[pa->typelib.count++]= pa->str+pa->length;
- pa->typelib.type_names[pa->typelib.count]= NullS; /* Put end-mark */
- VOID(strmov(pa->str+pa->length,name));
- pa->length+=length;
- DBUG_RETURN(0);
-} /* insert_pointer_name */
+}
- /* free pointer array */
-void free_pointer_array(POINTER_ARRAY *pa)
+/*
+ auxiluary macro used by reg_replace
+ makes sure the result buffer has sufficient length
+*/
+#define SECURE_REG_BUF if (buf_len < need_buf_len) \
+ { \
+ int off= res_p - buf; \
+ buf= (char*)my_realloc(buf,need_buf_len,MYF(MY_WME+MY_FAE)); \
+ res_p= buf + off; \
+ buf_len= need_buf_len; \
+ } \
+ \
+/*
+ Performs a regex substitution
+
+ IN:
+
+ buf_p - result buffer pointer. Will change if reallocated
+ buf_len_p - result buffer length. Will change if the buffer is reallocated
+ pattern - regexp pattern to match
+ replace - replacement expression
+ string - the string to perform substituions in
+ icase - flag, if set to 1 the match is case insensitive
+*/
+int reg_replace(char** buf_p, int* buf_len_p, char *pattern,
+ char *replace, char *string, int icase)
{
- if (pa->typelib.count)
+ my_regex_t r;
+ my_regmatch_t *subs;
+ char *buf_end, *replace_end;
+ char *buf= *buf_p;
+ int len;
+ int buf_len, need_buf_len;
+ int cflags= REG_EXTENDED;
+ int err_code;
+ char *res_p,*str_p,*str_end;
+
+ buf_len= *buf_len_p;
+ len= strlen(string);
+ str_end= string + len;
+
+ /* start with a buffer of a reasonable size that hopefully will not
+ need to be reallocated
+ */
+ need_buf_len= len * 2 + 1;
+ res_p= buf;
+
+ SECURE_REG_BUF
+
+ buf_end= buf + buf_len;
+
+ if (icase)
+ cflags|= REG_ICASE;
+
+ if ((err_code= my_regcomp(&r,pattern,cflags,&my_charset_latin1)))
{
- pa->typelib.count=0;
- my_free((gptr) pa->typelib.type_names,MYF(0));
- pa->typelib.type_names=0;
- my_free((gptr) pa->str,MYF(0));
+ check_regerr(&r,err_code);
+ return 1;
}
-} /* free_pointer_array */
+ subs= (my_regmatch_t*)my_malloc(sizeof(my_regmatch_t) * (r.re_nsub+1),
+ MYF(MY_WME+MY_FAE));
+
+ *res_p= 0;
+ str_p= string;
+ replace_end= replace + strlen(replace);
+
+ /* for each pattern match instance perform a replacement */
+ while (!err_code)
+ {
+ /* find the match */
+ err_code= my_regexec(&r,str_p, r.re_nsub+1, subs,
+ (str_p == string) ? REG_NOTBOL : 0);
+
+ /* if regular expression error (eg. bad syntax, or out of memory) */
+ if (err_code && err_code != REG_NOMATCH)
+ {
+ check_regerr(&r,err_code);
+ my_regfree(&r);
+ return 1;
+ }
+
+ /* if match found */
+ if (!err_code)
+ {
+ char* expr_p= replace;
+ int c;
+
+ /*
+ we need at least what we have so far in the buffer + the part
+ before this match
+ */
+ need_buf_len= (res_p - buf) + subs[0].rm_so;
+
+ /* on this pass, calculate the memory for the result buffer */
+ while (expr_p < replace_end)
+ {
+ int back_ref_num= -1;
+ c= *expr_p;
+
+ if (c == '\\' && expr_p + 1 < replace_end)
+ {
+ back_ref_num= expr_p[1] - '0';
+ }
+
+ /* found a valid back_ref (eg. \1)*/
+ if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub)
+ {
+ int start_off,end_off;
+ if ((start_off=subs[back_ref_num].rm_so) > -1 &&
+ (end_off=subs[back_ref_num].rm_eo) > -1)
+ {
+ need_buf_len += (end_off - start_off);
+ }
+ expr_p += 2;
+ }
+ else
+ {
+ expr_p++;
+ need_buf_len++;
+ }
+ }
+ need_buf_len++;
+ /*
+ now that we know the size of the buffer,
+ make sure it is big enough
+ */
+ SECURE_REG_BUF
+
+ /* copy the pre-match part */
+ if (subs[0].rm_so)
+ {
+ memcpy(res_p, str_p, subs[0].rm_so);
+ res_p+= subs[0].rm_so;
+ }
+
+ expr_p= replace;
+
+ /* copy the match and expand back_refs */
+ while (expr_p < replace_end)
+ {
+ int back_ref_num= -1;
+ c= *expr_p;
+
+ if (c == '\\' && expr_p + 1 < replace_end)
+ {
+ back_ref_num= expr_p[1] - '0';
+ }
- /* Code for replace rutines */
+ if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub)
+ {
+ int start_off,end_off;
+ if ((start_off=subs[back_ref_num].rm_so) > -1 &&
+ (end_off=subs[back_ref_num].rm_eo) > -1)
+ {
+ int block_len= end_off - start_off;
+ memcpy(res_p,str_p + start_off, block_len);
+ res_p += block_len;
+ }
+ expr_p += 2;
+ }
+ else
+ {
+ *res_p++ = *expr_p++;
+ }
+ }
+
+ /* handle the post-match part */
+ if (subs[0].rm_so == subs[0].rm_eo)
+ {
+ if (str_p + subs[0].rm_so >= str_end)
+ break;
+ str_p += subs[0].rm_eo ;
+ *res_p++ = *str_p++;
+ }
+ else
+ {
+ str_p += subs[0].rm_eo;
+ }
+ }
+ else /* no match this time, just copy the string as is */
+ {
+ int left_in_str= str_end-str_p;
+ need_buf_len= (res_p-buf) + left_in_str;
+ SECURE_REG_BUF
+ memcpy(res_p,str_p,left_in_str);
+ res_p += left_in_str;
+ str_p= str_end;
+ }
+ }
+ my_free((gptr)subs, MYF(0));
+ my_regfree(&r);
+ *res_p= 0;
+ *buf_p= buf;
+ *buf_len_p= buf_len;
+ return 0;
+}
+
+
+#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 */
@@ -5843,32 +6645,48 @@ typedef struct st_follow {
} FOLLOWS;
-static int init_sets(REP_SETS *sets,uint states);
-static REP_SET *make_new_set(REP_SETS *sets);
-static void make_sets_invisible(REP_SETS *sets);
-static void free_last_set(REP_SETS *sets);
-static void free_sets(REP_SETS *sets);
-static void internal_set_bit(REP_SET *set, uint bit);
-static void internal_clear_bit(REP_SET *set, uint bit);
-static void or_bits(REP_SET *to,REP_SET *from);
-static void copy_bits(REP_SET *to,REP_SET *from);
-static int cmp_bits(REP_SET *set1,REP_SET *set2);
-static int get_next_bit(REP_SET *set,uint lastpos);
-static int find_set(REP_SETS *sets,REP_SET *find);
-static int find_found(FOUND_SET *found_set,uint table_offset,
- int found_offset);
-static uint start_at_word(my_string pos);
-static uint end_of_word(my_string pos);
-static uint replace_len(my_string pos);
+int init_sets(REP_SETS *sets,uint states);
+REP_SET *make_new_set(REP_SETS *sets);
+void make_sets_invisible(REP_SETS *sets);
+void free_last_set(REP_SETS *sets);
+void free_sets(REP_SETS *sets);
+void internal_set_bit(REP_SET *set, uint bit);
+void internal_clear_bit(REP_SET *set, uint bit);
+void or_bits(REP_SET *to,REP_SET *from);
+void copy_bits(REP_SET *to,REP_SET *from);
+int cmp_bits(REP_SET *set1,REP_SET *set2);
+int get_next_bit(REP_SET *set,uint lastpos);
+int find_set(REP_SETS *sets,REP_SET *find);
+int find_found(FOUND_SET *found_set,uint table_offset,
+ int found_offset);
+uint start_at_word(my_string pos);
+uint end_of_word(my_string pos);
static uint found_sets=0;
- /* Init a replace structure for further calls */
+uint replace_len(my_string str)
+{
+ uint len=0;
+ while (*str)
+ {
+ if (str[0] == '\\' && str[1])
+ str++;
+ str++;
+ len++;
+ }
+ return len;
+}
+
+/* Init a replace structure for further calls */
REPLACE *init_replace(my_string *from, my_string *to,uint count,
my_string word_end_chars)
{
+ static const int SPACE_CHAR= 256;
+ static const int START_OF_LINE= 257;
+ static const int END_OF_LINE= 258;
+
uint i,j,states,set_nr,len,result_len,max_length,found_end,bits_set,bit_nr;
int used_sets,chr,default_state;
char used_chars[LAST_CHAR_CODE],is_word_end[256];
@@ -5921,7 +6739,7 @@ REPLACE *init_replace(my_string *from, my_string *to,uint count,
DBUG_RETURN(0);
}
- /* Init follow_ptr[] */
+ /* Init follow_ptr[] */
for (i=0, states=1, follow_ptr=follow+1 ; i < count ; i++)
{
if (from[i][0] == '\\' && from[i][1] == '^')
@@ -6104,7 +6922,7 @@ REPLACE *init_replace(my_string *from, my_string *to,uint count,
}
}
- /* Alloc replace structure for the replace-state-machine */
+ /* Alloc replace structure for the replace-state-machine */
if ((replace=(REPLACE*) my_malloc(sizeof(REPLACE)*(sets.count)+
sizeof(REPLACE_STRING)*(found_sets+1)+
@@ -6147,7 +6965,7 @@ REPLACE *init_replace(my_string *from, my_string *to,uint count,
}
-static int init_sets(REP_SETS *sets,uint states)
+int init_sets(REP_SETS *sets,uint states)
{
bzero((char*) sets,sizeof(*sets));
sets->size_of_bits=((states+7)/8);
@@ -6163,16 +6981,16 @@ static int init_sets(REP_SETS *sets,uint states)
return 0;
}
- /* Make help sets invisible for nicer codeing */
+/* Make help sets invisible for nicer codeing */
-static void make_sets_invisible(REP_SETS *sets)
+void make_sets_invisible(REP_SETS *sets)
{
sets->invisible=sets->count;
sets->set+=sets->count;
sets->count=0;
}
-static REP_SET *make_new_set(REP_SETS *sets)
+REP_SET *make_new_set(REP_SETS *sets)
{
uint i,count,*bit_buffer;
REP_SET *set;
@@ -6190,7 +7008,7 @@ static REP_SET *make_new_set(REP_SETS *sets)
}
count=sets->count+sets->invisible+SET_MALLOC_HUNC;
if (!(set=(REP_SET*) my_realloc((gptr) sets->set_buffer,
- sizeof(REP_SET)*count,
+ sizeof(REP_SET)*count,
MYF(MY_WME))))
return 0;
sets->set_buffer=set;
@@ -6209,34 +7027,34 @@ static REP_SET *make_new_set(REP_SETS *sets)
return make_new_set(sets);
}
-static void free_last_set(REP_SETS *sets)
+void free_last_set(REP_SETS *sets)
{
sets->count--;
sets->extra++;
return;
}
-static void free_sets(REP_SETS *sets)
+void free_sets(REP_SETS *sets)
{
my_free((gptr)sets->set_buffer,MYF(0));
my_free((gptr)sets->bit_buffer,MYF(0));
return;
}
-static void internal_set_bit(REP_SET *set, uint bit)
+void internal_set_bit(REP_SET *set, uint bit)
{
set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT);
return;
}
-static void internal_clear_bit(REP_SET *set, uint bit)
+void internal_clear_bit(REP_SET *set, uint bit)
{
set->bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT));
return;
}
-static void or_bits(REP_SET *to,REP_SET *from)
+void or_bits(REP_SET *to,REP_SET *from)
{
reg1 uint i;
for (i=0 ; i < to->size_of_bits ; i++)
@@ -6244,22 +7062,22 @@ static void or_bits(REP_SET *to,REP_SET *from)
return;
}
-static void copy_bits(REP_SET *to,REP_SET *from)
+void copy_bits(REP_SET *to,REP_SET *from)
{
memcpy((byte*) to->bits,(byte*) from->bits,
(size_t) (sizeof(uint) * to->size_of_bits));
}
-static int cmp_bits(REP_SET *set1,REP_SET *set2)
+int cmp_bits(REP_SET *set1,REP_SET *set2)
{
return bcmp((byte*) set1->bits,(byte*) set2->bits,
sizeof(uint) * set1->size_of_bits);
}
- /* Get next set bit from set. */
+/* Get next set bit from set. */
-static int get_next_bit(REP_SET *set,uint lastpos)
+int get_next_bit(REP_SET *set,uint lastpos)
{
uint pos,*start,*end,bits;
@@ -6280,11 +7098,11 @@ static int get_next_bit(REP_SET *set,uint lastpos)
return pos;
}
- /* find if there is a same set in sets. If there is, use it and
- free given set, else put in given set in sets and return its
- position */
+/* find if there is a same set in sets. If there is, use it and
+ free given set, else put in given set in sets and return its
+ position */
-static int find_set(REP_SETS *sets,REP_SET *find)
+int find_set(REP_SETS *sets,REP_SET *find)
{
uint i;
for (i=0 ; i < sets->count-1 ; i++)
@@ -6298,14 +7116,14 @@ static int find_set(REP_SETS *sets,REP_SET *find)
return i; /* return new postion */
}
- /* find if there is a found_set with same table_offset & found_offset
- If there is return offset to it, else add new offset and return pos.
- Pos returned is -offset-2 in found_set_structure because it is
- saved in set->next and set->next[] >= 0 points to next set and
- set->next[] == -1 is reserved for end without replaces.
- */
+/* find if there is a found_set with same table_offset & found_offset
+ If there is return offset to it, else add new offset and return pos.
+ Pos returned is -offset-2 in found_set_structure because it is
+ saved in set->next and set->next[] >= 0 points to next set and
+ set->next[] == -1 is reserved for end without replaces.
+*/
-static int find_found(FOUND_SET *found_set,uint table_offset, int found_offset)
+int find_found(FOUND_SET *found_set,uint table_offset, int found_offset)
{
int i;
for (i=0 ; (uint) i < found_sets ; i++)
@@ -6318,145 +7136,153 @@ static int find_found(FOUND_SET *found_set,uint table_offset, int found_offset)
return -i-2; /* return new postion */
}
- /* Return 1 if regexp starts with \b or ends with \b*/
+/* Return 1 if regexp starts with \b or ends with \b*/
-static uint start_at_word(my_string pos)
+uint start_at_word(my_string pos)
{
return (((!bcmp(pos,"\\b",2) && pos[2]) || !bcmp(pos,"\\^",2)) ? 1 : 0);
}
-static uint end_of_word(my_string pos)
+uint end_of_word(my_string pos)
{
my_string end=strend(pos);
return ((end > pos+2 && !bcmp(end-2,"\\b",2)) ||
(end >= pos+2 && !bcmp(end-2,"\\$",2))) ?
- 1 : 0;
+ 1 : 0;
}
+/****************************************************************************
+ * Handle replacement of strings
+ ****************************************************************************/
-static uint replace_len(my_string str)
-{
- uint len=0;
- while (*str)
- {
- if (str[0] == '\\' && str[1])
- str++;
- str++;
- len++;
- }
- return len;
-}
-
+#define PC_MALLOC 256 /* Bytes for pointers */
+#define PS_MALLOC 512 /* Bytes for data */
-/* Replace strings while appending to ds */
-void replace_strings_append(REPLACE *rep, DYNAMIC_STRING* ds,
- const char *str, int len)
+int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name)
{
- reg1 REPLACE *rep_pos;
- reg2 REPLACE_STRING *rep_str;
- const char *start, *from;
- DBUG_ENTER("replace_strings_append");
+ uint i,length,old_count;
+ byte *new_pos;
+ const char **new_array;
+ DBUG_ENTER("insert_pointer_name");
- start= from= str;
- rep_pos=rep+1;
- for (;;)
+ if (! pa->typelib.count)
{
- /* 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)
+ if (!(pa->typelib.type_names=(const char **)
+ my_malloc(((PC_MALLOC-MALLOC_OVERHEAD)/
+ (sizeof(my_string)+sizeof(*pa->flag))*
+ (sizeof(my_string)+sizeof(*pa->flag))),MYF(MY_WME))))
+ DBUG_RETURN(-1);
+ if (!(pa->str= (byte*) my_malloc((uint) (PS_MALLOC-MALLOC_OVERHEAD),
+ MYF(MY_WME))))
{
- /* 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;
+ my_free((gptr) pa->typelib.type_names,MYF(0));
+ DBUG_RETURN (-1);
}
-
- /* 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)
+ pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(byte*)+
+ sizeof(*pa->flag));
+ pa->flag= (int7*) (pa->typelib.type_names+pa->max_count);
+ pa->length=0;
+ pa->max_length=PS_MALLOC-MALLOC_OVERHEAD;
+ pa->array_allocs=1;
+ }
+ length=(uint) strlen(name)+1;
+ if (pa->length+length >= pa->max_length)
+ {
+ if (!(new_pos= (byte*) my_realloc((gptr) pa->str,
+ (uint) (pa->max_length+PS_MALLOC),
+ MYF(MY_WME))))
+ DBUG_RETURN(1);
+ if (new_pos != pa->str)
{
- /* End of from string */
- DBUG_PRINT("exit", ("Found end of from string"));
- DBUG_VOID_RETURN;
+ 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;
}
- DBUG_ASSERT(from <= str+len);
- start= from;
- rep_pos=rep;
+ 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((gptr) pa->typelib.type_names,
+ (uint) len/
+ (sizeof(byte*)+sizeof(*pa->flag))*
+ (sizeof(byte*)+sizeof(*pa->flag)),
+ MYF(MY_WME))))
+ DBUG_RETURN(1);
+ pa->typelib.type_names=new_array;
+ old_count=pa->max_count;
+ pa->max_count=len/(sizeof(byte*) + sizeof(*pa->flag));
+ pa->flag= (int7*) (pa->typelib.type_names+pa->max_count);
+ memcpy((byte*) pa->flag,(my_string) (pa->typelib.type_names+old_count),
+ old_count*sizeof(*pa->flag));
+ }
+ pa->flag[pa->typelib.count]=0; /* Reset flag */
+ pa->typelib.type_names[pa->typelib.count++]= pa->str+pa->length;
+ pa->typelib.type_names[pa->typelib.count]= NullS; /* Put end-mark */
+ VOID(strmov(pa->str+pa->length,name));
+ pa->length+=length;
+ DBUG_RETURN(0);
+} /* insert_pointer_name */
-/****************************************************************************
- Replace results for a column
-*****************************************************************************/
+/* free pointer array */
-static void free_replace_column()
+void free_pointer_array(POINTER_ARRAY *pa)
{
- uint i;
- for (i=0 ; i < max_replace_column ; i++)
+ if (pa->typelib.count)
{
- if (replace_column[i])
- {
- my_free(replace_column[i], 0);
- replace_column[i]= 0;
- }
+ pa->typelib.count=0;
+ my_free((gptr) pa->typelib.type_names,MYF(0));
+ pa->typelib.type_names=0;
+ my_free((gptr) pa->str,MYF(0));
}
- max_replace_column= 0;
-}
+} /* free_pointer_array */
-/*
- Get arguments for replace_columns. The syntax is:
- replace-column column_number to_string [column_number to_string ...]
- Where each argument may be quoted with ' or "
- A argument may also be a variable, in which case the value of the
- variable is replaced.
-*/
-static void get_replace_column(struct st_query *q)
-{
- char *from=q->first_argument;
- char *buff,*start;
- DBUG_ENTER("get_replace_columns");
+/* Functions that uses replace and replace_regex */
- free_replace_column();
- if (!*from)
- die("Missing argument in %s", q->query);
+/* 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
- /* Allocate a buffer for results */
- start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
- while (*from)
+ if (glob_replace_regex)
{
- char *to;
- uint column_number;
+ /* Regex replace */
+ if (!multi_reg_replace(glob_replace_regex, (char*)val))
+ {
+ val= glob_replace_regex->buf;
+ len= strlen(val);
+ }
+ }
- to= get_string(&buff, &from, q);
- if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS)
- die("Wrong column number to replace_column in '%s'", q->query);
- if (!*from)
- die("Wrong number of arguments to replace_column in '%s'", q->query);
- to= get_string(&buff, &from, q);
- my_free(replace_column[column_number-1], MY_ALLOW_ZERO_PTR);
- replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE));
- set_if_bigger(max_replace_column, column_number);
+ if (glob_replace)
+ {
+ /* Normal replace */
+ replace_strings_append(glob_replace, ds, val, len);
}
- my_free(start, MYF(0));
- q->last_argument= q->end;
+ 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);
+}