mysqltest.c
上传用户:tsgydb
上传日期:2007-04-14
资源大小:10674k
文件大小:57k
- /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
- /* mysqltest test tool
- * See man page for more information.
- *
- * Written by:
- * Sasha Pachev <sasha@mysql.com>
- * Matt Wagner <matt@mysql.com>
- * Monty
- **/
- /**********************************************************************
- TODO:
- - Print also the queries that returns a result to the log file; This makes
- it much easier to find out what's wrong.
- - 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.
- - Don't buffer lines from the test that you don't expect to need
- again.
- - Change 'read_line' to be faster by using the readline.cc code;
- We can do better than calling feof() for each character!
- **********************************************************************/
- #define MTEST_VERSION "1.7"
- #include <global.h>
- #include <my_sys.h>
- #include <m_string.h>
- #include <mysql.h>
- #include <mysql_version.h>
- #include <m_ctype.h>
- #include <my_config.h>
- #include <my_dir.h>
- #include <mysqld_error.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <getopt.h>
- #include <stdarg.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include <errno.h>
- #include <violite.h>
- #define MAX_QUERY 65536
- #define PAD_SIZE 128
- #define MAX_CONS 1024
- #define MAX_INCLUDE_DEPTH 16
- #define LAZY_GUESS_BUF_SIZE 8192
- #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
- static int record = 0, verbose = 0, silent = 0, opt_sleep=0;
- static char *db = 0, *pass=0;
- const char* user = 0, *host = 0, *unix_sock = 0;
- static int port = 0;
- static uint start_lineno, *lineno;
- static char **default_argv;
- static const char *load_default_groups[]= { "mysqltest","client",0 };
- static FILE* file_stack[MAX_INCLUDE_DEPTH];
- static FILE** cur_file;
- static FILE** file_stack_end;
- static uint lineno_stack[MAX_INCLUDE_DEPTH];
- static char TMPDIR[FN_REFLEN];
- static int block_stack[BLOCK_STACK_DEPTH];
- static int *cur_block, *block_stack_end;
- static uint global_expected_errno[MAX_EXPECTED_ERRORS];
- DYNAMIC_ARRAY q_lines;
- typedef struct
- {
- char file[FN_REFLEN];
- ulong pos;
- } MASTER_POS ;
- struct connection
- {
- MYSQL mysql;
- char *name;
- };
- typedef struct
- {
- int read_lines,current_line;
- } PARSER;
- PARSER parser;
- MASTER_POS master_pos;
- int block_ok = 1; /* set to 0 if the current block should not be executed */
- int false_block_depth = 0;
- const char* result_file = 0; /* if set, all results are concated and
- compared against this file*/
- typedef struct
- {
- char* name;
- char* str_val;
- int str_val_len;
- int int_val;
- int alloced_len;
- int int_dirty; /* do not update string if int is updated until first read */
- } VAR;
- VAR var_reg[10];
- /*Perl/shell-like variable registers */
- struct connection cons[MAX_CONS];
- struct connection* cur_con, *next_con, *cons_end;
- /* this should really be called command */
- struct st_query
- {
- char *query, *first_argument;
- int first_word_len;
- my_bool abort_on_error, require_file;
- uint expected_errno[MAX_EXPECTED_ERRORS];
- char record_file[FN_REFLEN];
- /* Add new commands before Q_UNKNOWN */
- enum { Q_CONNECTION=1, Q_QUERY,
- Q_CONNECT, Q_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_ERROR,
- Q_SEND, Q_REAP,
- Q_DIRTY_CLOSE, Q_REPLACE,
- Q_PING,
- Q_UNKNOWN, /* Unknown command. */
- Q_COMMENT, /* Comments, ignored. */
- Q_COMMENT_WITH_COMMAND
- } type;
- };
- const char *command_names[] = {
- "connection", "query",
- "connect", "sleep",
- "inc", "dec",
- "source", "disconnect",
- "let", "echo",
- "while", "end",
- "system", "result",
- "require", "save_master_pos",
- "sync_with_master", "error",
- "send", "reap",
- "dirty_close", "replace_result",
- "ping",
- 0
- };
- TYPELIB command_typelib= {array_elements(command_names),"",
- command_names};
- DYNAMIC_STRING ds_res;
- int dyn_string_cmp(DYNAMIC_STRING* ds, const char* fname);
- void reject_dump(const char* record_file, char* buf, int size);
- int close_connection(struct st_query* q);
- VAR* var_get(char* var_name, char* var_name_end, int raw);
- /* Definitions for replace */
- 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);
- uint replace_strings(struct st_replace *rep, my_string *start,
- uint *max_length, my_string from);
- static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name);
- void free_pointer_array(POINTER_ARRAY *pa);
- static int initialize_replace_buffer(void);
- static void free_replace_buffer(void);
- struct st_replace *glob_replace;
- static char *out_buff;
- static uint out_length;
- static void close_cons()
- {
- DBUG_ENTER("close_cons");
- for (--next_con; next_con >= cons; --next_con)
- {
- mysql_close(&next_con->mysql);
- my_free(next_con->name, MYF(MY_ALLOW_ZERO_PTR));
- }
- DBUG_VOID_RETURN;
- }
- static void close_files()
- {
- do
- {
- if (*cur_file != stdin)
- my_fclose(*cur_file,MYF(0));
- } while (cur_file-- != file_stack);
- }
- static void free_used_memory()
- {
- uint i;
- DBUG_ENTER("free_used_memory");
- close_cons();
- close_files();
- for (i=0 ; i < q_lines.elements ; i++)
- {
- struct st_query **q= dynamic_element(&q_lines, i, struct st_query**);
- my_free((gptr) (*q)->query,MYF(MY_ALLOW_ZERO_PTR));
- my_free((gptr) (*q),MYF(0));
- }
- delete_dynamic(&q_lines);
- dynstr_free(&ds_res);
- my_free(pass,MYF(MY_ALLOW_ZERO_PTR));
- free_defaults(default_argv);
- my_end(MY_CHECK_ERROR);
- DBUG_VOID_RETURN;
- }
- static void die(const char* fmt, ...)
- {
- va_list args;
- DBUG_ENTER("die");
- va_start(args, fmt);
- if (fmt)
- {
- fprintf(stderr, "%s: ", my_progname);
- vfprintf(stderr, fmt, args);
- fprintf(stderr, "n");
- }
- va_end(args);
- free_used_memory();
- exit(1);
- }
- static void abort_not_supported_test()
- {
- DBUG_ENTER("abort_not_supported_test");
- fprintf(stderr, "This test is not supported by this installationn");
- if (!silent)
- printf("skippedn");
- free_used_memory();
- exit(2);
- }
- static void verbose_msg(const char* fmt, ...)
- {
- va_list args;
- if (!verbose) return;
- va_start(args, fmt);
- fprintf(stderr, "%s: At line %u: ", my_progname, start_lineno);
- vfprintf(stderr, fmt, args);
- fprintf(stderr, "n");
- va_end(args);
- }
- void init_parser()
- {
- parser.current_line = parser.read_lines = 0;
- memset(&var_reg,0, sizeof(var_reg));
- }
- int hex_val(int c)
- {
- if (isdigit(c))
- return c - '0';
- else if ((c = tolower(c)) >= 'a' && c <= 'f')
- return c - 'a' + 10;
- else
- return -1;
- }
- int dyn_string_cmp(DYNAMIC_STRING* ds, const char* fname)
- {
- MY_STAT stat_info;
- char *tmp;
- int res;
- int fd;
- DBUG_ENTER("dyn_string_cmp");
- if (!my_stat(fname, &stat_info, MYF(MY_WME)))
- die(NullS);
- if (stat_info.st_size != ds->length)
- DBUG_RETURN(2);
- if (!(tmp = (char*) my_malloc(ds->length, MYF(MY_WME))))
- die(NullS);
- if ((fd = my_open(fname, O_RDONLY, MYF(MY_WME))) < 0)
- die(NullS);
- if (my_read(fd, (byte*)tmp, stat_info.st_size, MYF(MY_WME|MY_NABP)))
- die(NullS);
- res = (memcmp(tmp, ds->str, stat_info.st_size)) ? 1 : 0;
- my_free((gptr) tmp, MYF(0));
- my_close(fd, MYF(MY_WME));
- DBUG_RETURN(res);
- }
- static int check_result(DYNAMIC_STRING* ds, const char* fname,
- my_bool require_option)
- {
- int error = 0;
- int res=dyn_string_cmp(ds, fname);
- if (res && require_option)
- abort_not_supported_test();
- switch (res)
- {
- case 0:
- break; /* ok */
- case 2:
- verbose_msg("Result length mismatch");
- error = 1;
- break;
- case 1:
- verbose_msg("Result content mismatch");
- error = 1;
- break;
- default: /* impossible */
- die("Unknown error code from dyn_string_cmp()");
- }
- if (error)
- reject_dump(fname, ds->str, ds->length);
- return error;
- }
- VAR* var_get(char* var_name, char* var_name_end, int raw)
- {
- int digit;
- VAR* v;
- if (*var_name++ != '$')
- {
- --var_name;
- goto err;
- }
- digit = *var_name - '0';
- if (!(digit < 10 && digit >= 0))
- {
- --var_name;
- goto err;
- }
- v = var_reg + digit;
- if (!raw && v->int_dirty)
- {
- sprintf(v->str_val, "%d", v->int_val);
- v->int_dirty = 0;
- }
- return v;
- err:
- if (var_name_end)
- *var_name_end = 0;
- die("Unsupported variable name: %s", var_name);
- return 0;
- }
- int var_set(char* var_name, char* var_name_end, char* var_val,
- char* var_val_end)
- {
- int digit;
- int val_len;
- VAR* v;
- if (*var_name++ != '$')
- {
- --var_name;
- *var_name_end = 0;
- die("Variable name in %s does not start with '$'", var_name);
- }
- digit = *var_name - '0';
- if (!(digit < 10 && digit >= 0))
- {
- *var_name_end = 0;
- die("Unsupported variable name: %s", var_name);
- }
- v = var_reg + digit;
- if (v->alloced_len < (val_len = (int)(var_val_end - var_val)+1))
- {
- v->alloced_len = (val_len < MIN_VAR_ALLOC) ? MIN_VAR_ALLOC : val_len;
- if (!(v->str_val =
- v->str_val ? my_realloc(v->str_val, v->alloced_len, MYF(MY_WME)) :
- my_malloc(v->alloced_len, MYF(MY_WME))))
- die("Out of memory");
- }
- memcpy(v->str_val, var_val, val_len-1);
- v->str_val_len = val_len;
- v->str_val[val_len] = 0;
- v->int_val = atoi(v->str_val);
- return 0;
- }
- int open_file(const char* name)
- {
- if (*cur_file && cur_file == file_stack_end)
- die("Source directives are nesting too deep");
- if (!(*(cur_file+1) = my_fopen(name, O_RDONLY, MYF(MY_WME))))
- die(NullS);
- cur_file++;
- *++lineno=1;
- return 0;
- }
- int do_source(struct st_query* q)
- {
- char* p=q->first_argument, *name;
- if (!*p)
- die("Missing file name in sourcen");
- name = p;
- while (*p && !isspace(*p))
- p++;
- *p = 0;
- return open_file(name);
- }
- int eval_expr(VAR* v, char* p, char* p_end)
- {
- VAR* vp;
- if (*p == '$')
- {
- if ((vp = var_get(p,p_end,0)))
- {
- memcpy(v, vp, sizeof(VAR));
- return 0;
- }
- }
- else
- {
- v->str_val = p;
- v->str_val_len = p_end ? p_end - p : strlen(p);
- return 0;
- }
- if (p_end)
- *p_end = 0;
- die("Invalid expr: %s", p);
- return 1;
- }
- int do_inc(struct st_query* q)
- {
- char* p=q->first_argument;
- VAR* v;
- v = var_get(p, 0, 1);
- v->int_val++;
- v->int_dirty = 1;
- return 0;
- }
- int do_dec(struct st_query* q)
- {
- char* p=q->first_argument;
- VAR* v;
- v = var_get(p, 0, 1);
- v->int_val--;
- v->int_dirty = 1;
- return 0;
- }
- int do_system(struct st_query* q)
- {
- char* p=q->first_argument;
- VAR v;
- eval_expr(&v, p, 0); /* NULL terminated */
- if (v.str_val_len > 1)
- {
- char expr_buf[512];
- if ((uint)v.str_val_len > sizeof(expr_buf) - 1)
- v.str_val_len = sizeof(expr_buf) - 1;
- memcpy(expr_buf, v.str_val, v.str_val_len);
- expr_buf[v.str_val_len] = 0;
- if (system(expr_buf) && q->abort_on_error)
- die("system command '%s' failed", expr_buf);
- }
- return 0;
- }
- int do_echo(struct st_query* q)
- {
- char* p=q->first_argument;
- VAR v;
- eval_expr(&v, p, 0); /* NULL terminated */
- if (v.str_val_len > 1)
- {
- fflush(stdout);
- write(1, v.str_val, v.str_val_len - 1);
- }
- write(1, "n", 1);
- return 0;
- }
- int do_sync_with_master(struct st_query* q)
- {
- MYSQL_RES* res;
- MYSQL_ROW row;
- MYSQL* mysql = &cur_con->mysql;
- char query_buf[FN_REFLEN+128];
- int offset = 0;
- char* p = q->first_argument;
- if(*p)
- offset = atoi(p);
-
- sprintf(query_buf, "select master_pos_wait('%s', %ld)", master_pos.file,
- master_pos.pos + offset);
- if(mysql_query(mysql, query_buf))
- die("At line %u: failed in %s: %d: %s", start_lineno, query_buf,
- mysql_errno(mysql), mysql_error(mysql));
- if(!(res = mysql_store_result(mysql)))
- die("line %u: mysql_store_result() retuned NULL", start_lineno);
- if(!(row = mysql_fetch_row(res)))
- die("line %u: empty result in %s", start_lineno, query_buf);
- if(!row[0])
- die("Error on slave while syncing with master");
- mysql_free_result(res);
-
- return 0;
- }
- int do_save_master_pos()
- {
- MYSQL_RES* res;
- MYSQL_ROW row;
- MYSQL* mysql = &cur_con->mysql;
- if(mysql_query(mysql, "show master status"))
- die("At line %u: failed in show master status: %d: %s", start_lineno,
- mysql_errno(mysql), mysql_error(mysql));
- if(!(res = mysql_store_result(mysql)))
- die("line %u: mysql_store_result() retuned NULL", start_lineno);
- if(!(row = mysql_fetch_row(res)))
- die("line %u: empty result in show master status", start_lineno);
- strncpy(master_pos.file, row[0], sizeof(master_pos.file));
- master_pos.pos = strtoul(row[1], (char**) 0, 10);
- mysql_free_result(res);
-
- return 0;
- }
- int do_let(struct st_query* q)
- {
- char* p=q->first_argument;
- char *var_name, *var_name_end, *var_val_start;
- if (!*p)
- die("Missing variable name in letn");
- var_name = p;
- while(*p && (*p != '=' || isspace(*p)))
- p++;
- var_name_end = p;
- if (*p == '=') p++;
- while(*p && isspace(*p))
- p++;
- var_val_start = p;
- while(*p && !isspace(*p))
- p++;
- return var_set(var_name, var_name_end, var_val_start, p);
- }
- int do_sleep(struct st_query* q)
- {
- char* p=q->first_argument;
- struct timeval t;
- int dec_mul = 1000000;
- while(*p && isspace(*p)) p++;
- if (!*p)
- die("Missing argument in sleepn");
- t.tv_usec = 0;
- if (opt_sleep)
- t.tv_sec = opt_sleep;
- else
- {
- t.tv_sec = atoi(p);
- while(*p && *p != '.' && !isspace(*p))
- p++;
- if (*p == '.')
- {
- char c;
- char *p_end;
- p++;
- p_end = p + 6;
- for(;p <= p_end; ++p)
- {
- c = *p - '0';
- if (c < 10 && c >= 0)
- {
- t.tv_usec = t.tv_usec * 10 + c;
- dec_mul /= 10;
- }
- else
- break;
- }
- }
- }
- t.tv_usec *= dec_mul;
- return select(0,0,0,0, &t);
- }
- static void get_file_name(char *filename, struct st_query* q)
- {
- char* p=q->first_argument;
- strnmov(filename, p, FN_REFLEN);
- /* Remove end space */
- while (p > filename && isspace(p[-1]))
- p--;
- p[0]=0;
- }
- static void get_ints(uint *to,struct st_query* q)
- {
- char* p=q->first_argument;
- long val;
- DBUG_ENTER("get_ints");
- if (!*p)
- die("Missing argument in %sn", q->query);
- for (; (p=str2int(p,10,(long) INT_MIN, (long) INT_MAX, &val)) ; p++)
- {
- *to++= (uint) val;
- if (*p != ',')
- break;
- }
- *to++=0; /* End of data */
- DBUG_VOID_RETURN;
- }
- /*
- Get a string; Return ptr to end of string
- Strings may be surrounded by " or '
- */
- static void get_string(char **to_ptr, char **from_ptr,
- struct st_query* q)
- {
- reg1 char c,sep;
- char *to= *to_ptr, *from= *from_ptr;
- DBUG_ENTER("get_string");
- /* Find separator */
- if (*from == '"' || *from == ''')
- sep= *from++;
- else
- sep=' '; /* Separated with space */
- for ( ; (c=*from) ; from++)
- {
- if (c == '\' && from[1])
- { /* Escaped character */
- /* We can't translate