00001 /* Copyright (C) 2000 MySQL AB 00002 00003 This program is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU General Public License as published by 00005 the Free Software Foundation; either version 2 of the License, or 00006 (at your option) any later version. 00007 00008 This program is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00011 GNU General Public License for more details. 00012 00013 You should have received a copy of the GNU General Public License 00014 along with this program; if not, write to the Free Software 00015 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 00016 00017 /* mysqltest test tool 00018 * See the manual for more information 00019 * TODO: document better how mysqltest works 00020 * 00021 * Written by: 00022 * Sasha Pachev <sasha@mysql.com> 00023 * Matt Wagner <matt@mysql.com> 00024 * Monty 00025 * Jani 00026 **/ 00027 00028 /********************************************************************** 00029 TODO: 00030 00031 - Do comparison line by line, instead of doing a full comparison of 00032 the text file. This will save space as we don't need to keep many 00033 results in memory. It will also make it possible to do simple 00034 'comparison' fixes like accepting the result even if a float differed 00035 in the last decimals. 00036 00037 - Don't buffer lines from the test that you don't expect to need 00038 again. 00039 00040 - Change 'read_line' to be faster by using the readline.cc code; 00041 We can do better than calling feof() for each character! 00042 00043 **********************************************************************/ 00044 00045 #define MTEST_VERSION "2.6" 00046 00047 #include <my_global.h> 00048 #include <mysql_embed.h> 00049 #include <my_sys.h> 00050 #include <m_string.h> 00051 #include <mysql.h> 00052 #include <mysql_version.h> 00053 #include <mysqld_error.h> 00054 #include <m_ctype.h> 00055 #include <my_dir.h> 00056 #include <errmsg.h> /* Error codes */ 00057 #include <hash.h> 00058 #include <my_getopt.h> 00059 #include <stdarg.h> 00060 #include <sys/stat.h> 00061 #include <violite.h> 00062 #include "my_regex.h" /* Our own version of lib */ 00063 #ifdef HAVE_SYS_WAIT_H 00064 #include <sys/wait.h> 00065 #endif 00066 #ifndef WEXITSTATUS 00067 # ifdef __WIN__ 00068 # define WEXITSTATUS(stat_val) (stat_val) 00069 # else 00070 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) 00071 # endif 00072 #endif 00073 /* MAX_QUERY is 256K -- there is a test in sp-big that is >128K */ 00074 #define MAX_QUERY (256*1024) 00075 #define MAX_VAR_NAME 256 00076 #define MAX_COLUMNS 256 00077 #define MAX_CONS 128 00078 #define MAX_INCLUDE_DEPTH 16 00079 #define INIT_Q_LINES 1024 00080 #define MIN_VAR_ALLOC 32 00081 #define BLOCK_STACK_DEPTH 32 00082 #define MAX_EXPECTED_ERRORS 10 00083 #define QUERY_SEND 1 00084 #define QUERY_REAP 2 00085 #define MAX_SERVER_ARGS 64 00086 00087 00088 #define SLAVE_POLL_INTERVAL 300000 /* 0.3 of a sec */ 00089 #define DEFAULT_DELIMITER ";" 00090 #define MAX_DELIMITER 16 00091 00092 #define RESULT_OK 0 00093 #define RESULT_CONTENT_MISMATCH 1 00094 #define RESULT_LENGTH_MISMATCH 2 00095 00096 enum {OPT_SKIP_SAFEMALLOC=256, OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, 00097 OPT_SSL_CA, OPT_SSL_CAPATH, OPT_SSL_CIPHER, OPT_PS_PROTOCOL, 00098 OPT_SP_PROTOCOL, OPT_CURSOR_PROTOCOL, OPT_VIEW_PROTOCOL, 00099 OPT_SSL_VERIFY_SERVER_CERT, OPT_MAX_CONNECT_RETRIES, 00100 OPT_MARK_PROGRESS}; 00101 00102 /* ************************************************************************ */ 00103 /* 00104 The list of error codes to --error are stored in an internal array of 00105 structs. This struct can hold numeric SQL error codes or SQLSTATE codes 00106 as strings. The element next to the last active element in the list is 00107 set to type ERR_EMPTY. When an SQL statement returns an error, we use 00108 this list to check if this is an expected error. 00109 */ 00110 00111 enum match_err_type 00112 { 00113 ERR_EMPTY= 0, 00114 ERR_ERRNO, 00115 ERR_SQLSTATE 00116 }; 00117 00118 typedef struct 00119 { 00120 enum match_err_type type; 00121 union 00122 { 00123 uint errnum; 00124 char sqlstate[SQLSTATE_LENGTH+1]; /* \0 terminated string */ 00125 } code; 00126 } match_err; 00127 00128 typedef struct 00129 { 00130 const char *name; 00131 long code; 00132 } st_error; 00133 00134 static st_error global_error[] = 00135 { 00136 #include <mysqld_ername.h> 00137 { 0, 0 } 00138 }; 00139 00140 static match_err global_expected_errno[MAX_EXPECTED_ERRORS]; 00141 static uint global_expected_errors; 00142 00143 /* ************************************************************************ */ 00144 00145 static int record= 0, opt_sleep= -1; 00146 static char *db = 0, *pass=0; 00147 const char *user = 0, *host = 0, *unix_sock = 0, *opt_basedir="./"; 00148 const char *opt_include= 0; 00149 static int port = 0; 00150 static int opt_max_connect_retries; 00151 static my_bool opt_big_test= 0, opt_compress= 0, silent= 0, verbose = 0; 00152 static my_bool tty_password= 0; 00153 static my_bool opt_mark_progress= 0; 00154 static my_bool ps_protocol= 0, ps_protocol_enabled= 0; 00155 static my_bool sp_protocol= 0, sp_protocol_enabled= 0; 00156 static my_bool view_protocol= 0, view_protocol_enabled= 0; 00157 static my_bool cursor_protocol= 0, cursor_protocol_enabled= 0; 00158 static int parsing_disabled= 0; 00159 00160 static char **default_argv; 00161 static const char *load_default_groups[]= { "mysqltest","client",0 }; 00162 static char line_buffer[MAX_DELIMITER], *line_buffer_pos= line_buffer; 00163 00164 typedef struct 00165 { 00166 FILE* file; 00167 const char *file_name; 00168 uint lineno; /* Current line in file */ 00169 } test_file; 00170 00171 static test_file file_stack[MAX_INCLUDE_DEPTH]; 00172 static test_file* cur_file; 00173 static test_file* file_stack_end; 00174 uint start_lineno= 0; /* Start line of query */ 00175 00176 /* Stores regex substitutions */ 00177 00178 struct st_regex 00179 { 00180 char* pattern; /* Pattern to be replaced */ 00181 char* replace; /* String or expression to replace the pattern with */ 00182 int icase; /* true if the match is case insensitive */ 00183 }; 00184 00185 struct st_replace_regex 00186 { 00187 DYNAMIC_ARRAY regex_arr; /* stores a list of st_regex subsitutions */ 00188 00189 /* 00190 Temporary storage areas for substitutions. To reduce unnessary copying 00191 and memory freeing/allocation, we pre-allocate two buffers, and alternate 00192 their use, one for input/one for output, the roles changing on the next 00193 st_regex substition. At the end of substitutions buf points to the 00194 one containing the final result. 00195 */ 00196 char* buf; 00197 char* even_buf; 00198 char* odd_buf; 00199 int even_buf_len; 00200 int odd_buf_len; 00201 }; 00202 00203 struct st_replace_regex *glob_replace_regex= 0; 00204 00205 static char TMPDIR[FN_REFLEN]; 00206 static char delimiter[MAX_DELIMITER]= DEFAULT_DELIMITER; 00207 static uint delimiter_length= 1; 00208 00209 /* Block stack */ 00210 enum block_cmd { cmd_none, cmd_if, cmd_while }; 00211 typedef struct 00212 { 00213 int line; /* Start line of block */ 00214 my_bool ok; /* Should block be executed */ 00215 enum block_cmd cmd; /* Command owning the block */ 00216 } BLOCK; 00217 static BLOCK block_stack[BLOCK_STACK_DEPTH]; 00218 static BLOCK *cur_block, *block_stack_end; 00219 00220 static CHARSET_INFO *charset_info= &my_charset_latin1; /* Default charset */ 00221 static const char *charset_name= "latin1"; /* Default character set name */ 00222 00223 static int embedded_server_arg_count=0; 00224 static char *embedded_server_args[MAX_SERVER_ARGS]; 00225 00226 static my_bool display_result_vertically= FALSE, display_metadata= FALSE; 00227 00228 /* See the timer_output() definition for details */ 00229 static char *timer_file = NULL; 00230 static ulonglong timer_start, progress_start= 0; 00231 static int got_end_timer= FALSE; 00232 static void timer_output(void); 00233 static ulonglong timer_now(void); 00234 00235 /* Precompiled re's */ 00236 static my_regex_t ps_re; /* the query can be run using PS protocol */ 00237 static my_regex_t sp_re; /* the query can be run as a SP */ 00238 static my_regex_t view_re; /* the query can be run as a view*/ 00239 00240 static void init_re(void); 00241 static int match_re(my_regex_t *, char *); 00242 static void free_re(void); 00243 00244 static int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace, 00245 char *string, int icase); 00246 00247 static const char *embedded_server_groups[]= 00248 { 00249 "server", 00250 "embedded", 00251 "mysqltest_SERVER", 00252 NullS 00253 }; 00254 00255 DYNAMIC_ARRAY q_lines; 00256 00257 #include "sslopt-vars.h" 00258 00259 typedef struct 00260 { 00261 char file[FN_REFLEN]; 00262 ulong pos; 00263 } MASTER_POS ; 00264 00265 struct connection 00266 { 00267 MYSQL mysql; 00268 /* Used when creating views and sp, to avoid implicit commit */ 00269 MYSQL* util_mysql; 00270 char *name; 00271 MYSQL_STMT* stmt; 00272 }; 00273 00274 typedef struct 00275 { 00276 int read_lines,current_line; 00277 } PARSER; 00278 00279 PARSER parser; 00280 MASTER_POS master_pos; 00281 /* if set, all results are concated and compared against this file */ 00282 const char *result_file = 0; 00283 00284 typedef struct 00285 { 00286 char *name; 00287 int name_len; 00288 char *str_val; 00289 int str_val_len; 00290 int int_val; 00291 int alloced_len; 00292 int int_dirty; /* do not update string if int is updated until first read */ 00293 int alloced; 00294 char *env_s; 00295 } VAR; 00296 00297 VAR var_reg[10]; 00298 /*Perl/shell-like variable registers */ 00299 HASH var_hash; 00300 my_bool disable_query_log=0, disable_result_log=0, disable_warnings=0; 00301 my_bool disable_ps_warnings= 0; 00302 my_bool disable_info= 1; /* By default off */ 00303 my_bool abort_on_error= 1; 00304 00305 struct connection cons[MAX_CONS]; 00306 struct connection* cur_con, *next_con, *cons_end; 00307 00308 /* Add new commands before Q_UNKNOWN !*/ 00309 00310 enum enum_commands { 00311 Q_CONNECTION=1, Q_QUERY, 00312 Q_CONNECT, Q_SLEEP, Q_REAL_SLEEP, 00313 Q_INC, Q_DEC, 00314 Q_SOURCE, Q_DISCONNECT, 00315 Q_LET, Q_ECHO, 00316 Q_WHILE, Q_END_BLOCK, 00317 Q_SYSTEM, Q_RESULT, 00318 Q_REQUIRE, Q_SAVE_MASTER_POS, 00319 Q_SYNC_WITH_MASTER, 00320 Q_SYNC_SLAVE_WITH_MASTER, 00321 Q_ERROR, 00322 Q_SEND, Q_REAP, 00323 Q_DIRTY_CLOSE, Q_REPLACE, Q_REPLACE_COLUMN, 00324 Q_PING, Q_EVAL, 00325 Q_RPL_PROBE, Q_ENABLE_RPL_PARSE, 00326 Q_DISABLE_RPL_PARSE, Q_EVAL_RESULT, 00327 Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG, 00328 Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG, 00329 Q_WAIT_FOR_SLAVE_TO_STOP, 00330 Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS, 00331 Q_ENABLE_PS_WARNINGS, Q_DISABLE_PS_WARNINGS, 00332 Q_ENABLE_INFO, Q_DISABLE_INFO, 00333 Q_ENABLE_METADATA, Q_DISABLE_METADATA, 00334 Q_EXEC, Q_DELIMITER, 00335 Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR, 00336 Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS, 00337 Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, 00338 Q_START_TIMER, Q_END_TIMER, 00339 Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL, 00340 Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT, 00341 Q_IF, 00342 Q_DISABLE_PARSING, Q_ENABLE_PARSING, 00343 Q_REPLACE_REGEX, Q_DIE, 00344 00345 Q_UNKNOWN, /* Unknown command. */ 00346 Q_COMMENT, /* Comments, ignored. */ 00347 Q_COMMENT_WITH_COMMAND 00348 }; 00349 00350 /* this should really be called command */ 00351 struct st_query 00352 { 00353 char *query, *query_buf,*first_argument,*last_argument,*end; 00354 int first_word_len; 00355 my_bool abort_on_error, require_file; 00356 match_err expected_errno[MAX_EXPECTED_ERRORS]; 00357 uint expected_errors; 00358 char record_file[FN_REFLEN]; 00359 enum enum_commands type; 00360 }; 00361 00362 const char *command_names[]= 00363 { 00364 "connection", 00365 "query", 00366 "connect", 00367 "sleep", 00368 "real_sleep", 00369 "inc", 00370 "dec", 00371 "source", 00372 "disconnect", 00373 "let", 00374 "echo", 00375 "while", 00376 "end", 00377 "system", 00378 "result", 00379 "require", 00380 "save_master_pos", 00381 "sync_with_master", 00382 "sync_slave_with_master", 00383 "error", 00384 "send", 00385 "reap", 00386 "dirty_close", 00387 "replace_result", 00388 "replace_column", 00389 "ping", 00390 "eval", 00391 "rpl_probe", 00392 "enable_rpl_parse", 00393 "disable_rpl_parse", 00394 "eval_result", 00395 /* Enable/disable that the _query_ is logged to result file */ 00396 "enable_query_log", 00397 "disable_query_log", 00398 /* Enable/disable that the _result_ from a query is logged to result file */ 00399 "enable_result_log", 00400 "disable_result_log", 00401 "wait_for_slave_to_stop", 00402 "enable_warnings", 00403 "disable_warnings", 00404 "enable_ps_warnings", 00405 "disable_ps_warnings", 00406 "enable_info", 00407 "disable_info", 00408 "enable_metadata", 00409 "disable_metadata", 00410 "exec", 00411 "delimiter", 00412 "disable_abort_on_error", 00413 "enable_abort_on_error", 00414 "vertical_results", 00415 "horizontal_results", 00416 "query_vertical", 00417 "query_horizontal", 00418 "start_timer", 00419 "end_timer", 00420 "character_set", 00421 "disable_ps_protocol", 00422 "enable_ps_protocol", 00423 "disable_reconnect", 00424 "enable_reconnect", 00425 "if", 00426 "disable_parsing", 00427 "enable_parsing", 00428 "replace_regex", 00429 "die", 00430 0 00431 }; 00432 00433 TYPELIB command_typelib= {array_elements(command_names),"", 00434 command_names, 0}; 00435 00436 DYNAMIC_STRING ds_res, ds_progress; 00437 static void die(const char *fmt, ...); 00438 static void init_var_hash(); 00439 static VAR* var_from_env(const char *, const char *); 00440 static byte* get_var_key(const byte* rec, uint* len, my_bool t); 00441 static VAR* var_init(VAR* v, const char *name, int name_len, const char *val, 00442 int val_len); 00443 00444 static void var_free(void* v); 00445 00446 void dump_result_to_reject_file(const char *record_file, char *buf, int size); 00447 void dump_result_to_log_file(const char *record_file, char *buf, int size); 00448 00449 int close_connection(struct st_query*); 00450 static void set_charset(struct st_query*); 00451 VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw, 00452 my_bool ignore_not_existing); 00453 int eval_expr(VAR* v, const char *p, const char** p_end); 00454 static int read_server_arguments(const char *name); 00455 00456 /* Definitions for replace result */ 00457 00458 typedef struct st_pointer_array { /* when using array-strings */ 00459 TYPELIB typelib; /* Pointer to strings */ 00460 byte *str; /* Strings is here */ 00461 int7 *flag; /* Flag about each var. */ 00462 uint array_allocs,max_count,length,max_length; 00463 } POINTER_ARRAY; 00464 00465 struct st_replace; 00466 struct st_replace *init_replace(my_string *from, my_string *to, uint count, 00467 my_string word_end_chars); 00468 void free_replace(); 00469 static void free_replace_regex(); 00470 static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name); 00471 static void replace_strings_append(struct st_replace *rep, DYNAMIC_STRING* ds, 00472 const char *from, int len); 00473 void free_pointer_array(POINTER_ARRAY *pa); 00474 static void do_eval(DYNAMIC_STRING *query_eval, const char *query, 00475 my_bool pass_through_escape_chars); 00476 static void str_to_file(const char *fname, char *str, int size); 00477 00478 #ifdef __WIN__ 00479 static void free_tmp_sh_file(); 00480 static void free_win_path_patterns(); 00481 #endif 00482 00483 struct st_replace *glob_replace; 00484 static int eval_result = 0; 00485 00486 /* For column replace */ 00487 char *replace_column[MAX_COLUMNS]; 00488 uint max_replace_column= 0; 00489 00490 static void get_replace_column(struct st_query *q); 00491 static void free_replace_column(); 00492 00493 /* Disable functions that only exist in MySQL 4.0 */ 00494 #if MYSQL_VERSION_ID < 40000 00495 void mysql_enable_rpl_parse(MYSQL* mysql __attribute__((unused))) {} 00496 void mysql_disable_rpl_parse(MYSQL* mysql __attribute__((unused))) {} 00497 int mysql_rpl_parse_enabled(MYSQL* mysql __attribute__((unused))) { return 1; } 00498 my_bool mysql_rpl_probe(MYSQL *mysql __attribute__((unused))) { return 1; } 00499 #endif 00500 static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val, 00501 int len); 00502 static void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val); 00503 static void handle_error(const char *query, struct st_query *q, 00504 unsigned int err_errno, const char *err_error, 00505 const char *err_sqlstate, DYNAMIC_STRING *ds); 00506 static void handle_no_error(struct st_query *q); 00507 00508 static void do_eval(DYNAMIC_STRING* query_eval, const char *query, 00509 my_bool pass_through_escape_chars) 00510 { 00511 const char *p; 00512 register char c, next_c; 00513 register int escaped = 0; 00514 VAR* v; 00515 DBUG_ENTER("do_eval"); 00516 00517 for (p= query; (c = *p); ++p) 00518 { 00519 switch(c) { 00520 case '$': 00521 if (escaped) 00522 { 00523 escaped = 0; 00524 dynstr_append_mem(query_eval, p, 1); 00525 } 00526 else 00527 { 00528 if (!(v = var_get(p, &p, 0, 0))) 00529 die("Bad variable in eval"); 00530 dynstr_append_mem(query_eval, v->str_val, v->str_val_len); 00531 } 00532 break; 00533 case '\\': 00534 next_c= *(p+1); 00535 if (escaped) 00536 { 00537 escaped = 0; 00538 dynstr_append_mem(query_eval, p, 1); 00539 } 00540 else if (next_c == '\\' || next_c == '$') 00541 { 00542 /* Set escaped only if next char is \ or $ */ 00543 escaped = 1; 00544 00545 if (pass_through_escape_chars) 00546 { 00547 /* The escape char should be added to the output string. */ 00548 dynstr_append_mem(query_eval, p, 1); 00549 } 00550 } 00551 else 00552 dynstr_append_mem(query_eval, p, 1); 00553 break; 00554 default: 00555 dynstr_append_mem(query_eval, p, 1); 00556 break; 00557 } 00558 } 00559 DBUG_VOID_RETURN; 00560 } 00561 00562 00563 static void close_cons() 00564 { 00565 DBUG_ENTER("close_cons"); 00566 for (--next_con; next_con >= cons; --next_con) 00567 { 00568 if (next_con->stmt) 00569 mysql_stmt_close(next_con->stmt); 00570 next_con->stmt= 0; 00571 mysql_close(&next_con->mysql); 00572 if (next_con->util_mysql) 00573 mysql_close(next_con->util_mysql); 00574 my_free(next_con->name, MYF(MY_ALLOW_ZERO_PTR)); 00575 } 00576 DBUG_VOID_RETURN; 00577 } 00578 00579 00580 static void close_files() 00581 { 00582 DBUG_ENTER("close_files"); 00583 for (; cur_file >= file_stack; cur_file--) 00584 { 00585 DBUG_PRINT("info", ("file_name: %s", cur_file->file_name)); 00586 if (cur_file->file && cur_file->file != stdin) 00587 my_fclose(cur_file->file, MYF(0)); 00588 my_free((gptr)cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR)); 00589 cur_file->file_name= 0; 00590 } 00591 DBUG_VOID_RETURN; 00592 } 00593 00594 00595 static void free_used_memory() 00596 { 00597 uint i; 00598 DBUG_ENTER("free_used_memory"); 00599 00600 close_cons(); 00601 close_files(); 00602 hash_free(&var_hash); 00603 00604 for (i=0 ; i < q_lines.elements ; i++) 00605 { 00606 struct st_query **q= dynamic_element(&q_lines, i, struct st_query**); 00607 my_free((gptr) (*q)->query_buf,MYF(MY_ALLOW_ZERO_PTR)); 00608 my_free((gptr) (*q),MYF(0)); 00609 } 00610 for (i=0; i < 10; i++) 00611 { 00612 if (var_reg[i].alloced_len) 00613 my_free(var_reg[i].str_val, MYF(MY_WME)); 00614 } 00615 while (embedded_server_arg_count > 1) 00616 my_free(embedded_server_args[--embedded_server_arg_count],MYF(0)); 00617 delete_dynamic(&q_lines); 00618 dynstr_free(&ds_res); 00619 dynstr_free(&ds_progress); 00620 free_replace(); 00621 free_replace_column(); 00622 my_free(pass,MYF(MY_ALLOW_ZERO_PTR)); 00623 free_defaults(default_argv); 00624 mysql_server_end(); 00625 free_re(); 00626 #ifdef __WIN__ 00627 free_tmp_sh_file(); 00628 free_win_path_patterns(); 00629 #endif 00630 DBUG_VOID_RETURN; 00631 } 00632 00633 static void die(const char *fmt, ...) 00634 { 00635 va_list args; 00636 DBUG_ENTER("die"); 00637 00638 /* Print the error message */ 00639 va_start(args, fmt); 00640 if (fmt) 00641 { 00642 fprintf(stderr, "mysqltest: "); 00643 if (cur_file && cur_file != file_stack) 00644 fprintf(stderr, "In included file \"%s\": ", 00645 cur_file->file_name); 00646 if (start_lineno > 0) 00647 fprintf(stderr, "At line %u: ", start_lineno); 00648 vfprintf(stderr, fmt, args); 00649 fprintf(stderr, "\n"); 00650 fflush(stderr); 00651 } 00652 va_end(args); 00653 00654 /* Dump the result that has been accumulated so far to .log file */ 00655 if (result_file && ds_res.length) 00656 dump_result_to_log_file(result_file, ds_res.str, ds_res.length); 00657 00658 /* Clean up and exit */ 00659 free_used_memory(); 00660 my_end(MY_CHECK_ERROR); 00661 00662 if (!silent) 00663 printf("not ok\n"); 00664 00665 exit(1); 00666 } 00667 00668 00669 static void abort_not_supported_test(const char *fmt, ...) 00670 { 00671 va_list args; 00672 test_file* err_file= cur_file; 00673 DBUG_ENTER("abort_not_supported_test"); 00674 00675 /* Print include filestack */ 00676 fprintf(stderr, "The test '%s' is not supported by this installation\n", 00677 file_stack->file_name); 00678 fprintf(stderr, "Detected in file %s at line %d\n", 00679 err_file->file_name, err_file->lineno); 00680 while (err_file != file_stack) 00681 { 00682 err_file--; 00683 fprintf(stderr, "included from %s at line %d\n", 00684 err_file->file_name, err_file->lineno); 00685 } 00686 00687 /* Print error message */ 00688 va_start(args, fmt); 00689 if (fmt) 00690 { 00691 fprintf(stderr, "reason: "); 00692 vfprintf(stderr, fmt, args); 00693 fprintf(stderr, "\n"); 00694 fflush(stderr); 00695 } 00696 va_end(args); 00697 00698 /* Clean up and exit */ 00699 free_used_memory(); 00700 my_end(MY_CHECK_ERROR); 00701 00702 if (!silent) 00703 printf("skipped\n"); 00704 00705 exit(62); 00706 } 00707 00708 static void verbose_msg(const char *fmt, ...) 00709 { 00710 va_list args; 00711 DBUG_ENTER("verbose_msg"); 00712 if (!verbose) 00713 DBUG_VOID_RETURN; 00714 00715 va_start(args, fmt); 00716 fprintf(stderr, "mysqltest: "); 00717 if (start_lineno != 0) 00718 fprintf(stderr, "At line %u: ", start_lineno); 00719 vfprintf(stderr, fmt, args); 00720 fprintf(stderr, "\n"); 00721 va_end(args); 00722 00723 DBUG_VOID_RETURN; 00724 } 00725 00726 00727 void init_parser() 00728 { 00729 parser.current_line= parser.read_lines= 0; 00730 memset(&var_reg, 0, sizeof(var_reg)); 00731 } 00732 00733 00734 static int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) 00735 { 00736 MY_STAT stat_info; 00737 char *tmp, *res_ptr; 00738 char eval_file[FN_REFLEN]; 00739 int res; 00740 uint res_len; 00741 int fd; 00742 DYNAMIC_STRING res_ds; 00743 DBUG_ENTER("dyn_string_cmp"); 00744 00745 if (!test_if_hard_path(fname)) 00746 { 00747 strxmov(eval_file, opt_basedir, fname, NullS); 00748 fn_format(eval_file, eval_file, "", "", MY_UNPACK_FILENAME); 00749 } 00750 else 00751 fn_format(eval_file, fname, "", "", MY_UNPACK_FILENAME); 00752 00753 if (!my_stat(eval_file, &stat_info, MYF(MY_WME))) 00754 die(NullS); 00755 if (!eval_result && (uint) stat_info.st_size != ds->length) 00756 { 00757 DBUG_PRINT("info",("Size differs: result size: %u file size: %u", 00758 ds->length, stat_info.st_size)); 00759 DBUG_PRINT("info",("result: '%s'", ds->str)); 00760 DBUG_RETURN(RESULT_LENGTH_MISMATCH); 00761 } 00762 if (!(tmp = (char*) my_malloc(stat_info.st_size + 1, MYF(MY_WME)))) 00763 die(NullS); 00764 00765 if ((fd = my_open(eval_file, O_RDONLY, MYF(MY_WME))) < 0) 00766 die(NullS); 00767 if (my_read(fd, (byte*)tmp, stat_info.st_size, MYF(MY_WME|MY_NABP))) 00768 die(NullS); 00769 tmp[stat_info.st_size] = 0; 00770 init_dynamic_string(&res_ds, "", 0, 65536); 00771 if (eval_result) 00772 { 00773 do_eval(&res_ds, tmp, FALSE); 00774 res_ptr = res_ds.str; 00775 if ((res_len = res_ds.length) != ds->length) 00776 { 00777 res= RESULT_LENGTH_MISMATCH; 00778 goto err; 00779 } 00780 } 00781 else 00782 { 00783 res_ptr = tmp; 00784 res_len = stat_info.st_size; 00785 } 00786 00787 res= (memcmp(res_ptr, ds->str, res_len)) ? 00788 RESULT_CONTENT_MISMATCH : RESULT_OK; 00789 00790 err: 00791 if (res && eval_result) 00792 str_to_file(fn_format(eval_file, fname, "", ".eval", 00793 MY_REPLACE_EXT), 00794 res_ptr, res_len); 00795 00796 my_free((gptr) tmp, MYF(0)); 00797 my_close(fd, MYF(MY_WME)); 00798 dynstr_free(&res_ds); 00799 00800 DBUG_RETURN(res); 00801 } 00802 00803 /* 00804 Check the content of ds against content of file fname 00805 00806 SYNOPSIS 00807 check_result 00808 ds - content to be checked 00809 fname - name of file to check against 00810 require_option - if set and check fails, the test will be aborted 00811 with the special exit code "not supported test" 00812 00813 RETURN VALUES 00814 error - the function will not return 00815 00816 */ 00817 static void check_result(DYNAMIC_STRING* ds, const char *fname, 00818 my_bool require_option) 00819 { 00820 int res= dyn_string_cmp(ds, fname); 00821 DBUG_ENTER("check_result"); 00822 00823 if (res && require_option) 00824 { 00825 char reason[FN_REFLEN]; 00826 fn_format(reason, fname, "", "", MY_REPLACE_EXT | MY_REPLACE_DIR); 00827 abort_not_supported_test("Test requires: '%s'", reason); 00828 } 00829 switch (res) { 00830 case RESULT_OK: 00831 break; /* ok */ 00832 case RESULT_LENGTH_MISMATCH: 00833 dump_result_to_reject_file(fname, ds->str, ds->length); 00834 die("Result length mismatch"); 00835 break; 00836 case RESULT_CONTENT_MISMATCH: 00837 dump_result_to_reject_file(fname, ds->str, ds->length); 00838 die("Result content mismatch"); 00839 break; 00840 default: /* impossible */ 00841 die("Unknown error code from dyn_string_cmp()"); 00842 } 00843 00844 DBUG_VOID_RETURN; 00845 } 00846 00847 00848 VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw, 00849 my_bool ignore_not_existing) 00850 { 00851 int digit; 00852 VAR* v; 00853 DBUG_ENTER("var_get"); 00854 DBUG_PRINT("enter",("var_name: %s",var_name)); 00855 00856 if (*var_name != '$') 00857 goto err; 00858 digit = *++var_name - '0'; 00859 if (digit < 0 || digit >= 10) 00860 { 00861 const char *save_var_name = var_name, *end; 00862 uint length; 00863 end = (var_name_end) ? *var_name_end : 0; 00864 while (my_isvar(charset_info,*var_name) && var_name != end) 00865 var_name++; 00866 if (var_name == save_var_name) 00867 { 00868 if (ignore_not_existing) 00869 DBUG_RETURN(0); 00870 die("Empty variable"); 00871 } 00872 length= (uint) (var_name - save_var_name); 00873 if (length >= MAX_VAR_NAME) 00874 die("Too long variable name: %s", save_var_name); 00875 00876 if (!(v = (VAR*) hash_search(&var_hash, save_var_name, length))) 00877 { 00878 char buff[MAX_VAR_NAME+1]; 00879 strmake(buff, save_var_name, length); 00880 v= var_from_env(buff, ""); 00881 } 00882 var_name--; /* Point at last character */ 00883 } 00884 else 00885 v = var_reg + digit; 00886 00887 if (!raw && v->int_dirty) 00888 { 00889 sprintf(v->str_val, "%d", v->int_val); 00890 v->int_dirty = 0; 00891 v->str_val_len = strlen(v->str_val); 00892 } 00893 if (var_name_end) 00894 *var_name_end = var_name ; 00895 DBUG_RETURN(v); 00896 err: 00897 if (var_name_end) 00898 *var_name_end = 0; 00899 die("Unsupported variable name: %s", var_name); 00900 DBUG_RETURN(0); 00901 } 00902 00903 static VAR *var_obtain(const char *name, int len) 00904 { 00905 VAR* v; 00906 if ((v = (VAR*)hash_search(&var_hash, name, len))) 00907 return v; 00908 v = var_init(0, name, len, "", 0); 00909 my_hash_insert(&var_hash, (byte*)v); 00910 return v; 00911 } 00912 00913 /* 00914 - if variable starts with a $ it is regarded as a local test varable 00915 - if not it is treated as a environment variable, and the corresponding 00916 environment variable will be updated 00917 */ 00918 int var_set(const char *var_name, const char *var_name_end, 00919 const char *var_val, const char *var_val_end) 00920 { 00921 int digit, result, env_var= 0; 00922 VAR* v; 00923 DBUG_ENTER("var_set"); 00924 DBUG_PRINT("enter", ("var_name: '%.*s' = '%.*s' (length: %d)", 00925 (int) (var_name_end - var_name), var_name, 00926 (int) (var_val_end - var_val), var_val, 00927 (int) (var_val_end - var_val))); 00928 00929 if (*var_name != '$') 00930 env_var= 1; 00931 else 00932 var_name++; 00933 00934 digit = *var_name - '0'; 00935 if (!(digit < 10 && digit >= 0)) 00936 { 00937 v = var_obtain(var_name, (uint) (var_name_end - var_name)); 00938 } 00939 else 00940 v = var_reg + digit; 00941 00942 result= eval_expr(v, var_val, (const char**) &var_val_end); 00943 00944 if (env_var) 00945 { 00946 char buf[1024], *old_env_s= v->env_s; 00947 if (v->int_dirty) 00948 { 00949 sprintf(v->str_val, "%d", v->int_val); 00950 v->int_dirty= 0; 00951 v->str_val_len= strlen(v->str_val); 00952 } 00953 strxmov(buf, v->name, "=", v->str_val, NullS); 00954 if (!(v->env_s= my_strdup(buf, MYF(MY_WME)))) 00955 die("Out of memory"); 00956 putenv(v->env_s); 00957 my_free((gptr)old_env_s, MYF(MY_ALLOW_ZERO_PTR)); 00958 } 00959 DBUG_RETURN(result); 00960 } 00961 00962 00963 int open_file(const char *name) 00964 { 00965 char buff[FN_REFLEN]; 00966 DBUG_ENTER("open_file"); 00967 DBUG_PRINT("enter", ("name: %s", name)); 00968 if (!test_if_hard_path(name)) 00969 { 00970 strxmov(buff, opt_basedir, name, NullS); 00971 name=buff; 00972 } 00973 fn_format(buff, name, "", "", MY_UNPACK_FILENAME); 00974 00975 if (cur_file == file_stack_end) 00976 die("Source directives are nesting too deep"); 00977 cur_file++; 00978 if (!(cur_file->file = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0)))) 00979 { 00980 cur_file--; 00981 die("Could not open file %s", buff); 00982 } 00983 cur_file->file_name= my_strdup(buff, MYF(MY_FAE)); 00984 cur_file->lineno=1; 00985 DBUG_RETURN(0); 00986 } 00987 00988 00989 /* 00990 Check for unexpected "junk" after the end of query 00991 This is normally caused by missing delimiters 00992 */ 00993 00994 int check_eol_junk(const char *eol) 00995 { 00996 const char *p= eol; 00997 DBUG_ENTER("check_eol_junk"); 00998 DBUG_PRINT("enter", ("eol: %s", eol)); 00999 /* Remove all spacing chars except new line */ 01000 while (*p && my_isspace(charset_info, *p) && (*p != '\n')) 01001 p++; 01002 01003 /* Check for extra delimiter */ 01004 if (*p && !strncmp(p, delimiter, delimiter_length)) 01005 die("Extra delimiter \"%s\" found", delimiter); 01006 01007 /* Allow trailing # comment */ 01008 if (*p && *p != '#') 01009 { 01010 if (*p == '\n') 01011 die("Missing delimiter"); 01012 die("End of line junk detected: \"%s\"", p); 01013 } 01014 DBUG_RETURN(0); 01015 } 01016 01017 01018 /* ugly long name, but we are following the convention */ 01019 int do_wait_for_slave_to_stop(struct st_query *q __attribute__((unused))) 01020 { 01021 MYSQL* mysql = &cur_con->mysql; 01022 for (;;) 01023 { 01024 MYSQL_RES *res; 01025 MYSQL_ROW row; 01026 int done; 01027 LINT_INIT(res); 01028 01029 if (mysql_query(mysql,"show status like 'Slave_running'") || 01030 !(res=mysql_store_result(mysql))) 01031 die("Query failed while probing slave for stop: %s", 01032 mysql_error(mysql)); 01033 if (!(row=mysql_fetch_row(res)) || !row[1]) 01034 { 01035 mysql_free_result(res); 01036 die("Strange result from query while probing slave for stop"); 01037 } 01038 done = !strcmp(row[1],"OFF"); 01039 mysql_free_result(res); 01040 if (done) 01041 break; 01042 my_sleep(SLAVE_POLL_INTERVAL); 01043 } 01044 return 0; 01045 } 01046 01047 01048 /* 01049 Source and execute the given file 01050 01051 SYNOPSIS 01052 do_source() 01053 query called command 01054 01055 DESCRIPTION 01056 source <file_name> 01057 01058 Open the file <file_name> and execute it 01059 01060 */ 01061 01062 int do_source(struct st_query *query) 01063 { 01064 char *p= query->first_argument, *name; 01065 if (!*p) 01066 die("Missing file name in source"); 01067 name= p; 01068 while (*p && !my_isspace(charset_info,*p)) 01069 p++; 01070 if (*p) 01071 *p++= 0; 01072 query->last_argument= p; 01073 /* 01074 If this file has already been sourced, don't source it again. 01075 It's already available in the q_lines cache. 01076 */ 01077 if (parser.current_line < (parser.read_lines - 1)) 01078 return 0; 01079 return open_file(name); 01080 } 01081 01082 #ifdef __WIN__ 01083 /* Variables used for temuprary sh files used for emulating Unix on Windows */ 01084 char tmp_sh_name[64], tmp_sh_cmd[70]; 01085 01086 static void init_tmp_sh_file() 01087 { 01088 /* Format a name for the tmp sh file that is unique for this process */ 01089 my_snprintf(tmp_sh_name, sizeof(tmp_sh_name), "tmp_%d.sh", getpid()); 01090 /* Format the command to execute in order to run the script */ 01091 my_snprintf(tmp_sh_cmd, sizeof(tmp_sh_cmd), "sh %s", tmp_sh_name); 01092 } 01093 01094 static void free_tmp_sh_file() 01095 { 01096 my_delete(tmp_sh_name, MYF(0)); 01097 } 01098 #endif 01099 01100 FILE* my_popen(DYNAMIC_STRING* ds_cmd, const char* mode) 01101 { 01102 #ifdef __WIN__ 01103 /* Dump the command into a sh script file and execute with popen */ 01104 str_to_file(tmp_sh_name, ds_cmd->str, ds_cmd->length); 01105 return popen(tmp_sh_cmd, mode); 01106 #else 01107 return popen(ds_cmd->str, mode); 01108 #endif 01109 } 01110 01111 01112 /* 01113 Execute given command. 01114 01115 SYNOPSIS 01116 do_exec() 01117 query called command 01118 01119 DESCRIPTION 01120 exec <command> 01121 01122 Execute the text between exec and end of line in a subprocess. 01123 The error code returned from the subprocess is checked against the 01124 expected error array, previously set with the --error command. 01125 It can thus be used to execute a command that shall fail. 01126 01127 NOTE 01128 Although mysqltest is executed from cygwin shell, the command will be 01129 executed in "cmd.exe". Thus commands like "rm" etc can NOT be used, use 01130 system for those commands. 01131 */ 01132 01133 static void do_exec(struct st_query *query) 01134 { 01135 int error; 01136 char buf[1024]; 01137 FILE *res_file; 01138 char *cmd= query->first_argument; 01139 DYNAMIC_STRING ds_cmd; 01140 DBUG_ENTER("do_exec"); 01141 DBUG_PRINT("enter", ("cmd: '%s'", cmd)); 01142 01143 while (*cmd && my_isspace(charset_info, *cmd)) 01144 cmd++; 01145 if (!*cmd) 01146 die("Missing argument in exec"); 01147 query->last_argument= query->end; 01148 01149 init_dynamic_string(&ds_cmd, 0, strlen(cmd)+256, 256); 01150 /* Eval the command, thus replacing all environment variables */ 01151 do_eval(&ds_cmd, cmd, TRUE); 01152 cmd= ds_cmd.str; 01153 01154 DBUG_PRINT("info", ("Executing '%s' as '%s'", 01155 query->first_argument, cmd)); 01156 01157 if (!(res_file= my_popen(&ds_cmd, "r")) && query->abort_on_error) 01158 die("popen(\"%s\", \"r\") failed", query->first_argument); 01159 01160 while (fgets(buf, sizeof(buf), res_file)) 01161 { 01162 if (disable_result_log) 01163 { 01164 buf[strlen(buf)-1]=0; 01165 DBUG_PRINT("exec_result",("%s", buf)); 01166 } 01167 else 01168 { 01169 replace_dynstr_append(&ds_res, buf); 01170 } 01171 } 01172 error= pclose(res_file); 01173 if (error > 0) 01174 { 01175 uint status= WEXITSTATUS(error), i; 01176 my_bool ok= 0; 01177 01178 if (query->abort_on_error) 01179 die("command \"%s\" failed", query->first_argument); 01180 01181 DBUG_PRINT("info", 01182 ("error: %d, status: %d", error, status)); 01183 for (i= 0; i < query->expected_errors; i++) 01184 { 01185 DBUG_PRINT("info", ("expected error: %d", 01186 query->expected_errno[i].code.errnum)); 01187 if ((query->expected_errno[i].type == ERR_ERRNO) && 01188 (query->expected_errno[i].code.errnum == status)) 01189 { 01190 ok= 1; 01191 DBUG_PRINT("info", ("command \"%s\" failed with expected error: %d", 01192 query->first_argument, status)); 01193 } 01194 } 01195 if (!ok) 01196 die("command \"%s\" failed with wrong error: %d", 01197 query->first_argument, status); 01198 } 01199 else if (query->expected_errno[0].type == ERR_ERRNO && 01200 query->expected_errno[0].code.errnum != 0) 01201 { 01202 /* Error code we wanted was != 0, i.e. not an expected success */ 01203 die("command \"%s\" succeeded - should have failed with errno %d...", 01204 query->first_argument, query->expected_errno[0].code.errnum); 01205 } 01206 01207 free_replace(); 01208 DBUG_VOID_RETURN; 01209 } 01210 01211 /* 01212 Set variable from the result of a query 01213 01214 SYNOPSIS 01215 var_query_set() 01216 var variable to set from query 01217 query start of query string to execute 01218 query_end end of the query string to execute 01219 01220 01221 DESCRIPTION 01222 let @<var_name> = `<query>` 01223 01224 Execute the query and assign the first row of result to var as 01225 a tab separated strings 01226 01227 Also assign each column of the result set to 01228 variable "$<var_name>_<column_name>" 01229 Thus the tab separated output can be read from $<var_name> and 01230 and each individual column can be read as $<var_name>_<col_name> 01231 01232 */ 01233 01234 int var_query_set(VAR* var, const char *query, const char** query_end) 01235 { 01236 char* end = (char*)((query_end && *query_end) ? 01237 *query_end : query + strlen(query)); 01238 MYSQL_RES *res; 01239 MYSQL_ROW row; 01240 MYSQL* mysql = &cur_con->mysql; 01241 DBUG_ENTER("var_query_set"); 01242 LINT_INIT(res); 01243 01244 while (end > query && *end != '`') 01245 --end; 01246 if (query == end) 01247 die("Syntax error in query, missing '`'"); 01248 ++query; 01249 01250 if (mysql_real_query(mysql, query, (int)(end - query)) || 01251 !(res = mysql_store_result(mysql))) 01252 { 01253 *end = 0; 01254 die("Error running query '%s': %d: %s", query, 01255 mysql_errno(mysql) ,mysql_error(mysql)); 01256 } 01257 01258 if ((row = mysql_fetch_row(res)) && row[0]) 01259 { 01260 /* 01261 Concatenate all row results with tab in between to allow us to work 01262 with results from many columns (for example from SHOW VARIABLES) 01263 */ 01264 DYNAMIC_STRING result; 01265 uint i; 01266 ulong *lengths; 01267 char *end; 01268 #ifdef NOT_YET 01269 MYSQL_FIELD *fields= mysql_fetch_fields(res); 01270 #endif 01271 01272 init_dynamic_string(&result, "", 16384, 65536); 01273 lengths= mysql_fetch_lengths(res); 01274 for (i=0; i < mysql_num_fields(res); i++) 01275 { 01276 if (row[0]) 01277 { 01278 #ifdef NOT_YET 01279 /* Add to <var_name>_<col_name> */ 01280 uint j; 01281 char var_col_name[MAX_VAR_NAME]; 01282 uint length= snprintf(var_col_name, MAX_VAR_NAME, 01283 "$%s_%s", var->name, fields[i].name); 01284 /* Convert characters not allowed in variable names to '_' */ 01285 for (j= 1; j < length; j++) 01286 { 01287 if (!my_isvar(charset_info,var_col_name[j])) 01288 var_col_name[j]= '_'; 01289 } 01290 var_set(var_col_name, var_col_name + length, 01291 row[i], row[i] + lengths[i]); 01292 #endif 01293 /* Add column to tab separated string */ 01294 dynstr_append_mem(&result, row[i], lengths[i]); 01295 } 01296 dynstr_append_mem(&result, "\t", 1); 01297 } 01298 end= result.str + result.length-1; 01299 eval_expr(var, result.str, (const char**) &end); 01300 dynstr_free(&result); 01301 } 01302 else 01303 eval_expr(var, "", 0); 01304 01305 mysql_free_result(res); 01306 DBUG_RETURN(0); 01307 } 01308 01309 void var_copy(VAR *dest, VAR *src) 01310 { 01311 dest->int_val= src->int_val; 01312 dest->int_dirty= src->int_dirty; 01313 01314 /* Alloc/realloc data for str_val in dest */ 01315 if (dest->alloced_len < src->alloced_len && 01316 !(dest->str_val= dest->str_val 01317 ? my_realloc(dest->str_val, src->alloced_len, MYF(MY_WME)) 01318 : my_malloc(src->alloced_len, MYF(MY_WME)))) 01319 die("Out of memory"); 01320 else 01321 dest->alloced_len= src->alloced_len; 01322 01323 /* Copy str_val data to dest */ 01324 dest->str_val_len= src->str_val_len; 01325 if (src->str_val_len) 01326 memcpy(dest->str_val, src->str_val, src->str_val_len); 01327 } 01328 01329 int eval_expr(VAR* v, const char *p, const char** p_end) 01330 { 01331 VAR* vp; 01332 if (*p == '$') 01333 { 01334 if ((vp = var_get(p,p_end,0,0))) 01335 { 01336 var_copy(v, vp); 01337 return 0; 01338 } 01339 } 01340 else if (*p == '`') 01341 { 01342 return var_query_set(v, p, p_end); 01343 } 01344 else 01345 { 01346 int new_val_len = (p_end && *p_end) ? 01347 (int) (*p_end - p) : (int) strlen(p); 01348 if (new_val_len + 1 >= v->alloced_len) 01349 { 01350 v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ? 01351 MIN_VAR_ALLOC : new_val_len + 1; 01352 if (!(v->str_val = 01353 v->str_val ? my_realloc(v->str_val, v->alloced_len+1, 01354 MYF(MY_WME)) : 01355 my_malloc(v->alloced_len+1, MYF(MY_WME)))) 01356 die("Out of memory"); 01357 } 01358 v->str_val_len = new_val_len; 01359 memcpy(v->str_val, p, new_val_len); 01360 v->str_val[new_val_len] = 0; 01361 v->int_val=atoi(p); 01362 v->int_dirty=0; 01363 return 0; 01364 } 01365 01366 die("Invalid expr: %s", p); 01367 return 1; 01368 } 01369 01370 01371 enum enum_operator 01372 { 01373 DO_DEC, 01374 DO_INC 01375 }; 01376 01377 /* 01378 Decrease or increase the value of a variable 01379 01380 SYNOPSIS 01381 do_modify_var() 01382 query called command 01383 operator operation to perform on the var 01384 01385 DESCRIPTION 01386 dec $var_name 01387 inc $var_name 01388 01389 */ 01390 01391 int do_modify_var(struct st_query *query, 01392 enum enum_operator operator) 01393 { 01394 const char *p= query->first_argument; 01395 VAR* v; 01396 if (!*p) 01397 die("Missing argument to %.*s", query->first_word_len, query->query); 01398 if (*p != '$') 01399 die("The argument to %.*s must be a variable (start with $)", 01400 query->first_word_len, query->query); 01401 v= var_get(p, &p, 1, 0); 01402 switch (operator) { 01403 case DO_DEC: 01404 v->int_val--; 01405 break; 01406 case DO_INC: 01407 v->int_val++; 01408 break; 01409 default: 01410 die("Invalid operator to do_modify_var"); 01411 break; 01412 } 01413 v->int_dirty= 1; 01414 query->last_argument= (char*)++p; 01415 return 0; 01416 } 01417 01418 01419 /* 01420 Wrapper for 'system' function 01421 01422 NOTE 01423 If mysqltest is executed from cygwin shell, the command will be 01424 executed in the "windows command interpreter" cmd.exe and we prepend "sh" 01425 to make it be executed by cygwins "bash". Thus commands like "rm", 01426 "mkdir" as well as shellscripts can executed by "system" in Windows. 01427 01428 */ 01429 01430 int my_system(DYNAMIC_STRING* ds_cmd) 01431 { 01432 #ifdef __WIN__ 01433 /* Dump the command into a sh script file and execute with system */ 01434 str_to_file(tmp_sh_name, ds_cmd->str, ds_cmd->length); 01435 return system(tmp_sh_cmd); 01436 #else 01437 return system(ds_cmd->str); 01438 #endif 01439 } 01440 01441 01442 /* 01443 01444 SYNOPSIS 01445 do_system 01446 command called command 01447 01448 DESCRIPTION 01449 system <command> 01450 01451 Eval the query to expand any $variables in the command. 01452 Execute the command with the "system" command. 01453 01454 */ 01455 01456 void do_system(struct st_query *command) 01457 { 01458 DYNAMIC_STRING ds_cmd; 01459 DBUG_ENTER("do_system"); 01460 01461 if (strlen(command->first_argument) == 0) 01462 die("Missing arguments to system, nothing to do!"); 01463 01464 init_dynamic_string(&ds_cmd, 0, strlen(command->first_argument) + 64, 256); 01465 01466 /* Eval the system command, thus replacing all environment variables */ 01467 do_eval(&ds_cmd, command->first_argument, TRUE); 01468 01469 DBUG_PRINT("info", ("running system command '%s' as '%s'", 01470 command->first_argument, ds_cmd.str)); 01471 if (my_system(&ds_cmd)) 01472 { 01473 if (command->abort_on_error) 01474 die("system command '%s' failed", command->first_argument); 01475 01476 /* If ! abort_on_error, log message and continue */ 01477 dynstr_append(&ds_res, "system command '"); 01478 replace_dynstr_append(&ds_res, command->first_argument); 01479 dynstr_append(&ds_res, "' failed\n"); 01480 } 01481 01482 command->last_argument= command->end; 01483 dynstr_free(&ds_cmd); 01484 DBUG_VOID_RETURN; 01485 } 01486 01487 01488 /* 01489 Print the content between echo and <delimiter> to result file. 01490 Evaluate all variables in the string before printing, allow 01491 for variable names to be escaped using \ 01492 01493 SYNOPSIS 01494 do_echo() 01495 q called command 01496 01497 DESCRIPTION 01498 echo text 01499 Print the text after echo until end of command to result file 01500 01501 echo $<var_name> 01502 Print the content of the variable <var_name> to result file 01503 01504 echo Some text $<var_name> 01505 Print "Some text" plus the content of the variable <var_name> to 01506 result file 01507 01508 echo Some text \$<var_name> 01509 Print "Some text" plus $<var_name> to result file 01510 */ 01511 01512 int do_echo(struct st_query *command) 01513 { 01514 DYNAMIC_STRING *ds, ds_echo; 01515 01516 ds= &ds_res; 01517 01518 init_dynamic_string(&ds_echo, "", 256, 256); 01519 do_eval(&ds_echo, command->first_argument, FALSE); 01520 dynstr_append_mem(ds, ds_echo.str, ds_echo.length); 01521 dynstr_append_mem(ds, "\n", 1); 01522 dynstr_free(&ds_echo); 01523 command->last_argument= command->end; 01524 return(0); 01525 } 01526 01527 01528 int do_sync_with_master2(long offset) 01529 { 01530 MYSQL_RES* res; 01531 MYSQL_ROW row; 01532 MYSQL* mysql= &cur_con->mysql; 01533 char query_buf[FN_REFLEN+128]; 01534 int tries= 0; 01535 int rpl_parse; 01536 01537 if (!master_pos.file[0]) 01538 die("Calling 'sync_with_master' without calling 'save_master_pos'"); 01539 rpl_parse= mysql_rpl_parse_enabled(mysql); 01540 mysql_disable_rpl_parse(mysql); 01541 01542 sprintf(query_buf, "select master_pos_wait('%s', %ld)", master_pos.file, 01543 master_pos.pos + offset); 01544 01545 wait_for_position: 01546 01547 if (mysql_query(mysql, query_buf)) 01548 die("failed in %s: %d: %s", query_buf, mysql_errno(mysql), 01549 mysql_error(mysql)); 01550 01551 if (!(res= mysql_store_result(mysql))) 01552 die("mysql_store_result() returned NULL for '%s'", query_buf); 01553 if (!(row= mysql_fetch_row(res))) 01554 die("empty result in %s", query_buf); 01555 if (!row[0]) 01556 { 01557 /* 01558 It may be that the slave SQL thread has not started yet, though START 01559 SLAVE has been issued ? 01560 */ 01561 if (tries++ == 30) 01562 die("could not sync with master ('%s' returned NULL)", query_buf); 01563 sleep(1); /* So at most we will wait 30 seconds and make 31 tries */ 01564 mysql_free_result(res); 01565 goto wait_for_position; 01566 } 01567 mysql_free_result(res); 01568 if (rpl_parse) 01569 mysql_enable_rpl_parse(mysql); 01570 01571 return 0; 01572 } 01573 01574 int do_sync_with_master(struct st_query *query) 01575 { 01576 long offset= 0; 01577 char *p= query->first_argument; 01578 const char *offset_start= p; 01579 if (*offset_start) 01580 { 01581 for (; my_isdigit(charset_info, *p); p++) 01582 offset = offset * 10 + *p - '0'; 01583 01584 if(*p && !my_isspace(charset_info, *p)) 01585 die("Invalid integer argument \"%s\"", offset_start); 01586 query->last_argument= p; 01587 } 01588 return do_sync_with_master2(offset); 01589 } 01590 01591 /* 01592 when ndb binlog is on, this call will wait until last updated epoch 01593 (locally in the mysqld) has been received into the binlog 01594 */ 01595 int do_save_master_pos() 01596 { 01597 MYSQL_RES* res; 01598 MYSQL_ROW row; 01599 MYSQL* mysql = &cur_con->mysql; 01600 const char *query; 01601 int rpl_parse; 01602 01603 rpl_parse = mysql_rpl_parse_enabled(mysql); 01604 mysql_disable_rpl_parse(mysql); 01605 01606 #ifdef HAVE_NDB_BINLOG 01607 /* 01608 Wait for ndb binlog to be up-to-date with all changes 01609 done on the local mysql server 01610 */ 01611 { 01612 ulong have_ndbcluster; 01613 if (mysql_query(mysql, query= "show variables like 'have_ndbcluster'")) 01614 die("At line %u: failed in %s: %d: %s", start_lineno, query, 01615 mysql_errno(mysql), mysql_error(mysql)); 01616 if (!(res= mysql_store_result(mysql))) 01617 die("line %u: mysql_store_result() retuned NULL for '%s'", start_lineno, 01618 query); 01619 if (!(row= mysql_fetch_row(res))) 01620 die("line %u: empty result in %s", start_lineno, query); 01621 01622 have_ndbcluster= strcmp("YES", row[1]) == 0; 01623 mysql_free_result(res); 01624 01625 if (have_ndbcluster) 01626 { 01627 ulonglong epoch=0, tmp_epoch= 0; 01628 int count= 0; 01629 int do_continue= 1; 01630 while (do_continue) 01631 { 01632 const char binlog[]= "binlog"; 01633 const char latest_trans_epoch[]= 01634 "latest_trans_epoch="; 01635 const char latest_handled_binlog_epoch[]= 01636 "latest_handled_binlog_epoch="; 01637 if (count) 01638 sleep(1); 01639 if (mysql_query(mysql, query= "show engine ndb status")) 01640 die("At line %u: failed in '%s': %d: %s", start_lineno, query, 01641 mysql_errno(mysql), mysql_error(mysql)); 01642 if (!(res= mysql_store_result(mysql))) 01643 die("line %u: mysql_store_result() retuned NULL for '%s'", 01644 start_lineno, query); 01645 while ((row= mysql_fetch_row(res))) 01646 { 01647 if (strcmp(row[1], binlog) == 0) 01648 { 01649 const char *status= row[2]; 01650 /* latest_trans_epoch */ 01651 if (count == 0) 01652 { 01653 while (*status && strncmp(status, latest_trans_epoch, 01654 sizeof(latest_trans_epoch)-1)) 01655 status++; 01656 if (*status) 01657 { 01658 status+= sizeof(latest_trans_epoch)-1; 01659 epoch= strtoull(status, (char**) 0, 10); 01660 } 01661 else 01662 die("line %u: result does not contain '%s' in '%s'", 01663 start_lineno, latest_trans_epoch, query); 01664 } 01665 /* latest_applied_binlog_epoch */ 01666 while (*status && strncmp(status, latest_handled_binlog_epoch, 01667 sizeof(latest_handled_binlog_epoch)-1)) 01668 status++; 01669 if (*status) 01670 { 01671 status+= sizeof(latest_handled_binlog_epoch)-1; 01672 tmp_epoch= strtoull(status, (char**) 0, 10); 01673 } 01674 else 01675 die("line %u: result does not contain '%s' in '%s'", 01676 start_lineno, latest_handled_binlog_epoch, query); 01677 break; 01678 } 01679 } 01680 if (!row) 01681 die("line %u: result does not contain '%s' in '%s'", 01682 start_lineno, binlog, query); 01683 count++; 01684 if (tmp_epoch >= epoch) 01685 do_continue= 0; 01686 else if (count > 30) 01687 { 01688 break; 01689 } 01690 mysql_free_result(res); 01691 } 01692 } 01693 } 01694 #endif 01695 if (mysql_query(mysql, query= "show master status")) 01696 die("failed in show master status: %d: %s", 01697 mysql_errno(mysql), mysql_error(mysql)); 01698 01699 if (!(res = mysql_store_result(mysql))) 01700 die("mysql_store_result() retuned NULL for '%s'", query); 01701 if (!(row = mysql_fetch_row(res))) 01702 die("empty result in show master status"); 01703 strnmov(master_pos.file, row[0], sizeof(master_pos.file)-1); 01704 master_pos.pos = strtoul(row[1], (char**) 0, 10); 01705 mysql_free_result(res); 01706 01707 if (rpl_parse) 01708 mysql_enable_rpl_parse(mysql); 01709 01710 return 0; 01711 } 01712 01713 01714 /* 01715 Assign the variable <var_name> with <var_val> 01716 01717 SYNOPSIS 01718 do_let() 01719 query called command 01720 01721 DESCRIPTION 01722 let $<var_name>=<var_val><delimiter> 01723 01724 <var_name> - is the string string found between the $ and = 01725 <var_val> - is the content between the = and <delimiter>, it may span 01726 multiple line and contain any characters except <delimiter> 01727 <delimiter> - is a string containing of one or more chars, default is ; 01728 01729 RETURN VALUES 01730 Program will die if error detected 01731 */ 01732 01733 int do_let(struct st_query *query) 01734 { 01735 char *p= query->first_argument; 01736 char *var_name, *var_name_end, *var_val_start; 01737 01738 /* Find <var_name> */ 01739 if (!*p) 01740 die("Missing arguments to let"); 01741 var_name= p; 01742 while (*p && (*p != '=') && !my_isspace(charset_info,*p)) 01743 p++; 01744 var_name_end= p; 01745 if (var_name == var_name_end || 01746 (var_name+1 == var_name_end && *var_name == '$')) 01747 die("Missing variable name in let"); 01748 while (my_isspace(charset_info,*p)) 01749 p++; 01750 if (*p++ != '=') 01751 die("Missing assignment operator in let"); 01752 01753 /* Find start of <var_val> */ 01754 while (*p && my_isspace(charset_info,*p)) 01755 p++; 01756 var_val_start= p; 01757 query->last_argument= query->end; 01758 /* Assign var_val to var_name */ 01759 return var_set(var_name, var_name_end, var_val_start, query->end); 01760 } 01761 01762 01763 /* 01764 Store an integer (typically the returncode of the last SQL) 01765 statement in the mysqltest builtin variable $mysql_errno, by 01766 simulating of a user statement "let $mysql_errno= <integer>" 01767 */ 01768 01769 int var_set_errno(int sql_errno) 01770 { 01771 const char *var_name= "$mysql_errno"; 01772 char var_val[21]; 01773 uint length= my_sprintf(var_val, (var_val, "%d", sql_errno)); 01774 return var_set(var_name, var_name + 12, var_val, var_val + length); 01775 } 01776 01777 01778 int do_rpl_probe(struct st_query *query __attribute__((unused))) 01779 { 01780 DBUG_ENTER("do_rpl_probe"); 01781 if (mysql_rpl_probe(&cur_con->mysql)) 01782 die("Failed in mysql_rpl_probe(): '%s'", mysql_error(&cur_con->mysql)); 01783 DBUG_RETURN(0); 01784 } 01785 01786 01787 int do_enable_rpl_parse(struct st_query *query __attribute__((unused))) 01788 { 01789 mysql_enable_rpl_parse(&cur_con->mysql); 01790 return 0; 01791 } 01792 01793 01794 int do_disable_rpl_parse(struct st_query *query __attribute__((unused))) 01795 { 01796 mysql_disable_rpl_parse(&cur_con->mysql); 01797 return 0; 01798 } 01799 01800 01801 /* 01802 Sleep the number of specifed seconds 01803 01804 SYNOPSIS 01805 do_sleep() 01806 q called command 01807 real_sleep use the value from opt_sleep as number of seconds to sleep 01808 if real_sleep is false 01809 01810 DESCRIPTION 01811 sleep <seconds> 01812 real_sleep <seconds> 01813 01814 The difference between the sleep and real_sleep commands is that sleep 01815 uses the delay from the --sleep command-line option if there is one. 01816 (If the --sleep option is not given, the sleep command uses the delay 01817 specified by its argument.) The real_sleep command always uses the 01818 delay specified by its argument. The logic is that sometimes delays are 01819 cpu-dependent, and --sleep can be used to set this delay. real_sleep is 01820 used for cpu-independent delays. 01821 */ 01822 01823 int do_sleep(struct st_query *query, my_bool real_sleep) 01824 { 01825 int error= 0; 01826 char *p= query->first_argument; 01827 char *sleep_start, *sleep_end= query->end; 01828 double sleep_val; 01829 01830 while (my_isspace(charset_info, *p)) 01831 p++; 01832 if (!*p) 01833 die("Missing argument to %.*s", query->first_word_len, query->query); 01834 sleep_start= p; 01835 /* Check that arg starts with a digit, not handled by my_strtod */ 01836 if (!my_isdigit(charset_info, *sleep_start)) 01837 die("Invalid argument to %.*s \"%s\"", query->first_word_len, query->query, 01838 query->first_argument); 01839 sleep_val= my_strtod(sleep_start, &sleep_end, &error); 01840 if (error) 01841 die("Invalid argument to %.*s \"%s\"", query->first_word_len, query->query, 01842 query->first_argument); 01843 01844 /* Fixed sleep time selected by --sleep option */ 01845 if (opt_sleep >= 0 && !real_sleep) 01846 sleep_val= opt_sleep; 01847 01848 DBUG_PRINT("info", ("sleep_val: %f", sleep_val)); 01849 if (sleep_val) 01850 my_sleep((ulong) (sleep_val * 1000000L)); 01851 query->last_argument= sleep_end; 01852 return 0; 01853 } 01854 01855 static void get_file_name(char *filename, struct st_query *q) 01856 { 01857 char *p= q->first_argument, *name; 01858 if (!*p) 01859 die("Missing file name argument"); 01860 name= p; 01861 while (*p && !my_isspace(charset_info,*p)) 01862 p++; 01863 if (*p) 01864 *p++= 0; 01865 q->last_argument= p; 01866 strmake(filename, name, FN_REFLEN); 01867 } 01868 01869 static void set_charset(struct st_query *q) 01870 { 01871 char *charset_name= q->first_argument; 01872 char *p; 01873 01874 if (!charset_name || !*charset_name) 01875 die("Missing charset name in 'character_set'"); 01876 /* Remove end space */ 01877 p= charset_name; 01878 while (*p && !my_isspace(charset_info,*p)) 01879 p++; 01880 if(*p) 01881 *p++= 0; 01882 q->last_argument= p; 01883 charset_info= get_charset_by_csname(charset_name,MY_CS_PRIMARY,MYF(MY_WME)); 01884 if (!charset_info) 01885 abort_not_supported_test("Test requires charset '%s'", charset_name); 01886 } 01887 01888 static uint get_errcodes(match_err *to,struct st_query *q) 01889 { 01890 char *p= q->first_argument; 01891 uint count= 0; 01892 01893 DBUG_ENTER("get_errcodes"); 01894 01895 if (!*p) 01896 die("Missing argument in %s", q->query); 01897 01898 do 01899 { 01900 if (*p == 'S') 01901 { 01902 /* SQLSTATE string */ 01903 char *end= ++p + SQLSTATE_LENGTH; 01904 char *to_ptr= to[count].code.sqlstate; 01905 01906 for (; my_isalnum(charset_info, *p) && p != end; p++) 01907 *to_ptr++= *p; 01908 *to_ptr= 0; 01909 01910 to[count].type= ERR_SQLSTATE; 01911 } 01912 else if (*p == 'E') 01913 { 01914 /* SQL error as string */ 01915 st_error *e= global_error; 01916 char *start= p++; 01917 01918 for (; *p == '_' || my_isalnum(charset_info, *p); p++) 01919 ; 01920 for (; e->name; e++) 01921 { 01922 /* 01923 If we get a match, we need to check the length of the name we 01924 matched against in case it was longer than what we are checking 01925 (as in ER_WRONG_VALUE vs. ER_WRONG_VALUE_COUNT). 01926 */ 01927 if (!strncmp(start, e->name, (int) (p - start)) && 01928 (uint) strlen(e->name) == (uint) (p - start)) 01929 { 01930 to[count].code.errnum= (uint) e->code; 01931 to[count].type= ERR_ERRNO; 01932 break; 01933 } 01934 } 01935 if (!e->name) 01936 die("Unknown SQL error '%s'", start); 01937 } 01938 else 01939 { 01940 long val; 01941 01942 if (!(p= str2int(p,10,(long) INT_MIN, (long) INT_MAX, &val))) 01943 die("Invalid argument in %s", q->query); 01944 to[count].code.errnum= (uint) val; 01945 to[count].type= ERR_ERRNO; 01946 } 01947 count++; 01948 } while (*(p++) == ','); 01949 q->last_argument= (p - 1); 01950 to[count].type= ERR_EMPTY; /* End of data */ 01951 DBUG_RETURN(count); 01952 } 01953 01954 /* 01955 Get a string; Return ptr to end of string 01956 Strings may be surrounded by " or ' 01957 01958 If string is a '$variable', return the value of the variable. 01959 */ 01960 01961 01962 static char *get_string(char **to_ptr, char **from_ptr, 01963 struct st_query *q) 01964 { 01965 reg1 char c,sep; 01966 char *to= *to_ptr, *from= *from_ptr, *start=to; 01967 DBUG_ENTER("get_string"); 01968 01969 /* Find separator */ 01970 if (*from == '"' || *from == '\'') 01971 sep= *from++; 01972 else 01973 sep=' '; /* Separated with space */ 01974 01975 for ( ; (c=*from) ; from++) 01976 { 01977 if (c == '\\' && from[1]) 01978 { /* Escaped character */ 01979 /* We can't translate \0 -> ASCII 0 as replace can't handle ASCII 0 */ 01980 switch (*++from) { 01981 case 'n': 01982 *to++= '\n'; 01983 break; 01984 case 't': 01985 *to++= '\t'; 01986 break; 01987 case 'r': 01988 *to++ = '\r'; 01989 break; 01990 case 'b': 01991 *to++ = '\b'; 01992 break; 01993 case 'Z': /* ^Z must be escaped on Win32 */ 01994 *to++='\032'; 01995 break; 01996 default: 01997 *to++ = *from; 01998 break; 01999 } 02000 } 02001 else if (c == sep) 02002 { 02003 if (c == ' ' || c != *++from) 02004 break; /* Found end of string */ 02005 *to++=c; /* Copy duplicated separator */ 02006 } 02007 else 02008 *to++=c; 02009 } 02010 if (*from != ' ' && *from) 02011 die("Wrong string argument in %s", q->query); 02012 02013 while (my_isspace(charset_info,*from)) /* Point to next string */ 02014 from++; 02015 02016 *to =0; /* End of string marker */ 02017 *to_ptr= to+1; /* Store pointer to end */ 02018 *from_ptr= from; 02019 02020 /* Check if this was a variable */ 02021 if (*start == '$') 02022 { 02023 const char *end= to; 02024 VAR *var=var_get(start, &end, 0, 1); 02025 if (var && to == (char*) end+1) 02026 { 02027 DBUG_PRINT("info",("var: '%s' -> '%s'", start, var->str_val)); 02028 DBUG_RETURN(var->str_val); /* return found variable value */ 02029 } 02030 } 02031 DBUG_RETURN(start); 02032 } 02033 02034 /* 02035 Finds the next (non-escaped) '/' in the expression. 02036 (If the character '/' is needed, it can be escaped using '\'.) 02037 */ 02038 02039 #define PARSE_REGEX_ARG \ 02040 while (p < expr_end) \ 02041 {\ 02042 char c= *p;\ 02043 if (c == '/')\ 02044 {\ 02045 if (last_c == '\\')\ 02046 {\ 02047 buf_p[-1]= '/';\ 02048 }\ 02049 else\ 02050 {\ 02051 *buf_p++ = 0;\ 02052 break;\ 02053 } \ 02054 } \ 02055 else\ 02056 *buf_p++ = c;\ 02057 \ 02058 last_c= c;\ 02059 p++;\ 02060 } \ 02061 02062 /* 02063 Initializes the regular substitution expression to be used in the 02064 result output of test. 02065 02066 Returns: st_replace_regex struct with pairs of substitutions 02067 */ 02068 02069 static struct st_replace_regex* init_replace_regex(char* expr) 02070 { 02071 struct st_replace_regex* res; 02072 char* buf,*expr_end; 02073 char* p; 02074 char* buf_p; 02075 uint expr_len= strlen(expr); 02076 char last_c = 0; 02077 struct st_regex reg; 02078 02079 /* my_malloc() will die on fail with MY_FAE */ 02080 res=(struct st_replace_regex*)my_malloc( 02081 sizeof(*res)+expr_len ,MYF(MY_FAE+MY_WME)); 02082 my_init_dynamic_array(&res->regex_arr,sizeof(struct st_regex),128,128); 02083 02084 buf= (char*)res + sizeof(*res); 02085 expr_end= expr + expr_len; 02086 p= expr; 02087 buf_p= buf; 02088 02089 /* for each regexp substitution statement */ 02090 while (p < expr_end) 02091 { 02092 bzero(®,sizeof(reg)); 02093 /* find the start of the statement */ 02094 while (p < expr_end) 02095 { 02096 if (*p == '/') 02097 break; 02098 p++; 02099 } 02100 02101 if (p == expr_end || ++p == expr_end) 02102 { 02103 if (res->regex_arr.elements) 02104 break; 02105 else 02106 goto err; 02107 } 02108 /* we found the start */ 02109 reg.pattern= buf_p; 02110 02111 /* Find first argument -- pattern string to be removed */ 02112 PARSE_REGEX_ARG 02113 02114 if (p == expr_end || ++p == expr_end) 02115 goto err; 02116 02117 /* buf_p now points to the replacement pattern terminated with \0 */ 02118 reg.replace= buf_p; 02119 02120 /* Find second argument -- replace string to replace pattern */ 02121 PARSE_REGEX_ARG 02122 02123 if (p == expr_end) 02124 goto err; 02125 02126 /* skip the ending '/' in the statement */ 02127 p++; 02128 02129 /* Check if we should do matching case insensitive */ 02130 if (p < expr_end && *p == 'i') 02131 reg.icase= 1; 02132 02133 /* done parsing the statement, now place it in regex_arr */ 02134 if (insert_dynamic(&res->regex_arr,(gptr) ®)) 02135 die("Out of memory"); 02136 } 02137 res->odd_buf_len= res->even_buf_len= 8192; 02138 res->even_buf= (char*)my_malloc(res->even_buf_len,MYF(MY_WME+MY_FAE)); 02139 res->odd_buf= (char*)my_malloc(res->odd_buf_len,MYF(MY_WME+MY_FAE)); 02140 res->buf= res->even_buf; 02141 02142 return res; 02143 02144 err: 02145 my_free((gptr)res,0); 02146 die("Error parsing replace_regex \"%s\"", expr); 02147 return 0; 02148 } 02149 02150 /* 02151 Execute all substitutions on val. 02152 02153 Returns: true if substituition was made, false otherwise 02154 Side-effect: Sets r->buf to be the buffer with all substitutions done. 02155 02156 IN: 02157 struct st_replace_regex* r 02158 char* val 02159 Out: 02160 struct st_replace_regex* r 02161 r->buf points at the resulting buffer 02162 r->even_buf and r->odd_buf might have been reallocated 02163 r->even_buf_len and r->odd_buf_len might have been changed 02164 02165 TODO: at some point figure out if there is a way to do everything 02166 in one pass 02167 */ 02168 02169 static int multi_reg_replace(struct st_replace_regex* r,char* val) 02170 { 02171 uint i; 02172 char* in_buf, *out_buf; 02173 int* buf_len_p; 02174 02175 in_buf= val; 02176 out_buf= r->even_buf; 02177 buf_len_p= &r->even_buf_len; 02178 r->buf= 0; 02179 02180 /* For each substitution, do the replace */ 02181 for (i= 0; i < r->regex_arr.elements; i++) 02182 { 02183 struct st_regex re; 02184 char* save_out_buf= out_buf; 02185 02186 get_dynamic(&r->regex_arr,(gptr)&re,i); 02187 02188 if (!reg_replace(&out_buf, buf_len_p, re.pattern, re.replace, 02189 in_buf, re.icase)) 02190 { 02191 /* if the buffer has been reallocated, make adjustements */ 02192 if (save_out_buf != out_buf) 02193 { 02194 if (save_out_buf == r->even_buf) 02195 r->even_buf= out_buf; 02196 else 02197 r->odd_buf= out_buf; 02198 } 02199 02200 r->buf= out_buf; 02201 if (in_buf == val) 02202 in_buf= r->odd_buf; 02203 02204 swap_variables(char*,in_buf,out_buf); 02205 02206 buf_len_p= (out_buf == r->even_buf) ? &r->even_buf_len : 02207 &r->odd_buf_len; 02208 } 02209 } 02210 02211 return (r->buf == 0); 02212 } 02213 02214 /* 02215 Parse the regular expression to be used in all result files 02216 from now on. 02217 02218 The syntax is --replace_regex /from/to/i /from/to/i ... 02219 i means case-insensitive match. If omitted, the match is 02220 case-sensitive 02221 02222 */ 02223 static void get_replace_regex(struct st_query *q) 02224 { 02225 char *expr= q->first_argument; 02226 free_replace_regex(); 02227 if (!(glob_replace_regex=init_replace_regex(expr))) 02228 die("Could not init replace_regex"); 02229 q->last_argument= q->end; 02230 } 02231 02232 02233 /* 02234 Get arguments for replace. The syntax is: 02235 replace from to [from to ...] 02236 Where each argument may be quoted with ' or " 02237 A argument may also be a variable, in which case the value of the 02238 variable is replaced. 02239 */ 02240 02241 static void get_replace(struct st_query *q) 02242 { 02243 uint i; 02244 char *from= q->first_argument; 02245 char *buff,*start; 02246 char word_end_chars[256],*pos; 02247 POINTER_ARRAY to_array,from_array; 02248 DBUG_ENTER("get_replace"); 02249 02250 free_replace(); 02251 02252 bzero((char*) &to_array,sizeof(to_array)); 02253 bzero((char*) &from_array,sizeof(from_array)); 02254 if (!*from) 02255 die("Missing argument in %s", q->query); 02256 start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE)); 02257 while (*from) 02258 { 02259 char *to=buff; 02260 to=get_string(&buff, &from, q); 02261 if (!*from) 02262 die("Wrong number of arguments to replace_result in '%s'", q->query); 02263 insert_pointer_name(&from_array,to); 02264 to=get_string(&buff, &from, q); 02265 insert_pointer_name(&to_array,to); 02266 } 02267 for (i=1,pos=word_end_chars ; i < 256 ; i++) 02268 if (my_isspace(charset_info,i)) 02269 *pos++= i; 02270 *pos=0; /* End pointer */ 02271 if (!(glob_replace=init_replace((char**) from_array.typelib.type_names, 02272 (char**) to_array.typelib.type_names, 02273 (uint) from_array.typelib.count, 02274 word_end_chars))) 02275 die("Can't initialize replace from '%s'", q->query); 02276 free_pointer_array(&from_array); 02277 free_pointer_array(&to_array); 02278 my_free(start, MYF(0)); 02279 q->last_argument= q->end; 02280 DBUG_VOID_RETURN; 02281 } 02282 02283 static void free_replace_regex() 02284 { 02285 if (glob_replace_regex) 02286 { 02287 my_free(glob_replace_regex->even_buf,MYF(MY_ALLOW_ZERO_PTR)); 02288 my_free(glob_replace_regex->odd_buf,MYF(MY_ALLOW_ZERO_PTR)); 02289 my_free((char*) glob_replace_regex,MYF(0)); 02290 glob_replace_regex=0; 02291 } 02292 } 02293 02294 02295 void free_replace() 02296 { 02297 DBUG_ENTER("free_replace"); 02298 if (glob_replace) 02299 { 02300 my_free((char*) glob_replace,MYF(0)); 02301 glob_replace=0; 02302 } 02303 DBUG_VOID_RETURN; 02304 } 02305 02306 struct connection * find_connection_by_name(const char *name) 02307 { 02308 struct connection *con; 02309 for (con= cons; con < next_con; con++) 02310 { 02311 if (!strcmp(con->name, name)) 02312 { 02313 return con; 02314 } 02315 } 02316 return 0; /* Connection not found */ 02317 } 02318 02319 02320 int select_connection_name(const char *name) 02321 { 02322 DBUG_ENTER("select_connection2"); 02323 DBUG_PRINT("enter",("name: '%s'", name)); 02324 02325 if (!(cur_con= find_connection_by_name(name))) 02326 die("connection '%s' not found in connection pool", name); 02327 DBUG_RETURN(0); 02328 } 02329 02330 02331 int select_connection(struct st_query *query) 02332 { 02333 char *name; 02334 char *p= query->first_argument; 02335 DBUG_ENTER("select_connection"); 02336 02337 if (!*p) 02338 die("Missing connection name in connect"); 02339 name= p; 02340 while (*p && !my_isspace(charset_info,*p)) 02341 p++; 02342 if (*p) 02343 *p++= 0; 02344 query->last_argument= p; 02345 return select_connection_name(name); 02346 } 02347 02348 02349 int close_connection(struct st_query *q) 02350 { 02351 char *p= q->first_argument, *name; 02352 struct connection *con; 02353 DBUG_ENTER("close_connection"); 02354 DBUG_PRINT("enter",("name: '%s'",p)); 02355 02356 if (!*p) 02357 die("Missing connection name in disconnect"); 02358 name= p; 02359 while (*p && !my_isspace(charset_info,*p)) 02360 p++; 02361 02362 if (*p) 02363 *p++= 0; 02364 q->last_argument= p; 02365 for (con= cons; con < next_con; con++) 02366 { 02367 if (!strcmp(con->name, name)) 02368 { 02369 #ifndef EMBEDDED_LIBRARY 02370 if (q->type == Q_DIRTY_CLOSE) 02371 { 02372 if (con->mysql.net.vio) 02373 { 02374 vio_delete(con->mysql.net.vio); 02375 con->mysql.net.vio = 0; 02376 } 02377 } 02378 #endif 02379 mysql_close(&con->mysql); 02380 if (con->util_mysql) 02381 mysql_close(con->util_mysql); 02382 con->util_mysql= 0; 02383 my_free(con->name, MYF(0)); 02384 /* 02385 When the connection is closed set name to "closed_connection" 02386 to make it possible to reuse the connection name. 02387 The connection slot will not be reused 02388 */ 02389 if (!(con->name = my_strdup("closed_connection", MYF(MY_WME)))) 02390 die("Out of memory"); 02391 DBUG_RETURN(0); 02392 } 02393 } 02394 die("connection '%s' not found in connection pool", name); 02395 DBUG_RETURN(1); /* Never reached */ 02396 } 02397 02398 02399 /* 02400 This one now is a hack - we may want to improve in in the 02401 future to handle quotes. For now we assume that anything that is not 02402 a comma, a space or ) belongs to the argument. space is a chopper, comma or 02403 ) are delimiters/terminators 02404 02405 SYNOPSIS 02406 safe_get_param 02407 str - string to get param from 02408 arg - pointer to string where result will be stored 02409 msg - Message to display if param is not found 02410 if msg is 0 this param is not required and param may be empty 02411 02412 RETURNS 02413 pointer to str after param 02414 02415 */ 02416 02417 char* safe_get_param(char *str, char** arg, const char *msg) 02418 { 02419 DBUG_ENTER("safe_get_param"); 02420 if(!*str) 02421 { 02422 if (msg) 02423 die(msg); 02424 *arg= str; 02425 DBUG_RETURN(str); 02426 } 02427 while (*str && my_isspace(charset_info,*str)) 02428 str++; 02429 *arg= str; 02430 while (*str && *str != ',' && *str != ')') 02431 str++; 02432 if (msg && !*arg) 02433 die(msg); 02434 02435 *str++= 0; 02436 DBUG_RETURN(str); 02437 } 02438 02439 02440 /* 02441 Connect to a server doing several retries if needed. 02442 02443 SYNOPSIS 02444 safe_connect() 02445 con - connection structure to be used 02446 host, user, pass, - connection parameters 02447 db, port, sock 02448 02449 NOTE 02450 02451 Sometimes in a test the client starts before 02452 the server - to solve the problem, we try again 02453 after some sleep if connection fails the first 02454 time 02455 02456 This function will try to connect to the given server 02457 "opt_max_connect_retries" times and sleep "connection_retry_sleep" 02458 seconds between attempts before finally giving up. 02459 This helps in situation when the client starts 02460 before the server (which happens sometimes). 02461 It will ignore any errors during these retries. One should use 02462 connect_n_handle_errors() if he expects a connection error and wants 02463 handle as if it was an error from a usual statement. 02464 02465 RETURN VALUE 02466 0 - success, non-0 - failure 02467 */ 02468 02469 int safe_connect(MYSQL* mysql, const char *host, const char *user, 02470 const char *pass, const char *db, int port, const char *sock) 02471 { 02472 int con_error= 1; 02473 my_bool reconnect= 1; 02474 static ulong connection_retry_sleep= 100000; /* Microseconds */ 02475 int i; 02476 for (i= 0; i < opt_max_connect_retries; i++) 02477 { 02478 if (mysql_real_connect(mysql, host,user, pass, db, port, sock, 02479 CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS)) 02480 { 02481 con_error= 0; 02482 break; 02483 } 02484 my_sleep(connection_retry_sleep); 02485 } 02486 /* 02487 TODO: change this to 0 in future versions, but the 'kill' test relies on 02488 existing behavior 02489 */ 02490 mysql_options(mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect); 02491 return con_error; 02492 } 02493 02494 02495 /* 02496 Connect to a server and handle connection errors in case they occur. 02497 02498 SYNOPSIS 02499 connect_n_handle_errors() 02500 q - context of connect "query" (command) 02501 con - connection structure to be used 02502 host, user, pass, - connection parameters 02503 db, port, sock 02504 create_conn - out parameter, set to zero if connection was 02505 not established and is not touched otherwise 02506 02507 DESCRIPTION 02508 This function will try to establish a connection to server and handle 02509 possible errors in the same manner as if "connect" was usual SQL-statement 02510 (If error is expected it will ignore it once it occurs and log the 02511 "statement" to the query log). 02512 Unlike safe_connect() it won't do several attempts. 02513 02514 RETURN VALUE 02515 0 - success, non-0 - failure 02516 */ 02517 02518 int connect_n_handle_errors(struct st_query *q, MYSQL* con, const char* host, 02519 const char* user, const char* pass, 02520 const char* db, int port, const char* sock, 02521 int* create_conn) 02522 { 02523 DYNAMIC_STRING *ds; 02524 my_bool reconnect= 1; 02525 int error= 0; 02526 02527 ds= &ds_res; 02528 02529 if (!disable_query_log) 02530 { 02531 /* 02532 It is nice to have connect() statement logged in result file 02533 in this case. 02534 QQ: Should we do this only if we are expecting an error ? 02535 */ 02536 char port_buff[22]; /* This should be enough for any int */ 02537 char *port_end; 02538 dynstr_append_mem(ds, "connect(", 8); 02539 replace_dynstr_append(ds, host); 02540 dynstr_append_mem(ds, ",", 1); 02541 replace_dynstr_append(ds, user); 02542 dynstr_append_mem(ds, ",", 1); 02543 replace_dynstr_append(ds, pass); 02544 dynstr_append_mem(ds, ",", 1); 02545 if (db) 02546 replace_dynstr_append(ds, db); 02547 dynstr_append_mem(ds, ",", 1); 02548 port_end= int10_to_str(port, port_buff, 10); 02549 replace_dynstr_append_mem(ds, port_buff, port_end - port_buff); 02550 dynstr_append_mem(ds, ",", 1); 02551 if (sock) 02552 replace_dynstr_append(ds, sock); 02553 dynstr_append_mem(ds, ")", 1); 02554 dynstr_append_mem(ds, delimiter, delimiter_length); 02555 dynstr_append_mem(ds, "\n", 1); 02556 } 02557 if (!mysql_real_connect(con, host, user, pass, db, port, sock ? sock: 0, 02558 CLIENT_MULTI_STATEMENTS)) 02559 { 02560 handle_error("connect", q, mysql_errno(con), mysql_error(con), 02561 mysql_sqlstate(con), ds); 02562 *create_conn= 0; 02563 goto err; 02564 } 02565 02566 handle_no_error(q); 02567 02568 /* 02569 TODO: change this to 0 in future versions, but the 'kill' test relies on 02570 existing behavior 02571 */ 02572 mysql_options(con, MYSQL_OPT_RECONNECT, (char *)&reconnect); 02573 02574 err: 02575 free_replace(); 02576 free_replace_regex(); 02577 return error; 02578 } 02579 02580 02581 /* 02582 Open a new connection to MySQL Server with the parameters 02583 specified 02584 02585 SYNOPSIS 02586 do_connect() 02587 q called command 02588 02589 DESCRIPTION 02590 connect(<name>,<host>,<user>,<pass>,<db>,[<port>,<sock>[<opts>]]); 02591 02592 <name> - name of the new connection 02593 <host> - hostname of server 02594 <user> - user to connect as 02595 <pass> - password used when connecting 02596 <db> - initial db when connected 02597 <port> - server port 02598 <sock> - server socket 02599 <opts> - options to use for the connection 02600 SSL - use SSL if available 02601 COMPRESS - use compression if available 02602 02603 */ 02604 02605 int do_connect(struct st_query *q) 02606 { 02607 char *con_name, *con_user,*con_pass, *con_host, *con_port_str, 02608 *con_db, *con_sock, *con_options; 02609 char *con_buf, *p; 02610 char buff[FN_REFLEN]; 02611 int con_port; 02612 bool con_ssl= 0; 02613 bool con_compress= 0; 02614 int free_con_sock= 0; 02615 int error= 0; 02616 int create_conn= 1; 02617 VAR *var_port, *var_sock; 02618 02619 DBUG_ENTER("do_connect"); 02620 DBUG_PRINT("enter",("connect: %s", q->first_argument)); 02621 02622 /* Make a copy of query before parsing, safe_get_param will modify */ 02623 if (!(con_buf= my_strdup(q->first_argument, MYF(MY_WME)))) 02624 die("Could not allocate con_buf"); 02625 p= con_buf; 02626 02627 if (*p != '(') 02628 die("Syntax error in connect - expected '(' found '%c'", *p); 02629 p++; 02630 p= safe_get_param(p, &con_name, "Missing connection name"); 02631 p= safe_get_param(p, &con_host, "Missing connection host"); 02632 p= safe_get_param(p, &con_user, "Missing connection user"); 02633 p= safe_get_param(p, &con_pass, "Missing connection password"); 02634 p= safe_get_param(p, &con_db, "Missing connection db"); 02635 02636 /* Port */ 02637 p= safe_get_param(p, &con_port_str, 0); 02638 if (*con_port_str) 02639 { 02640 if (*con_port_str == '$') 02641 { 02642 if (!(var_port= var_get(con_port_str, 0, 0, 0))) 02643 die("Unknown variable '%s'", con_port_str+1); 02644 con_port= var_port->int_val; 02645 } 02646 else 02647 { 02648 con_port= atoi(con_port_str); 02649 if (con_port == 0) 02650 die("Illegal argument for port: '%s'", con_port_str); 02651 } 02652 } 02653 else 02654 { 02655 con_port= port; 02656 } 02657 02658 /* Sock */ 02659 p= safe_get_param(p, &con_sock, 0); 02660 if (*con_sock) 02661 { 02662 if (*con_sock == '$') 02663 { 02664 if (!(var_sock= var_get(con_sock, 0, 0, 0))) 02665 die("Unknown variable '%s'", con_sock+1); 02666 if (!(con_sock= (char*)my_malloc(var_sock->str_val_len+1, MYF(0)))) 02667 die("Out of memory"); 02668 free_con_sock= 1; 02669 memcpy(con_sock, var_sock->str_val, var_sock->str_val_len); 02670 con_sock[var_sock->str_val_len]= 0; 02671 } 02672 } 02673 else 02674 { 02675 con_sock= (char*) unix_sock; 02676 } 02677 02678 /* Options */ 02679 p= safe_get_param(p, &con_options, 0); 02680 while (*con_options) 02681 { 02682 char* str= con_options; 02683 while (*str && !my_isspace(charset_info, *str)) 02684 str++; 02685 *str++= 0; 02686 if (!strcmp(con_options, "SSL")) 02687 con_ssl= 1; 02688 else if (!strcmp(con_options, "COMPRESS")) 02689 con_compress= 1; 02690 else 02691 die("Illegal option to connect: %s", con_options); 02692 con_options= str; 02693 } 02694 /* Note: 'p' is pointing into the copy 'con_buf' */ 02695 q->last_argument= q->first_argument + (p - con_buf); 02696 02697 if (next_con == cons_end) 02698 die("Connection limit exhausted - increase MAX_CONS in mysqltest.c"); 02699 02700 if (find_connection_by_name(con_name)) 02701 die("Connection %s already exists", con_name); 02702 02703 if (!mysql_init(&next_con->mysql)) 02704 die("Failed on mysql_init()"); 02705 if (opt_compress || con_compress) 02706 mysql_options(&next_con->mysql, MYSQL_OPT_COMPRESS, NullS); 02707 mysql_options(&next_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0); 02708 mysql_options(&next_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name); 02709 02710 #ifdef HAVE_OPENSSL 02711 if (opt_use_ssl || con_ssl) 02712 { 02713 /* Turn on ssl_verify_server_cert only if host is "localhost" */ 02714 opt_ssl_verify_server_cert= !strcmp(con_host, "localhost"); 02715 02716 mysql_ssl_set(&next_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, 02717 opt_ssl_capath, opt_ssl_cipher); 02718 mysql_options(&next_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, 02719 &opt_ssl_verify_server_cert); 02720 } 02721 #endif 02722 if (con_sock && !free_con_sock && *con_sock && *con_sock != FN_LIBCHAR) 02723 con_sock=fn_format(buff, con_sock, TMPDIR, "", 0); 02724 if (!con_db[0]) 02725 con_db= db; 02726 /* Special database to allow one to connect without a database name */ 02727 if (con_db && !strcmp(con_db,"*NO-ONE*")) 02728 con_db= 0; 02729 if (q->abort_on_error) 02730 { 02731 if (safe_connect(&next_con->mysql, con_host, con_user, con_pass, 02732 con_db, con_port, con_sock ? con_sock: 0)) 02733 die("Could not open connection '%s': %d %s", con_name, 02734 mysql_errno(&next_con->mysql), mysql_error(&next_con->mysql)); 02735 } 02736 else 02737 error= connect_n_handle_errors(q, &next_con->mysql, con_host, con_user, 02738 con_pass, con_db, con_port, con_sock, 02739 &create_conn); 02740 02741 if (create_conn) 02742 { 02743 if (!(next_con->name= my_strdup(con_name, MYF(MY_WME)))) 02744 die(NullS); 02745 cur_con= next_con++; 02746 } 02747 if (free_con_sock) 02748 my_free(con_sock, MYF(MY_WME)); 02749 my_free(con_buf, MYF(MY_WME)); 02750 DBUG_RETURN(error); 02751 } 02752 02753 02754 int do_done(struct st_query *q) 02755 { 02756 /* Check if empty block stack */ 02757 if (cur_block == block_stack) 02758 { 02759 if (*q->query != '}') 02760 die("Stray 'end' command - end of block before beginning"); 02761 die("Stray '}' - end of block before beginning"); 02762 } 02763 02764 /* Test if inner block has been executed */ 02765 if (cur_block->ok && cur_block->cmd == cmd_while) 02766 { 02767 /* Pop block from stack, re-execute outer block */ 02768 cur_block--; 02769 parser.current_line = cur_block->line; 02770 } 02771 else 02772 { 02773 /* Pop block from stack, goto next line */ 02774 cur_block--; 02775 parser.current_line++; 02776 } 02777 return 0; 02778 } 02779 02780 02781 /* 02782 Process start of a "if" or "while" statement 02783 02784 SYNOPSIS 02785 do_block() 02786 cmd Type of block 02787 q called command 02788 02789 DESCRIPTION 02790 if ([!]<expr>) 02791 { 02792 <block statements> 02793 } 02794 02795 while ([!]<expr>) 02796 { 02797 <block statements> 02798 } 02799 02800 Evaluates the <expr> and if it evaluates to 02801 greater than zero executes the following code block. 02802 A '!' can be used before the <expr> to indicate it should 02803 be executed if it evaluates to zero. 02804 02805 */ 02806 02807 void do_block(enum block_cmd cmd, struct st_query* q) 02808 { 02809 char *p= q->first_argument; 02810 const char *expr_start, *expr_end; 02811 VAR v; 02812 const char *cmd_name= (cmd == cmd_while ? "while" : "if"); 02813 my_bool not_expr= FALSE; 02814 DBUG_ENTER("do_block"); 02815 DBUG_PRINT("enter", ("%s", cmd_name)); 02816 02817 /* Check stack overflow */ 02818 if (cur_block == block_stack_end) 02819 die("Nesting too deeply"); 02820 02821 /* Set way to find outer block again, increase line counter */ 02822 cur_block->line= parser.current_line++; 02823 02824 /* If this block is ignored */ 02825 if (!cur_block->ok) 02826 { 02827 /* Inner block should be ignored too */ 02828 cur_block++; 02829 cur_block->cmd= cmd; 02830 cur_block->ok= FALSE; 02831 DBUG_VOID_RETURN; 02832 } 02833 02834 /* Parse and evaluate test expression */ 02835 expr_start= strchr(p, '('); 02836 if (!expr_start++) 02837 die("missing '(' in %s", cmd_name); 02838 02839 /* Check for !<expr> */ 02840 if (*expr_start == '!') 02841 { 02842 not_expr= TRUE; 02843 expr_start++; /* Step past the '!' */ 02844 } 02845 /* Find ending ')' */ 02846 expr_end= strrchr(expr_start, ')'); 02847 if (!expr_end) 02848 die("missing ')' in %s", cmd_name); 02849 p= (char*)expr_end+1; 02850 02851 while (*p && my_isspace(charset_info, *p)) 02852 p++; 02853 if (*p == '{') 02854 die("Missing newline between %s and '{'", cmd_name); 02855 if (*p) 02856 die("Missing '{' after %s. Found \"%s\"", cmd_name, p); 02857 02858 var_init(&v,0,0,0,0); 02859 eval_expr(&v, expr_start, &expr_end); 02860 02861 /* Define inner block */ 02862 cur_block++; 02863 cur_block->cmd= cmd; 02864 cur_block->ok= (v.int_val ? TRUE : FALSE); 02865 02866 if (not_expr) 02867 cur_block->ok = !cur_block->ok; 02868 02869 DBUG_PRINT("info", ("OK: %d", cur_block->ok)); 02870 02871 var_free(&v); 02872 DBUG_VOID_RETURN; 02873 } 02874 02875 02876 /* 02877 Read characters from line buffer or file. This is needed to allow 02878 my_ungetc() to buffer MAX_DELIMITER characters for a file 02879 02880 NOTE: 02881 This works as long as one doesn't change files (with 'source file_name') 02882 when there is things pushed into the buffer. This should however not 02883 happen for any tests in the test suite. 02884 */ 02885 02886 int my_getc(FILE *file) 02887 { 02888 if (line_buffer_pos == line_buffer) 02889 return fgetc(file); 02890 return *--line_buffer_pos; 02891 } 02892 02893 void my_ungetc(int c) 02894 { 02895 *line_buffer_pos++= (char) c; 02896 } 02897 02898 02899 my_bool end_of_query(int c) 02900 { 02901 uint i; 02902 char tmp[MAX_DELIMITER]; 02903 02904 if (c != *delimiter) 02905 return 0; 02906 02907 for (i= 1; i < delimiter_length && 02908 (c= my_getc(cur_file->file)) == *(delimiter + i); 02909 i++) 02910 tmp[i]= c; 02911 02912 if (i == delimiter_length) 02913 return 1; /* Found delimiter */ 02914 02915 /* didn't find delimiter, push back things that we read */ 02916 my_ungetc(c); 02917 while (i > 1) 02918 my_ungetc(tmp[--i]); 02919 return 0; 02920 } 02921 02922 02923 /* 02924 Read one "line" from the file 02925 02926 SYNOPSIS 02927 read_line 02928 buf buffer for the read line 02929 size size of the buffer i.e max size to read 02930 02931 DESCRIPTION 02932 This function actually reads several lines and adds them to the 02933 buffer buf. It continues to read until it finds what it believes 02934 is a complete query. 02935 02936 Normally that means it will read lines until it reaches the 02937 "delimiter" that marks end of query. Default delimiter is ';' 02938 The function should be smart enough not to detect delimiter's 02939 found inside strings surrounded with '"' and '\'' escaped strings. 02940 02941 If the first line in a query starts with '#' or '-' this line is treated 02942 as a comment. A comment is always terminated when end of line '\n' is 02943 reached. 02944 02945 */ 02946 02947 int read_line(char *buf, int size) 02948 { 02949 int c; 02950 char quote; 02951 char *p= buf, *buf_end= buf + size - 1; 02952 int no_save= 0; 02953 enum {R_NORMAL, R_Q, R_Q_IN_Q, R_SLASH_IN_Q, 02954 R_COMMENT, R_LINE_START} state= R_LINE_START; 02955 DBUG_ENTER("read_line"); 02956 LINT_INIT(quote); 02957 02958 start_lineno= cur_file->lineno; 02959 for (; p < buf_end ;) 02960 { 02961 no_save= 0; 02962 c= my_getc(cur_file->file); 02963 if (feof(cur_file->file)) 02964 { 02965 found_eof: 02966 if (cur_file->file != stdin) 02967 { 02968 my_fclose(cur_file->file, MYF(0)); 02969 cur_file->file= 0; 02970 } 02971 my_free((gptr)cur_file->file_name, MYF(MY_ALLOW_ZERO_PTR)); 02972 cur_file->file_name= 0; 02973 if (cur_file == file_stack) 02974 { 02975 /* We're back at the first file, check if 02976 all { have matching } 02977 */ 02978 if (cur_block != block_stack) 02979 die("Missing end of block"); 02980 02981 DBUG_PRINT("info", ("end of file")); 02982 DBUG_RETURN(1); 02983 } 02984 cur_file--; 02985 start_lineno= cur_file->lineno; 02986 continue; 02987 } 02988 02989 if (c == '\n') 02990 { 02991 /* Line counting is independent of state */ 02992 cur_file->lineno++; 02993 02994 /* Convert cr/lf to lf */ 02995 if (p != buf && *(p-1) == '\r') 02996 *(p-1)= 0; 02997 } 02998 02999 switch(state) { 03000 case R_NORMAL: 03001 /* Only accept '{' in the beginning of a line */ 03002 if (end_of_query(c)) 03003 { 03004 *p= 0; 03005 DBUG_RETURN(0); 03006 } 03007 else if (c == '\'' || c == '"' || c == '`') 03008 { 03009 quote= c; 03010 state= R_Q; 03011 } 03012 else if (c == '\n') 03013 { 03014 state = R_LINE_START; 03015 } 03016 break; 03017 case R_COMMENT: 03018 if (c == '\n') 03019 { 03020 *p= 0; 03021 DBUG_RETURN(0); 03022 } 03023 break; 03024 case R_LINE_START: 03025 /* Only accept start of comment if this is the first line in query */ 03026 if ((cur_file->lineno == start_lineno) && 03027 (c == '#' || c == '-' || parsing_disabled)) 03028 { 03029 state = R_COMMENT; 03030 } 03031 else if (my_isspace(charset_info, c)) 03032 { 03033 if (c == '\n') 03034 start_lineno= cur_file->lineno; /* Query hasn't started yet */ 03035 no_save= 1; 03036 } 03037 else if (c == '}') 03038 { 03039 *buf++= '}'; 03040 *buf= 0; 03041 DBUG_RETURN(0); 03042 } 03043 else if (end_of_query(c) || c == '{') 03044 { 03045 *p= 0; 03046 DBUG_RETURN(0); 03047 } 03048 else if (c == '\'' || c == '"' || c == '`') 03049 { 03050 quote= c; 03051 state= R_Q; 03052 } 03053 else 03054 state= R_NORMAL; 03055 break; 03056 03057 case R_Q: 03058 if (c == quote) 03059 state= R_Q_IN_Q; 03060 else if (c == '\\') 03061 state= R_SLASH_IN_Q; 03062 break; 03063 case R_Q_IN_Q: 03064 if (end_of_query(c)) 03065 { 03066 *p= 0; 03067 DBUG_RETURN(0); 03068 } 03069 if (c != quote) 03070 state= R_NORMAL; 03071 else 03072 state= R_Q; 03073 break; 03074 case R_SLASH_IN_Q: 03075 state= R_Q; 03076 break; 03077 03078 } 03079 03080 if (!no_save) 03081 { 03082 /* Could be a multibyte character */ 03083 /* This code is based on the code in "sql_load.cc" */ 03084 #ifdef USE_MB 03085 int charlen = my_mbcharlen(charset_info, c); 03086 /* We give up if multibyte character is started but not */ 03087 /* completed before we pass buf_end */ 03088 if ((charlen > 1) && (p + charlen) <= buf_end) 03089 { 03090 int i; 03091 char* mb_start = p; 03092 03093 *p++ = c; 03094 03095 for (i= 1; i < charlen; i++) 03096 { 03097 if (feof(cur_file->file)) 03098 goto found_eof; /* FIXME: could we just break here?! */ 03099 c= my_getc(cur_file->file); 03100 *p++ = c; 03101 } 03102 if (! my_ismbchar(charset_info, mb_start, p)) 03103 { 03104 /* It was not a multiline char, push back the characters */ 03105 /* We leave first 'c', i.e. pretend it was a normal char */ 03106 while (p > mb_start) 03107 my_ungetc(*--p); 03108 } 03109 } 03110 else 03111 #endif 03112 *p++= c; 03113 } 03114 } 03115 *p= 0; /* Always end with \0 */ 03116 DBUG_RETURN(feof(cur_file->file)); 03117 } 03118 03119 /* 03120 Create a query from a set of lines 03121 03122 SYNOPSIS 03123 read_query() 03124 q_ptr pointer where to return the new query 03125 03126 DESCRIPTION 03127 Converts lines returned by read_line into a query, this involves 03128 parsing the first word in the read line to find the query type. 03129 03130 03131 A -- comment may contain a valid query as the first word after the 03132 comment start. Thus it's always checked to see if that is the case. 03133 The advantage with this approach is to be able to execute commands 03134 terminated by new line '\n' regardless how many "delimiter" it contain. 03135 03136 */ 03137 03138 static char read_query_buf[MAX_QUERY]; 03139 03140 int read_query(struct st_query** q_ptr) 03141 { 03142 char *p= read_query_buf; 03143 struct st_query* q; 03144 DBUG_ENTER("read_query"); 03145 03146 if (parser.current_line < parser.read_lines) 03147 { 03148 get_dynamic(&q_lines, (gptr) q_ptr, parser.current_line) ; 03149 DBUG_RETURN(0); 03150 } 03151 if (!(*q_ptr= q= (struct st_query*) my_malloc(sizeof(*q), MYF(MY_WME))) || 03152 insert_dynamic(&q_lines, (gptr) &q)) 03153 die(NullS); 03154 03155 q->record_file[0]= 0; 03156 q->require_file= 0; 03157 q->first_word_len= 0; 03158 03159 q->type= Q_UNKNOWN; 03160 q->query_buf= q->query= 0; 03161 read_query_buf[0]= 0; 03162 if (read_line(read_query_buf, sizeof(read_query_buf))) 03163 { 03164 check_eol_junk(read_query_buf); 03165 DBUG_RETURN(1); 03166 } 03167 03168 DBUG_PRINT("info", ("query: %s", read_query_buf)); 03169 if (*p == '#') 03170 { 03171 q->type= Q_COMMENT; 03172 /* This goto is to avoid losing the "expected error" info. */ 03173 goto end; 03174 } 03175 if (!parsing_disabled) 03176 { 03177 memcpy((gptr) q->expected_errno, (gptr) global_expected_errno, 03178 sizeof(global_expected_errno)); 03179 q->expected_errors= global_expected_errors; 03180 q->abort_on_error= (global_expected_errors == 0 && abort_on_error); 03181 } 03182 03183 if (p[0] == '-' && p[1] == '-') 03184 { 03185 q->type= Q_COMMENT_WITH_COMMAND; 03186 p+= 2; /* To calculate first word */ 03187 } 03188 else if (!parsing_disabled) 03189 { 03190 while (*p && my_isspace(charset_info, *p)) 03191 p++ ; 03192 } 03193 03194 end: 03195 while (*p && my_isspace(charset_info, *p)) 03196 p++; 03197 03198 if (!(q->query_buf= q->query= my_strdup(p, MYF(MY_WME)))) 03199 die(NullS); 03200 03201 /* Calculate first word and first argument */ 03202 for (p= q->query; *p && !my_isspace(charset_info, *p) ; p++) ; 03203 q->first_word_len= (uint) (p - q->query); 03204 while (*p && my_isspace(charset_info, *p)) 03205 p++; 03206 q->first_argument= p; 03207 q->end= strend(q->query); 03208 parser.read_lines++; 03209 DBUG_RETURN(0); 03210 } 03211 03212 03213 static struct my_option my_long_options[] = 03214 { 03215 {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 03216 0, 0, 0, 0, 0, 0}, 03217 {"basedir", 'b', "Basedir for tests.", (gptr*) &opt_basedir, 03218 (gptr*) &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 03219 {"big-test", 'B', "Define BIG_TEST to 1.", (gptr*) &opt_big_test, 03220 (gptr*) &opt_big_test, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 03221 {"compress", 'C', "Use the compressed server/client protocol.", 03222 (gptr*) &opt_compress, (gptr*) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0, 03223 0, 0, 0}, 03224 {"cursor-protocol", OPT_CURSOR_PROTOCOL, "Use cursors for prepared statements.", 03225 (gptr*) &cursor_protocol, (gptr*) &cursor_protocol, 0, 03226 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 03227 {"database", 'D', "Database to use.", (gptr*) &db, (gptr*) &db, 0, 03228 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 03229 #ifdef DBUG_OFF 03230 {"debug", '#', "This is a non-debug version. Catch this and exit", 03231 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, 03232 #else 03233 {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.", 03234 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, 03235 #endif 03236 {"host", 'h', "Connect to host.", (gptr*) &host, (gptr*) &host, 0, 03237 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 03238 {"include", 'i', "Include SQL before each test case.", (gptr*) &opt_include, 03239 (gptr*) &opt_include, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 03240 {"mark-progress", OPT_MARK_PROGRESS, 03241 "Write linenumber and elapsed time to <testname>.progress ", 03242 (gptr*) &opt_mark_progress, (gptr*) &opt_mark_progress, 0, 03243 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 03244 {"max-connect-retries", OPT_MAX_CONNECT_RETRIES, 03245 "Max number of connection attempts when connecting to server", 03246 (gptr*) &opt_max_connect_retries, (gptr*) &opt_max_connect_retries, 0, 03247 GET_INT, REQUIRED_ARG, 500, 1, 10000, 0, 0, 0}, 03248 {"password", 'p', "Password to use when connecting to server.", 03249 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, 03250 {"port", 'P', "Port number to use for connection.", (gptr*) &port, 03251 (gptr*) &port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 03252 {"ps-protocol", OPT_PS_PROTOCOL, "Use prepared statements protocol for communication", 03253 (gptr*) &ps_protocol, (gptr*) &ps_protocol, 0, 03254 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 03255 {"quiet", 's', "Suppress all normal output.", (gptr*) &silent, 03256 (gptr*) &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 03257 {"record", 'r', "Record output of test_file into result file.", 03258 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 03259 {"result-file", 'R', "Read/Store result from/in this file.", 03260 (gptr*) &result_file, (gptr*) &result_file, 0, GET_STR, REQUIRED_ARG, 03261 0, 0, 0, 0, 0, 0}, 03262 {"server-arg", 'A', "Send option value to embedded server as a parameter.", 03263 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 03264 {"server-file", 'F', "Read embedded server arguments from file.", 03265 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 03266 {"silent", 's', "Suppress all normal output. Synonym for --quiet.", 03267 (gptr*) &silent, (gptr*) &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 03268 {"skip-safemalloc", OPT_SKIP_SAFEMALLOC, 03269 "Don't use the memory allocation checking.", 0, 0, 0, GET_NO_ARG, NO_ARG, 03270 0, 0, 0, 0, 0, 0}, 03271 {"sleep", 'T', "Sleep always this many seconds on sleep commands.", 03272 (gptr*) &opt_sleep, (gptr*) &opt_sleep, 0, GET_INT, REQUIRED_ARG, -1, 0, 0, 03273 0, 0, 0}, 03274 {"socket", 'S', "Socket file to use for connection.", 03275 (gptr*) &unix_sock, (gptr*) &unix_sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 03276 0, 0, 0}, 03277 {"sp-protocol", OPT_SP_PROTOCOL, "Use stored procedures for select", 03278 (gptr*) &sp_protocol, (gptr*) &sp_protocol, 0, 03279 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 03280 #include "sslopt-longopts.h" 03281 {"test-file", 'x', "Read test from/in this file (default stdin).", 03282 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 03283 {"timer-file", 'm', "File where the timing in micro seconds is stored.", 03284 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 03285 {"tmpdir", 't', "Temporary directory where sockets are put.", 03286 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 03287 {"user", 'u', "User for login.", (gptr*) &user, (gptr*) &user, 0, GET_STR, 03288 REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 03289 {"verbose", 'v', "Write more.", (gptr*) &verbose, (gptr*) &verbose, 0, 03290 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 03291 {"version", 'V', "Output version information and exit.", 03292 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 03293 {"view-protocol", OPT_VIEW_PROTOCOL, "Use views for select", 03294 (gptr*) &view_protocol, (gptr*) &view_protocol, 0, 03295 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 03296 { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} 03297 }; 03298 03299 03300 #include <help_start.h> 03301 03302 static void print_version(void) 03303 { 03304 printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname,MTEST_VERSION, 03305 MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE); 03306 } 03307 03308 void usage() 03309 { 03310 print_version(); 03311 printf("MySQL AB, by Sasha, Matt, Monty & Jani\n"); 03312 printf("This software comes with ABSOLUTELY NO WARRANTY\n\n"); 03313 printf("Runs a test against the mysql server and compares output with a results file.\n\n"); 03314 printf("Usage: %s [OPTIONS] [database] < test_file\n", my_progname); 03315 my_print_help(my_long_options); 03316 printf(" --no-defaults Don't read default options from any options file.\n"); 03317 my_print_variables(my_long_options); 03318 } 03319 03320 #include <help_end.h> 03321 03322 03323 static my_bool 03324 get_one_option(int optid, const struct my_option *opt __attribute__((unused)), 03325 char *argument) 03326 { 03327 switch(optid) { 03328 case '#': 03329 #ifndef DBUG_OFF 03330 DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysqltest.trace"); 03331 #endif 03332 break; 03333 case 'r': 03334 record = 1; 03335 break; 03336 case 'x': 03337 { 03338 char buff[FN_REFLEN]; 03339 if (!test_if_hard_path(argument)) 03340 { 03341 strxmov(buff, opt_basedir, argument, NullS); 03342 argument= buff; 03343 } 03344 fn_format(buff, argument, "", "", MY_UNPACK_FILENAME); 03345 DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0); 03346 if (!(cur_file->file= 03347 my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0)))) 03348 die("Could not open %s: errno = %d", buff, errno); 03349 cur_file->file_name= my_strdup(buff, MYF(MY_FAE)); 03350 cur_file->lineno= 1; 03351 break; 03352 } 03353 case 'm': 03354 { 03355 static char buff[FN_REFLEN]; 03356 if (!test_if_hard_path(argument)) 03357 { 03358 strxmov(buff, opt_basedir, argument, NullS); 03359 argument= buff; 03360 } 03361 fn_format(buff, argument, "", "", MY_UNPACK_FILENAME); 03362 timer_file= buff; 03363 unlink(timer_file); /* Ignore error, may not exist */ 03364 break; 03365 } 03366 case 'p': 03367 if (argument) 03368 { 03369 my_free(pass, MYF(MY_ALLOW_ZERO_PTR)); 03370 pass= my_strdup(argument, MYF(MY_FAE)); 03371 while (*argument) *argument++= 'x'; /* Destroy argument */ 03372 tty_password= 0; 03373 } 03374 else 03375 tty_password= 1; 03376 break; 03377 #include <sslopt-case.h> 03378 case 't': 03379 strnmov(TMPDIR, argument, sizeof(TMPDIR)); 03380 break; 03381 case 'A': 03382 if (!embedded_server_arg_count) 03383 { 03384 embedded_server_arg_count=1; 03385 embedded_server_args[0]= (char*) ""; 03386 } 03387 if (embedded_server_arg_count == MAX_SERVER_ARGS-1 || 03388 !(embedded_server_args[embedded_server_arg_count++]= 03389 my_strdup(argument, MYF(MY_FAE)))) 03390 { 03391 die("Can't use server argument"); 03392 } 03393 break; 03394 case 'F': 03395 if (read_server_arguments(argument)) 03396 die(NullS); 03397 break; 03398 case OPT_SKIP_SAFEMALLOC: 03399 #ifdef SAFEMALLOC 03400 sf_malloc_quick=1; 03401 #endif 03402 break; 03403 case 'V': 03404 print_version(); 03405 exit(0); 03406 case '?': 03407 usage(); 03408 exit(1); 03409 } 03410 return 0; 03411 } 03412 03413 03414 int parse_args(int argc, char **argv) 03415 { 03416 load_defaults("my",load_default_groups,&argc,&argv); 03417 default_argv= argv; 03418 03419 if ((handle_options(&argc, &argv, my_long_options, get_one_option))) 03420 exit(1); 03421 03422 if (argc > 1) 03423 { 03424 usage(); 03425 exit(1); 03426 } 03427 if (argc == 1) 03428 db= *argv; 03429 if (tty_password) 03430 pass=get_tty_password(NullS); 03431 03432 return 0; 03433 } 03434 03435 03436 /* 03437 Write the content of str into file 03438 03439 SYNOPSIS 03440 str_to_file 03441 fname - name of file to truncate/create and write to 03442 str - content to write to file 03443 size - size of content witten to file 03444 */ 03445 03446 static void str_to_file(const char *fname, char *str, int size) 03447 { 03448 int fd; 03449 char buff[FN_REFLEN]; 03450 if (!test_if_hard_path(fname)) 03451 { 03452 strxmov(buff, opt_basedir, fname, NullS); 03453 fname= buff; 03454 } 03455 fn_format(buff, fname, "", "", MY_UNPACK_FILENAME); 03456 03457 if ((fd= my_open(buff, O_WRONLY | O_CREAT | O_TRUNC, 03458 MYF(MY_WME | MY_FFNF))) < 0) 03459 die("Could not open %s: errno = %d", buff, errno); 03460 if (my_write(fd, (byte*)str, size, MYF(MY_WME|MY_FNABP))) 03461 die("write failed"); 03462 my_close(fd, MYF(0)); 03463 } 03464 03465 03466 void dump_result_to_reject_file(const char *record_file, char *buf, int size) 03467 { 03468 char reject_file[FN_REFLEN]; 03469 str_to_file(fn_format(reject_file, record_file, "", ".reject", 03470 MY_REPLACE_EXT), 03471 buf, size); 03472 } 03473 03474 void dump_result_to_log_file(const char *record_file, char *buf, int size) 03475 { 03476 char log_file[FN_REFLEN]; 03477 str_to_file(fn_format(log_file, record_file, "", ".log", 03478 MY_REPLACE_EXT), 03479 buf, size); 03480 } 03481 03482 void dump_progress(const char *record_file) 03483 { 03484 char log_file[FN_REFLEN]; 03485 str_to_file(fn_format(log_file, record_file, "", ".progress", 03486 MY_REPLACE_EXT), 03487 ds_progress.str, ds_progress.length); 03488 } 03489 03490 static void check_regerr(my_regex_t* r, int err) 03491 { 03492 char err_buf[1024]; 03493 03494 if (err) 03495 { 03496 my_regerror(err,r,err_buf,sizeof(err_buf)); 03497 die("Regex error: %s\n", err_buf); 03498 } 03499 } 03500 03501 /* 03502 auxiluary macro used by reg_replace 03503 makes sure the result buffer has sufficient length 03504 */ 03505 #define SECURE_REG_BUF if (buf_len < need_buf_len)\ 03506 {\ 03507 int off= res_p - buf;\ 03508 buf= (char*)my_realloc(buf,need_buf_len,MYF(MY_WME+MY_FAE));\ 03509 res_p= buf + off;\ 03510 buf_len= need_buf_len;\ 03511 }\ 03512 03513 /* 03514 Performs a regex substitution 03515 03516 IN: 03517 03518 buf_p - result buffer pointer. Will change if reallocated 03519 buf_len_p - result buffer length. Will change if the buffer is reallocated 03520 pattern - regexp pattern to match 03521 replace - replacement expression 03522 string - the string to perform substituions in 03523 icase - flag, if set to 1 the match is case insensitive 03524 */ 03525 static int reg_replace(char** buf_p, int* buf_len_p, char *pattern, 03526 char *replace, char *string, int icase) 03527 { 03528 my_regex_t r; 03529 my_regmatch_t* subs; 03530 char* buf_end, *replace_end; 03531 char* buf= *buf_p; 03532 int len; 03533 int buf_len,need_buf_len; 03534 int cflags= REG_EXTENDED; 03535 int err_code; 03536 char* res_p,*str_p,*str_end; 03537 03538 buf_len= *buf_len_p; 03539 len= strlen(string); 03540 str_end= string + len; 03541 03542 /* start with a buffer of a reasonable size that hopefully will not 03543 need to be reallocated 03544 */ 03545 need_buf_len= len * 2 + 1; 03546 res_p= buf; 03547 03548 SECURE_REG_BUF 03549 03550 buf_end = buf + buf_len; 03551 03552 if (icase) 03553 cflags |= REG_ICASE; 03554 03555 if ((err_code=my_regcomp(&r,pattern,cflags,&my_charset_latin1))) 03556 { 03557 check_regerr(&r,err_code); 03558 return 1; 03559 } 03560 03561 subs= (my_regmatch_t*)my_malloc(sizeof(my_regmatch_t) * (r.re_nsub+1), 03562 MYF(MY_WME+MY_FAE)); 03563 03564 *res_p= 0; 03565 str_p= string; 03566 replace_end= replace + strlen(replace); 03567 03568 /* for each pattern match instance perform a replacement */ 03569 while (!err_code) 03570 { 03571 /* find the match */ 03572 err_code= my_regexec(&r,str_p, r.re_nsub+1, subs, 03573 (str_p == string) ? REG_NOTBOL : 0); 03574 03575 /* if regular expression error (eg. bad syntax, or out of memory) */ 03576 if (err_code && err_code != REG_NOMATCH) 03577 { 03578 check_regerr(&r,err_code); 03579 my_regfree(&r); 03580 return 1; 03581 } 03582 03583 /* if match found */ 03584 if (!err_code) 03585 { 03586 char* expr_p= replace; 03587 int c; 03588 03589 /* 03590 we need at least what we have so far in the buffer + the part 03591 before this match 03592 */ 03593 need_buf_len= (res_p - buf) + subs[0].rm_so; 03594 03595 /* on this pass, calculate the memory for the result buffer */ 03596 while (expr_p < replace_end) 03597 { 03598 int back_ref_num= -1; 03599 c= *expr_p; 03600 03601 if (c == '\\' && expr_p + 1 < replace_end) 03602 { 03603 back_ref_num= expr_p[1] - '0'; 03604 } 03605 03606 /* found a valid back_ref (eg. \1)*/ 03607 if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub) 03608 { 03609 int start_off,end_off; 03610 if ((start_off=subs[back_ref_num].rm_so) > -1 && 03611 (end_off=subs[back_ref_num].rm_eo) > -1) 03612 { 03613 need_buf_len += (end_off - start_off); 03614 } 03615 expr_p += 2; 03616 } 03617 else 03618 { 03619 expr_p++; 03620 need_buf_len++; 03621 } 03622 } 03623 need_buf_len++; 03624 /* 03625 now that we know the size of the buffer, 03626 make sure it is big enough 03627 */ 03628 SECURE_REG_BUF 03629 03630 /* copy the pre-match part */ 03631 if (subs[0].rm_so) 03632 { 03633 memcpy(res_p,str_p,subs[0].rm_so); 03634 res_p += subs[0].rm_so; 03635 } 03636 03637 expr_p= replace; 03638 03639 /* copy the match and expand back_refs */ 03640 while (expr_p < replace_end) 03641 { 03642 int back_ref_num= -1; 03643 c= *expr_p; 03644 03645 if (c == '\\' && expr_p + 1 < replace_end) 03646 { 03647 back_ref_num= expr_p[1] - '0'; 03648 } 03649 03650 if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub) 03651 { 03652 int start_off,end_off; 03653 if ((start_off=subs[back_ref_num].rm_so) > -1 && 03654 (end_off=subs[back_ref_num].rm_eo) > -1) 03655 { 03656 int block_len= end_off - start_off; 03657 memcpy(res_p,str_p + start_off, block_len); 03658 res_p += block_len; 03659 } 03660 expr_p += 2; 03661 } 03662 else 03663 { 03664 *res_p++ = *expr_p++; 03665 } 03666 } 03667 03668 /* handle the post-match part */ 03669 if (subs[0].rm_so == subs[0].rm_eo) 03670 { 03671 if (str_p + subs[0].rm_so >= str_end) 03672 break; 03673 str_p += subs[0].rm_eo ; 03674 *res_p++ = *str_p++; 03675 } 03676 else 03677 { 03678 str_p += subs[0].rm_eo; 03679 } 03680 } 03681 else /* no match this time, just copy the string as is */ 03682 { 03683 int left_in_str= str_end-str_p; 03684 need_buf_len= (res_p-buf) + left_in_str; 03685 SECURE_REG_BUF 03686 memcpy(res_p,str_p,left_in_str); 03687 res_p += left_in_str; 03688 str_p= str_end; 03689 } 03690 } 03691 my_regfree(&r); 03692 *res_p= 0; 03693 *buf_p= buf; 03694 *buf_len_p= buf_len; 03695 return 0; 03696 } 03697 03698 03699 #ifdef __WIN__ 03700 03701 DYNAMIC_ARRAY patterns; 03702 03703 /* 03704 init_win_path_patterns 03705 03706 DESCRIPTION 03707 Setup string patterns that will be used to detect filenames that 03708 needs to be converted from Win to Unix format 03709 03710 */ 03711 03712 static void init_win_path_patterns() 03713 { 03714 /* List of string patterns to match in order to find paths */ 03715 const char* paths[] = { "$MYSQL_TEST_DIR", 03716 "$MYSQL_TMP_DIR", 03717 "./test/", 0 }; 03718 int num_paths= 3; 03719 int i; 03720 char* p; 03721 03722 DBUG_ENTER("init_win_path_patterns"); 03723 03724 my_init_dynamic_array(&patterns, sizeof(const char*), 16, 16); 03725 03726 /* Loop through all paths in the array */ 03727 for (i= 0; i < num_paths; i++) 03728 { 03729 VAR* v; 03730 if (*(paths[i]) == '$') 03731 { 03732 v= var_get(paths[i], 0, 0, 0); 03733 p= my_strdup(v->str_val, MYF(MY_FAE)); 03734 } 03735 else 03736 p= my_strdup(paths[i], MYF(MY_FAE)); 03737 03738 if (insert_dynamic(&patterns, (gptr) &p)) 03739 die(NullS); 03740 03741 DBUG_PRINT("info", ("p: %s", p)); 03742 while (*p) 03743 { 03744 if (*p == '/') 03745 *p='\\'; 03746 p++; 03747 } 03748 } 03749 DBUG_VOID_RETURN; 03750 } 03751 03752 static void free_win_path_patterns() 03753 { 03754 uint i= 0; 03755 for (i=0 ; i < patterns.elements ; i++) 03756 { 03757 const char** pattern= dynamic_element(&patterns, i, const char**); 03758 my_free((gptr) *pattern, MYF(0)); 03759 } 03760 delete_dynamic(&patterns); 03761 } 03762 03763 /* 03764 fix_win_paths 03765 03766 DESCRIPTION 03767 Search the string 'val' for the patterns that are known to be 03768 strings that contain filenames. Convert all \ to / in the 03769 filenames that are found. 03770 03771 Ex: 03772 val = 'Error "c:\mysql\mysql-test\var\test\t1.frm" didn't exist' 03773 => $MYSQL_TEST_DIR is found by strstr 03774 => all \ from c:\mysql\m... until next space is converted into / 03775 */ 03776 03777 static void fix_win_paths(const char* val, int len) 03778 { 03779 uint i; 03780 char *p; 03781 03782 DBUG_ENTER("fix_win_paths"); 03783 for (i= 0; i < patterns.elements; i++) 03784 { 03785 const char** pattern= dynamic_element(&patterns, i, const char**); 03786 DBUG_PRINT("info", ("pattern: %s", *pattern)); 03787 if (strlen(*pattern) == 0) continue; 03788 /* Search for the path in string */ 03789 while ((p= strstr(val, *pattern))) 03790 { 03791 DBUG_PRINT("info", ("Found %s in val p: %s", *pattern, p)); 03792 03793 while (*p && !my_isspace(charset_info, *p)) 03794 { 03795 if (*p == '\\') 03796 *p= '/'; 03797 p++; 03798 } 03799 DBUG_PRINT("info", ("Converted \\ to /, p: %s", p)); 03800 } 03801 } 03802 DBUG_PRINT("exit", (" val: %s, len: %d", val, len)); 03803 DBUG_VOID_RETURN; 03804 } 03805 #endif 03806 03807 /* Append the string to ds, with optional replace */ 03808 static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, 03809 const char *val, int len) 03810 { 03811 #ifdef __WIN__ 03812 fix_win_paths(val, len); 03813 #endif 03814 03815 if (glob_replace_regex) 03816 { 03817 if (!multi_reg_replace(glob_replace_regex, (char*)val)) 03818 { 03819 val= glob_replace_regex->buf; 03820 len= strlen(val); 03821 } 03822 } 03823 03824 if (glob_replace) 03825 replace_strings_append(glob_replace, ds, val, len); 03826 else 03827 dynstr_append_mem(ds, val, len); 03828 } 03829 03830 03831 /* Append zero-terminated string to ds, with optional replace */ 03832 static void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val) 03833 { 03834 replace_dynstr_append_mem(ds, val, strlen(val)); 03835 } 03836 03837 03838 /* 03839 Append the result for one field to the dynamic string ds 03840 */ 03841 03842 static void append_field(DYNAMIC_STRING *ds, uint col_idx, MYSQL_FIELD* field, 03843 const char* val, ulonglong len, bool is_null) 03844 { 03845 if (col_idx < max_replace_column && replace_column[col_idx]) 03846 { 03847 val= replace_column[col_idx]; 03848 len= strlen(val); 03849 } 03850 else if (is_null) 03851 { 03852 val= "NULL"; 03853 len= 4; 03854 } 03855 #ifdef __WIN__ 03856 else if ((field->type == MYSQL_TYPE_DOUBLE || 03857 field->type == MYSQL_TYPE_FLOAT ) && 03858 field->decimals >= 31) 03859 { 03860 /* Convert 1.2e+018 to 1.2e+18 and 1.2e-018 to 1.2e-18 */ 03861 char *start= strchr(val, 'e'); 03862 if (start && strlen(start) >= 5 && 03863 (start[1] == '-' || start[1] == '+') && start[2] == '0') 03864 { 03865 start+=2; /* Now points at first '0' */ 03866 /* Move all chars after the first '0' one step left */ 03867 memmove(start, start + 1, strlen(start)); 03868 len--; 03869 } 03870 } 03871 #endif 03872 03873 if (!display_result_vertically) 03874 { 03875 if (col_idx) 03876 dynstr_append_mem(ds, "\t", 1); 03877 replace_dynstr_append_mem(ds, val, (int)len); 03878 } 03879 else 03880 { 03881 dynstr_append(ds, field->name); 03882 dynstr_append_mem(ds, "\t", 1); 03883 replace_dynstr_append_mem(ds, val, (int)len); 03884 dynstr_append_mem(ds, "\n", 1); 03885 } 03886 } 03887 03888 03889 /* 03890 Append all results to the dynamic string separated with '\t' 03891 Values may be converted with 'replace_column' 03892 */ 03893 03894 static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res) 03895 { 03896 MYSQL_ROW row; 03897 uint num_fields= mysql_num_fields(res); 03898 MYSQL_FIELD *fields= mysql_fetch_fields(res); 03899 ulong *lengths; 03900 03901 while ((row = mysql_fetch_row(res))) 03902 { 03903 uint i; 03904 lengths = mysql_fetch_lengths(res); 03905 for (i = 0; i < num_fields; i++) 03906 append_field(ds, i, &fields[i], 03907 (const char*)row[i], lengths[i], !row[i]); 03908 if (!display_result_vertically) 03909 dynstr_append_mem(ds, "\n", 1); 03910 } 03911 free_replace_column(); 03912 } 03913 03914 03915 /* 03916 Append all results from ps execution to the dynamic string separated 03917 with '\t'. Values may be converted with 'replace_column' 03918 */ 03919 03920 static void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, 03921 MYSQL_FIELD *fields, uint num_fields) 03922 { 03923 MYSQL_BIND *bind; 03924 my_bool *is_null; 03925 ulong *length; 03926 uint i; 03927 03928 /* Allocate array with bind structs, lengths and NULL flags */ 03929 bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND), 03930 MYF(MY_WME | MY_FAE | MY_ZEROFILL)); 03931 length= (ulong*) my_malloc(num_fields * sizeof(ulong), 03932 MYF(MY_WME | MY_FAE)); 03933 is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool), 03934 MYF(MY_WME | MY_FAE)); 03935 03936 /* Allocate data for the result of each field */ 03937 for (i= 0; i < num_fields; i++) 03938 { 03939 uint max_length= fields[i].max_length + 1; 03940 bind[i].buffer_type= MYSQL_TYPE_STRING; 03941 bind[i].buffer= (char *)my_malloc(max_length, MYF(MY_WME | MY_FAE)); 03942 bind[i].buffer_length= max_length; 03943 bind[i].is_null= &is_null[i]; 03944 bind[i].length= &length[i]; 03945 03946 DBUG_PRINT("bind", ("col[%d]: buffer_type: %d, buffer_length: %d", 03947 i, bind[i].buffer_type, bind[i].buffer_length)); 03948 } 03949 03950 if (mysql_stmt_bind_result(stmt, bind)) 03951 die("mysql_stmt_bind_result failed: %d: %s", 03952 mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); 03953 03954 while (mysql_stmt_fetch(stmt) == 0) 03955 { 03956 for (i= 0; i < num_fields; i++) 03957 append_field(ds, i, &fields[i], (const char *) bind[i].buffer, 03958 *bind[i].length, *bind[i].is_null); 03959 if (!display_result_vertically) 03960 dynstr_append_mem(ds, "\n", 1); 03961 } 03962 03963 if (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) 03964 die("fetch didn't end with MYSQL_NO_DATA from statement: %d %s", 03965 mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); 03966 03967 free_replace_column(); 03968 03969 for (i= 0; i < num_fields; i++) 03970 { 03971 /* Free data for output */ 03972 my_free((gptr)bind[i].buffer, MYF(MY_WME | MY_FAE)); 03973 } 03974 /* Free array with bind structs, lengths and NULL flags */ 03975 my_free((gptr)bind , MYF(MY_WME | MY_FAE)); 03976 my_free((gptr)length , MYF(MY_WME | MY_FAE)); 03977 my_free((gptr)is_null , MYF(MY_WME | MY_FAE)); 03978 } 03979 03980 03981 /* 03982 Append metadata for fields to output 03983 */ 03984 03985 static void append_metadata(DYNAMIC_STRING *ds, 03986 MYSQL_FIELD *field, 03987 uint num_fields) 03988 { 03989 MYSQL_FIELD *field_end; 03990 dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\t" 03991 "Column_alias\tType\tLength\tMax length\tIs_null\t" 03992 "Flags\tDecimals\tCharsetnr\n"); 03993 03994 for (field_end= field+num_fields ; 03995 field < field_end ; 03996 field++) 03997 { 03998 char buff[22]; 03999 dynstr_append_mem(ds, field->catalog, 04000 field->catalog_length); 04001 dynstr_append_mem(ds, "\t", 1); 04002 dynstr_append_mem(ds, field->db, field->db_length); 04003 dynstr_append_mem(ds, "\t", 1); 04004 dynstr_append_mem(ds, field->org_table, 04005 field->org_table_length); 04006 dynstr_append_mem(ds, "\t", 1); 04007 dynstr_append_mem(ds, field->table, 04008 field->table_length); 04009 dynstr_append_mem(ds, "\t", 1); 04010 dynstr_append_mem(ds, field->org_name, 04011 field->org_name_length); 04012 dynstr_append_mem(ds, "\t", 1); 04013 dynstr_append_mem(ds, field->name, field->name_length); 04014 dynstr_append_mem(ds, "\t", 1); 04015 int10_to_str((int) field->type, buff, 10); 04016 dynstr_append(ds, buff); 04017 dynstr_append_mem(ds, "\t", 1); 04018 longlong10_to_str((unsigned int) field->length, buff, 10); 04019 dynstr_append(ds, buff); 04020 dynstr_append_mem(ds, "\t", 1); 04021 longlong10_to_str((unsigned int) field->max_length, buff, 10); 04022 dynstr_append(ds, buff); 04023 dynstr_append_mem(ds, "\t", 1); 04024 dynstr_append_mem(ds, (char*) (IS_NOT_NULL(field->flags) ? 04025 "N" : "Y"), 1); 04026 dynstr_append_mem(ds, "\t", 1); 04027 04028 int10_to_str((int) field->flags, buff, 10); 04029 dynstr_append(ds, buff); 04030 dynstr_append_mem(ds, "\t", 1); 04031 int10_to_str((int) field->decimals, buff, 10); 04032 dynstr_append(ds, buff); 04033 dynstr_append_mem(ds, "\t", 1); 04034 int10_to_str((int) field->charsetnr, buff, 10); 04035 dynstr_append(ds, buff); 04036 dynstr_append_mem(ds, "\n", 1); 04037 } 04038 } 04039 04040 04041 /* 04042 Append affected row count and other info to output 04043 */ 04044 04045 static void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows, 04046 const char *info) 04047 { 04048 char buf[40], buff2[21]; 04049 sprintf(buf,"affected rows: %s\n", llstr(affected_rows, buff2)); 04050 dynstr_append(ds, buf); 04051 if (info) 04052 { 04053 dynstr_append(ds, "info: "); 04054 dynstr_append(ds, info); 04055 dynstr_append_mem(ds, "\n", 1); 04056 } 04057 } 04058 04059 04060 /* 04061 Display the table headings with the names tab separated 04062 */ 04063 04064 static void append_table_headings(DYNAMIC_STRING *ds, 04065 MYSQL_FIELD *field, 04066 uint num_fields) 04067 { 04068 uint col_idx; 04069 for (col_idx= 0; col_idx < num_fields; col_idx++) 04070 { 04071 if (col_idx) 04072 dynstr_append_mem(ds, "\t", 1); 04073 replace_dynstr_append(ds, field[col_idx].name); 04074 } 04075 dynstr_append_mem(ds, "\n", 1); 04076 } 04077 04078 /* 04079 Fetch warnings from server and append to ds 04080 04081 RETURN VALUE 04082 Number of warnings appended to ds 04083 */ 04084 04085 static int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql) 04086 { 04087 uint count; 04088 MYSQL_RES *warn_res; 04089 DBUG_ENTER("append_warnings"); 04090 04091 if (!(count= mysql_warning_count(mysql))) 04092 DBUG_RETURN(0); 04093 04094 /* 04095 If one day we will support execution of multi-statements 04096 through PS API we should not issue SHOW WARNINGS until 04097 we have not read all results... 04098 */ 04099 DBUG_ASSERT(!mysql_more_results(mysql)); 04100 04101 if (mysql_real_query(mysql, "SHOW WARNINGS", 13)) 04102 die("Error running query \"SHOW WARNINGS\": %s", mysql_error(mysql)); 04103 04104 if (!(warn_res= mysql_store_result(mysql))) 04105 die("Warning count is %u but didn't get any warnings", 04106 count); 04107 04108 append_result(ds, warn_res); 04109 mysql_free_result(warn_res); 04110 04111 DBUG_PRINT("warnings", ("%s", ds->str)); 04112 04113 DBUG_RETURN(count); 04114 } 04115 04116 04117 04118 /* 04119 Run query using MySQL C API 04120 04121 SYNPOSIS 04122 run_query_normal 04123 mysql - mysql handle 04124 command - currrent command pointer 04125 flags -flags indicating wheter to SEND and/or REAP 04126 query - query string to execute 04127 query_len - length query string to execute 04128 ds - output buffer wherte to store result form query 04129 04130 RETURN VALUE 04131 error - function will not return 04132 */ 04133 04134 static void run_query_normal(MYSQL *mysql, struct st_query *command, 04135 int flags, char *query, int query_len, 04136 DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings) 04137 { 04138 MYSQL_RES *res= 0; 04139 int err= 0, counter= 0; 04140 DBUG_ENTER("run_query_normal"); 04141 DBUG_PRINT("enter",("flags: %d", flags)); 04142 DBUG_PRINT("enter", ("query: '%-.60s'", query)); 04143 04144 if (flags & QUERY_SEND) 04145 { 04146 /* 04147 Send the query 04148 */ 04149 if (mysql_send_query(mysql, query, query_len)) 04150 { 04151 handle_error(query, command, mysql_errno(mysql), mysql_error(mysql), 04152 mysql_sqlstate(mysql), ds); 04153 goto end; 04154 } 04155 } 04156 04157 if (!(flags & QUERY_REAP)) 04158 DBUG_VOID_RETURN; 04159 04160 do 04161 { 04162 /* 04163 When on first result set, call mysql_read_query_result to retrieve 04164 answer to the query sent earlier 04165 */ 04166 if ((counter==0) && mysql_read_query_result(mysql)) 04167 { 04168 handle_error(query, command, mysql_errno(mysql), mysql_error(mysql), 04169 mysql_sqlstate(mysql), ds); 04170 goto end; 04171 04172 } 04173 04174 /* 04175 Store the result. If res is NULL, use mysql_field_count to 04176 determine if that was expected 04177 */ 04178 if (!(res= mysql_store_result(mysql)) && mysql_field_count(mysql)) 04179 { 04180 handle_error(query, command, mysql_errno(mysql), mysql_error(mysql), 04181 mysql_sqlstate(mysql), ds); 04182 goto end; 04183 } 04184 04185 if (!disable_result_log) 04186 { 04187 ulonglong affected_rows; /* Ok to be undef if 'disable_info' is set */ 04188 LINT_INIT(affected_rows); 04189 04190 if (res) 04191 { 04192 MYSQL_FIELD *fields= mysql_fetch_fields(res); 04193 uint num_fields= mysql_num_fields(res); 04194 04195 if (display_metadata) 04196 append_metadata(ds, fields, num_fields); 04197 04198 if (!display_result_vertically) 04199 append_table_headings(ds, fields, num_fields); 04200 04201 append_result(ds, res); 04202 } 04203 04204 /* 04205 Need to call mysql_affected_rows() before the "new" 04206 query to find the warnings 04207 */ 04208 if (!disable_info) 04209 affected_rows= mysql_affected_rows(mysql); 04210 04211 /* 04212 Add all warnings to the result. We can't do this if we are in 04213 the middle of processing results from multi-statement, because 04214 this will break protocol. 04215 */ 04216 if (!disable_warnings && !mysql_more_results(mysql)) 04217 { 04218 if (append_warnings(ds_warnings, mysql) || ds_warnings->length) 04219 { 04220 dynstr_append_mem(ds, "Warnings:\n", 10); 04221 dynstr_append_mem(ds, ds_warnings->str, ds_warnings->length); 04222 } 04223 } 04224 04225 if (!disable_info) 04226 append_info(ds, affected_rows, mysql_info(mysql)); 04227 } 04228 04229 if (res) 04230 mysql_free_result(res); 04231 counter++; 04232 } while (!(err= mysql_next_result(mysql))); 04233 if (err > 0) 04234 { 04235 /* We got an error from mysql_next_result, maybe expected */ 04236 handle_error(query, command, mysql_errno(mysql), mysql_error(mysql), 04237 mysql_sqlstate(mysql), ds); 04238 goto end; 04239 } 04240 DBUG_ASSERT(err == -1); /* Successful and there are no more results */ 04241 04242 /* If we come here the query is both executed and read successfully */ 04243 handle_no_error(command); 04244 04245 end: 04246 free_replace(); 04247 free_replace_regex(); 04248 04249 /* 04250 We save the return code (mysql_errno(mysql)) from the last call sent 04251 to the server into the mysqltest builtin variable $mysql_errno. This 04252 variable then can be used from the test case itself. 04253 */ 04254 var_set_errno(mysql_errno(mysql)); 04255 DBUG_VOID_RETURN; 04256 } 04257 04258 04259 /* 04260 Handle errors which occurred during execution 04261 04262 SYNOPSIS 04263 handle_error() 04264 query - query string 04265 q - query context 04266 err_errno - error number 04267 err_error - error message 04268 err_sqlstate - sql state 04269 ds - dynamic string which is used for output buffer 04270 04271 NOTE 04272 If there is an unexpected error this function will abort mysqltest 04273 immediately. 04274 04275 RETURN VALUE 04276 error - function will not return 04277 */ 04278 04279 static void handle_error(const char *query, struct st_query *q, 04280 unsigned int err_errno, const char *err_error, 04281 const char *err_sqlstate, DYNAMIC_STRING *ds) 04282 { 04283 uint i; 04284 04285 DBUG_ENTER("handle_error"); 04286 04287 if (q->require_file) 04288 { 04289 /* 04290 The query after a "--require" failed. This is fine as long the server 04291 returned a valid reponse. Don't allow 2013 or 2006 to trigger an 04292 abort_not_supported_test 04293 */ 04294 if (err_errno == CR_SERVER_LOST || 04295 err_errno == CR_SERVER_GONE_ERROR) 04296 die("require query '%s' failed: %d: %s", query, err_errno, err_error); 04297 04298 /* Abort the run of this test, pass the failed query as reason */ 04299 abort_not_supported_test("Query '%s' failed, required functionality not supported", query); 04300 } 04301 04302 if (q->abort_on_error) 04303 die("query '%s' failed: %d: %s", query, err_errno, err_error); 04304 04305 for (i= 0 ; (uint) i < q->expected_errors ; i++) 04306 { 04307 if (((q->expected_errno[i].type == ERR_ERRNO) && 04308 (q->expected_errno[i].code.errnum == err_errno)) || 04309 ((q->expected_errno[i].type == ERR_SQLSTATE) && 04310 (strcmp(q->expected_errno[i].code.sqlstate, err_sqlstate) == 0))) 04311 { 04312 if (!disable_result_log) 04313 { 04314 if (q->expected_errors == 1) 04315 { 04316 /* Only log error if there is one possible error */ 04317 dynstr_append_mem(ds, "ERROR ", 6); 04318 replace_dynstr_append(ds, err_sqlstate); 04319 dynstr_append_mem(ds, ": ", 2); 04320 replace_dynstr_append(ds, err_error); 04321 dynstr_append_mem(ds,"\n",1); 04322 } 04323 /* Don't log error if we may not get an error */ 04324 else if (q->expected_errno[0].type == ERR_SQLSTATE || 04325 (q->expected_errno[0].type == ERR_ERRNO && 04326 q->expected_errno[0].code.errnum != 0)) 04327 dynstr_append(ds,"Got one of the listed errors\n"); 04328 } 04329 /* OK */ 04330 DBUG_VOID_RETURN; 04331 } 04332 } 04333 04334 DBUG_PRINT("info",("i: %d expected_errors: %d", i, q->expected_errors)); 04335 04336 if (!disable_result_log) 04337 { 04338 dynstr_append_mem(ds, "ERROR ",6); 04339 replace_dynstr_append(ds, err_sqlstate); 04340 dynstr_append_mem(ds, ": ", 2); 04341 replace_dynstr_append(ds, err_error); 04342 dynstr_append_mem(ds, "\n", 1); 04343 } 04344 04345 if (i) 04346 { 04347 if (q->expected_errno[0].type == ERR_ERRNO) 04348 die("query '%s' failed with wrong errno %d: '%s', instead of %d...", 04349 q->query, err_errno, err_error, q->expected_errno[0].code.errnum); 04350 else 04351 die("query '%s' failed with wrong sqlstate %s: '%s', instead of %s...", 04352 q->query, err_sqlstate, err_error, 04353 q->expected_errno[0].code.sqlstate); 04354 } 04355 04356 DBUG_VOID_RETURN; 04357 } 04358 04359 04360 /* 04361 Handle absence of errors after execution 04362 04363 SYNOPSIS 04364 handle_no_error() 04365 q - context of query 04366 04367 RETURN VALUE 04368 error - function will not return 04369 */ 04370 04371 static void handle_no_error(struct st_query *q) 04372 { 04373 DBUG_ENTER("handle_no_error"); 04374 04375 if (q->expected_errno[0].type == ERR_ERRNO && 04376 q->expected_errno[0].code.errnum != 0) 04377 { 04378 /* Error code we wanted was != 0, i.e. not an expected success */ 04379 die("query '%s' succeeded - should have failed with errno %d...", 04380 q->query, q->expected_errno[0].code.errnum); 04381 } 04382 else if (q->expected_errno[0].type == ERR_SQLSTATE && 04383 strcmp(q->expected_errno[0].code.sqlstate,"00000") != 0) 04384 { 04385 /* SQLSTATE we wanted was != "00000", i.e. not an expected success */ 04386 die("query '%s' succeeded - should have failed with sqlstate %s...", 04387 q->query, q->expected_errno[0].code.sqlstate); 04388 } 04389 04390 DBUG_VOID_RETURN; 04391 } 04392 04393 04394 /* 04395 Run query using prepared statement C API 04396 04397 SYNPOSIS 04398 run_query_stmt 04399 mysql - mysql handle 04400 command - currrent command pointer 04401 query - query string to execute 04402 query_len - length query string to execute 04403 ds - output buffer where to store result form query 04404 04405 RETURN VALUE 04406 error - function will not return 04407 */ 04408 04409 static void run_query_stmt(MYSQL *mysql, struct st_query *command, 04410 char *query, int query_len, DYNAMIC_STRING *ds, 04411 DYNAMIC_STRING *ds_warnings) 04412 { 04413 MYSQL_RES *res= NULL; /* Note that here 'res' is meta data result set */ 04414 MYSQL_STMT *stmt; 04415 DYNAMIC_STRING ds_prepare_warnings; 04416 DYNAMIC_STRING ds_execute_warnings; 04417 DBUG_ENTER("run_query_stmt"); 04418 DBUG_PRINT("query", ("'%-.60s'", query)); 04419 04420 /* 04421 Init a new stmt if it's not already one created for this connection 04422 */ 04423 if(!(stmt= cur_con->stmt)) 04424 { 04425 if (!(stmt= mysql_stmt_init(mysql))) 04426 die("unable to init stmt structure"); 04427 cur_con->stmt= stmt; 04428 } 04429 04430 /* Init dynamic strings for warnings */ 04431 if (!disable_warnings) 04432 { 04433 init_dynamic_string(&ds_prepare_warnings, NULL, 0, 256); 04434 init_dynamic_string(&ds_execute_warnings, NULL, 0, 256); 04435 } 04436 04437 /* 04438 Prepare the query 04439 */ 04440 if (mysql_stmt_prepare(stmt, query, query_len)) 04441 { 04442 handle_error(query, command, mysql_stmt_errno(stmt), 04443 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); 04444 goto end; 04445 } 04446 04447 /* 04448 Get the warnings from mysql_stmt_prepare and keep them in a 04449 separate string 04450 */ 04451 if (!disable_warnings) 04452 append_warnings(&ds_prepare_warnings, mysql); 04453 04454 /* 04455 No need to call mysql_stmt_bind_param() because we have no 04456 parameter markers. 04457 */ 04458 04459 if (cursor_protocol_enabled) 04460 { 04461 /* 04462 Use cursor when retrieving result 04463 */ 04464 ulong type= CURSOR_TYPE_READ_ONLY; 04465 if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type)) 04466 die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s", 04467 mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); 04468 } 04469 04470 /* 04471 Execute the query 04472 */ 04473 if (mysql_stmt_execute(stmt)) 04474 { 04475 handle_error(query, command, mysql_stmt_errno(stmt), 04476 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); 04477 goto end; 04478 } 04479 04480 /* 04481 When running in cursor_protocol get the warnings from execute here 04482 and keep them in a separate string for later. 04483 */ 04484 if (cursor_protocol_enabled && !disable_warnings) 04485 append_warnings(&ds_execute_warnings, mysql); 04486 04487 /* 04488 We instruct that we want to update the "max_length" field in 04489 mysql_stmt_store_result(), this is our only way to know how much 04490 buffer to allocate for result data 04491 */ 04492 { 04493 my_bool one= 1; 04494 if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one)) 04495 die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s", 04496 mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); 04497 } 04498 04499 /* 04500 If we got here the statement succeeded and was expected to do so, 04501 get data. Note that this can still give errors found during execution! 04502 */ 04503 if (mysql_stmt_store_result(stmt)) 04504 { 04505 handle_error(query, command, mysql_stmt_errno(stmt), 04506 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); 04507 goto end; 04508 } 04509 04510 /* If we got here the statement was both executed and read successfully */ 04511 handle_no_error(command); 04512 if (!disable_result_log) 04513 { 04514 /* 04515 Not all statements creates a result set. If there is one we can 04516 now create another normal result set that contains the meta 04517 data. This set can be handled almost like any other non prepared 04518 statement result set. 04519 */ 04520 if ((res= mysql_stmt_result_metadata(stmt)) != NULL) 04521 { 04522 /* Take the column count from meta info */ 04523 MYSQL_FIELD *fields= mysql_fetch_fields(res); 04524 uint num_fields= mysql_num_fields(res); 04525 04526 if (display_metadata) 04527 append_metadata(ds, fields, num_fields); 04528 04529 if (!display_result_vertically) 04530 append_table_headings(ds, fields, num_fields); 04531 04532 append_stmt_result(ds, stmt, fields, num_fields); 04533 04534 mysql_free_result(res); /* Free normal result set with meta data */ 04535 04536 /* Clear prepare warnings */ 04537 dynstr_set(&ds_prepare_warnings, NULL); 04538 } 04539 else 04540 { 04541 /* 04542 This is a query without resultset 04543 */ 04544 } 04545 04546 if (!disable_warnings) 04547 { 04548 /* Get the warnings from execute */ 04549 04550 /* Append warnings to ds - if there are any */ 04551 if (append_warnings(&ds_execute_warnings, mysql) || 04552 ds_execute_warnings.length || 04553 ds_prepare_warnings.length || 04554 ds_warnings->length) 04555 { 04556 dynstr_append_mem(ds, "Warnings:\n", 10); 04557 if (ds_warnings->length) 04558 dynstr_append_mem(ds, ds_warnings->str, 04559 ds_warnings->length); 04560 if (ds_prepare_warnings.length) 04561 dynstr_append_mem(ds, ds_prepare_warnings.str, 04562 ds_prepare_warnings.length); 04563 if (ds_execute_warnings.length) 04564 dynstr_append_mem(ds, ds_execute_warnings.str, 04565 ds_execute_warnings.length); 04566 } 04567 } 04568 04569 if (!disable_info) 04570 append_info(ds, mysql_affected_rows(mysql), mysql_info(mysql)); 04571 04572 } 04573 04574 end: 04575 free_replace(); 04576 free_replace_regex(); 04577 04578 if (!disable_warnings) 04579 { 04580 dynstr_free(&ds_prepare_warnings); 04581 dynstr_free(&ds_execute_warnings); 04582 } 04583 04584 /* 04585 We save the return code (mysql_stmt_errno(stmt)) from the last call sent 04586 to the server into the mysqltest builtin variable $mysql_errno. This 04587 variable then can be used from the test case itself. 04588 */ 04589 04590 var_set_errno(mysql_stmt_errno(stmt)); 04591 #ifndef BUG15518_FIXED 04592 mysql_stmt_close(stmt); 04593 cur_con->stmt= NULL; 04594 #endif 04595 DBUG_VOID_RETURN; 04596 } 04597 04598 04599 04600 /* 04601 Create a util connection if one does not already exists 04602 and use that to run the query 04603 This is done to avoid implict commit when creating/dropping objects such 04604 as view, sp etc. 04605 */ 04606 04607 static int util_query(MYSQL* org_mysql, const char* query){ 04608 04609 MYSQL* mysql; 04610 DBUG_ENTER("util_query"); 04611 04612 if(!(mysql= cur_con->util_mysql)) 04613 { 04614 DBUG_PRINT("info", ("Creating util_mysql")); 04615 if (!(mysql= mysql_init(mysql))) 04616 die("Failed in mysql_init()"); 04617 04618 if (safe_connect(mysql, org_mysql->host, org_mysql->user, 04619 org_mysql->passwd, org_mysql->db, org_mysql->port, 04620 org_mysql->unix_socket)) 04621 die("Could not open util connection: %d %s", 04622 mysql_errno(mysql), mysql_error(mysql)); 04623 04624 cur_con->util_mysql= mysql; 04625 } 04626 04627 return mysql_query(mysql, query); 04628 } 04629 04630 04631 04632 /* 04633 Run query 04634 04635 flags control the phased/stages of query execution to be performed 04636 if QUERY_SEND bit is on, the query will be sent. If QUERY_REAP is on 04637 the result will be read - for regular query, both bits must be on 04638 04639 SYNPOSIS 04640 run_query 04641 mysql - mysql handle 04642 command - currrent command pointer 04643 04644 */ 04645 04646 static void run_query(MYSQL *mysql, struct st_query *command, int flags) 04647 { 04648 DYNAMIC_STRING *ds; 04649 DYNAMIC_STRING ds_result; 04650 DYNAMIC_STRING ds_warnings; 04651 DYNAMIC_STRING eval_query; 04652 char *query; 04653 int query_len; 04654 my_bool view_created= 0, sp_created= 0; 04655 my_bool complete_query= ((flags & QUERY_SEND) && (flags & QUERY_REAP)); 04656 04657 init_dynamic_string(&ds_warnings, NULL, 0, 256); 04658 04659 /* 04660 Evaluate query if this is an eval command 04661 */ 04662 if (command->type == Q_EVAL) 04663 { 04664 init_dynamic_string(&eval_query, "", 16384, 65536); 04665 do_eval(&eval_query, command->query, FALSE); 04666 query = eval_query.str; 04667 query_len = eval_query.length; 04668 } 04669 else 04670 { 04671 query = command->query; 04672 query_len = strlen(query); 04673 } 04674 04675 /* 04676 When command->record_file is set the output of _this_ query 04677 should be compared with an already existing file 04678 Create a temporary dynamic string to contain the output from 04679 this query. 04680 */ 04681 if (command->record_file[0]) 04682 { 04683 init_dynamic_string(&ds_result, "", 16384, 65536); 04684 ds= &ds_result; 04685 } 04686 else 04687 ds= &ds_res; 04688 04689 /* 04690 Log the query into the output buffer 04691 */ 04692 if (!disable_query_log && (flags & QUERY_SEND)) 04693 { 04694 replace_dynstr_append_mem(ds, query, query_len); 04695 dynstr_append_mem(ds, delimiter, delimiter_length); 04696 dynstr_append_mem(ds, "\n", 1); 04697 } 04698 04699 if (view_protocol_enabled && 04700 complete_query && 04701 match_re(&view_re, query)) 04702 { 04703 /* 04704 Create the query as a view. 04705 Use replace since view can exist from a failed mysqltest run 04706 */ 04707 DYNAMIC_STRING query_str; 04708 init_dynamic_string(&query_str, 04709 "CREATE OR REPLACE VIEW mysqltest_tmp_v AS ", 04710 query_len+64, 256); 04711 dynstr_append_mem(&query_str, query, query_len); 04712 if (util_query(mysql, query_str.str)) 04713 { 04714 /* 04715 Failed to create the view, this is not fatal 04716 just run the query the normal way 04717 */ 04718 DBUG_PRINT("view_create_error", 04719 ("Failed to create view '%s': %d: %s", query_str.str, 04720 mysql_errno(mysql), mysql_error(mysql))); 04721 04722 /* Log error to create view */ 04723 verbose_msg("Failed to create view '%s' %d: %s", query_str.str, 04724 mysql_errno(mysql), mysql_error(mysql)); 04725 } 04726 else 04727 { 04728 /* 04729 Yes, it was possible to create this query as a view 04730 */ 04731 view_created= 1; 04732 query= (char*)"SELECT * FROM mysqltest_tmp_v"; 04733 query_len = strlen(query); 04734 04735 /* 04736 Collect warnings from create of the view that should otherwise 04737 have been produced when the SELECT was executed 04738 */ 04739 append_warnings(&ds_warnings, cur_con->util_mysql); 04740 } 04741 04742 dynstr_free(&query_str); 04743 04744 } 04745 04746 if (sp_protocol_enabled && 04747 complete_query && 04748 match_re(&sp_re, query)) 04749 { 04750 /* 04751 Create the query as a stored procedure 04752 Drop first since sp can exist from a failed mysqltest run 04753 */ 04754 DYNAMIC_STRING query_str; 04755 init_dynamic_string(&query_str, 04756 "DROP PROCEDURE IF EXISTS mysqltest_tmp_sp;", 04757 query_len+64, 256); 04758 util_query(mysql, query_str.str); 04759 dynstr_set(&query_str, "CREATE PROCEDURE mysqltest_tmp_sp()\n"); 04760 dynstr_append_mem(&query_str, query, query_len); 04761 if (util_query(mysql, query_str.str)) 04762 { 04763 /* 04764 Failed to create the stored procedure for this query, 04765 this is not fatal just run the query the normal way 04766 */ 04767 DBUG_PRINT("sp_create_error", 04768 ("Failed to create sp '%s': %d: %s", query_str.str, 04769 mysql_errno(mysql), mysql_error(mysql))); 04770 04771 /* Log error to create sp */ 04772 verbose_msg("Failed to create sp '%s' %d: %s", query_str.str, 04773 mysql_errno(mysql), mysql_error(mysql)); 04774 04775 } 04776 else 04777 { 04778 sp_created= 1; 04779 04780 query= (char*)"CALL mysqltest_tmp_sp()"; 04781 query_len = strlen(query); 04782 } 04783 dynstr_free(&query_str); 04784 } 04785 04786 /* 04787 Find out how to run this query 04788 04789 Always run with normal C API if it's not a complete 04790 SEND + REAP 04791 04792 If it is a '?' in the query it may be a SQL level prepared 04793 statement already and we can't do it twice 04794 */ 04795 if (ps_protocol_enabled && 04796 complete_query && 04797 match_re(&ps_re, query)) 04798 run_query_stmt(mysql, command, query, query_len, ds, &ds_warnings); 04799 else 04800 run_query_normal(mysql, command, flags, query, query_len, 04801 ds, &ds_warnings); 04802 04803 if (sp_created) 04804 { 04805 if (util_query(mysql, "DROP PROCEDURE mysqltest_tmp_sp ")) 04806 die("Failed to drop sp: %d: %s", mysql_errno(mysql), mysql_error(mysql)); 04807 } 04808 04809 if (view_created) 04810 { 04811 if (util_query(mysql, "DROP VIEW mysqltest_tmp_v ")) 04812 die("Failed to drop view: %d: %s", 04813 mysql_errno(mysql), mysql_error(mysql)); 04814 } 04815 04816 if (command->record_file[0]) 04817 { 04818 04819 /* A result file was specified for _this_ query */ 04820 if (record) 04821 { 04822 /* 04823 Recording in progress 04824 Dump the output from _this_ query to the specified record_file 04825 */ 04826 str_to_file(command->record_file, ds->str, ds->length); 04827 04828 } else { 04829 04830 /* 04831 The output from _this_ query should be checked against an already 04832 existing file which has been specified using --require or --result 04833 */ 04834 check_result(ds, command->record_file, command->require_file); 04835 } 04836 } 04837 04838 dynstr_free(&ds_warnings); 04839 if (ds == &ds_result) 04840 dynstr_free(&ds_result); 04841 if (command->type == Q_EVAL) 04842 dynstr_free(&eval_query); 04843 } 04844 04845 04846 /****************************************************************************\ 04847 * Functions to detect different SQL statements 04848 \****************************************************************************/ 04849 04850 static char *re_eprint(int err) 04851 { 04852 static char epbuf[100]; 04853 size_t len= my_regerror(REG_ITOA|err, (my_regex_t *)NULL, 04854 epbuf, sizeof(epbuf)); 04855 assert(len <= sizeof(epbuf)); 04856 return(epbuf); 04857 } 04858 04859 static void init_re_comp(my_regex_t *re, const char* str) 04860 { 04861 int err= my_regcomp(re, str, (REG_EXTENDED | REG_ICASE | REG_NOSUB), 04862 &my_charset_latin1); 04863 if (err) 04864 { 04865 char erbuf[100]; 04866 int len= my_regerror(err, re, erbuf, sizeof(erbuf)); 04867 die("error %s, %d/%d `%s'\n", 04868 re_eprint(err), len, (int)sizeof(erbuf), erbuf); 04869 } 04870 } 04871 04872 static void init_re(void) 04873 { 04874 /* 04875 Filter for queries that can be run using the 04876 MySQL Prepared Statements C API 04877 */ 04878 const char *ps_re_str = 04879 "^(" 04880 "[[:space:]]*REPLACE[[:space:]]|" 04881 "[[:space:]]*INSERT[[:space:]]|" 04882 "[[:space:]]*UPDATE[[:space:]]|" 04883 "[[:space:]]*DELETE[[:space:]]|" 04884 "[[:space:]]*SELECT[[:space:]]|" 04885 "[[:space:]]*CREATE[[:space:]]+TABLE[[:space:]]|" 04886 "[[:space:]]*DO[[:space:]]|" 04887 "[[:space:]]*SET[[:space:]]+OPTION[[:space:]]|" 04888 "[[:space:]]*DELETE[[:space:]]+MULTI[[:space:]]|" 04889 "[[:space:]]*UPDATE[[:space:]]+MULTI[[:space:]]|" 04890 "[[:space:]]*INSERT[[:space:]]+SELECT[[:space:]])"; 04891 04892 /* 04893 Filter for queries that can be run using the 04894 Stored procedures 04895 */ 04896 const char *sp_re_str =ps_re_str; 04897 04898 /* 04899 Filter for queries that can be run as views 04900 */ 04901 const char *view_re_str = 04902 "^(" 04903 "[[:space:]]*SELECT[[:space:]])"; 04904 04905 init_re_comp(&ps_re, ps_re_str); 04906 init_re_comp(&sp_re, sp_re_str); 04907 init_re_comp(&view_re, view_re_str); 04908 } 04909 04910 04911 static int match_re(my_regex_t *re, char *str) 04912 { 04913 int err= my_regexec(re, str, (size_t)0, NULL, 0); 04914 04915 if (err == 0) 04916 return 1; 04917 else if (err == REG_NOMATCH) 04918 return 0; 04919 04920 { 04921 char erbuf[100]; 04922 int len= my_regerror(err, re, erbuf, sizeof(erbuf)); 04923 die("error %s, %d/%d `%s'\n", 04924 re_eprint(err), len, (int)sizeof(erbuf), erbuf); 04925 } 04926 return 0; 04927 } 04928 04929 static void free_re(void) 04930 { 04931 my_regfree(&ps_re); 04932 my_regfree(&sp_re); 04933 my_regfree(&view_re); 04934 my_regex_end(); 04935 } 04936 04937 /****************************************************************************/ 04938 04939 void get_query_type(struct st_query* q) 04940 { 04941 char save; 04942 uint type; 04943 DBUG_ENTER("get_query_type"); 04944 04945 if (!parsing_disabled && *q->query == '}') 04946 { 04947 q->type = Q_END_BLOCK; 04948 DBUG_VOID_RETURN; 04949 } 04950 if (q->type != Q_COMMENT_WITH_COMMAND) 04951 q->type= parsing_disabled ? Q_COMMENT : Q_QUERY; 04952 04953 save=q->query[q->first_word_len]; 04954 q->query[q->first_word_len]=0; 04955 type=find_type(q->query, &command_typelib, 1+2); 04956 q->query[q->first_word_len]=save; 04957 if (type > 0) 04958 { 04959 q->type=(enum enum_commands) type; /* Found command */ 04960 /* 04961 If queries are disabled, only recognize 04962 --enable_parsing and --disable_parsing 04963 */ 04964 if (parsing_disabled && q->type != Q_ENABLE_PARSING && 04965 q->type != Q_DISABLE_PARSING) 04966 q->type= Q_COMMENT; 04967 } 04968 else if (q->type == Q_COMMENT_WITH_COMMAND && 04969 q->first_word_len && 04970 q->query[q->first_word_len-1] == ';') 04971 { 04972 /* 04973 Detect comment with command using extra delimiter 04974 Ex --disable_query_log; 04975 ^ Extra delimiter causing the command 04976 to be skipped 04977 */ 04978 save= q->query[q->first_word_len-1]; 04979 q->query[q->first_word_len-1]= 0; 04980 type= find_type(q->query, &command_typelib, 1+2); 04981 q->query[q->first_word_len-1]= save; 04982 if (type > 0) 04983 die("Extra delimiter \";\" found"); 04984 } 04985 DBUG_VOID_RETURN; 04986 } 04987 04988 04989 static byte *get_var_key(const byte* var, uint* len, 04990 my_bool __attribute__((unused)) t) 04991 { 04992 register char* key; 04993 key = ((VAR*)var)->name; 04994 *len = ((VAR*)var)->name_len; 04995 return (byte*)key; 04996 } 04997 04998 static VAR *var_init(VAR *v, const char *name, int name_len, const char *val, 04999 int val_len) 05000 { 05001 int val_alloc_len; 05002 VAR *tmp_var; 05003 if (!name_len && name) 05004 name_len = strlen(name); 05005 if (!val_len && val) 05006 val_len = strlen(val) ; 05007 val_alloc_len = val_len + 16; /* room to grow */ 05008 if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var) 05009 + name_len+1, MYF(MY_WME)))) 05010 die("Out of memory"); 05011 05012 tmp_var->name = (name) ? (char*) tmp_var + sizeof(*tmp_var) : 0; 05013 tmp_var->alloced = (v == 0); 05014 05015 if (!(tmp_var->str_val = my_malloc(val_alloc_len+1, MYF(MY_WME)))) 05016 die("Out of memory"); 05017 05018 memcpy(tmp_var->name, name, name_len); 05019 if (val) 05020 { 05021 memcpy(tmp_var->str_val, val, val_len); 05022 tmp_var->str_val[val_len]= 0; 05023 } 05024 tmp_var->name_len = name_len; 05025 tmp_var->str_val_len = val_len; 05026 tmp_var->alloced_len = val_alloc_len; 05027 tmp_var->int_val = (val) ? atoi(val) : 0; 05028 tmp_var->int_dirty = 0; 05029 tmp_var->env_s = 0; 05030 return tmp_var; 05031 } 05032 05033 static void var_free(void *v) 05034 { 05035 my_free(((VAR*) v)->str_val, MYF(MY_WME)); 05036 if (((VAR*)v)->alloced) 05037 my_free((char*) v, MYF(MY_WME)); 05038 } 05039 05040 05041 static VAR* var_from_env(const char *name, const char *def_val) 05042 { 05043 const char *tmp; 05044 VAR *v; 05045 if (!(tmp = getenv(name))) 05046 tmp = def_val; 05047 05048 v = var_init(0, name, strlen(name), tmp, strlen(tmp)); 05049 my_hash_insert(&var_hash, (byte*)v); 05050 return v; 05051 } 05052 05053 05054 static void init_var_hash(MYSQL *mysql) 05055 { 05056 VAR *v; 05057 DBUG_ENTER("init_var_hash"); 05058 if (hash_init(&var_hash, charset_info, 05059 1024, 0, 0, get_var_key, var_free, MYF(0))) 05060 die("Variable hash initialization failed"); 05061 my_hash_insert(&var_hash, (byte*) var_init(0,"BIG_TEST", 0, 05062 (opt_big_test) ? "1" : "0", 0)); 05063 v= var_init(0,"MAX_TABLES", 0, (sizeof(ulong) == 4) ? "31" : "62",0); 05064 my_hash_insert(&var_hash, (byte*) v); 05065 v= var_init(0,"SERVER_VERSION", 0, mysql_get_server_info(mysql), 0); 05066 my_hash_insert(&var_hash, (byte*) v); v= var_init(0,"DB", 2, db, 0); 05067 my_hash_insert(&var_hash, (byte*) v); 05068 DBUG_VOID_RETURN; 05069 } 05070 05071 05072 /* 05073 Record how many milliseconds it took to execute the test file 05074 up until the current line and save it in the dynamic string ds_progress. 05075 05076 The ds_progress will be dumped to <test_name>.progress when 05077 test run completes 05078 05079 */ 05080 static void mark_progress(struct st_query* q __attribute__((unused)), int line) 05081 { 05082 char buf[32], *end; 05083 ulonglong timer= timer_now(); 05084 if (!progress_start) 05085 progress_start= timer; 05086 timer-= progress_start; 05087 05088 /* Milliseconds since start */ 05089 end= longlong2str(timer, buf, 10); 05090 dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); 05091 dynstr_append_mem(&ds_progress, "\t", 1); 05092 05093 /* Parser line number */ 05094 end= int10_to_str(line, buf, 10); 05095 dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); 05096 dynstr_append_mem(&ds_progress, "\t", 1); 05097 05098 /* Filename */ 05099 dynstr_append(&ds_progress, cur_file->file_name); 05100 dynstr_append_mem(&ds_progress, ":", 1); 05101 05102 /* Line in file */ 05103 end= int10_to_str(cur_file->lineno, buf, 10); 05104 dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); 05105 05106 05107 dynstr_append_mem(&ds_progress, "\n", 1); 05108 05109 } 05110 05111 05112 int main(int argc, char **argv) 05113 { 05114 struct st_query *q; 05115 my_bool require_file=0, q_send_flag=0, 05116 query_executed= 0; 05117 char save_file[FN_REFLEN]; 05118 MY_STAT res_info; 05119 MY_INIT(argv[0]); 05120 05121 /* Use all time until exit if no explicit 'start_timer' */ 05122 timer_start= timer_now(); 05123 05124 save_file[0]=0; 05125 TMPDIR[0]=0; 05126 05127 /* Init cons */ 05128 memset(cons, 0, sizeof(cons)); 05129 cons_end = cons + MAX_CONS; 05130 next_con = cons + 1; 05131 cur_con = cons; 05132 05133 /* Init file stack */ 05134 memset(file_stack, 0, sizeof(file_stack)); 05135 file_stack_end= file_stack + MAX_INCLUDE_DEPTH - 1; 05136 cur_file= file_stack; 05137 05138 /* Init block stack */ 05139 memset(block_stack, 0, sizeof(block_stack)); 05140 block_stack_end= block_stack + BLOCK_STACK_DEPTH - 1; 05141 cur_block= block_stack; 05142 cur_block->ok= TRUE; /* Outer block should always be executed */ 05143 cur_block->cmd= cmd_none; 05144 05145 my_init_dynamic_array(&q_lines, sizeof(struct st_query*), INIT_Q_LINES, 05146 INIT_Q_LINES); 05147 05148 memset(&master_pos, 0, sizeof(master_pos)); 05149 05150 init_dynamic_string(&ds_res, "", 0, 65536); 05151 init_dynamic_string(&ds_progress, "", 0, 2048); 05152 parse_args(argc, argv); 05153 05154 DBUG_PRINT("info",("result_file: '%s'", result_file ? result_file : "")); 05155 if (mysql_server_init(embedded_server_arg_count, 05156 embedded_server_args, 05157 (char**) embedded_server_groups)) 05158 die("Can't initialize MySQL server"); 05159 if (cur_file == file_stack && cur_file->file == 0) 05160 { 05161 cur_file->file= stdin; 05162 cur_file->file_name= my_strdup("<stdin>", MYF(MY_WME)); 05163 cur_file->lineno= 1; 05164 } 05165 init_re(); 05166 ps_protocol_enabled= ps_protocol; 05167 sp_protocol_enabled= sp_protocol; 05168 view_protocol_enabled= view_protocol; 05169 cursor_protocol_enabled= cursor_protocol; 05170 /* Cursor protcol implies ps protocol */ 05171 if (cursor_protocol_enabled) 05172 ps_protocol_enabled= 1; 05173 05174 if (!( mysql_init(&cur_con->mysql))) 05175 die("Failed in mysql_init()"); 05176 if (opt_compress) 05177 mysql_options(&cur_con->mysql,MYSQL_OPT_COMPRESS,NullS); 05178 mysql_options(&cur_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0); 05179 mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name); 05180 05181 #ifdef HAVE_OPENSSL 05182 opt_ssl_verify_server_cert= TRUE; /* Always on in mysqltest */ 05183 if (opt_use_ssl) 05184 { 05185 mysql_ssl_set(&cur_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, 05186 opt_ssl_capath, opt_ssl_cipher); 05187 mysql_options(&cur_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, 05188 &opt_ssl_verify_server_cert); 05189 } 05190 #endif 05191 05192 if (!(cur_con->name = my_strdup("default", MYF(MY_WME)))) 05193 die("Out of memory"); 05194 05195 if (safe_connect(&cur_con->mysql, host, user, pass, db, port, unix_sock)) 05196 die("Could not open connection '%s': %d %s", cur_con->name, 05197 mysql_errno(&cur_con->mysql), mysql_error(&cur_con->mysql)); 05198 05199 init_var_hash(&cur_con->mysql); 05200 05201 #ifdef __WIN__ 05202 init_tmp_sh_file(); 05203 init_win_path_patterns(); 05204 #endif 05205 05206 /* 05207 Initialize $mysql_errno with -1, so we can 05208 - distinguish it from valid values ( >= 0 ) and 05209 - detect if there was never a command sent to the server 05210 */ 05211 var_set_errno(-1); 05212 05213 if (opt_include) 05214 { 05215 open_file(opt_include); 05216 } 05217 05218 while (!read_query(&q)) 05219 { 05220 int current_line_inc = 1, processed = 0; 05221 if (q->type == Q_UNKNOWN || q->type == Q_COMMENT_WITH_COMMAND) 05222 get_query_type(q); 05223 if (cur_block->ok) 05224 { 05225 q->last_argument= q->first_argument; 05226 processed = 1; 05227 switch (q->type) { 05228 case Q_CONNECT: 05229 do_connect(q); 05230 break; 05231 case Q_CONNECTION: select_connection(q); break; 05232 case Q_DISCONNECT: 05233 case Q_DIRTY_CLOSE: 05234 close_connection(q); break; 05235 case Q_RPL_PROBE: do_rpl_probe(q); break; 05236 case Q_ENABLE_RPL_PARSE: do_enable_rpl_parse(q); break; 05237 case Q_DISABLE_RPL_PARSE: do_disable_rpl_parse(q); break; 05238 case Q_ENABLE_QUERY_LOG: disable_query_log=0; break; 05239 case Q_DISABLE_QUERY_LOG: disable_query_log=1; break; 05240 case Q_ENABLE_ABORT_ON_ERROR: abort_on_error=1; break; 05241 case Q_DISABLE_ABORT_ON_ERROR: abort_on_error=0; break; 05242 case Q_ENABLE_RESULT_LOG: disable_result_log=0; break; 05243 case Q_DISABLE_RESULT_LOG: disable_result_log=1; break; 05244 case Q_ENABLE_WARNINGS: disable_warnings=0; break; 05245 case Q_DISABLE_WARNINGS: disable_warnings=1; break; 05246 case Q_ENABLE_PS_WARNINGS: disable_ps_warnings=0; break; 05247 case Q_DISABLE_PS_WARNINGS: disable_ps_warnings=1; break; 05248 case Q_ENABLE_INFO: disable_info=0; break; 05249 case Q_DISABLE_INFO: disable_info=1; break; 05250 case Q_ENABLE_METADATA: display_metadata=1; break; 05251 case Q_DISABLE_METADATA: display_metadata=0; break; 05252 case Q_SOURCE: do_source(q); break; 05253 case Q_SLEEP: do_sleep(q, 0); break; 05254 case Q_REAL_SLEEP: do_sleep(q, 1); break; 05255 case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(q); break; 05256 case Q_INC: do_modify_var(q, DO_INC); break; 05257 case Q_DEC: do_modify_var(q, DO_DEC); break; 05258 case Q_ECHO: do_echo(q); query_executed= 1; break; 05259 case Q_SYSTEM: do_system(q); break; 05260 case Q_DELIMITER: 05261 strmake(delimiter, q->first_argument, sizeof(delimiter) - 1); 05262 delimiter_length= strlen(delimiter); 05263 q->last_argument= q->first_argument+delimiter_length; 05264 break; 05265 case Q_DISPLAY_VERTICAL_RESULTS: 05266 display_result_vertically= TRUE; 05267 break; 05268 case Q_DISPLAY_HORIZONTAL_RESULTS: 05269 display_result_vertically= FALSE; 05270 break; 05271 case Q_LET: do_let(q); break; 05272 case Q_EVAL_RESULT: 05273 eval_result = 1; break; 05274 case Q_EVAL: 05275 if (q->query == q->query_buf) 05276 { 05277 q->query= q->first_argument; 05278 q->first_word_len= 0; 05279 } 05280 /* fall through */ 05281 case Q_QUERY_VERTICAL: 05282 case Q_QUERY_HORIZONTAL: 05283 { 05284 my_bool old_display_result_vertically= display_result_vertically; 05285 /* fix up query pointer if this is first iteration for this line */ 05286 if (q->query == q->query_buf) 05287 q->query += q->first_word_len + 1; 05288 display_result_vertically= (q->type==Q_QUERY_VERTICAL); 05289 if (save_file[0]) 05290 { 05291 strmov(q->record_file,save_file); 05292 q->require_file=require_file; 05293 save_file[0]=0; 05294 } 05295 run_query(&cur_con->mysql, q, QUERY_REAP|QUERY_SEND); 05296 display_result_vertically= old_display_result_vertically; 05297 q->last_argument= q->end; 05298 query_executed= 1; 05299 break; 05300 } 05301 case Q_QUERY: 05302 case Q_REAP: 05303 { 05304 /* 05305 We read the result always regardless of the mode for both full 05306 query and read-result only (reap) 05307 */ 05308 int flags = QUERY_REAP; 05309 if (q->type != Q_REAP) /* for a full query, enable the send stage */ 05310 flags |= QUERY_SEND; 05311 if (q_send_flag) 05312 { 05313 flags= QUERY_SEND; 05314 q_send_flag=0; 05315 } 05316 if (save_file[0]) 05317 { 05318 strmov(q->record_file,save_file); 05319 q->require_file=require_file; 05320 save_file[0]=0; 05321 } 05322 /* 05323 To force something being sent as a query to the mysqld one can 05324 use the prefix "query". Remove "query" from string before executing 05325 */ 05326 if (strncmp(q->query, "query ", 6) == 0) 05327 { 05328 q->query= q->first_argument; 05329 } 05330 run_query(&cur_con->mysql, q, flags); 05331 query_executed= 1; 05332 q->last_argument= q->end; 05333 break; 05334 } 05335 case Q_SEND: 05336 if (!q->query[q->first_word_len]) 05337 { 05338 /* This happens when we use 'send' on its own line */ 05339 q_send_flag=1; 05340 break; 05341 } 05342 /* fix up query pointer if this is first iteration for this line */ 05343 if (q->query == q->query_buf) 05344 q->query += q->first_word_len; 05345 /* 05346 run_query() can execute a query partially, depending on the flags. 05347 QUERY_SEND flag without QUERY_REAP tells it to just send the 05348 query and read the result some time later when reap instruction 05349 is given on this connection. 05350 */ 05351 run_query(&cur_con->mysql, q, QUERY_SEND); 05352 query_executed= 1; 05353 q->last_argument= q->end; 05354 break; 05355 case Q_RESULT: 05356 get_file_name(save_file,q); 05357 require_file=0; 05358 break; 05359 case Q_ERROR: 05360 global_expected_errors=get_errcodes(global_expected_errno,q); 05361 break; 05362 case Q_REQUIRE: 05363 get_file_name(save_file,q); 05364 require_file=1; 05365 break; 05366 case Q_REPLACE: 05367 get_replace(q); 05368 break; 05369 case Q_REPLACE_REGEX: 05370 get_replace_regex(q); 05371 break; 05372 05373 case Q_REPLACE_COLUMN: 05374 get_replace_column(q); 05375 break; 05376 case Q_SAVE_MASTER_POS: do_save_master_pos(); break; 05377 case Q_SYNC_WITH_MASTER: do_sync_with_master(q); break; 05378 case Q_SYNC_SLAVE_WITH_MASTER: 05379 { 05380 do_save_master_pos(); 05381 if (*q->first_argument) 05382 select_connection(q); 05383 else 05384 select_connection_name("slave"); 05385 do_sync_with_master2(0); 05386 break; 05387 } 05388 case Q_COMMENT: /* Ignore row */ 05389 case Q_COMMENT_WITH_COMMAND: 05390 q->last_argument= q->end; 05391 break; 05392 case Q_PING: 05393 (void) mysql_ping(&cur_con->mysql); 05394 break; 05395 case Q_EXEC: 05396 do_exec(q); 05397 query_executed= 1; 05398 break; 05399 case Q_START_TIMER: 05400 /* Overwrite possible earlier start of timer */ 05401 timer_start= timer_now(); 05402 break; 05403 case Q_END_TIMER: 05404 /* End timer before ending mysqltest */ 05405 timer_output(); 05406 got_end_timer= TRUE; 05407 break; 05408 case Q_CHARACTER_SET: 05409 set_charset(q); 05410 break; 05411 case Q_DISABLE_PS_PROTOCOL: 05412 ps_protocol_enabled= 0; 05413 break; 05414 case Q_ENABLE_PS_PROTOCOL: 05415 ps_protocol_enabled= ps_protocol; 05416 break; 05417 case Q_DISABLE_RECONNECT: 05418 { 05419 my_bool reconnect= 0; 05420 mysql_options(&cur_con->mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect); 05421 break; 05422 } 05423 case Q_ENABLE_RECONNECT: 05424 { 05425 my_bool reconnect= 1; 05426 mysql_options(&cur_con->mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect); 05427 break; 05428 } 05429 case Q_DISABLE_PARSING: 05430 parsing_disabled++; 05431 break; 05432 case Q_ENABLE_PARSING: 05433 /* 05434 Ensure we don't get parsing_disabled < 0 as this would accidentally 05435 disable code we don't want to have disabled 05436 */ 05437 if (parsing_disabled > 0) 05438 parsing_disabled--; 05439 break; 05440 05441 case Q_DIE: 05442 die("%s", q->first_argument); 05443 break; 05444 05445 default: 05446 processed= 0; 05447 break; 05448 } 05449 } 05450 05451 if (!processed) 05452 { 05453 current_line_inc= 0; 05454 switch (q->type) { 05455 case Q_WHILE: do_block(cmd_while, q); break; 05456 case Q_IF: do_block(cmd_if, q); break; 05457 case Q_END_BLOCK: do_done(q); break; 05458 default: current_line_inc = 1; break; 05459 } 05460 } 05461 else 05462 check_eol_junk(q->last_argument); 05463 05464 if (q->type != Q_ERROR) 05465 { 05466 /* 05467 As soon as any non "error" command has been executed, 05468 the array with expected errors should be cleared 05469 */ 05470 global_expected_errors= 0; 05471 bzero((gptr) global_expected_errno, sizeof(global_expected_errno)); 05472 } 05473 05474 parser.current_line += current_line_inc; 05475 if ( opt_mark_progress ) 05476 mark_progress(q, parser.current_line); 05477 } 05478 05479 start_lineno= 0; 05480 05481 /* 05482 The whole test has been executed _sucessfully_. 05483 Time to compare result or save it to record file. 05484 The entire output from test is now kept in ds_res. 05485 */ 05486 if (ds_res.length) 05487 { 05488 if (result_file) 05489 { 05490 if (record) 05491 { 05492 /* Dump the output from test to result file */ 05493 str_to_file(result_file, ds_res.str, ds_res.length); 05494 } 05495 else 05496 { 05497 /* Check that the output from test is equal to result file 05498 - detect missing result file 05499 - detect zero size result file 05500 */ 05501 check_result(&ds_res, result_file, 0); 05502 } 05503 } 05504 else 05505 { 05506 /* No result_file specified to compare with, print to stdout */ 05507 printf("%s", ds_res.str); 05508 } 05509 } 05510 else 05511 { 05512 die("The test didn't produce any output"); 05513 } 05514 05515 if (!query_executed && result_file && my_stat(result_file, &res_info, 0)) 05516 { 05517 /* 05518 my_stat() successful on result file. Check if we have not run a 05519 single query, but we do have a result file that contains data. 05520 Note that we don't care, if my_stat() fails. For example, for a 05521 non-existing or non-readable file, we assume it's fine to have 05522 no query output from the test file, e.g. regarded as no error. 05523 */ 05524 die("No queries executed but result file found!"); 05525 } 05526 05527 if ( opt_mark_progress ) 05528 dump_progress(result_file); 05529 dynstr_free(&ds_progress); 05530 05531 dynstr_free(&ds_res); 05532 05533 if (!got_end_timer) 05534 timer_output(); /* No end_timer cmd, end it */ 05535 free_used_memory(); 05536 my_end(MY_CHECK_ERROR); 05537 05538 /* Yes, if we got this far the test has suceeded! Sakila smiles */ 05539 if (!silent) 05540 printf("ok\n"); 05541 exit(0); 05542 return 0; /* Keep compiler happy */ 05543 } 05544 05545 05546 /* 05547 Read arguments for embedded server and put them into 05548 embedded_server_args_count and embedded_server_args[] 05549 */ 05550 05551 05552 static int read_server_arguments(const char *name) 05553 { 05554 char argument[1024],buff[FN_REFLEN], *str=0; 05555 FILE *file; 05556 05557 if (!test_if_hard_path(name)) 05558 { 05559 strxmov(buff, opt_basedir, name, NullS); 05560 name=buff; 05561 } 05562 fn_format(buff, name, "", "", MY_UNPACK_FILENAME); 05563 05564 if (!embedded_server_arg_count) 05565 { 05566 embedded_server_arg_count=1; 05567 embedded_server_args[0]= (char*) ""; /* Progname */ 05568 } 05569 if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME)))) 05570 return 1; 05571 while (embedded_server_arg_count < MAX_SERVER_ARGS && 05572 (str=fgets(argument,sizeof(argument), file))) 05573 { 05574 *(strend(str)-1)=0; /* Remove end newline */ 05575 if (!(embedded_server_args[embedded_server_arg_count]= 05576 (char*) my_strdup(str,MYF(MY_WME)))) 05577 { 05578 my_fclose(file,MYF(0)); 05579 return 1; 05580 } 05581 embedded_server_arg_count++; 05582 } 05583 my_fclose(file,MYF(0)); 05584 if (str) 05585 { 05586 fprintf(stderr,"Too many arguments in option file: %s\n",name); 05587 return 1; 05588 } 05589 return 0; 05590 } 05591 05592 /****************************************************************************\ 05593 * 05594 * A primitive timer that give results in milliseconds if the 05595 * --timer-file=<filename> is given. The timer result is written 05596 * to that file when the result is available. To not confuse 05597 * mysql-test-run with an old obsolete result, we remove the file 05598 * before executing any commands. The time we measure is 05599 * 05600 * - If no explicit 'start_timer' or 'end_timer' is given in the 05601 * test case, the timer measure how long we execute in mysqltest. 05602 * 05603 * - If only 'start_timer' is given we measure how long we execute 05604 * from that point until we terminate mysqltest. 05605 * 05606 * - If only 'end_timer' is given we measure how long we execute 05607 * from that we enter mysqltest to the 'end_timer' is command is 05608 * executed. 05609 * 05610 * - If both 'start_timer' and 'end_timer' are given we measure 05611 * the time between executing the two commands. 05612 * 05613 \****************************************************************************/ 05614 05615 static void timer_output(void) 05616 { 05617 if (timer_file) 05618 { 05619 char buf[32], *end; 05620 ulonglong timer= timer_now() - timer_start; 05621 end= longlong2str(timer, buf, 10); 05622 str_to_file(timer_file,buf, (int) (end-buf)); 05623 } 05624 } 05625 05626 static ulonglong timer_now(void) 05627 { 05628 return my_getsystime() / 10000; 05629 } 05630 05631 /**************************************************************************** 05632 * Handle replacement of strings 05633 ****************************************************************************/ 05634 05635 #define PC_MALLOC 256 /* Bytes for pointers */ 05636 #define PS_MALLOC 512 /* Bytes for data */ 05637 05638 #define SPACE_CHAR 256 05639 #define START_OF_LINE 257 05640 #define END_OF_LINE 258 05641 #define LAST_CHAR_CODE 259 05642 05643 typedef struct st_replace { 05644 bool found; 05645 struct st_replace *next[256]; 05646 } REPLACE; 05647 05648 typedef struct st_replace_found { 05649 bool found; 05650 char *replace_string; 05651 uint to_offset; 05652 int from_offset; 05653 } REPLACE_STRING; 05654 05655 #ifndef WORD_BIT 05656 #define WORD_BIT (8*sizeof(uint)) 05657 #endif 05658 05659 05660 static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name) 05661 { 05662 uint i,length,old_count; 05663 byte *new_pos; 05664 const char **new_array; 05665 DBUG_ENTER("insert_pointer_name"); 05666 05667 if (! pa->typelib.count) 05668 { 05669 if (!(pa->typelib.type_names=(const char **) 05670 my_malloc(((PC_MALLOC-MALLOC_OVERHEAD)/ 05671 (sizeof(my_string)+sizeof(*pa->flag))* 05672 (sizeof(my_string)+sizeof(*pa->flag))),MYF(MY_WME)))) 05673 DBUG_RETURN(-1); 05674 if (!(pa->str= (byte*) my_malloc((uint) (PS_MALLOC-MALLOC_OVERHEAD), 05675 MYF(MY_WME)))) 05676 { 05677 my_free((gptr) pa->typelib.type_names,MYF(0)); 05678 DBUG_RETURN (-1); 05679 } 05680 pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(byte*)+ 05681 sizeof(*pa->flag)); 05682 pa->flag= (int7*) (pa->typelib.type_names+pa->max_count); 05683 pa->length=0; 05684 pa->max_length=PS_MALLOC-MALLOC_OVERHEAD; 05685 pa->array_allocs=1; 05686 } 05687 length=(uint) strlen(name)+1; 05688 if (pa->length+length >= pa->max_length) 05689 { 05690 if (!(new_pos= (byte*) my_realloc((gptr) pa->str, 05691 (uint) (pa->max_length+PS_MALLOC), 05692 MYF(MY_WME)))) 05693 DBUG_RETURN(1); 05694 if (new_pos != pa->str) 05695 { 05696 my_ptrdiff_t diff=PTR_BYTE_DIFF(new_pos,pa->str); 05697 for (i=0 ; i < pa->typelib.count ; i++) 05698 pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff, 05699 char*); 05700 pa->str=new_pos; 05701 } 05702 pa->max_length+=PS_MALLOC; 05703 } 05704 if (pa->typelib.count >= pa->max_count-1) 05705 { 05706 int len; 05707 pa->array_allocs++; 05708 len=(PC_MALLOC*pa->array_allocs - MALLOC_OVERHEAD); 05709 if (!(new_array=(const char **) my_realloc((gptr) pa->typelib.type_names, 05710 (uint) len/ 05711 (sizeof(byte*)+sizeof(*pa->flag))* 05712 (sizeof(byte*)+sizeof(*pa->flag)), 05713 MYF(MY_WME)))) 05714 DBUG_RETURN(1); 05715 pa->typelib.type_names=new_array; 05716 old_count=pa->max_count; 05717 pa->max_count=len/(sizeof(byte*) + sizeof(*pa->flag)); 05718 pa->flag= (int7*) (pa->typelib.type_names+pa->max_count); 05719 memcpy((byte*) pa->flag,(my_string) (pa->typelib.type_names+old_count), 05720 old_count*sizeof(*pa->flag)); 05721 } 05722 pa->flag[pa->typelib.count]=0; /* Reset flag */ 05723 pa->typelib.type_names[pa->typelib.count++]= pa->str+pa->length; 05724 pa->typelib.type_names[pa->typelib.count]= NullS; /* Put end-mark */ 05725 VOID(strmov(pa->str+pa->length,name)); 05726 pa->length+=length; 05727 DBUG_RETURN(0); 05728 } /* insert_pointer_name */ 05729 05730 05731 /* free pointer array */ 05732 05733 void free_pointer_array(POINTER_ARRAY *pa) 05734 { 05735 if (pa->typelib.count) 05736 { 05737 pa->typelib.count=0; 05738 my_free((gptr) pa->typelib.type_names,MYF(0)); 05739 pa->typelib.type_names=0; 05740 my_free((gptr) pa->str,MYF(0)); 05741 } 05742 } /* free_pointer_array */ 05743 05744 05745 /* Code for replace rutines */ 05746 05747 #define SET_MALLOC_HUNC 64 05748 05749 typedef struct st_rep_set { 05750 uint *bits; /* Pointer to used sets */ 05751 short next[LAST_CHAR_CODE]; /* Pointer to next sets */ 05752 uint found_len; /* Best match to date */ 05753 int found_offset; 05754 uint table_offset; 05755 uint size_of_bits; /* For convinience */ 05756 } REP_SET; 05757 05758 typedef struct st_rep_sets { 05759 uint count; /* Number of sets */ 05760 uint extra; /* Extra sets in buffer */ 05761 uint invisible; /* Sets not chown */ 05762 uint size_of_bits; 05763 REP_SET *set,*set_buffer; 05764 uint *bit_buffer; 05765 } REP_SETS; 05766 05767 typedef struct st_found_set { 05768 uint table_offset; 05769 int found_offset; 05770 } FOUND_SET; 05771 05772 typedef struct st_follow { 05773 int chr; 05774 uint table_offset; 05775 uint len; 05776 } FOLLOWS; 05777 05778 05779 static int init_sets(REP_SETS *sets,uint states); 05780 static REP_SET *make_new_set(REP_SETS *sets); 05781 static void make_sets_invisible(REP_SETS *sets); 05782 static void free_last_set(REP_SETS *sets); 05783 static void free_sets(REP_SETS *sets); 05784 static void internal_set_bit(REP_SET *set, uint bit); 05785 static void internal_clear_bit(REP_SET *set, uint bit); 05786 static void or_bits(REP_SET *to,REP_SET *from); 05787 static void copy_bits(REP_SET *to,REP_SET *from); 05788 static int cmp_bits(REP_SET *set1,REP_SET *set2); 05789 static int get_next_bit(REP_SET *set,uint lastpos); 05790 static int find_set(REP_SETS *sets,REP_SET *find); 05791 static int find_found(FOUND_SET *found_set,uint table_offset, 05792 int found_offset); 05793 static uint start_at_word(my_string pos); 05794 static uint end_of_word(my_string pos); 05795 static uint replace_len(my_string pos); 05796 05797 static uint found_sets=0; 05798 05799 05800 /* Init a replace structure for further calls */ 05801 05802 REPLACE *init_replace(my_string *from, my_string *to,uint count, 05803 my_string word_end_chars) 05804 { 05805 uint i,j,states,set_nr,len,result_len,max_length,found_end,bits_set,bit_nr; 05806 int used_sets,chr,default_state; 05807 char used_chars[LAST_CHAR_CODE],is_word_end[256]; 05808 my_string pos,to_pos,*to_array; 05809 REP_SETS sets; 05810 REP_SET *set,*start_states,*word_states,*new_set; 05811 FOLLOWS *follow,*follow_ptr; 05812 REPLACE *replace; 05813 FOUND_SET *found_set; 05814 REPLACE_STRING *rep_str; 05815 DBUG_ENTER("init_replace"); 05816 05817 /* Count number of states */ 05818 for (i=result_len=max_length=0 , states=2 ; i < count ; i++) 05819 { 05820 len=replace_len(from[i]); 05821 if (!len) 05822 { 05823 errno=EINVAL; 05824 my_message(0,"No to-string for last from-string",MYF(ME_BELL)); 05825 DBUG_RETURN(0); 05826 } 05827 states+=len+1; 05828 result_len+=(uint) strlen(to[i])+1; 05829 if (len > max_length) 05830 max_length=len; 05831 } 05832 bzero((char*) is_word_end,sizeof(is_word_end)); 05833 for (i=0 ; word_end_chars[i] ; i++) 05834 is_word_end[(uchar) word_end_chars[i]]=1; 05835 05836 if (init_sets(&sets,states)) 05837 DBUG_RETURN(0); 05838 found_sets=0; 05839 if (!(found_set= (FOUND_SET*) my_malloc(sizeof(FOUND_SET)*max_length*count, 05840 MYF(MY_WME)))) 05841 { 05842 free_sets(&sets); 05843 DBUG_RETURN(0); 05844 } 05845 VOID(make_new_set(&sets)); /* Set starting set */ 05846 make_sets_invisible(&sets); /* Hide previus sets */ 05847 used_sets=-1; 05848 word_states=make_new_set(&sets); /* Start of new word */ 05849 start_states=make_new_set(&sets); /* This is first state */ 05850 if (!(follow=(FOLLOWS*) my_malloc((states+2)*sizeof(FOLLOWS),MYF(MY_WME)))) 05851 { 05852 free_sets(&sets); 05853 my_free((gptr) found_set,MYF(0)); 05854 DBUG_RETURN(0); 05855 } 05856 05857 /* Init follow_ptr[] */ 05858 for (i=0, states=1, follow_ptr=follow+1 ; i < count ; i++) 05859 { 05860 if (from[i][0] == '\\' && from[i][1] == '^') 05861 { 05862 internal_set_bit(start_states,states+1); 05863 if (!from[i][2]) 05864 { 05865 start_states->table_offset=i; 05866 start_states->found_offset=1; 05867 } 05868 } 05869 else if (from[i][0] == '\\' && from[i][1] == '$') 05870 { 05871 internal_set_bit(start_states,states); 05872 internal_set_bit(word_states,states); 05873 if (!from[i][2] && start_states->table_offset == (uint) ~0) 05874 { 05875 start_states->table_offset=i; 05876 start_states->found_offset=0; 05877 } 05878 } 05879 else 05880 { 05881 internal_set_bit(word_states,states); 05882 if (from[i][0] == '\\' && (from[i][1] == 'b' && from[i][2])) 05883 internal_set_bit(start_states,states+1); 05884 else 05885 internal_set_bit(start_states,states); 05886 } 05887 for (pos=from[i], len=0; *pos ; pos++) 05888 { 05889 if (*pos == '\\' && *(pos+1)) 05890 { 05891 pos++; 05892 switch (*pos) { 05893 case 'b': 05894 follow_ptr->chr = SPACE_CHAR; 05895 break; 05896 case '^': 05897 follow_ptr->chr = START_OF_LINE; 05898 break; 05899 case '$': 05900 follow_ptr->chr = END_OF_LINE; 05901 break; 05902 case 'r': 05903 follow_ptr->chr = '\r'; 05904 break; 05905 case 't': 05906 follow_ptr->chr = '\t'; 05907 break; 05908 case 'v': 05909 follow_ptr->chr = '\v'; 05910 break; 05911 default: 05912 follow_ptr->chr = (uchar) *pos; 05913 break; 05914 } 05915 } 05916 else 05917 follow_ptr->chr= (uchar) *pos; 05918 follow_ptr->table_offset=i; 05919 follow_ptr->len= ++len; 05920 follow_ptr++; 05921 } 05922 follow_ptr->chr=0; 05923 follow_ptr->table_offset=i; 05924 follow_ptr->len=len; 05925 follow_ptr++; 05926 states+=(uint) len+1; 05927 } 05928 05929 05930 for (set_nr=0,pos=0 ; set_nr < sets.count ; set_nr++) 05931 { 05932 set=sets.set+set_nr; 05933 default_state= 0; /* Start from beginning */ 05934 05935 /* If end of found-string not found or start-set with current set */ 05936 05937 for (i= (uint) ~0; (i=get_next_bit(set,i)) ;) 05938 { 05939 if (!follow[i].chr) 05940 { 05941 if (! default_state) 05942 default_state= find_found(found_set,set->table_offset, 05943 set->found_offset+1); 05944 } 05945 } 05946 copy_bits(sets.set+used_sets,set); /* Save set for changes */ 05947 if (!default_state) 05948 or_bits(sets.set+used_sets,sets.set); /* Can restart from start */ 05949 05950 /* Find all chars that follows current sets */ 05951 bzero((char*) used_chars,sizeof(used_chars)); 05952 for (i= (uint) ~0; (i=get_next_bit(sets.set+used_sets,i)) ;) 05953 { 05954 used_chars[follow[i].chr]=1; 05955 if ((follow[i].chr == SPACE_CHAR && !follow[i+1].chr && 05956 follow[i].len > 1) || follow[i].chr == END_OF_LINE) 05957 used_chars[0]=1; 05958 } 05959 05960 /* Mark word_chars used if \b is in state */ 05961 if (used_chars[SPACE_CHAR]) 05962 for (pos= word_end_chars ; *pos ; pos++) 05963 used_chars[(int) (uchar) *pos] = 1; 05964 05965 /* Handle other used characters */ 05966 for (chr= 0 ; chr < 256 ; chr++) 05967 { 05968 if (! used_chars[chr]) 05969 set->next[chr]= chr ? default_state : -1; 05970 else 05971 { 05972 new_set=make_new_set(&sets); 05973 set=sets.set+set_nr; /* if realloc */ 05974 new_set->table_offset=set->table_offset; 05975 new_set->found_len=set->found_len; 05976 new_set->found_offset=set->found_offset+1; 05977 found_end=0; 05978 05979 for (i= (uint) ~0 ; (i=get_next_bit(sets.set+used_sets,i)) ; ) 05980 { 05981 if (!follow[i].chr || follow[i].chr == chr || 05982 (follow[i].chr == SPACE_CHAR && 05983 (is_word_end[chr] || 05984 (!chr && follow[i].len > 1 && ! follow[i+1].chr))) || 05985 (follow[i].chr == END_OF_LINE && ! chr)) 05986 { 05987 if ((! chr || (follow[i].chr && !follow[i+1].chr)) && 05988 follow[i].len > found_end) 05989 found_end=follow[i].len; 05990 if (chr && follow[i].chr) 05991 internal_set_bit(new_set,i+1); /* To next set */ 05992 else 05993 internal_set_bit(new_set,i); 05994 } 05995 } 05996 if (found_end) 05997 { 05998 new_set->found_len=0; /* Set for testing if first */ 05999 bits_set=0; 06000 for (i= (uint) ~0; (i=get_next_bit(new_set,i)) ;) 06001 { 06002 if ((follow[i].chr == SPACE_CHAR || 06003 follow[i].chr == END_OF_LINE) && ! chr) 06004 bit_nr=i+1; 06005 else 06006 bit_nr=i; 06007 if (follow[bit_nr-1].len < found_end || 06008 (new_set->found_len && 06009 (chr == 0 || !follow[bit_nr].chr))) 06010 internal_clear_bit(new_set,i); 06011 else 06012 { 06013 if (chr == 0 || !follow[bit_nr].chr) 06014 { /* best match */ 06015 new_set->table_offset=follow[bit_nr].table_offset; 06016 if (chr || (follow[i].chr == SPACE_CHAR || 06017 follow[i].chr == END_OF_LINE)) 06018 new_set->found_offset=found_end; /* New match */ 06019 new_set->found_len=found_end; 06020 } 06021 bits_set++; 06022 } 06023 } 06024 if (bits_set == 1) 06025 { 06026 set->next[chr] = find_found(found_set, 06027 new_set->table_offset, 06028 new_set->found_offset); 06029 free_last_set(&sets); 06030 } 06031 else 06032 set->next[chr] = find_set(&sets,new_set); 06033 } 06034 else 06035 set->next[chr] = find_set(&sets,new_set); 06036 } 06037 } 06038 } 06039 06040 /* Alloc replace structure for the replace-state-machine */ 06041 06042 if ((replace=(REPLACE*) my_malloc(sizeof(REPLACE)*(sets.count)+ 06043 sizeof(REPLACE_STRING)*(found_sets+1)+ 06044 sizeof(my_string)*count+result_len, 06045 MYF(MY_WME | MY_ZEROFILL)))) 06046 { 06047 rep_str=(REPLACE_STRING*) (replace+sets.count); 06048 to_array=(my_string*) (rep_str+found_sets+1); 06049 to_pos=(my_string) (to_array+count); 06050 for (i=0 ; i < count ; i++) 06051 { 06052 to_array[i]=to_pos; 06053 to_pos=strmov(to_pos,to[i])+1; 06054 } 06055 rep_str[0].found=1; 06056 rep_str[0].replace_string=0; 06057 for (i=1 ; i <= found_sets ; i++) 06058 { 06059 pos=from[found_set[i-1].table_offset]; 06060 rep_str[i].found= !bcmp(pos,"\\^",3) ? 2 : 1; 06061 rep_str[i].replace_string=to_array[found_set[i-1].table_offset]; 06062 rep_str[i].to_offset=found_set[i-1].found_offset-start_at_word(pos); 06063 rep_str[i].from_offset=found_set[i-1].found_offset-replace_len(pos)+ 06064 end_of_word(pos); 06065 } 06066 for (i=0 ; i < sets.count ; i++) 06067 { 06068 for (j=0 ; j < 256 ; j++) 06069 if (sets.set[i].next[j] >= 0) 06070 replace[i].next[j]=replace+sets.set[i].next[j]; 06071 else 06072 replace[i].next[j]=(REPLACE*) (rep_str+(-sets.set[i].next[j]-1)); 06073 } 06074 } 06075 my_free((gptr) follow,MYF(0)); 06076 free_sets(&sets); 06077 my_free((gptr) found_set,MYF(0)); 06078 DBUG_PRINT("exit",("Replace table has %d states",sets.count)); 06079 DBUG_RETURN(replace); 06080 } 06081 06082 06083 static int init_sets(REP_SETS *sets,uint states) 06084 { 06085 bzero((char*) sets,sizeof(*sets)); 06086 sets->size_of_bits=((states+7)/8); 06087 if (!(sets->set_buffer=(REP_SET*) my_malloc(sizeof(REP_SET)*SET_MALLOC_HUNC, 06088 MYF(MY_WME)))) 06089 return 1; 06090 if (!(sets->bit_buffer=(uint*) my_malloc(sizeof(uint)*sets->size_of_bits* 06091 SET_MALLOC_HUNC,MYF(MY_WME)))) 06092 { 06093 my_free((gptr) sets->set,MYF(0)); 06094 return 1; 06095 } 06096 return 0; 06097 } 06098 06099 /* Make help sets invisible for nicer codeing */ 06100 06101 static void make_sets_invisible(REP_SETS *sets) 06102 { 06103 sets->invisible=sets->count; 06104 sets->set+=sets->count; 06105 sets->count=0; 06106 } 06107 06108 static REP_SET *make_new_set(REP_SETS *sets) 06109 { 06110 uint i,count,*bit_buffer; 06111 REP_SET *set; 06112 if (sets->extra) 06113 { 06114 sets->extra--; 06115 set=sets->set+ sets->count++; 06116 bzero((char*) set->bits,sizeof(uint)*sets->size_of_bits); 06117 bzero((char*) &set->next[0],sizeof(set->next[0])*LAST_CHAR_CODE); 06118 set->found_offset=0; 06119 set->found_len=0; 06120 set->table_offset= (uint) ~0; 06121 set->size_of_bits=sets->size_of_bits; 06122 return set; 06123 } 06124 count=sets->count+sets->invisible+SET_MALLOC_HUNC; 06125 if (!(set=(REP_SET*) my_realloc((gptr) sets->set_buffer, 06126 sizeof(REP_SET)*count, 06127 MYF(MY_WME)))) 06128 return 0; 06129 sets->set_buffer=set; 06130 sets->set=set+sets->invisible; 06131 if (!(bit_buffer=(uint*) my_realloc((gptr) sets->bit_buffer, 06132 (sizeof(uint)*sets->size_of_bits)*count, 06133 MYF(MY_WME)))) 06134 return 0; 06135 sets->bit_buffer=bit_buffer; 06136 for (i=0 ; i < count ; i++) 06137 { 06138 sets->set_buffer[i].bits=bit_buffer; 06139 bit_buffer+=sets->size_of_bits; 06140 } 06141 sets->extra=SET_MALLOC_HUNC; 06142 return make_new_set(sets); 06143 } 06144 06145 static void free_last_set(REP_SETS *sets) 06146 { 06147 sets->count--; 06148 sets->extra++; 06149 return; 06150 } 06151 06152 static void free_sets(REP_SETS *sets) 06153 { 06154 my_free((gptr)sets->set_buffer,MYF(0)); 06155 my_free((gptr)sets->bit_buffer,MYF(0)); 06156 return; 06157 } 06158 06159 static void internal_set_bit(REP_SET *set, uint bit) 06160 { 06161 set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT); 06162 return; 06163 } 06164 06165 static void internal_clear_bit(REP_SET *set, uint bit) 06166 { 06167 set->bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT)); 06168 return; 06169 } 06170 06171 06172 static void or_bits(REP_SET *to,REP_SET *from) 06173 { 06174 reg1 uint i; 06175 for (i=0 ; i < to->size_of_bits ; i++) 06176 to->bits[i]|=from->bits[i]; 06177 return; 06178 } 06179 06180 static void copy_bits(REP_SET *to,REP_SET *from) 06181 { 06182 memcpy((byte*) to->bits,(byte*) from->bits, 06183 (size_t) (sizeof(uint) * to->size_of_bits)); 06184 } 06185 06186 static int cmp_bits(REP_SET *set1,REP_SET *set2) 06187 { 06188 return bcmp((byte*) set1->bits,(byte*) set2->bits, 06189 sizeof(uint) * set1->size_of_bits); 06190 } 06191 06192 06193 /* Get next set bit from set. */ 06194 06195 static int get_next_bit(REP_SET *set,uint lastpos) 06196 { 06197 uint pos,*start,*end,bits; 06198 06199 start=set->bits+ ((lastpos+1) / WORD_BIT); 06200 end=set->bits + set->size_of_bits; 06201 bits=start[0] & ~((1 << ((lastpos+1) % WORD_BIT)) -1); 06202 06203 while (! bits && ++start < end) 06204 bits=start[0]; 06205 if (!bits) 06206 return 0; 06207 pos=(uint) (start-set->bits)*WORD_BIT; 06208 while (! (bits & 1)) 06209 { 06210 bits>>=1; 06211 pos++; 06212 } 06213 return pos; 06214 } 06215 06216 /* find if there is a same set in sets. If there is, use it and 06217 free given set, else put in given set in sets and return its 06218 position */ 06219 06220 static int find_set(REP_SETS *sets,REP_SET *find) 06221 { 06222 uint i; 06223 for (i=0 ; i < sets->count-1 ; i++) 06224 { 06225 if (!cmp_bits(sets->set+i,find)) 06226 { 06227 free_last_set(sets); 06228 return i; 06229 } 06230 } 06231 return i; /* return new postion */ 06232 } 06233 06234 /* find if there is a found_set with same table_offset & found_offset 06235 If there is return offset to it, else add new offset and return pos. 06236 Pos returned is -offset-2 in found_set_structure because it is 06237 saved in set->next and set->next[] >= 0 points to next set and 06238 set->next[] == -1 is reserved for end without replaces. 06239 */ 06240 06241 static int find_found(FOUND_SET *found_set,uint table_offset, int found_offset) 06242 { 06243 int i; 06244 for (i=0 ; (uint) i < found_sets ; i++) 06245 if (found_set[i].table_offset == table_offset && 06246 found_set[i].found_offset == found_offset) 06247 return -i-2; 06248 found_set[i].table_offset=table_offset; 06249 found_set[i].found_offset=found_offset; 06250 found_sets++; 06251 return -i-2; /* return new postion */ 06252 } 06253 06254 /* Return 1 if regexp starts with \b or ends with \b*/ 06255 06256 static uint start_at_word(my_string pos) 06257 { 06258 return (((!bcmp(pos,"\\b",2) && pos[2]) || !bcmp(pos,"\\^",2)) ? 1 : 0); 06259 } 06260 06261 static uint end_of_word(my_string pos) 06262 { 06263 my_string end=strend(pos); 06264 return ((end > pos+2 && !bcmp(end-2,"\\b",2)) || 06265 (end >= pos+2 && !bcmp(end-2,"\\$",2))) ? 06266 1 : 0; 06267 } 06268 06269 06270 static uint replace_len(my_string str) 06271 { 06272 uint len=0; 06273 while (*str) 06274 { 06275 if (str[0] == '\\' && str[1]) 06276 str++; 06277 str++; 06278 len++; 06279 } 06280 return len; 06281 } 06282 06283 06284 /* Replace strings while appending to ds */ 06285 void replace_strings_append(REPLACE *rep, DYNAMIC_STRING* ds, 06286 const char *str, int len) 06287 { 06288 reg1 REPLACE *rep_pos; 06289 reg2 REPLACE_STRING *rep_str; 06290 const char *start, *from; 06291 DBUG_ENTER("replace_strings_append"); 06292 06293 start= from= str; 06294 rep_pos=rep+1; 06295 for (;;) 06296 { 06297 /* Loop through states */ 06298 DBUG_PRINT("info", ("Looping through states")); 06299 while (!rep_pos->found) 06300 rep_pos= rep_pos->next[(uchar) *from++]; 06301 06302 /* Does this state contain a string to be replaced */ 06303 if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string) 06304 { 06305 /* No match found */ 06306 dynstr_append_mem(ds, start, from - start - 1); 06307 DBUG_PRINT("exit", ("Found no more string to replace, appended: %s", start)); 06308 DBUG_VOID_RETURN; 06309 } 06310 06311 /* Found a string that needs to be replaced */ 06312 DBUG_PRINT("info", ("found: %d, to_offset: %d, from_offset: %d, string: %s", 06313 rep_str->found, rep_str->to_offset, 06314 rep_str->from_offset, rep_str->replace_string)); 06315 06316 /* Append part of original string before replace string */ 06317 dynstr_append_mem(ds, start, (from - rep_str->to_offset) - start); 06318 06319 /* Append replace string */ 06320 dynstr_append_mem(ds, rep_str->replace_string, 06321 strlen(rep_str->replace_string)); 06322 06323 if (!*(from-=rep_str->from_offset) && rep_pos->found != 2) 06324 { 06325 /* End of from string */ 06326 DBUG_PRINT("exit", ("Found end of from string")); 06327 DBUG_VOID_RETURN; 06328 } 06329 DBUG_ASSERT(from <= str+len); 06330 start= from; 06331 rep_pos=rep; 06332 } 06333 } 06334 06335 06336 /**************************************************************************** 06337 Replace results for a column 06338 *****************************************************************************/ 06339 06340 static void free_replace_column() 06341 { 06342 uint i; 06343 for (i=0 ; i < max_replace_column ; i++) 06344 { 06345 if (replace_column[i]) 06346 { 06347 my_free(replace_column[i], 0); 06348 replace_column[i]= 0; 06349 } 06350 } 06351 max_replace_column= 0; 06352 } 06353 06354 /* 06355 Get arguments for replace_columns. The syntax is: 06356 replace-column column_number to_string [column_number to_string ...] 06357 Where each argument may be quoted with ' or " 06358 A argument may also be a variable, in which case the value of the 06359 variable is replaced. 06360 */ 06361 06362 static void get_replace_column(struct st_query *q) 06363 { 06364 char *from=q->first_argument; 06365 char *buff,*start; 06366 DBUG_ENTER("get_replace_columns"); 06367 06368 free_replace_column(); 06369 if (!*from) 06370 die("Missing argument in %s", q->query); 06371 06372 /* Allocate a buffer for results */ 06373 start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE)); 06374 while (*from) 06375 { 06376 char *to; 06377 uint column_number; 06378 06379 to= get_string(&buff, &from, q); 06380 if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS) 06381 die("Wrong column number to replace_column in '%s'", q->query); 06382 if (!*from) 06383 die("Wrong number of arguments to replace_column in '%s'", q->query); 06384 to= get_string(&buff, &from, q); 06385 my_free(replace_column[column_number-1], MY_ALLOW_ZERO_PTR); 06386 replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE)); 06387 set_if_bigger(max_replace_column, column_number); 06388 } 06389 my_free(start, MYF(0)); 06390 q->last_argument= q->end; 06391 } 06392 06393 06394 06395
1.4.7

