📄 mysqltest.c
字号:
/* Copyright (C) 2000 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; 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 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:- 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 "2.6"#include <my_global.h>#include <mysql_embed.h>#include <my_sys.h>#include <m_string.h>#include <mysql.h>#include <mysql_version.h>#include <mysqld_error.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 */#ifdef HAVE_SYS_WAIT_H#include <sys/wait.h>#endif#ifndef WEXITSTATUS# ifdef __WIN__# define WEXITSTATUS(stat_val) (stat_val)# else# 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#ifndef MYSQL_MANAGER_PORT#define MYSQL_MANAGER_PORT 23546#endif#define MAX_SERVER_ARGS 64/* 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*/#define CON_RETRY_SLEEP 2#define MAX_CON_TRIES 5#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 2enum {OPT_MANAGER_USER=256,OPT_MANAGER_HOST,OPT_MANAGER_PASSWD, OPT_MANAGER_PORT,OPT_MANAGER_WAIT_TIMEOUT, OPT_SKIP_SAFEMALLOC, 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};/* ************************************************************************ *//* 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 }};static match_err global_expected_errno[MAX_EXPECTED_ERRORS];static uint global_expected_errors;/* ************************************************************************ */static int record = 0, opt_sleep=0;static char *db = 0, *pass=0;const char *user = 0, *host = 0, *unix_sock = 0, *opt_basedir="./";static int port = 0;static my_bool opt_big_test= 0, opt_compress= 0, silent= 0, verbose = 0;static my_bool tty_password= 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 int parsing_disabled= 0;const char *manager_user="root",*manager_host=0;char *manager_pass=0;int manager_port=MYSQL_MANAGER_PORT;int manager_wait_timeout=3;MYSQL_MANAGER* manager=0;static char **default_argv;static const char *load_default_groups[]= { "mysqltest","client",0 };static char line_buffer[MAX_DELIMITER], *line_buffer_pos= line_buffer;typedef struct{ FILE* file; const char *file_name; uint lineno; /* Current line in file */} test_file;static test_file file_stack[MAX_INCLUDE_DEPTH];static test_file* cur_file;static test_file* file_stack_end;uint start_lineno; /* Start line of query */static char TMPDIR[FN_REFLEN];static char delimiter[MAX_DELIMITER]= DEFAULT_DELIMITER;static uint delimiter_length= 1;/* 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 my_bool display_result_vertically= FALSE, display_metadata= FALSE;/* See the timer_output() definition for details */static char *timer_file = NULL;static ulonglong timer_start;static int got_end_timer= FALSE;static void timer_output(void);static ulonglong timer_now(void);/* Precompiled re's */static my_regex_t ps_re; /* the query can be run using PS protocol */static my_regex_t sp_re; /* the query can be run as a SP */static my_regex_t view_re; /* the query can be run as a view*/static void init_re(void);static int match_re(my_regex_t *, char *);static void free_re(void);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{ MYSQL mysql; /* Used when creating views and sp, to avoid implicit commit */ MYSQL* util_mysql; char *name; MYSQL_STMT* stmt;};typedef struct{ int read_lines,current_line;} PARSER;PARSER parser;MASTER_POS master_pos;/* if set, all results are concated and compared against this file */const char *result_file = 0;typedef struct{ char *name; int name_len; char *str_val; int str_val_len; int int_val; int alloced_len; int int_dirty; /* do not update string if int is updated until first read */ int alloced;} 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;struct connection cons[MAX_CONS];struct connection* cur_con, *next_con, *cons_end; /* 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_SERVER_START, Q_SERVER_STOP,Q_REQUIRE_MANAGER,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_EXIT,Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT,Q_IF,Q_DISABLE_PARSING, Q_ENABLE_PARSING,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[]={ "connection", "query", "connect", "sleep", "real_sleep", "inc", "dec", "source", "disconnect", "let", "echo", "while", "end", "system", "result", "require", "save_master_pos", "sync_with_master", "sync_slave_with_master", "error", "send", "reap", "dirty_close", "replace_result", "replace_column", "ping", "eval", "rpl_probe", "enable_rpl_parse", "disable_rpl_parse", "eval_result", /* Enable/disable that the _query_ is logged to result file */ "enable_query_log", "disable_query_log", /* Enable/disable that the _result_ from a query is logged to result file */ "enable_result_log", "disable_result_log", "server_start", "server_stop", "require_manager", "wait_for_slave_to_stop", "enable_warnings", "disable_warnings", "enable_ps_warnings", "disable_ps_warnings", "enable_info", "disable_info", "enable_metadata", "disable_metadata", "exec", "delimiter", "disable_abort_on_error", "enable_abort_on_error", "vertical_results", "horizontal_results", "query_vertical", "query_horizontal", "start_timer", "end_timer", "character_set", "disable_ps_protocol", "enable_ps_protocol", "exit", "disable_reconnect", "enable_reconnect", "if", "disable_parsing", "enable_parsing", 0};TYPELIB command_typelib= {array_elements(command_names),"", command_names, 0};DYNAMIC_STRING ds_res;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);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);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);/* 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);void free_replace();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);#ifdef __WIN__static void free_tmp_sh_file();static void free_win_path_patterns();#endifstruct st_replace *glob_replace;static int eval_result = 0;/* For column replace */char *replace_column[MAX_COLUMNS];uint max_replace_column= 0;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 < 40000void mysql_enable_rpl_parse(MYSQL* mysql __attribute__((unused))) {}void mysql_disable_rpl_parse(MYSQL* mysql __attribute__((unused))) {}int mysql_rpl_parse_enabled(MYSQL* mysql __attribute__((unused))) { return 1; }my_bool mysql_rpl_probe(MYSQL *mysql __attribute__((unused))) { return 1; }#endifstatic 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){ const char *p; register char c, next_c; register int escaped = 0; VAR* v; DBUG_ENTER("do_eval"); for (p= query; (c = *p); ++p) { switch(c) { case '$': if (escaped) { escaped = 0; dynstr_append_mem(query_eval, p, 1); } else { if (!(v = var_get(p, &p, 0, 0))) die("Bad variable in eval");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -