1 /* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 /*
24 mysqltest
25
26 Tool used for executing a .test file
27
28 See the "MySQL Test framework manual" for more information
29 http://dev.mysql.com/doc/mysqltest/en/index.html
30
31 Please keep the test framework tools identical in all versions!
32 */
33
34 #define MTEST_VERSION "3.3"
35
36 #include "client_priv.h"
37 #include "my_default.h"
38 #include <mysql_version.h>
39 #include <mysqld_error.h>
40 #include <sql_common.h>
41 #include <m_ctype.h>
42 #include <my_dir.h>
43 #include <hash.h>
44 #include <stdarg.h>
45 #include <string>
46 #include <violite.h>
47 #include "my_regex.h" /* Our own version of regex */
48 #ifdef HAVE_SYS_WAIT_H
49 #include <sys/wait.h>
50 #endif
51 #ifdef __WIN__
52 #include <direct.h>
53 #endif
54 #include <signal.h>
55 #include <my_stacktrace.h>
56
57 #include <welcome_copyright_notice.h> // ORACLE_WELCOME_COPYRIGHT_NOTICE
58
59 #include <algorithm>
60 #include <sstream>
61
62 using std::min;
63 using std::max;
64 using std::string;
65
66 #ifdef __WIN__
67 #include <crtdbg.h>
68 #define SIGNAL_FMT "exception 0x%x"
69 #else
70 #define SIGNAL_FMT "signal %d"
71 #endif
72
73 /* Use cygwin for --exec and --system before 5.0 */
74 #if MYSQL_VERSION_ID < 50000
75 #define USE_CYGWIN
76 #endif
77
78 #define MAX_VAR_NAME_LENGTH 256
79 #define MAX_COLUMNS 256
80 #define MAX_EMBEDDED_SERVER_ARGS 64
81 #define MAX_DELIMITER_LENGTH 16
82 #define DEFAULT_MAX_CONN 128
83
84 /* Flags controlling send and reap */
85 #define QUERY_SEND_FLAG 1
86 #define QUERY_REAP_FLAG 2
87
88 #ifndef HAVE_SETENV
89 static int setenv(const char *name, const char *value, int overwrite);
90 #endif
91
92 C_MODE_START
93 static sig_handler signal_handler(int sig);
94 static my_bool get_one_option(int optid, const struct my_option *,
95 char *argument);
96 C_MODE_END
97
98 enum {
99 OPT_PS_PROTOCOL=OPT_MAX_CLIENT_OPTION, OPT_SP_PROTOCOL,
100 OPT_CURSOR_PROTOCOL, OPT_VIEW_PROTOCOL, OPT_MAX_CONNECT_RETRIES,
101 OPT_MAX_CONNECTIONS, OPT_MARK_PROGRESS, OPT_LOG_DIR,
102 OPT_TAIL_LINES, OPT_RESULT_FORMAT_VERSION, OPT_TRACE_PROTOCOL,
103 OPT_EXPLAIN_PROTOCOL, OPT_JSON_EXPLAIN_PROTOCOL
104 };
105
106 static int record= 0, opt_sleep= -1;
107 static char *opt_db= 0, *opt_pass= 0;
108 const char *opt_user= 0, *opt_host= 0, *unix_sock= 0, *opt_basedir= "./";
109 static char *shared_memory_base_name=0;
110 const char *opt_logdir= "";
111 const char *opt_include= 0, *opt_charsets_dir;
112 static int opt_port= 0;
113 static int opt_max_connect_retries;
114 static int opt_result_format_version;
115 static int opt_max_connections= DEFAULT_MAX_CONN;
116 static my_bool opt_compress= 0, silent= 0, verbose= 0;
117 static my_bool debug_info_flag= 0, debug_check_flag= 0;
118 static my_bool tty_password= 0;
119 static my_bool opt_mark_progress= 0;
120 static my_bool ps_protocol= 0, ps_protocol_enabled= 0;
121 static my_bool sp_protocol= 0, sp_protocol_enabled= 0;
122 static my_bool view_protocol= 0, view_protocol_enabled= 0;
123 static my_bool opt_trace_protocol= 0, opt_trace_protocol_enabled= 0;
124 static my_bool explain_protocol= 0, explain_protocol_enabled= 0;
125 static my_bool json_explain_protocol= 0, json_explain_protocol_enabled= 0;
126 static my_bool cursor_protocol= 0, cursor_protocol_enabled= 0;
127 static my_bool parsing_disabled= 0;
128 static my_bool display_result_vertically= FALSE, display_result_lower= FALSE,
129 display_metadata= FALSE, display_result_sorted= FALSE;
130 static my_bool disable_query_log= 0, disable_result_log= 0;
131 static my_bool disable_connect_log= 1;
132 static my_bool disable_warnings= 0;
133 static my_bool disable_info= 1;
134 static my_bool abort_on_error= 1;
135 static my_bool server_initialized= 0;
136 static my_bool is_windows= 0;
137 static char **default_argv;
138 static const char *load_default_groups[]= { "mysqltest", "client", 0 };
139 static char line_buffer[MAX_DELIMITER_LENGTH], *line_buffer_pos= line_buffer;
140 static const char *opt_server_public_key= 0;
141 static my_bool can_handle_expired_passwords= TRUE;
142
143 /* Info on properties that can be set with --enable_X and --disable_X */
144
145 struct property {
146 my_bool *var; /* Actual variable */
147 my_bool set; /* Has been set for ONE command */
148 my_bool old; /* If set, thus is the old value */
149 my_bool reverse; /* Varible is true if disabled */
150 const char *env_name; /* Env. variable name */
151 };
152
153 static struct property prop_list[] = {
154 { &abort_on_error, 0, 1, 0, "$ENABLED_ABORT_ON_ERROR" },
155 { &disable_connect_log, 0, 1, 1, "$ENABLED_CONNECT_LOG" },
156 { &disable_info, 0, 1, 1, "$ENABLED_INFO" },
157 { &display_metadata, 0, 0, 0, "$ENABLED_METADATA" },
158 { &ps_protocol_enabled, 0, 0, 0, "$ENABLED_PS_PROTOCOL" },
159 { &disable_query_log, 0, 0, 1, "$ENABLED_QUERY_LOG" },
160 { &disable_result_log, 0, 0, 1, "$ENABLED_RESULT_LOG" },
161 { &disable_warnings, 0, 0, 1, "$ENABLED_WARNINGS" }
162 };
163
164 static my_bool once_property= FALSE;
165
166 enum enum_prop {
167 P_ABORT= 0,
168 P_CONNECT,
169 P_INFO,
170 P_META,
171 P_PS,
172 P_QUERY,
173 P_RESULT,
174 P_WARN,
175 P_MAX
176 };
177
178 static uint start_lineno= 0; /* Start line of current command */
179 static uint my_end_arg= 0;
180
181 /* Number of lines of the result to include in failure report */
182 static uint opt_tail_lines= 0;
183
184 static uint opt_connect_timeout= 0;
185
186 static char delimiter[MAX_DELIMITER_LENGTH]= ";";
187 static uint delimiter_length= 1;
188
189 static char TMPDIR[FN_REFLEN];
190
191 /* Block stack */
192 enum block_cmd {
193 cmd_none,
194 cmd_if,
195 cmd_while
196 };
197
198 struct st_block
199 {
200 int line; /* Start line of block */
201 my_bool ok; /* Should block be executed */
202 enum block_cmd cmd; /* Command owning the block */
203 char delim[MAX_DELIMITER_LENGTH]; /* Delimiter before block */
204 };
205
206 static struct st_block block_stack[32];
207 static struct st_block *cur_block, *block_stack_end;
208
209 /* Open file stack */
210 struct st_test_file
211 {
212 FILE* file;
213 char *file_name;
214 uint lineno; /* Current line in file */
215 };
216
217 static struct st_test_file file_stack[16];
218 static struct st_test_file* cur_file;
219 static struct st_test_file* file_stack_end;
220
221
222 static CHARSET_INFO *charset_info= &my_charset_latin1; /* Default charset */
223
224 static const char *embedded_server_groups[]=
225 {
226 "server",
227 "embedded",
228 "mysqltest_SERVER",
229 NullS
230 };
231
232 static int embedded_server_arg_count=0;
233 static char *embedded_server_args[MAX_EMBEDDED_SERVER_ARGS];
234
235 /*
236 Timer related variables
237 See the timer_output() definition for details
238 */
239 static char *timer_file = NULL;
240 static ulonglong timer_start;
241 static void timer_output(void);
242 static ulonglong timer_now(void);
243
244
245 static ulong connection_retry_sleep= 100000; /* Microseconds */
246
247 static char *opt_plugin_dir= 0;
248
249 /* Precompiled re's */
250 static my_regex_t ps_re; /* the query can be run using PS protocol */
251 static my_regex_t sp_re; /* the query can be run as a SP */
252 static my_regex_t view_re; /* the query can be run as a view*/
253 /* the query can be traced with optimizer trace*/
254 static my_regex_t opt_trace_re;
255 static my_regex_t explain_re;/* the query can be converted to EXPLAIN */
256
257 static void init_re(void);
258 static int match_re(my_regex_t *, char *);
259 static void free_re(void);
260
261 #ifndef EMBEDDED_LIBRARY
262 static uint opt_protocol= 0;
263 #endif
264
265 DYNAMIC_ARRAY q_lines;
266
267 #include "sslopt-vars.h"
268
269 struct Parser
270 {
271 int read_lines,current_line;
272 } parser;
273
274 struct MasterPos
275 {
276 char file[FN_REFLEN];
277 ulong pos;
278 } master_pos;
279
280 /* if set, all results are concated and compared against this file */
281 const char *result_file_name= 0;
282
283 typedef struct
284 {
285 char *name;
286 int name_len;
287 char *str_val;
288 int str_val_len;
289 int int_val;
290 int alloced_len;
291 bool int_dirty; /* do not update string if int is updated until first read */
292 bool is_int;
293 bool alloced;
294 } VAR;
295
296 /*Perl/shell-like variable registers */
297 VAR var_reg[10];
298
299 HASH var_hash;
300
301 struct st_connection
302 {
303 MYSQL mysql;
304 /* Used when creating views and sp, to avoid implicit commit */
305 MYSQL* util_mysql;
306 char *name;
307 size_t name_len;
308 MYSQL_STMT* stmt;
309 /* Set after send to disallow other queries before reap */
310 my_bool pending;
311
312 #ifdef EMBEDDED_LIBRARY
313 pthread_t tid;
314 const char *cur_query;
315 int cur_query_len;
316 int command, result;
317 pthread_mutex_t query_mutex;
318 pthread_cond_t query_cond;
319 pthread_mutex_t result_mutex;
320 pthread_cond_t result_cond;
321 int query_done;
322 my_bool has_thread;
323 #endif /*EMBEDDED_LIBRARY*/
324 };
325
326 struct st_connection *connections= NULL;
327 struct st_connection* cur_con= NULL, *next_con, *connections_end;
328
329 /*
330 List of commands in mysqltest
331 Must match the "command_names" array
332 Add new commands before Q_UNKNOWN!
333 */
334 enum enum_commands {
335 Q_CONNECTION=1, Q_QUERY,
336 Q_CONNECT, Q_SLEEP, Q_REAL_SLEEP,
337 Q_INC, Q_DEC,
338 Q_SOURCE, Q_DISCONNECT,
339 Q_LET, Q_ECHO,
340 Q_WHILE, Q_END_BLOCK,
341 Q_SYSTEM, Q_RESULT,
342 Q_REQUIRE, Q_SAVE_MASTER_POS,
343 Q_SYNC_WITH_MASTER,
344 Q_SYNC_SLAVE_WITH_MASTER,
345 Q_ERROR,
346 Q_SEND, Q_REAP,
347 Q_DIRTY_CLOSE, Q_REPLACE, Q_REPLACE_COLUMN,
348 Q_PING, Q_EVAL,
349 Q_EVAL_RESULT,
350 Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
351 Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
352 Q_ENABLE_CONNECT_LOG, Q_DISABLE_CONNECT_LOG,
353 Q_WAIT_FOR_SLAVE_TO_STOP,
354 Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
355 Q_ENABLE_INFO, Q_DISABLE_INFO,
356 Q_ENABLE_METADATA, Q_DISABLE_METADATA,
357 Q_EXEC, Q_EXECW, Q_DELIMITER,
358 Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR,
359 Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS,
360 Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, Q_SORTED_RESULT,
361 Q_LOWERCASE,
362 Q_START_TIMER, Q_END_TIMER,
363 Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL,
364 Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT,
365 Q_IF,
366 Q_DISABLE_PARSING, Q_ENABLE_PARSING,
367 Q_REPLACE_REGEX, Q_REMOVE_FILE, Q_FILE_EXIST,
368 Q_WRITE_FILE, Q_COPY_FILE, Q_PERL, Q_DIE, Q_EXIT, Q_SKIP,
369 Q_CHMOD_FILE, Q_APPEND_FILE, Q_CAT_FILE, Q_DIFF_FILES,
370 Q_SEND_QUIT, Q_CHANGE_USER, Q_MKDIR, Q_RMDIR,
371 Q_LIST_FILES, Q_LIST_FILES_WRITE_FILE, Q_LIST_FILES_APPEND_FILE,
372 Q_SEND_SHUTDOWN, Q_SHUTDOWN_SERVER,
373 Q_RESULT_FORMAT_VERSION,
374 Q_MOVE_FILE, Q_REMOVE_FILES_WILDCARD, Q_SEND_EVAL,
375 Q_UNKNOWN, /* Unknown command. */
376 Q_COMMENT, /* Comments, ignored. */
377 Q_COMMENT_WITH_COMMAND,
378 Q_EMPTY_LINE
379 };
380
381
382 const char *command_names[]=
383 {
384 "connection",
385 "query",
386 "connect",
387 "sleep",
388 "real_sleep",
389 "inc",
390 "dec",
391 "source",
392 "disconnect",
393 "let",
394 "echo",
395 "while",
396 "end",
397 "system",
398 "result",
399 "require",
400 "save_master_pos",
401 "sync_with_master",
402 "sync_slave_with_master",
403 "error",
404 "send",
405 "reap",
406 "dirty_close",
407 "replace_result",
408 "replace_column",
409 "ping",
410 "eval",
411 "eval_result",
412 /* Enable/disable that the _query_ is logged to result file */
413 "enable_query_log",
414 "disable_query_log",
415 /* Enable/disable that the _result_ from a query is logged to result file */
416 "enable_result_log",
417 "disable_result_log",
418 "enable_connect_log",
419 "disable_connect_log",
420 "wait_for_slave_to_stop",
421 "enable_warnings",
422 "disable_warnings",
423 "enable_info",
424 "disable_info",
425 "enable_metadata",
426 "disable_metadata",
427 "exec",
428 "execw",
429 "delimiter",
430 "disable_abort_on_error",
431 "enable_abort_on_error",
432 "vertical_results",
433 "horizontal_results",
434 "query_vertical",
435 "query_horizontal",
436 "sorted_result",
437 "lowercase_result",
438 "start_timer",
439 "end_timer",
440 "character_set",
441 "disable_ps_protocol",
442 "enable_ps_protocol",
443 "disable_reconnect",
444 "enable_reconnect",
445 "if",
446 "disable_parsing",
447 "enable_parsing",
448 "replace_regex",
449 "remove_file",
450 "file_exists",
451 "write_file",
452 "copy_file",
453 "perl",
454 "die",
455
456 /* Don't execute any more commands, compare result */
457 "exit",
458 "skip",
459 "chmod",
460 "append_file",
461 "cat_file",
462 "diff_files",
463 "send_quit",
464 "change_user",
465 "mkdir",
466 "rmdir",
467 "list_files",
468 "list_files_write_file",
469 "list_files_append_file",
470 "send_shutdown",
471 "shutdown_server",
472 "result_format",
473 "move_file",
474 "remove_files_wildcard",
475 "send_eval",
476
477 0
478 };
479
480
481 /*
482 The list of error codes to --error are stored in an internal array of
483 structs. This struct can hold numeric SQL error codes, error names or
484 SQLSTATE codes as strings. The element next to the last active element
485 in the list is set to type ERR_EMPTY. When an SQL statement returns an
486 error, we use this list to check if this is an expected error.
487 */
488 enum match_err_type
489 {
490 ERR_EMPTY= 0,
491 ERR_ERRNO,
492 ERR_SQLSTATE
493 };
494
495 struct st_match_err
496 {
497 enum match_err_type type;
498 union
499 {
500 uint errnum;
501 char sqlstate[SQLSTATE_LENGTH+1]; /* \0 terminated string */
502 } code;
503 };
504
505 struct st_expected_errors
506 {
507 struct st_match_err err[10];
508 uint count;
509 };
510 static struct st_expected_errors saved_expected_errors;
511
512 struct st_command
513 {
514 char *query, *query_buf,*first_argument,*last_argument,*end;
515 DYNAMIC_STRING content;
516 int first_word_len, query_len;
517 my_bool abort_on_error, used_replace;
518 struct st_expected_errors expected_errors;
519 char require_file[FN_REFLEN];
520 enum enum_commands type;
521 };
522
523 TYPELIB command_typelib= {array_elements(command_names),"",
524 command_names, 0};
525
526 DYNAMIC_STRING ds_res;
527 /* Points to ds_warning in run_query, so it can be freed */
528 DYNAMIC_STRING *ds_warn= 0;
529 struct st_command *curr_command= 0;
530
531 char builtin_echo[FN_REFLEN];
532
533 struct st_replace_regex
534 {
535 DYNAMIC_ARRAY regex_arr; /* stores a list of st_regex subsitutions */
536
537 /*
538 Temporary storage areas for substitutions. To reduce unnessary copying
539 and memory freeing/allocation, we pre-allocate two buffers, and alternate
540 their use, one for input/one for output, the roles changing on the next
541 st_regex substition. At the end of substitutions buf points to the
542 one containing the final result.
543 */
544 char* buf;
545 char* even_buf;
546 char* odd_buf;
547 int even_buf_len;
548 int odd_buf_len;
549 };
550
551 struct st_replace_regex *glob_replace_regex= 0;
552
553 struct st_replace;
554 struct st_replace *glob_replace= 0;
555 void replace_strings_append(struct st_replace *rep, DYNAMIC_STRING* ds,
556 const char *from, int len);
557
558 static void cleanup_and_exit(int exit_code) MY_ATTRIBUTE((noreturn));
559
560 void die(const char *fmt, ...)
561 ATTRIBUTE_FORMAT(printf, 1, 2) MY_ATTRIBUTE((noreturn));
562 void abort_not_supported_test(const char *fmt, ...)
563 ATTRIBUTE_FORMAT(printf, 1, 2) MY_ATTRIBUTE((noreturn));
564 void verbose_msg(const char *fmt, ...)
565 ATTRIBUTE_FORMAT(printf, 1, 2);
566 void log_msg(const char *fmt, ...)
567 ATTRIBUTE_FORMAT(printf, 1, 2);
568
569 VAR* var_from_env(const char *, const char *);
570 VAR* var_init(VAR* v, const char *name, int name_len, const char *val,
571 int val_len);
572 VAR* var_get(const char *var_name, const char** var_name_end,
573 my_bool raw, my_bool ignore_not_existing);
574 void eval_expr(VAR* v, const char *p, const char** p_end,
575 bool open_end=false, bool do_eval=true);
576 my_bool match_delimiter(int c, const char *delim, uint length);
577 void dump_result_to_reject_file(char *buf, int size);
578 void dump_warning_messages();
579
580 void do_eval(DYNAMIC_STRING *query_eval, const char *query,
581 const char *query_end, my_bool pass_through_escape_chars);
582 void str_to_file(const char *fname, char *str, int size);
583 void str_to_file2(const char *fname, char *str, int size, my_bool append);
584
585 void fix_win_paths(const char *val, int len);
586 const char *get_errname_from_code (uint error_code);
587 int multi_reg_replace(struct st_replace_regex* r,char* val);
588
589 #ifdef __WIN__
590 void free_tmp_sh_file();
591 void free_win_path_patterns();
592 #endif
593
594
595 /* For replace_column */
596 static char *replace_column[MAX_COLUMNS];
597 static uint max_replace_column= 0;
598 void do_get_replace_column(struct st_command*);
599 void free_replace_column();
600
601 /* For replace */
602 void do_get_replace(struct st_command *command);
603 void free_replace();
604
605 /* For replace_regex */
606 void do_get_replace_regex(struct st_command *command);
607 void free_replace_regex();
608
609 /* Used by sleep */
610 void check_eol_junk_line(const char *eol);
611
free_all_replace()612 void free_all_replace(){
613 free_replace();
614 free_replace_regex();
615 free_replace_column();
616 }
617
618
619 class LogFile {
620 FILE* m_file;
621 char m_file_name[FN_REFLEN];
622 size_t m_bytes_written;
623 public:
LogFile()624 LogFile() : m_file(NULL), m_bytes_written(0) {
625 memset(m_file_name, 0, sizeof(m_file_name));
626 }
627
~LogFile()628 ~LogFile() {
629 close();
630 }
631
file_name() const632 const char* file_name() const { return m_file_name; }
bytes_written() const633 size_t bytes_written() const { return m_bytes_written; }
634
open(const char * dir,const char * name,const char * ext)635 void open(const char* dir, const char* name, const char* ext)
636 {
637 DBUG_ENTER("LogFile::open");
638 DBUG_PRINT("enter", ("dir: '%s', name: '%s'",
639 dir, name));
640 if (!name)
641 {
642 m_file= stdout;
643 DBUG_VOID_RETURN;
644 }
645
646 fn_format(m_file_name, name, dir, ext,
647 *dir ? MY_REPLACE_DIR | MY_REPLACE_EXT :
648 MY_REPLACE_EXT);
649
650 DBUG_PRINT("info", ("file_name: %s", m_file_name));
651
652 if ((m_file= fopen(m_file_name, "wb+")) == NULL)
653 die("Failed to open log file %s, errno: %d", m_file_name, errno);
654
655 DBUG_VOID_RETURN;
656 }
657
close()658 void close()
659 {
660 if (m_file) {
661 if (m_file != stdout)
662 fclose(m_file);
663 else
664 fflush(m_file);
665 }
666 m_file= NULL;
667 }
668
flush()669 void flush()
670 {
671 if (m_file && m_file != stdout)
672 {
673 if (fflush(m_file))
674 die("Failed to flush '%s', errno: %d", m_file_name, errno);
675 }
676 }
677
write(DYNAMIC_STRING * ds)678 void write(DYNAMIC_STRING* ds)
679 {
680 DBUG_ENTER("LogFile::write");
681 DBUG_ASSERT(m_file);
682
683 if (ds->length == 0)
684 DBUG_VOID_RETURN;
685 DBUG_ASSERT(ds->str);
686
687 if (fwrite(ds->str, 1, ds->length, m_file) != ds->length)
688 die("Failed to write %lu bytes to '%s', errno: %d",
689 (unsigned long)ds->length, m_file_name, errno);
690 m_bytes_written+= ds->length;
691 DBUG_VOID_RETURN;
692 }
693
show_tail(uint lines)694 void show_tail(uint lines) {
695 DBUG_ENTER("LogFile::show_tail");
696
697 if (!m_file || m_file == stdout)
698 DBUG_VOID_RETURN;
699
700 if (lines == 0)
701 DBUG_VOID_RETURN;
702 lines++;
703
704 int show_offset= 0;
705 char buf[256];
706 size_t bytes;
707 bool found_bof= false;
708
709 /* Search backward in file until "lines" newline has been found */
710 while (lines && !found_bof)
711 {
712 show_offset-= sizeof(buf);
713 while(fseek(m_file, show_offset, SEEK_END) != 0 && show_offset < 0)
714 {
715 found_bof= true;
716 // Seeking before start of file
717 show_offset++;
718 }
719
720 if ((bytes= fread(buf, 1, sizeof(buf), m_file)) <= 0)
721 {
722 // ferror=0 will happen here if no queries executed yet
723 if (ferror(m_file))
724 fprintf(stderr,
725 "Failed to read from '%s', errno: %d, feof:%d, ferror:%d\n",
726 m_file_name, errno, feof(m_file), ferror(m_file));
727 DBUG_VOID_RETURN;
728 }
729
730 DBUG_PRINT("info", ("Read %lu bytes from file, buf: %s",
731 (unsigned long)bytes, buf));
732
733 char* show_from= buf + bytes;
734 while(show_from > buf && lines > 0 )
735 {
736 show_from--;
737 if (*show_from == '\n')
738 lines--;
739 }
740 if (show_from != buf)
741 {
742 // The last new line was found in this buf, adjust offset
743 show_offset+= (show_from - buf) + 1;
744 DBUG_PRINT("info", ("adjusted offset to %d", show_offset));
745 }
746 DBUG_PRINT("info", ("show_offset: %d", show_offset));
747 }
748
749 fprintf(stderr, "\nThe result from queries just before the failure was:\n");
750
751 DBUG_PRINT("info", ("show_offset: %d", show_offset));
752 if (!lines)
753 {
754 fprintf(stderr, "< snip >\n");
755
756 if (fseek(m_file, show_offset, SEEK_END) != 0)
757 {
758 fprintf(stderr, "Failed to seek to position %d in '%s', errno: %d",
759 show_offset, m_file_name, errno);
760 DBUG_VOID_RETURN;
761 }
762
763 }
764 else {
765 DBUG_PRINT("info", ("Showing the whole file"));
766 if (fseek(m_file, 0L, SEEK_SET) != 0)
767 {
768 fprintf(stderr, "Failed to seek to pos 0 in '%s', errno: %d",
769 m_file_name, errno);
770 DBUG_VOID_RETURN;
771 }
772 }
773
774 while ((bytes= fread(buf, 1, sizeof(buf), m_file)) > 0)
775 fwrite(buf, 1, bytes, stderr);
776
777 fflush(stderr);
778
779 DBUG_VOID_RETURN;
780 }
781 };
782
783 LogFile log_file;
784 LogFile progress_file;
785
786 void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
787 int len);
788 void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val);
789 void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val);
790 void dynstr_append_sorted(DYNAMIC_STRING* ds, DYNAMIC_STRING* ds_input);
791
792 static int match_expected_error(struct st_command *command,
793 unsigned int err_errno,
794 const char *err_sqlstate);
795 void handle_error(struct st_command*,
796 unsigned int err_errno, const char *err_error,
797 const char *err_sqlstate, DYNAMIC_STRING *ds);
798 void handle_no_error(struct st_command*);
799 void revert_properties();
800
801 #ifdef EMBEDDED_LIBRARY
802
803 #define EMB_SEND_QUERY 1
804 #define EMB_READ_QUERY_RESULT 2
805 #define EMB_END_CONNECTION 3
806
807 /* attributes of the query thread */
808 pthread_attr_t cn_thd_attrib;
809
810
811 /*
812 This procedure represents the connection and actually
813 runs queries when in the EMBEDDED-SERVER mode.
814 The run_query_normal() just sends request for running
815 mysql_send_query and mysql_read_query_result() here.
816 */
817
connection_thread(void * arg)818 pthread_handler_t connection_thread(void *arg)
819 {
820 struct st_connection *cn= (struct st_connection*)arg;
821
822 mysql_thread_init();
823 while (cn->command != EMB_END_CONNECTION)
824 {
825 if (!cn->command)
826 {
827 pthread_mutex_lock(&cn->query_mutex);
828 while (!cn->command)
829 pthread_cond_wait(&cn->query_cond, &cn->query_mutex);
830 pthread_mutex_unlock(&cn->query_mutex);
831 }
832 switch (cn->command)
833 {
834 case EMB_END_CONNECTION:
835 goto end_thread;
836 case EMB_SEND_QUERY:
837 cn->result= mysql_send_query(&cn->mysql, cn->cur_query, cn->cur_query_len);
838 break;
839 case EMB_READ_QUERY_RESULT:
840 cn->result= mysql_read_query_result(&cn->mysql);
841 break;
842 default:
843 DBUG_ASSERT(0);
844 }
845 cn->command= 0;
846 pthread_mutex_lock(&cn->result_mutex);
847 cn->query_done= 1;
848 pthread_cond_signal(&cn->result_cond);
849 pthread_mutex_unlock(&cn->result_mutex);
850 }
851
852 end_thread:
853 cn->query_done= 1;
854 mysql_thread_end();
855 pthread_exit(0);
856 return 0;
857 }
858
wait_query_thread_done(struct st_connection * con)859 static void wait_query_thread_done(struct st_connection *con)
860 {
861 DBUG_ASSERT(con->has_thread);
862 if (!con->query_done)
863 {
864 pthread_mutex_lock(&con->result_mutex);
865 while (!con->query_done)
866 pthread_cond_wait(&con->result_cond, &con->result_mutex);
867 pthread_mutex_unlock(&con->result_mutex);
868 }
869 }
870
871
signal_connection_thd(struct st_connection * cn,int command)872 static void signal_connection_thd(struct st_connection *cn, int command)
873 {
874 DBUG_ASSERT(cn->has_thread);
875 cn->query_done= 0;
876 cn->command= command;
877 pthread_mutex_lock(&cn->query_mutex);
878 pthread_cond_signal(&cn->query_cond);
879 pthread_mutex_unlock(&cn->query_mutex);
880 }
881
882
883 /*
884 Sometimes we try to execute queries when the connection is closed.
885 It's done to make sure it was closed completely.
886 So that if our connection is closed (cn->has_thread == 0), we just return
887 the mysql_send_query() result which is an error in this case.
888 */
889
do_send_query(struct st_connection * cn,const char * q,int q_len)890 static int do_send_query(struct st_connection *cn, const char *q, int q_len)
891 {
892 if (!cn->has_thread)
893 return mysql_send_query(&cn->mysql, q, q_len);
894 cn->cur_query= q;
895 cn->cur_query_len= q_len;
896 signal_connection_thd(cn, EMB_SEND_QUERY);
897 return 0;
898 }
899
do_read_query_result(struct st_connection * cn)900 static int do_read_query_result(struct st_connection *cn)
901 {
902 DBUG_ASSERT(cn->has_thread);
903 wait_query_thread_done(cn);
904 signal_connection_thd(cn, EMB_READ_QUERY_RESULT);
905 wait_query_thread_done(cn);
906
907 return cn->result;
908 }
909
910
emb_close_connection(struct st_connection * cn)911 static void emb_close_connection(struct st_connection *cn)
912 {
913 if (!cn->has_thread)
914 return;
915 wait_query_thread_done(cn);
916 signal_connection_thd(cn, EMB_END_CONNECTION);
917 pthread_join(cn->tid, NULL);
918 cn->has_thread= FALSE;
919 pthread_mutex_destroy(&cn->query_mutex);
920 pthread_cond_destroy(&cn->query_cond);
921 pthread_mutex_destroy(&cn->result_mutex);
922 pthread_cond_destroy(&cn->result_cond);
923 }
924
925
init_connection_thd(struct st_connection * cn)926 static void init_connection_thd(struct st_connection *cn)
927 {
928 cn->query_done= 1;
929 cn->command= 0;
930 if (pthread_mutex_init(&cn->query_mutex, NULL) ||
931 pthread_cond_init(&cn->query_cond, NULL) ||
932 pthread_mutex_init(&cn->result_mutex, NULL) ||
933 pthread_cond_init(&cn->result_cond, NULL) ||
934 pthread_create(&cn->tid, &cn_thd_attrib, connection_thread, (void*)cn))
935 die("Error in the thread library");
936 cn->has_thread=TRUE;
937 }
938
939 #else /*EMBEDDED_LIBRARY*/
940
941 #define do_send_query(cn,q,q_len) mysql_send_query(&cn->mysql, q, q_len)
942 #define do_read_query_result(cn) mysql_read_query_result(&cn->mysql)
943
944 #endif /*EMBEDDED_LIBRARY*/
945
do_eval(DYNAMIC_STRING * query_eval,const char * query,const char * query_end,my_bool pass_through_escape_chars)946 void do_eval(DYNAMIC_STRING *query_eval, const char *query,
947 const char *query_end, my_bool pass_through_escape_chars)
948 {
949 const char *p;
950 char c, next_c;
951 int escaped = 0;
952 VAR *v;
953 DBUG_ENTER("do_eval");
954
955 for (p= query; (c= *p) && p < query_end; ++p)
956 {
957 switch(c) {
958 case '$':
959 if (escaped)
960 {
961 escaped= 0;
962 dynstr_append_mem(query_eval, p, 1);
963 }
964 else
965 {
966 if (!(v= var_get(p, &p, 0, 0)))
967 die("Bad variable in eval");
968 dynstr_append_mem(query_eval, v->str_val, v->str_val_len);
969 }
970 break;
971 case '\\':
972 next_c= *(p+1);
973 if (escaped)
974 {
975 escaped= 0;
976 dynstr_append_mem(query_eval, p, 1);
977 }
978 else if (next_c == '\\' || next_c == '$' || next_c == '"')
979 {
980 /* Set escaped only if next char is \, " or $ */
981 escaped= 1;
982
983 if (pass_through_escape_chars)
984 {
985 /* The escape char should be added to the output string. */
986 dynstr_append_mem(query_eval, p, 1);
987 }
988 }
989 else
990 dynstr_append_mem(query_eval, p, 1);
991 break;
992 default:
993 escaped= 0;
994 dynstr_append_mem(query_eval, p, 1);
995 break;
996 }
997 }
998 #ifdef __WIN__
999 fix_win_paths(query_eval->str, query_eval->length);
1000 #endif
1001 DBUG_VOID_RETURN;
1002 }
1003
1004
1005 /*
1006 Run query and dump the result to stderr in vertical format
1007
1008 NOTE! This function should be safe to call when an error
1009 has occured and thus any further errors will be ignored(although logged)
1010
1011 SYNOPSIS
1012 show_query
1013 mysql - connection to use
1014 query - query to run
1015
1016 */
1017
show_query(MYSQL * mysql,const char * query)1018 static void show_query(MYSQL* mysql, const char* query)
1019 {
1020 MYSQL_RES* res;
1021 DBUG_ENTER("show_query");
1022
1023 if (!mysql)
1024 DBUG_VOID_RETURN;
1025
1026 if (mysql_query(mysql, query))
1027 {
1028 log_msg("Error running query '%s': %d %s",
1029 query, mysql_errno(mysql), mysql_error(mysql));
1030 DBUG_VOID_RETURN;
1031 }
1032
1033 if ((res= mysql_store_result(mysql)) == NULL)
1034 {
1035 /* No result set returned */
1036 DBUG_VOID_RETURN;
1037 }
1038
1039 {
1040 MYSQL_ROW row;
1041 unsigned int i;
1042 unsigned int row_num= 0;
1043 unsigned int num_fields= mysql_num_fields(res);
1044 MYSQL_FIELD *fields= mysql_fetch_fields(res);
1045
1046 fprintf(stderr, "=== %s ===\n", query);
1047 while ((row= mysql_fetch_row(res)))
1048 {
1049 unsigned long *lengths= mysql_fetch_lengths(res);
1050 row_num++;
1051
1052 fprintf(stderr, "---- %d. ----\n", row_num);
1053 for(i= 0; i < num_fields; i++)
1054 {
1055 /* looks ugly , but put here to convince parfait */
1056 assert(lengths);
1057 fprintf(stderr, "%s\t%.*s\n",
1058 fields[i].name,
1059 (int)lengths[i], row[i] ? row[i] : "NULL");
1060 }
1061 }
1062 for (i= 0; i < strlen(query)+8; i++)
1063 fprintf(stderr, "=");
1064 fprintf(stderr, "\n\n");
1065 }
1066 mysql_free_result(res);
1067
1068 DBUG_VOID_RETURN;
1069 }
1070
1071
1072 /*
1073 Show any warnings just before the error. Since the last error
1074 is added to the warning stack, only print @@warning_count-1 warnings.
1075
1076 NOTE! This function should be safe to call when an error
1077 has occured and this any further errors will be ignored(although logged)
1078
1079 SYNOPSIS
1080 show_warnings_before_error
1081 mysql - connection to use
1082
1083 */
1084
show_warnings_before_error(MYSQL * mysql)1085 static void show_warnings_before_error(MYSQL* mysql)
1086 {
1087 MYSQL_RES* res;
1088 const char* query= "SHOW WARNINGS";
1089 DBUG_ENTER("show_warnings_before_error");
1090
1091 if (!mysql)
1092 DBUG_VOID_RETURN;
1093
1094 if (mysql_query(mysql, query))
1095 {
1096 log_msg("Error running query '%s': %d %s",
1097 query, mysql_errno(mysql), mysql_error(mysql));
1098 DBUG_VOID_RETURN;
1099 }
1100
1101 if ((res= mysql_store_result(mysql)) == NULL)
1102 {
1103 /* No result set returned */
1104 DBUG_VOID_RETURN;
1105 }
1106
1107 if (mysql_num_rows(res) <= 1)
1108 {
1109 /* Don't display the last row, it's "last error" */
1110 }
1111 else
1112 {
1113 MYSQL_ROW row;
1114 unsigned int row_num= 0;
1115 unsigned int num_fields= mysql_num_fields(res);
1116
1117 fprintf(stderr, "\nWarnings from just before the error:\n");
1118 while ((row= mysql_fetch_row(res)))
1119 {
1120 unsigned int i;
1121 unsigned long *lengths= mysql_fetch_lengths(res);
1122
1123 if (++row_num >= mysql_num_rows(res))
1124 {
1125 /* Don't display the last row, it's "last error" */
1126 break;
1127 }
1128
1129 for(i= 0; i < num_fields; i++)
1130 {
1131 /* looks ugly , but put here to convince parfait */
1132 assert(lengths);
1133 fprintf(stderr, "%.*s ", (int)lengths[i],
1134 row[i] ? row[i] : "NULL");
1135 }
1136 fprintf(stderr, "\n");
1137 }
1138 }
1139 mysql_free_result(res);
1140
1141 DBUG_VOID_RETURN;
1142 }
1143
1144
1145 enum arg_type
1146 {
1147 ARG_STRING,
1148 ARG_REST
1149 };
1150
1151 struct command_arg {
1152 const char *argname; /* Name of argument */
1153 enum arg_type type; /* Type of argument */
1154 my_bool required; /* Argument required */
1155 DYNAMIC_STRING *ds; /* Storage for argument */
1156 const char *description; /* Description of the argument */
1157 };
1158
1159
check_command_args(struct st_command * command,const char * arguments,const struct command_arg * args,int num_args,const char delimiter_arg)1160 void check_command_args(struct st_command *command,
1161 const char *arguments,
1162 const struct command_arg *args,
1163 int num_args, const char delimiter_arg)
1164 {
1165 int i;
1166 const char *ptr= arguments;
1167 const char *start;
1168 DBUG_ENTER("check_command_args");
1169 DBUG_PRINT("enter", ("num_args: %d", num_args));
1170
1171 for (i= 0; i < num_args; i++)
1172 {
1173 const struct command_arg *arg= &args[i];
1174 char delimiter;
1175
1176 switch (arg->type) {
1177 /* A string */
1178 case ARG_STRING:
1179 /* Skip leading spaces */
1180 while (*ptr && *ptr == ' ')
1181 ptr++;
1182 start= ptr;
1183 delimiter = delimiter_arg;
1184 /* If start of arg is ' ` or " search to matching quote end instead */
1185 if (*ptr && strchr ("'`\"", *ptr))
1186 {
1187 delimiter= *ptr;
1188 start= ++ptr;
1189 }
1190 /* Find end of arg, terminated by "delimiter" */
1191 while (*ptr && *ptr != delimiter)
1192 ptr++;
1193 if (ptr > start)
1194 {
1195 init_dynamic_string(arg->ds, 0, ptr-start, 32);
1196 do_eval(arg->ds, start, ptr, FALSE);
1197 }
1198 else
1199 {
1200 /* Empty string */
1201 init_dynamic_string(arg->ds, "", 0, 0);
1202 }
1203 /* Find real end of arg, terminated by "delimiter_arg" */
1204 /* This will do nothing if arg was not closed by quotes */
1205 while (*ptr && *ptr != delimiter_arg)
1206 ptr++;
1207
1208 command->last_argument= (char*)ptr;
1209
1210 /* Step past the delimiter */
1211 if (*ptr && *ptr == delimiter_arg)
1212 ptr++;
1213 DBUG_PRINT("info", ("val: %s", arg->ds->str));
1214 break;
1215
1216 /* Rest of line */
1217 case ARG_REST:
1218 start= ptr;
1219 init_dynamic_string(arg->ds, 0, command->query_len, 256);
1220 do_eval(arg->ds, start, command->end, FALSE);
1221 command->last_argument= command->end;
1222 DBUG_PRINT("info", ("val: %s", arg->ds->str));
1223 break;
1224
1225 default:
1226 DBUG_ASSERT("Unknown argument type");
1227 break;
1228 }
1229
1230 /* Check required arg */
1231 if (arg->ds->length == 0 && arg->required)
1232 die("Missing required argument '%s' to command '%.*s'", arg->argname,
1233 command->first_word_len, command->query);
1234
1235 }
1236 /* Check for too many arguments passed */
1237 ptr= command->last_argument;
1238 while(ptr <= command->end && *ptr != '#')
1239 {
1240 if (*ptr && *ptr != ' ')
1241 die("Extra argument '%s' passed to '%.*s'",
1242 ptr, command->first_word_len, command->query);
1243 ptr++;
1244 }
1245 DBUG_VOID_RETURN;
1246 }
1247
handle_command_error(struct st_command * command,uint error)1248 void handle_command_error(struct st_command *command, uint error)
1249 {
1250 DBUG_ENTER("handle_command_error");
1251 DBUG_PRINT("enter", ("error: %d", error));
1252 if (error != 0)
1253 {
1254 int i;
1255
1256 if (command->abort_on_error)
1257 die("command \"%.*s\" failed with error %d. my_errno=%d",
1258 command->first_word_len, command->query, error, my_errno);
1259
1260 i= match_expected_error(command, error, NULL);
1261
1262 if (i >= 0)
1263 {
1264 DBUG_PRINT("info", ("command \"%.*s\" failed with expected error: %d",
1265 command->first_word_len, command->query, error));
1266 revert_properties();
1267 DBUG_VOID_RETURN;
1268 }
1269 if (command->expected_errors.count > 0)
1270 die("command \"%.*s\" failed with wrong error: %d. my_errno=%d",
1271 command->first_word_len, command->query, error, my_errno);
1272 }
1273 else if (command->expected_errors.err[0].type == ERR_ERRNO &&
1274 command->expected_errors.err[0].code.errnum != 0)
1275 {
1276 /* Error code we wanted was != 0, i.e. not an expected success */
1277 die("command \"%.*s\" succeeded - should have failed with errno %d...",
1278 command->first_word_len, command->query,
1279 command->expected_errors.err[0].code.errnum);
1280 }
1281 revert_properties();
1282 DBUG_VOID_RETURN;
1283 }
1284
1285
close_connections()1286 void close_connections()
1287 {
1288 DBUG_ENTER("close_connections");
1289 for (--next_con; next_con >= connections; --next_con)
1290 {
1291 #ifdef EMBEDDED_LIBRARY
1292 emb_close_connection(next_con);
1293 #endif
1294 if (next_con->stmt)
1295 mysql_stmt_close(next_con->stmt);
1296 next_con->stmt= 0;
1297 mysql_close(&next_con->mysql);
1298 if (next_con->util_mysql)
1299 mysql_close(next_con->util_mysql);
1300 my_free(next_con->name);
1301 }
1302 my_free(connections);
1303 DBUG_VOID_RETURN;
1304 }
1305
1306
close_statements()1307 void close_statements()
1308 {
1309 struct st_connection *con;
1310 DBUG_ENTER("close_statements");
1311 for (con= connections; con < next_con; con++)
1312 {
1313 if (con->stmt)
1314 mysql_stmt_close(con->stmt);
1315 con->stmt= 0;
1316 }
1317 DBUG_VOID_RETURN;
1318 }
1319
1320
close_files()1321 void close_files()
1322 {
1323 DBUG_ENTER("close_files");
1324 for (; cur_file >= file_stack; cur_file--)
1325 {
1326 if (cur_file->file && cur_file->file != stdin)
1327 {
1328 DBUG_PRINT("info", ("closing file: %s", cur_file->file_name));
1329 fclose(cur_file->file);
1330 }
1331 my_free(cur_file->file_name);
1332 cur_file->file_name= 0;
1333 }
1334 DBUG_VOID_RETURN;
1335 }
1336
1337
free_used_memory()1338 void free_used_memory()
1339 {
1340 uint i;
1341 // Do not use DBUG_ENTER("free_used_memory"); here, see below.
1342
1343 if (connections)
1344 close_connections();
1345 close_files();
1346 my_hash_free(&var_hash);
1347
1348 for (i= 0 ; i < q_lines.elements ; i++)
1349 {
1350 struct st_command **q= dynamic_element(&q_lines, i, struct st_command**);
1351 my_free((*q)->query_buf);
1352 if ((*q)->content.str)
1353 dynstr_free(&(*q)->content);
1354 my_free((*q));
1355 }
1356 for (i= 0; i < 10; i++)
1357 {
1358 if (var_reg[i].alloced_len)
1359 my_free(var_reg[i].str_val);
1360 }
1361 while (embedded_server_arg_count > 1)
1362 my_free(embedded_server_args[--embedded_server_arg_count]);
1363 delete_dynamic(&q_lines);
1364 dynstr_free(&ds_res);
1365 if (ds_warn)
1366 dynstr_free(ds_warn);
1367 free_all_replace();
1368 my_free(opt_pass);
1369 free_defaults(default_argv);
1370 free_re();
1371 #ifdef __WIN__
1372 free_tmp_sh_file();
1373 free_win_path_patterns();
1374 #endif
1375
1376 /* Only call mysql_server_end if mysql_server_init has been called */
1377 if (server_initialized)
1378 mysql_server_end();
1379
1380 /* Don't use DBUG after mysql_server_end() */
1381 return;
1382 }
1383
1384
cleanup_and_exit(int exit_code)1385 static void cleanup_and_exit(int exit_code)
1386 {
1387 free_used_memory();
1388 my_end(my_end_arg);
1389
1390 if (!silent) {
1391 switch (exit_code) {
1392 case 1:
1393 printf("not ok\n");
1394 break;
1395 case 0:
1396 printf("ok\n");
1397 break;
1398 case 62:
1399 printf("skipped\n");
1400 break;
1401 default:
1402 printf("unknown exit code: %d\n", exit_code);
1403 DBUG_ASSERT(0);
1404 }
1405 }
1406
1407 /* exit() appears to be not 100% reliable on Windows under some conditions */
1408 #ifdef __WIN__
1409 fflush(stdout);
1410 fflush(stderr);
1411 _exit(exit_code);
1412 #else
1413 exit(exit_code);
1414 #endif
1415 }
1416
print_file_stack()1417 void print_file_stack()
1418 {
1419 for (struct st_test_file* err_file= cur_file;
1420 err_file != file_stack;
1421 err_file--)
1422 {
1423 fprintf(stderr, "included from %s at line %d:\n",
1424 err_file->file_name, err_file->lineno);
1425 }
1426 }
1427
die(const char * fmt,...)1428 void die(const char *fmt, ...)
1429 {
1430 static int dying= 0;
1431 va_list args;
1432 DBUG_PRINT("enter", ("start_lineno: %d", start_lineno));
1433
1434 /*
1435 Protect against dying twice
1436 first time 'die' is called, try to write log files
1437 second time, just exit
1438 */
1439 if (dying)
1440 cleanup_and_exit(1);
1441 dying= 1;
1442
1443 /* Print the error message */
1444 fprintf(stderr, "mysqltest: ");
1445 if (cur_file && cur_file != file_stack)
1446 {
1447 fprintf(stderr, "In included file \"%s\": \n",
1448 cur_file->file_name);
1449 print_file_stack();
1450 }
1451
1452 if (start_lineno > 0)
1453 fprintf(stderr, "At line %u: ", start_lineno);
1454 if (fmt)
1455 {
1456 va_start(args, fmt);
1457 vfprintf(stderr, fmt, args);
1458 va_end(args);
1459 }
1460 else
1461 fprintf(stderr, "unknown error");
1462 fprintf(stderr, "\n");
1463 fflush(stderr);
1464
1465 log_file.show_tail(opt_tail_lines);
1466
1467 /*
1468 Help debugging by displaying any warnings that might have
1469 been produced prior to the error
1470 */
1471 if (cur_con && !cur_con->pending)
1472 show_warnings_before_error(&cur_con->mysql);
1473
1474 cleanup_and_exit(1);
1475 }
1476
1477
abort_not_supported_test(const char * fmt,...)1478 void abort_not_supported_test(const char *fmt, ...)
1479 {
1480 va_list args;
1481 DBUG_ENTER("abort_not_supported_test");
1482
1483 /* Print include filestack */
1484 fprintf(stderr, "The test '%s' is not supported by this installation\n",
1485 file_stack->file_name);
1486 fprintf(stderr, "Detected in file %s at line %d\n",
1487 cur_file->file_name, cur_file->lineno);
1488 print_file_stack();
1489
1490 /* Print error message */
1491 va_start(args, fmt);
1492 if (fmt)
1493 {
1494 fprintf(stderr, "reason: ");
1495 vfprintf(stderr, fmt, args);
1496 fprintf(stderr, "\n");
1497 fflush(stderr);
1498 }
1499 va_end(args);
1500
1501 cleanup_and_exit(62);
1502 }
1503
1504
abort_not_in_this_version()1505 void abort_not_in_this_version()
1506 {
1507 die("Not available in this version of mysqltest");
1508 }
1509
1510
verbose_msg(const char * fmt,...)1511 void verbose_msg(const char *fmt, ...)
1512 {
1513 va_list args;
1514 DBUG_ENTER("verbose_msg");
1515 if (!verbose)
1516 DBUG_VOID_RETURN;
1517
1518 va_start(args, fmt);
1519 fprintf(stderr, "mysqltest: ");
1520 if (cur_file && cur_file != file_stack)
1521 fprintf(stderr, "In included file \"%s\": ",
1522 cur_file->file_name);
1523 if (start_lineno != 0)
1524 fprintf(stderr, "At line %u: ", start_lineno);
1525 vfprintf(stderr, fmt, args);
1526 fprintf(stderr, "\n");
1527 va_end(args);
1528
1529 DBUG_VOID_RETURN;
1530 }
1531
1532
log_msg(const char * fmt,...)1533 void log_msg(const char *fmt, ...)
1534 {
1535 va_list args;
1536 char buff[1024];
1537 size_t len;
1538 DBUG_ENTER("log_msg");
1539
1540 va_start(args, fmt);
1541 len= my_vsnprintf(buff, sizeof(buff)-1, fmt, args);
1542 va_end(args);
1543
1544 dynstr_append_mem(&ds_res, buff, len);
1545 dynstr_append(&ds_res, "\n");
1546
1547 DBUG_VOID_RETURN;
1548 }
1549
1550
1551 /*
1552 Read a file and append it to ds
1553
1554 SYNOPSIS
1555 cat_file
1556 ds - pointer to dynamic string where to add the files content
1557 filename - name of the file to read
1558
1559 */
1560
cat_file(DYNAMIC_STRING * ds,const char * filename)1561 int cat_file(DYNAMIC_STRING* ds, const char* filename)
1562 {
1563 int fd;
1564 size_t len;
1565 char buff[512];
1566
1567 if ((fd= my_open(filename, O_RDONLY, MYF(0))) < 0)
1568 return 1;
1569 while((len= my_read(fd, (uchar*)&buff,
1570 sizeof(buff), MYF(0))) > 0)
1571 {
1572 char *p= buff, *start= buff;
1573 while (p < buff+len)
1574 {
1575 /* Convert cr/lf to lf */
1576 if (*p == '\r' && *(p+1) && *(p+1)== '\n')
1577 {
1578 /* Add fake newline instead of cr and output the line */
1579 *p= '\n';
1580 p++; /* Step past the "fake" newline */
1581 dynstr_append_mem(ds, start, p-start);
1582 p++; /* Step past the "fake" newline */
1583 start= p;
1584 }
1585 else
1586 p++;
1587 }
1588
1589 /* Output any chars that migh be left */
1590 dynstr_append_mem(ds, start, p-start);
1591 }
1592 my_close(fd, MYF(0));
1593 return 0;
1594 }
1595
1596
1597 /*
1598 Run the specified command with popen
1599
1600 SYNOPSIS
1601 run_command
1602 cmd - command to execute(should be properly quoted
1603 ds_res- pointer to dynamic string where to store the result
1604
1605 */
1606
run_command(char * cmd,DYNAMIC_STRING * ds_res)1607 static int run_command(char* cmd,
1608 DYNAMIC_STRING *ds_res)
1609 {
1610 char buf[512]= {0};
1611 FILE *res_file;
1612 int error;
1613
1614 if (!(res_file= popen(cmd, "r")))
1615 die("popen(\"%s\", \"r\") failed", cmd);
1616
1617 while (fgets(buf, sizeof(buf), res_file))
1618 {
1619 DBUG_PRINT("info", ("buf: %s", buf));
1620 if(ds_res)
1621 {
1622 /* Save the output of this command in the supplied string */
1623 dynstr_append(ds_res, buf);
1624 }
1625 else
1626 {
1627 /* Print it directly on screen */
1628 fprintf(stdout, "%s", buf);
1629 }
1630 }
1631
1632 error= pclose(res_file);
1633 return WEXITSTATUS(error);
1634 }
1635
1636
1637 /*
1638 Run the specified tool with variable number of arguments
1639
1640 SYNOPSIS
1641 run_tool
1642 tool_path - the name of the tool to run
1643 ds_res - pointer to dynamic string where to store the result
1644 ... - variable number of arguments that will be properly
1645 quoted and appended after the tool's name
1646
1647 */
1648
run_tool(const char * tool_path,DYNAMIC_STRING * ds_res,...)1649 static int run_tool(const char *tool_path, DYNAMIC_STRING *ds_res, ...)
1650 {
1651 int ret;
1652 const char* arg;
1653 va_list args;
1654 DYNAMIC_STRING ds_cmdline;
1655
1656 DBUG_ENTER("run_tool");
1657 DBUG_PRINT("enter", ("tool_path: %s", tool_path));
1658
1659 if (init_dynamic_string(&ds_cmdline, IF_WIN("\"", ""), FN_REFLEN, FN_REFLEN))
1660 die("Out of memory");
1661
1662 dynstr_append_os_quoted(&ds_cmdline, tool_path, NullS);
1663 dynstr_append(&ds_cmdline, " ");
1664
1665 va_start(args, ds_res);
1666
1667 while ((arg= va_arg(args, char *)))
1668 {
1669 /* Options should be os quoted */
1670 if (strncmp(arg, "--", 2) == 0)
1671 dynstr_append_os_quoted(&ds_cmdline, arg, NullS);
1672 else
1673 dynstr_append(&ds_cmdline, arg);
1674 dynstr_append(&ds_cmdline, " ");
1675 }
1676
1677 va_end(args);
1678
1679 #ifdef __WIN__
1680 dynstr_append(&ds_cmdline, "\"");
1681 #endif
1682
1683 DBUG_PRINT("info", ("Running: %s", ds_cmdline.str));
1684 ret= run_command(ds_cmdline.str, ds_res);
1685 DBUG_PRINT("exit", ("ret: %d", ret));
1686 dynstr_free(&ds_cmdline);
1687 DBUG_RETURN(ret);
1688 }
1689
1690
1691 /*
1692 Test if diff is present. This is needed on Windows systems
1693 as the OS returns 1 whether diff is successful or if it is
1694 not present.
1695
1696 We run diff -v and look for output in stdout.
1697 We don't redirect stderr to stdout to make for a simplified check
1698 Windows will output '"diff"' is not recognized... to stderr if it is
1699 not present.
1700 */
1701
1702 #ifdef __WIN__
1703
diff_check(const char * diff_name)1704 static int diff_check(const char *diff_name)
1705 {
1706 FILE *res_file;
1707 char buf[128];
1708 int have_diff= 0;
1709
1710 my_snprintf(buf, sizeof(buf), "%s -v", diff_name);
1711
1712 if (!(res_file= popen(buf, "r")))
1713 die("popen(\"%s\", \"r\") failed", buf);
1714
1715 /* if diff is not present, nothing will be in stdout to increment have_diff */
1716 if (fgets(buf, sizeof(buf), res_file))
1717 have_diff= 1;
1718
1719 pclose(res_file);
1720
1721 return have_diff;
1722 }
1723
1724 #endif
1725
1726
1727 /*
1728 Show the diff of two files using the systems builtin diff
1729 command. If no such diff command exist, just dump the content
1730 of the two files and inform about how to get "diff"
1731
1732 SYNOPSIS
1733 show_diff
1734 ds - pointer to dynamic string where to add the diff(may be NULL)
1735 filename1 - name of first file
1736 filename2 - name of second file
1737
1738 */
1739
show_diff(DYNAMIC_STRING * ds,const char * filename1,const char * filename2)1740 void show_diff(DYNAMIC_STRING* ds,
1741 const char* filename1, const char* filename2)
1742 {
1743 DYNAMIC_STRING ds_tmp;
1744 const char *diff_name = 0;
1745
1746 if (init_dynamic_string(&ds_tmp, "", 256, 256))
1747 die("Out of memory");
1748
1749 /* determine if we have diff on Windows
1750 needs special processing due to return values
1751 on that OS
1752 This test is only done on Windows since it's only needed there
1753 in order to correctly detect non-availibility of 'diff', and
1754 the way it's implemented does not work with default 'diff' on Solaris.
1755 */
1756 #ifdef __WIN__
1757 if (diff_check("diff"))
1758 diff_name = "diff";
1759 else if (diff_check("mtrdiff"))
1760 diff_name = "mtrdiff";
1761 else
1762 diff_name = 0;
1763 #else
1764 diff_name = "diff"; /* Otherwise always assume it's called diff */
1765 #endif
1766
1767 if (diff_name)
1768 {
1769 /* First try with unified diff */
1770 if (run_tool(diff_name,
1771 &ds_tmp, /* Get output from diff in ds_tmp */
1772 "-u",
1773 filename1,
1774 filename2,
1775 "2>&1",
1776 NULL) > 1) /* Most "diff" tools return >1 if error */
1777 {
1778 dynstr_set(&ds_tmp, "");
1779
1780 /* Fallback to context diff with "diff -c" */
1781 if (run_tool(diff_name,
1782 &ds_tmp, /* Get output from diff in ds_tmp */
1783 "-c",
1784 filename1,
1785 filename2,
1786 "2>&1",
1787 NULL) > 1) /* Most "diff" tools return >1 if error */
1788 {
1789 dynstr_set(&ds_tmp, "");
1790
1791 /* Fallback to simple diff with "diff" */
1792 if (run_tool(diff_name,
1793 &ds_tmp, /* Get output from diff in ds_tmp */
1794 filename1,
1795 filename2,
1796 "2>&1",
1797 NULL) > 1) /* Most "diff" tools return >1 if error */
1798 {
1799 diff_name= 0;
1800 }
1801 }
1802 }
1803 }
1804
1805 if (! diff_name)
1806 {
1807 /*
1808 Fallback to dump both files to result file and inform
1809 about installing "diff"
1810 */
1811 dynstr_append(&ds_tmp, "\n");
1812 dynstr_append(&ds_tmp,
1813 "\n"
1814 "The two files differ but it was not possible to execute 'diff' in\n"
1815 "order to show only the difference. Instead the whole content of the\n"
1816 "two files was shown for you to diff manually.\n\n"
1817 "To get a better report you should install 'diff' on your system, which you\n"
1818 "for example can get from http://www.gnu.org/software/diffutils/diffutils.html\n"
1819 #ifdef __WIN__
1820 "or http://gnuwin32.sourceforge.net/packages/diffutils.htm\n"
1821 #endif
1822 "\n");
1823
1824 dynstr_append(&ds_tmp, " --- ");
1825 dynstr_append(&ds_tmp, filename1);
1826 dynstr_append(&ds_tmp, " >>>\n");
1827 cat_file(&ds_tmp, filename1);
1828 dynstr_append(&ds_tmp, "<<<\n --- ");
1829 dynstr_append(&ds_tmp, filename1);
1830 dynstr_append(&ds_tmp, " >>>\n");
1831 cat_file(&ds_tmp, filename2);
1832 dynstr_append(&ds_tmp, "<<<<\n");
1833 }
1834
1835 if (ds)
1836 {
1837 /* Add the diff to output */
1838 dynstr_append_mem(ds, ds_tmp.str, ds_tmp.length);
1839 }
1840 else
1841 {
1842 /* Print diff directly to stdout */
1843 fprintf(stderr, "%s\n", ds_tmp.str);
1844 }
1845
1846 dynstr_free(&ds_tmp);
1847
1848 }
1849
1850
1851 enum compare_files_result_enum {
1852 RESULT_OK= 0,
1853 RESULT_CONTENT_MISMATCH= 1,
1854 RESULT_LENGTH_MISMATCH= 2
1855 };
1856
1857 /*
1858 Compare two files, given a fd to the first file and
1859 name of the second file
1860
1861 SYNOPSIS
1862 compare_files2
1863 fd - Open file descriptor of the first file
1864 filename2 - Name of second file
1865
1866 RETURN VALUES
1867 According to the values in "compare_files_result_enum"
1868
1869 */
1870
compare_files2(File fd,const char * filename2)1871 int compare_files2(File fd, const char* filename2)
1872 {
1873 int error= RESULT_OK;
1874 File fd2;
1875 size_t len, len2;
1876 char buff[512], buff2[512];
1877
1878 if ((fd2= my_open(filename2, O_RDONLY, MYF(0))) < 0)
1879 {
1880 my_close(fd, MYF(0));
1881 die("Failed to open second file: '%s'", filename2);
1882 }
1883 while((len= my_read(fd, (uchar*)&buff,
1884 sizeof(buff), MYF(0))) > 0)
1885 {
1886 if ((len2= my_read(fd2, (uchar*)&buff2,
1887 sizeof(buff2), MYF(0))) < len)
1888 {
1889 /* File 2 was smaller */
1890 error= RESULT_LENGTH_MISMATCH;
1891 break;
1892 }
1893 if (len2 > len)
1894 {
1895 /* File 1 was smaller */
1896 error= RESULT_LENGTH_MISMATCH;
1897 break;
1898 }
1899 if ((memcmp(buff, buff2, len)))
1900 {
1901 /* Content of this part differed */
1902 error= RESULT_CONTENT_MISMATCH;
1903 break;
1904 }
1905 }
1906 if (!error && my_read(fd2, (uchar*)&buff2,
1907 sizeof(buff2), MYF(0)) > 0)
1908 {
1909 /* File 1 was smaller */
1910 error= RESULT_LENGTH_MISMATCH;
1911 }
1912
1913 my_close(fd2, MYF(0));
1914
1915 return error;
1916 }
1917
1918
1919 /*
1920 Compare two files, given their filenames
1921
1922 SYNOPSIS
1923 compare_files
1924 filename1 - Name of first file
1925 filename2 - Name of second file
1926
1927 RETURN VALUES
1928 See 'compare_files2'
1929
1930 */
1931
compare_files(const char * filename1,const char * filename2)1932 int compare_files(const char* filename1, const char* filename2)
1933 {
1934 File fd;
1935 int error;
1936
1937 if ((fd= my_open(filename1, O_RDONLY, MYF(0))) < 0)
1938 die("Failed to open first file: '%s'", filename1);
1939
1940 error= compare_files2(fd, filename2);
1941
1942 my_close(fd, MYF(0));
1943
1944 return error;
1945 }
1946
1947
1948 /*
1949 Compare content of the string in ds to content of file fname
1950
1951 SYNOPSIS
1952 dyn_string_cmp
1953 ds - Dynamic string containing the string o be compared
1954 fname - Name of file to compare with
1955
1956 RETURN VALUES
1957 See 'compare_files2'
1958 */
1959
dyn_string_cmp(DYNAMIC_STRING * ds,const char * fname)1960 int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname)
1961 {
1962 int error;
1963 File fd;
1964 char temp_file_path[FN_REFLEN];
1965
1966 DBUG_ENTER("dyn_string_cmp");
1967 DBUG_PRINT("enter", ("fname: %s", fname));
1968
1969 if ((fd= create_temp_file(temp_file_path, TMPDIR,
1970 "tmp", O_CREAT | O_SHARE | O_RDWR,
1971 MYF(MY_WME))) < 0)
1972 die("Failed to create temporary file for ds");
1973
1974 /* Write ds to temporary file and set file pos to beginning*/
1975 if (my_write(fd, (uchar *) ds->str, ds->length,
1976 MYF(MY_FNABP | MY_WME)) ||
1977 my_seek(fd, 0, SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR)
1978 {
1979 my_close(fd, MYF(0));
1980 /* Remove the temporary file */
1981 my_delete(temp_file_path, MYF(0));
1982 die("Failed to write file '%s'", temp_file_path);
1983 }
1984
1985 error= compare_files2(fd, fname);
1986
1987 my_close(fd, MYF(0));
1988 /* Remove the temporary file */
1989 my_delete(temp_file_path, MYF(0));
1990
1991 DBUG_RETURN(error);
1992 }
1993
1994
1995 /*
1996 Check the content of log against result file
1997
1998 SYNOPSIS
1999 check_result
2000
2001 RETURN VALUES
2002 error - the function will not return
2003
2004 */
2005
check_result()2006 void check_result()
2007 {
2008 const char* mess= "Result content mismatch\n";
2009
2010 DBUG_ENTER("check_result");
2011 DBUG_ASSERT(result_file_name);
2012 DBUG_PRINT("enter", ("result_file_name: %s", result_file_name));
2013
2014 switch (compare_files(log_file.file_name(), result_file_name)) {
2015 case RESULT_OK:
2016 break; /* ok */
2017 case RESULT_LENGTH_MISMATCH:
2018 mess= "Result length mismatch\n";
2019 /* Fallthrough */
2020 case RESULT_CONTENT_MISMATCH:
2021 {
2022 /*
2023 Result mismatched, dump results to .reject file
2024 and then show the diff
2025 */
2026 char reject_file[FN_REFLEN];
2027 size_t reject_length;
2028 dirname_part(reject_file, result_file_name, &reject_length);
2029
2030 /* Put reject file in opt_logdir */
2031 fn_format(reject_file, result_file_name, opt_logdir,
2032 ".reject", MY_REPLACE_DIR | MY_REPLACE_EXT);
2033
2034 if (my_copy(log_file.file_name(), reject_file, MYF(0)) != 0)
2035 die("Failed to copy '%s' to '%s', errno: %d",
2036 log_file.file_name(), reject_file, errno);
2037
2038 show_diff(NULL, result_file_name, reject_file);
2039 die("%s", mess);
2040 break;
2041 }
2042 default: /* impossible */
2043 die("Unknown error code from dyn_string_cmp()");
2044 }
2045
2046 DBUG_VOID_RETURN;
2047 }
2048
2049
2050 /*
2051 Check the content of ds against a require file
2052 If match fails, abort the test with special error code
2053 indicating that test is not supported
2054
2055 SYNOPSIS
2056 check_require
2057 ds - content to be checked
2058 fname - name of file to check against
2059
2060 RETURN VALUES
2061 error - the function will not return
2062
2063 */
2064
check_require(DYNAMIC_STRING * ds,const char * fname)2065 void check_require(DYNAMIC_STRING* ds, const char *fname)
2066 {
2067 DBUG_ENTER("check_require");
2068
2069 if (dyn_string_cmp(ds, fname))
2070 {
2071 char reason[FN_REFLEN];
2072 fn_format(reason, fname, "", "", MY_REPLACE_EXT | MY_REPLACE_DIR);
2073 abort_not_supported_test("Test requires: '%s'", reason);
2074 }
2075 DBUG_VOID_RETURN;
2076 }
2077
2078
2079 /*
2080 Remove surrounding chars from string
2081
2082 Return 1 if first character is found but not last
2083 */
strip_surrounding(char * str,char c1,char c2)2084 static int strip_surrounding(char* str, char c1, char c2)
2085 {
2086 char* ptr= str;
2087
2088 /* Check if the first non space character is c1 */
2089 while(*ptr && my_isspace(charset_info, *ptr))
2090 ptr++;
2091 if (*ptr == c1)
2092 {
2093 /* Replace it with a space */
2094 *ptr= ' ';
2095
2096 /* Last non space charecter should be c2 */
2097 ptr= strend(str)-1;
2098 while(*ptr && my_isspace(charset_info, *ptr))
2099 ptr--;
2100 if (*ptr == c2)
2101 {
2102 /* Replace it with \0 */
2103 *ptr= 0;
2104 }
2105 else
2106 {
2107 /* Mismatch detected */
2108 return 1;
2109 }
2110 }
2111 return 0;
2112 }
2113
2114
strip_parentheses(struct st_command * command)2115 static void strip_parentheses(struct st_command *command)
2116 {
2117 if (strip_surrounding(command->first_argument, '(', ')'))
2118 die("%.*s - argument list started with '%c' must be ended with '%c'",
2119 command->first_word_len, command->query, '(', ')');
2120 }
2121
2122
2123 C_MODE_START
2124
get_var_key(const uchar * var,size_t * len,my_bool MY_ATTRIBUTE ((unused))t)2125 static uchar *get_var_key(const uchar* var, size_t *len,
2126 my_bool MY_ATTRIBUTE((unused)) t)
2127 {
2128 char* key;
2129 key = ((VAR*)var)->name;
2130 *len = ((VAR*)var)->name_len;
2131 return (uchar*)key;
2132 }
2133
2134
var_free(void * v)2135 static void var_free(void *v)
2136 {
2137 VAR *var= (VAR*) v;
2138 my_free(var->str_val);
2139 if (var->alloced)
2140 my_free(var);
2141 }
2142
2143 C_MODE_END
2144
var_check_int(VAR * v)2145 void var_check_int(VAR *v)
2146 {
2147 char *endptr;
2148 char *str= v->str_val;
2149
2150 /* Initially assume not a number */
2151 v->int_val= 0;
2152 v->is_int= false;
2153 v->int_dirty= false;
2154 if (!str) return;
2155
2156 v->int_val = (int) strtol(str, &endptr, 10);
2157 /* It is an int if strtol consumed something up to end/space/tab */
2158 if (endptr > str && (!*endptr || *endptr == ' ' || *endptr == '\t'))
2159 v->is_int= true;
2160 }
2161
2162
var_init(VAR * v,const char * name,int name_len,const char * val,int val_len)2163 VAR *var_init(VAR *v, const char *name, int name_len, const char *val,
2164 int val_len)
2165 {
2166 int val_alloc_len;
2167 VAR *tmp_var;
2168 if (!name_len && name)
2169 name_len = strlen(name);
2170 if (!val_len && val)
2171 val_len = strlen(val) ;
2172 if (!val)
2173 val_len= 0;
2174 val_alloc_len = val_len + 16; /* room to grow */
2175 if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var)
2176 + name_len+2, MYF(MY_WME))))
2177 die("Out of memory");
2178
2179 if (name != NULL)
2180 {
2181 tmp_var->name= reinterpret_cast<char*>(tmp_var) + sizeof(*tmp_var);
2182 memcpy(tmp_var->name, name, name_len);
2183 tmp_var->name[name_len]= 0;
2184 }
2185 else
2186 tmp_var->name= NULL;
2187
2188 tmp_var->alloced = (v == 0);
2189
2190 if (!(tmp_var->str_val = (char*)my_malloc(val_alloc_len+1, MYF(MY_WME))))
2191 die("Out of memory");
2192
2193 if (val)
2194 memcpy(tmp_var->str_val, val, val_len);
2195 tmp_var->str_val[val_len]= 0;
2196
2197 var_check_int(tmp_var);
2198 tmp_var->name_len = name_len;
2199 tmp_var->str_val_len = val_len;
2200 tmp_var->alloced_len = val_alloc_len;
2201 return tmp_var;
2202 }
2203
2204
var_from_env(const char * name,const char * def_val)2205 VAR* var_from_env(const char *name, const char *def_val)
2206 {
2207 const char *tmp;
2208 VAR *v;
2209 if (!(tmp = getenv(name)))
2210 tmp = def_val;
2211
2212 v = var_init(0, name, strlen(name), tmp, strlen(tmp));
2213 my_hash_insert(&var_hash, (uchar*)v);
2214 return v;
2215 }
2216
2217
var_get(const char * var_name,const char ** var_name_end,my_bool raw,my_bool ignore_not_existing)2218 VAR* var_get(const char *var_name, const char **var_name_end, my_bool raw,
2219 my_bool ignore_not_existing)
2220 {
2221 int digit;
2222 VAR *v;
2223 DBUG_ENTER("var_get");
2224 DBUG_PRINT("enter", ("var_name: %s",var_name));
2225
2226 if (*var_name != '$')
2227 goto err;
2228 digit = *++var_name - '0';
2229 if (digit < 0 || digit >= 10)
2230 {
2231 const char *save_var_name = var_name, *end;
2232 uint length;
2233 end = (var_name_end) ? *var_name_end : 0;
2234 while (my_isvar(charset_info,*var_name) && var_name != end)
2235 var_name++;
2236 if (var_name == save_var_name)
2237 {
2238 if (ignore_not_existing)
2239 DBUG_RETURN(0);
2240 die("Empty variable");
2241 }
2242 length= (uint) (var_name - save_var_name);
2243 if (length >= MAX_VAR_NAME_LENGTH)
2244 die("Too long variable name: %s", save_var_name);
2245
2246 if (!(v = (VAR*) my_hash_search(&var_hash, (const uchar*) save_var_name,
2247 length)))
2248 {
2249 char buff[MAX_VAR_NAME_LENGTH+1];
2250 strmake(buff, save_var_name, length);
2251 v= var_from_env(buff, "");
2252 }
2253 var_name--; /* Point at last character */
2254 }
2255 else
2256 v = var_reg + digit;
2257
2258 if (!raw && v->int_dirty)
2259 {
2260 sprintf(v->str_val, "%d", v->int_val);
2261 v->int_dirty= false;
2262 v->str_val_len = strlen(v->str_val);
2263 }
2264 if (var_name_end)
2265 *var_name_end = var_name ;
2266 DBUG_RETURN(v);
2267 err:
2268 if (var_name_end)
2269 *var_name_end = 0;
2270 die("Unsupported variable name: %s", var_name);
2271 DBUG_RETURN(0);
2272 }
2273
2274
var_obtain(const char * name,int len)2275 VAR *var_obtain(const char *name, int len)
2276 {
2277 VAR* v;
2278 if ((v = (VAR*)my_hash_search(&var_hash, (const uchar *) name, len)))
2279 return v;
2280 v = var_init(0, name, len, "", 0);
2281 my_hash_insert(&var_hash, (uchar*)v);
2282 return v;
2283 }
2284
2285
2286 /*
2287 - if variable starts with a $ it is regarded as a local test varable
2288 - if not it is treated as a environment variable, and the corresponding
2289 environment variable will be updated
2290 */
2291
var_set(const char * var_name,const char * var_name_end,const char * var_val,const char * var_val_end)2292 void var_set(const char *var_name, const char *var_name_end,
2293 const char *var_val, const char *var_val_end)
2294 {
2295 int digit, env_var= 0;
2296 VAR *v;
2297 DBUG_ENTER("var_set");
2298 DBUG_PRINT("enter", ("var_name: '%.*s' = '%.*s' (length: %d)",
2299 (int) (var_name_end - var_name), var_name,
2300 (int) (var_val_end - var_val), var_val,
2301 (int) (var_val_end - var_val)));
2302
2303 if (*var_name != '$')
2304 env_var= 1;
2305 else
2306 var_name++;
2307
2308 digit= *var_name - '0';
2309 if (!(digit < 10 && digit >= 0))
2310 {
2311 v= var_obtain(var_name, (uint) (var_name_end - var_name));
2312 }
2313 else
2314 v= var_reg + digit;
2315
2316 eval_expr(v, var_val, (const char**) &var_val_end);
2317
2318 if (env_var)
2319 {
2320 if (v->int_dirty)
2321 {
2322 sprintf(v->str_val, "%d", v->int_val);
2323 v->int_dirty=false;
2324 v->str_val_len= strlen(v->str_val);
2325 }
2326 /* setenv() expects \0-terminated strings */
2327 DBUG_ASSERT(v->name[v->name_len] == 0);
2328 setenv(v->name, v->str_val, 1);
2329 }
2330 DBUG_VOID_RETURN;
2331 }
2332
2333
var_set_string(const char * name,const char * value)2334 void var_set_string(const char* name, const char* value)
2335 {
2336 var_set(name, name + strlen(name), value, value + strlen(value));
2337 }
2338
2339
var_set_int(const char * name,int value)2340 void var_set_int(const char* name, int value)
2341 {
2342 char buf[21];
2343 my_snprintf(buf, sizeof(buf), "%d", value);
2344 var_set_string(name, buf);
2345 }
2346
2347
2348 /*
2349 Store an integer (typically the returncode of the last SQL)
2350 statement in the mysqltest builtin variable $mysql_errno
2351 */
2352
var_set_errno(int sql_errno)2353 void var_set_errno(int sql_errno)
2354 {
2355 var_set_int("$mysql_errno", sql_errno);
2356 var_set_string("$mysql_errname", get_errname_from_code(sql_errno));
2357 }
2358
2359 /* Functions to handle --disable and --enable properties */
2360
set_once_property(enum_prop prop,my_bool val)2361 void set_once_property(enum_prop prop, my_bool val)
2362 {
2363 property &pr= prop_list[prop];
2364 pr.set= 1;
2365 pr.old= *pr.var;
2366 *pr.var= val;
2367 var_set_int(pr.env_name, (val != pr.reverse));
2368 once_property= TRUE;
2369 }
2370
set_property(st_command * command,enum_prop prop,my_bool val)2371 void set_property(st_command *command, enum_prop prop, my_bool val)
2372 {
2373 char* p= command->first_argument;
2374 if (p && !strcmp (p, "ONCE"))
2375 {
2376 command->last_argument= p + 4;
2377 set_once_property(prop, val);
2378 return;
2379 }
2380 property &pr= prop_list[prop];
2381 *pr.var= val;
2382 pr.set= 0;
2383 var_set_int(pr.env_name, (val != pr.reverse));
2384 }
2385
revert_properties()2386 void revert_properties()
2387 {
2388 if (! once_property)
2389 return;
2390 for (int i= 0; i < (int) P_MAX; i++)
2391 {
2392 property &pr= prop_list[i];
2393 if (pr.set)
2394 {
2395 *pr.var= pr.old;
2396 pr.set= 0;
2397 var_set_int(pr.env_name, (pr.old != pr.reverse));
2398 }
2399 }
2400 once_property=FALSE;
2401 }
2402
2403
2404 /*
2405 Set variable from the result of a query
2406
2407 SYNOPSIS
2408 var_query_set()
2409 var variable to set from query
2410 query start of query string to execute
2411 query_end end of the query string to execute
2412
2413
2414 DESCRIPTION
2415 let @<var_name> = `<query>`
2416
2417 Execute the query and assign the first row of result to var as
2418 a tab separated strings
2419
2420 Also assign each column of the result set to
2421 variable "$<var_name>_<column_name>"
2422 Thus the tab separated output can be read from $<var_name> and
2423 and each individual column can be read as $<var_name>_<col_name>
2424
2425 */
2426
var_query_set(VAR * var,const char * query,const char ** query_end)2427 void var_query_set(VAR *var, const char *query, const char** query_end)
2428 {
2429 char *end = (char*)((query_end && *query_end) ?
2430 *query_end : query + strlen(query));
2431 MYSQL_RES *res;
2432 MYSQL_ROW row;
2433 MYSQL* mysql = &cur_con->mysql;
2434 DYNAMIC_STRING ds_query;
2435 DBUG_ENTER("var_query_set");
2436 LINT_INIT(res);
2437
2438 /* Only white space or ) allowed past ending ` */
2439 while (end > query && *end != '`')
2440 {
2441 if (*end && (*end != ' ' && *end != '\t' && *end != '\n' && *end != ')'))
2442 die("Spurious text after `query` expression");
2443 --end;
2444 }
2445
2446 if (query == end)
2447 die("Syntax error in query, missing '`'");
2448 ++query;
2449
2450 /* Eval the query, thus replacing all environment variables */
2451 init_dynamic_string(&ds_query, 0, (end - query) + 32, 256);
2452 do_eval(&ds_query, query, end, FALSE);
2453
2454 if (mysql_real_query(mysql, ds_query.str, ds_query.length))
2455 {
2456 handle_error (curr_command, mysql_errno(mysql), mysql_error(mysql),
2457 mysql_sqlstate(mysql), &ds_res);
2458 /* If error was acceptable, return empty string */
2459 dynstr_free(&ds_query);
2460 eval_expr(var, "", 0);
2461 DBUG_VOID_RETURN;
2462 }
2463
2464 if (!(res= mysql_store_result(mysql)))
2465 die("Query '%s' didn't return a result set", ds_query.str);
2466 dynstr_free(&ds_query);
2467
2468 if ((row= mysql_fetch_row(res)) && row[0])
2469 {
2470 /*
2471 Concatenate all fields in the first row with tab in between
2472 and assign that string to the $variable
2473 */
2474 DYNAMIC_STRING result;
2475 uint i;
2476 ulong *lengths;
2477
2478 init_dynamic_string(&result, "", 512, 512);
2479 lengths= mysql_fetch_lengths(res);
2480 for (i= 0; i < mysql_num_fields(res); i++)
2481 {
2482 if (row[i])
2483 {
2484 /* Add column to tab separated string */
2485 char *val= row[i];
2486 int len= lengths[i];
2487
2488 if (glob_replace_regex)
2489 {
2490 /* Regex replace */
2491 if (!multi_reg_replace(glob_replace_regex, (char*)val))
2492 {
2493 val= glob_replace_regex->buf;
2494 len= strlen(val);
2495 }
2496 }
2497
2498 if (glob_replace)
2499 replace_strings_append(glob_replace, &result, val, len);
2500 else
2501 dynstr_append_mem(&result, val, len);
2502 }
2503 dynstr_append_mem(&result, "\t", 1);
2504 }
2505 end= result.str + result.length-1;
2506 /* Evaluation should not recurse via backtick */
2507 eval_expr(var, result.str, (const char**) &end, false, false);
2508 dynstr_free(&result);
2509 }
2510 else
2511 eval_expr(var, "", 0);
2512
2513 mysql_free_result(res);
2514 DBUG_VOID_RETURN;
2515 }
2516
2517
2518 static void
set_result_format_version(ulong new_version)2519 set_result_format_version(ulong new_version)
2520 {
2521 switch (new_version){
2522 case 1:
2523 /* The first format */
2524 break;
2525 case 2:
2526 /* New format that also writes comments and empty lines
2527 from test file to result */
2528 break;
2529 default:
2530 die("Version format %lu has not yet been implemented", new_version);
2531 break;
2532 }
2533 opt_result_format_version= new_version;
2534 }
2535
2536
2537 /*
2538 Set the result format version to use when generating
2539 the .result file
2540 */
2541
2542 static void
do_result_format_version(struct st_command * command)2543 do_result_format_version(struct st_command *command)
2544 {
2545 long version;
2546 static DYNAMIC_STRING ds_version;
2547 const struct command_arg result_format_args[] = {
2548 {"version", ARG_STRING, TRUE, &ds_version, "Version to use"}
2549 };
2550
2551 DBUG_ENTER("do_result_format_version");
2552
2553 check_command_args(command, command->first_argument,
2554 result_format_args,
2555 sizeof(result_format_args)/sizeof(struct command_arg),
2556 ',');
2557
2558 /* Convert version number to int */
2559 if (!str2int(ds_version.str, 10, (long) 0, (long) INT_MAX, &version))
2560 die("Invalid version number: '%s'", ds_version.str);
2561
2562 set_result_format_version(version);
2563
2564 dynstr_append(&ds_res, "result_format: ");
2565 dynstr_append_mem(&ds_res, ds_version.str, ds_version.length);
2566 dynstr_append(&ds_res, "\n");
2567 dynstr_free(&ds_version);
2568 DBUG_VOID_RETURN;
2569 }
2570
2571 /* List of error names to error codes */
2572 typedef struct
2573 {
2574 const char *name;
2575 uint code;
2576 const char *text;
2577 } st_error;
2578
2579 static st_error global_error_names[] =
2580 {
2581 { "<No error>", -1U, "" },
2582 #include <mysqld_ername.h>
2583 { 0, 0, 0 }
2584 };
2585
2586 uint get_errcode_from_name(char *, char *);
2587
2588 /*
2589
2590 This function is useful when one needs to convert between error numbers and error strings
2591
2592 SYNOPSIS
2593 var_set_convert_error(struct st_command *command,VAR *var)
2594
2595 DESCRIPTION
2596 let $var=convert_error(ER_UNKNOWN_ERROR);
2597 let $var=convert_error(1234);
2598
2599 The variable var will be populated with error number if the argument is string.
2600 The variable var will be populated with error string if the argument is number.
2601
2602 */
var_set_convert_error(struct st_command * command,VAR * var)2603 void var_set_convert_error(struct st_command *command,VAR *var)
2604 {
2605 char *last;
2606 char *first=command->query;
2607 const char *err_name;
2608
2609 DBUG_ENTER("var_set_query_get_value");
2610
2611 DBUG_PRINT("info", ("query: %s", command->query));
2612
2613 /* the command->query contains the statement convert_error(1234) */
2614 first=strchr(command->query,'(') + 1;
2615 last=strchr(command->query,')');
2616
2617
2618 if( last == first ) /* denoting an empty string */
2619 {
2620 eval_expr(var,"0",0);
2621 DBUG_VOID_RETURN;
2622 }
2623
2624
2625 /* if the string is an error string , it starts with 'E' as is the norm*/
2626 if ( *first == 'E')
2627 {
2628 char str[100];
2629 uint num;
2630 num=get_errcode_from_name(first, last);
2631 sprintf(str,"%i",num);
2632 eval_expr(var,str,0);
2633 }
2634 else if (my_isdigit(charset_info, *first ))/* if the error is a number */
2635 {
2636 long int err;
2637
2638 err=strtol(first,&last,0);
2639 err_name = get_errname_from_code(err);
2640 eval_expr(var,err_name,0);
2641 }
2642 else
2643 {
2644 die("Invalid error in input");
2645 }
2646
2647 DBUG_VOID_RETURN;
2648 }
2649
2650 /*
2651 Set variable from the result of a field in a query
2652
2653 This function is useful when checking for a certain value
2654 in the output from a query that can't be restricted to only
2655 return some values. A very good example of that is most SHOW
2656 commands.
2657
2658 SYNOPSIS
2659 var_set_query_get_value()
2660
2661 DESCRIPTION
2662 let $variable= query_get_value(<query to run>,<column name>,<row no>);
2663
2664 <query to run> - The query that should be sent to the server
2665 <column name> - Name of the column that holds the field be compared
2666 against the expected value
2667 <row no> - Number of the row that holds the field to be
2668 compared against the expected value
2669
2670 */
2671
var_set_query_get_value(struct st_command * command,VAR * var)2672 void var_set_query_get_value(struct st_command *command, VAR *var)
2673 {
2674 long row_no;
2675 int col_no= -1;
2676 MYSQL_RES* res;
2677 MYSQL* mysql= &cur_con->mysql;
2678
2679 static DYNAMIC_STRING ds_query;
2680 static DYNAMIC_STRING ds_col;
2681 static DYNAMIC_STRING ds_row;
2682 const struct command_arg query_get_value_args[] = {
2683 {"query", ARG_STRING, TRUE, &ds_query, "Query to run"},
2684 {"column name", ARG_STRING, TRUE, &ds_col, "Name of column"},
2685 {"row number", ARG_STRING, TRUE, &ds_row, "Number for row"}
2686 };
2687
2688 DBUG_ENTER("var_set_query_get_value");
2689 LINT_INIT(res);
2690
2691 strip_parentheses(command);
2692 DBUG_PRINT("info", ("query: %s", command->query));
2693 check_command_args(command, command->first_argument, query_get_value_args,
2694 sizeof(query_get_value_args)/sizeof(struct command_arg),
2695 ',');
2696
2697 DBUG_PRINT("info", ("query: %s", ds_query.str));
2698 DBUG_PRINT("info", ("col: %s", ds_col.str));
2699
2700 /* Convert row number to int */
2701 if (!str2int(ds_row.str, 10, (long) 0, (long) INT_MAX, &row_no))
2702 die("Invalid row number: '%s'", ds_row.str);
2703 DBUG_PRINT("info", ("row: %s, row_no: %ld", ds_row.str, row_no));
2704 dynstr_free(&ds_row);
2705
2706 /* Remove any surrounding "'s from the query - if there is any */
2707 if (strip_surrounding(ds_query.str, '"', '"'))
2708 die("Mismatched \"'s around query '%s'", ds_query.str);
2709
2710 /* Run the query */
2711 if (mysql_real_query(mysql, ds_query.str, ds_query.length))
2712 {
2713 handle_error (curr_command, mysql_errno(mysql), mysql_error(mysql),
2714 mysql_sqlstate(mysql), &ds_res);
2715 /* If error was acceptable, return empty string */
2716 dynstr_free(&ds_query);
2717 dynstr_free(&ds_col);
2718 eval_expr(var, "", 0);
2719 DBUG_VOID_RETURN;
2720 }
2721
2722 if (!(res= mysql_store_result(mysql)))
2723 die("Query '%s' didn't return a result set", ds_query.str);
2724
2725 {
2726 /* Find column number from the given column name */
2727 uint i;
2728 uint num_fields= mysql_num_fields(res);
2729 MYSQL_FIELD *fields= mysql_fetch_fields(res);
2730
2731 for (i= 0; i < num_fields; i++)
2732 {
2733 if (strcmp(fields[i].name, ds_col.str) == 0 &&
2734 strlen(fields[i].name) == ds_col.length)
2735 {
2736 col_no= i;
2737 break;
2738 }
2739 }
2740 if (col_no == -1)
2741 {
2742 mysql_free_result(res);
2743 die("Could not find column '%s' in the result of '%s'",
2744 ds_col.str, ds_query.str);
2745 }
2746 DBUG_PRINT("info", ("Found column %d with name '%s'",
2747 i, fields[i].name));
2748 }
2749 dynstr_free(&ds_col);
2750
2751 {
2752 /* Get the value */
2753 MYSQL_ROW row;
2754 long rows= 0;
2755 const char* value= "No such row";
2756
2757 while ((row= mysql_fetch_row(res)))
2758 {
2759 if (++rows == row_no)
2760 {
2761
2762 DBUG_PRINT("info", ("At row %ld, column %d is '%s'",
2763 row_no, col_no, row[col_no]));
2764 /* Found the row to get */
2765 if (row[col_no])
2766 value= row[col_no];
2767 else
2768 value= "NULL";
2769
2770 break;
2771 }
2772 }
2773 eval_expr(var, value, 0, false, false);
2774 }
2775 dynstr_free(&ds_query);
2776 mysql_free_result(res);
2777
2778 DBUG_VOID_RETURN;
2779 }
2780
2781
var_copy(VAR * dest,VAR * src)2782 void var_copy(VAR *dest, VAR *src)
2783 {
2784 dest->int_val= src->int_val;
2785 dest->is_int= src->is_int;
2786 dest->int_dirty= src->int_dirty;
2787
2788 /* Alloc/realloc data for str_val in dest */
2789 if (dest->alloced_len < src->alloced_len &&
2790 !(dest->str_val= dest->str_val
2791 ? (char*)my_realloc(dest->str_val, src->alloced_len, MYF(MY_WME))
2792 : (char*)my_malloc(src->alloced_len, MYF(MY_WME))))
2793 die("Out of memory");
2794 else
2795 dest->alloced_len= src->alloced_len;
2796
2797 /* Copy str_val data to dest */
2798 dest->str_val_len= src->str_val_len;
2799 if (src->str_val_len)
2800 memcpy(dest->str_val, src->str_val, src->str_val_len);
2801 }
2802
2803
eval_expr(VAR * v,const char * p,const char ** p_end,bool open_end,bool do_eval)2804 void eval_expr(VAR *v, const char *p, const char **p_end,
2805 bool open_end, bool do_eval)
2806 {
2807
2808 DBUG_ENTER("eval_expr");
2809 DBUG_PRINT("enter", ("p: '%s'", p));
2810
2811 /* Skip to treat as pure string if no evaluation */
2812 if (! do_eval)
2813 goto NO_EVAL;
2814
2815 if (*p == '$')
2816 {
2817 VAR *vp;
2818 const char* expected_end= *p_end; // Remember var end
2819 if ((vp= var_get(p, p_end, 0, 0)))
2820 var_copy(v, vp);
2821
2822 /* Apparently it is not safe to assume null-terminated string */
2823 v->str_val[v->str_val_len]= 0;
2824
2825 /* Make sure there was just a $variable and nothing else */
2826 const char* end= *p_end + 1;
2827 if (end < expected_end && !open_end)
2828 die("Found junk '%.*s' after $variable in expression",
2829 (int)(expected_end - end - 1), end);
2830
2831 DBUG_VOID_RETURN;
2832 }
2833
2834 if (*p == '`')
2835 {
2836 var_query_set(v, p, p_end);
2837 DBUG_VOID_RETURN;
2838 }
2839
2840 {
2841 /* Check if this is a "let $var= query_get_value()" */
2842 const char* get_value_str= "query_get_value";
2843 const size_t len= strlen(get_value_str);
2844 if (strncmp(p, get_value_str, len)==0)
2845 {
2846 struct st_command command;
2847 memset(&command, 0, sizeof(command));
2848 command.query= (char*)p;
2849 command.first_word_len= len;
2850 command.first_argument= command.query + len;
2851 command.end= (char*)*p_end;
2852 var_set_query_get_value(&command, v);
2853 DBUG_VOID_RETURN;
2854 }
2855 /* Check if this is a "let $var= convert_error()" */
2856 const char* get_value_str1= "convert_error";
2857 const size_t len1= strlen(get_value_str1);
2858 if (strncmp(p, get_value_str1, len1)==0)
2859 {
2860 struct st_command command;
2861 memset(&command, 0, sizeof(command));
2862 command.query= (char*)p;
2863 command.first_word_len= len;
2864 command.first_argument= command.query + len;
2865 command.end= (char*)*p_end;
2866 var_set_convert_error(&command, v);
2867 DBUG_VOID_RETURN;
2868 }
2869 }
2870
2871 NO_EVAL:
2872 {
2873 int new_val_len = (p_end && *p_end) ?
2874 (int) (*p_end - p) : (int) strlen(p);
2875 if (new_val_len + 1 >= v->alloced_len)
2876 {
2877 static int MIN_VAR_ALLOC= 32;
2878 v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ?
2879 MIN_VAR_ALLOC : new_val_len + 1;
2880 if (!(v->str_val =
2881 v->str_val ?
2882 (char*)my_realloc(v->str_val, v->alloced_len+1, MYF(MY_WME)) :
2883 (char*)my_malloc(v->alloced_len+1, MYF(MY_WME))))
2884 die("Out of memory");
2885 }
2886 v->str_val_len = new_val_len;
2887 memcpy(v->str_val, p, new_val_len);
2888 v->str_val[new_val_len] = 0;
2889 var_check_int(v);
2890 }
2891 DBUG_VOID_RETURN;
2892 }
2893
2894
open_file(const char * name)2895 int open_file(const char *name)
2896 {
2897 char buff[FN_REFLEN];
2898 size_t length;
2899 DBUG_ENTER("open_file");
2900 DBUG_PRINT("enter", ("name: %s", name));
2901
2902 /* Extract path from current file and try it as base first */
2903 if (dirname_part(buff, cur_file->file_name, &length))
2904 {
2905 strxmov(buff, buff, name, NullS);
2906 if (access(buff, F_OK) == 0){
2907 DBUG_PRINT("info", ("The file exists"));
2908 name= buff;
2909 }
2910 }
2911 if (!test_if_hard_path(name))
2912 {
2913 strxmov(buff, opt_basedir, name, NullS);
2914 name=buff;
2915 }
2916 fn_format(buff, name, "", "", MY_UNPACK_FILENAME);
2917
2918 if (cur_file == file_stack_end)
2919 die("Source directives are nesting too deep");
2920 cur_file++;
2921 if (!(cur_file->file = fopen(buff, "rb")))
2922 {
2923 cur_file--;
2924 die("Could not open '%s' for reading, errno: %d", buff, errno);
2925 }
2926 cur_file->file_name= my_strdup(buff, MYF(MY_FAE));
2927 cur_file->lineno=1;
2928 DBUG_RETURN(0);
2929 }
2930
2931
2932 /*
2933 Source and execute the given file
2934
2935 SYNOPSIS
2936 do_source()
2937 query called command
2938
2939 DESCRIPTION
2940 source <file_name>
2941
2942 Open the file <file_name> and execute it
2943
2944 */
2945
do_source(struct st_command * command)2946 void do_source(struct st_command *command)
2947 {
2948 static DYNAMIC_STRING ds_filename;
2949 const struct command_arg source_args[] = {
2950 { "filename", ARG_STRING, TRUE, &ds_filename, "File to source" }
2951 };
2952 DBUG_ENTER("do_source");
2953
2954 check_command_args(command, command->first_argument, source_args,
2955 sizeof(source_args)/sizeof(struct command_arg),
2956 ' ');
2957
2958 /*
2959 If this file has already been sourced, don't source it again.
2960 It's already available in the q_lines cache.
2961 */
2962 if (parser.current_line < (parser.read_lines - 1))
2963 ; /* Do nothing */
2964 else
2965 {
2966 DBUG_PRINT("info", ("sourcing file: %s", ds_filename.str));
2967 open_file(ds_filename.str);
2968 }
2969
2970 dynstr_free(&ds_filename);
2971 DBUG_VOID_RETURN;
2972 }
2973
2974
2975 #if defined __WIN__
2976
2977 #ifdef USE_CYGWIN
2978 /* Variables used for temporary sh files used for emulating Unix on Windows */
2979 char tmp_sh_name[64], tmp_sh_cmd[70];
2980 #endif
2981
init_tmp_sh_file()2982 void init_tmp_sh_file()
2983 {
2984 #ifdef USE_CYGWIN
2985 /* Format a name for the tmp sh file that is unique for this process */
2986 my_snprintf(tmp_sh_name, sizeof(tmp_sh_name), "tmp_%d.sh", getpid());
2987 /* Format the command to execute in order to run the script */
2988 my_snprintf(tmp_sh_cmd, sizeof(tmp_sh_cmd), "sh %s", tmp_sh_name);
2989 #endif
2990 }
2991
2992
free_tmp_sh_file()2993 void free_tmp_sh_file()
2994 {
2995 #ifdef USE_CYGWIN
2996 my_delete(tmp_sh_name, MYF(0));
2997 #endif
2998 }
2999 #endif
3000
3001
my_popen(DYNAMIC_STRING * ds_cmd,const char * mode,struct st_command * command)3002 FILE* my_popen(DYNAMIC_STRING *ds_cmd, const char *mode,
3003 struct st_command *command)
3004 {
3005 #if __WIN__
3006 /*
3007 --execw is for tests executing commands containing non-ASCII characters.
3008
3009 To correctly start such a program on Windows, we need to use the "wide"
3010 version of popen, with prior translation of the command line from
3011 the file character set to wide string. We use the current value
3012 of --character_set as a file character set, so before using --execw
3013 make sure to set --character_set properly.
3014
3015 If we use the non-wide version of popen, Windows internally
3016 converts command line from the current ANSI code page to wide string.
3017 In case when character set of the command line does not match the
3018 current ANSI code page, non-ASCII characters get garbled in most cases.
3019
3020 On Linux, the command line passed to popen() is considered
3021 as a binary string, no any internal to-wide and from-wide
3022 character set conversion happens, so we don't need to do anything.
3023 On Linux --execw is just a synonym to --exec.
3024
3025 For simplicity, assume that command line is limited to 4KB
3026 (like in cmd.exe) and that mode at most 10 characters.
3027 */
3028 if (command->type == Q_EXECW)
3029 {
3030 wchar_t wcmd[4096];
3031 wchar_t wmode[10];
3032 const char *cmd= ds_cmd->str;
3033 uint dummy_errors;
3034 size_t len;
3035 len= my_convert((char *) wcmd, sizeof(wcmd) - sizeof(wcmd[0]),
3036 &my_charset_utf16le_bin,
3037 ds_cmd->str, strlen(ds_cmd->str), charset_info,
3038 &dummy_errors);
3039 wcmd[len / sizeof(wchar_t)]= 0;
3040 len= my_convert((char *) wmode, sizeof(wmode) - sizeof(wmode[0]),
3041 &my_charset_utf16le_bin,
3042 mode, strlen(mode), charset_info, &dummy_errors);
3043 wmode[len / sizeof(wchar_t)]= 0;
3044 return _wpopen(wcmd, wmode);
3045 }
3046 #endif /* __WIN__ */
3047
3048 #if defined __WIN__ && defined USE_CYGWIN
3049 /* Dump the command into a sh script file and execute with popen */
3050 str_to_file(tmp_sh_name, ds_cmd->str, ds_cmd->length);
3051 return popen(tmp_sh_cmd, mode);
3052 #else
3053 errno= 0;
3054 FILE *file= popen(ds_cmd->str, mode);
3055 if (file == NULL)
3056 {
3057 if (errno != 0)
3058 {
3059 fprintf(stderr, "mysqltest: popen failed with errno %d (%s)\n", errno,
3060 strerror(errno));
3061 }
3062 else
3063 {
3064 fprintf(stderr,
3065 "mysqltest: popen returned NULL without setting errno "
3066 "(out-of-memory?)\n");
3067 }
3068 }
3069 return file;
3070 #endif
3071 }
3072
3073
init_builtin_echo(void)3074 static void init_builtin_echo(void)
3075 {
3076 #ifdef __WIN__
3077 size_t echo_length;
3078
3079 /* Look for "echo.exe" in same dir as mysqltest was started from */
3080 dirname_part(builtin_echo, my_progname, &echo_length);
3081 fn_format(builtin_echo, ".\\echo.exe",
3082 builtin_echo, "", MYF(MY_REPLACE_DIR));
3083
3084 /* Make sure echo.exe exists */
3085 if (access(builtin_echo, F_OK) != 0)
3086 builtin_echo[0]= 0;
3087 return;
3088
3089 #else
3090
3091 builtin_echo[0]= 0;
3092 return;
3093
3094 #endif
3095 }
3096
3097
3098 /*
3099 Replace a substring
3100
3101 SYNOPSIS
3102 replace
3103 ds_str The string to search and perform the replace in
3104 search_str The string to search for
3105 search_len Length of the string to search for
3106 replace_str The string to replace with
3107 replace_len Length of the string to replace with
3108
3109 RETURN
3110 0 String replaced
3111 1 Could not find search_str in str
3112 */
3113
replace(DYNAMIC_STRING * ds_str,const char * search_str,ulong search_len,const char * replace_str,ulong replace_len)3114 static int replace(DYNAMIC_STRING *ds_str,
3115 const char *search_str, ulong search_len,
3116 const char *replace_str, ulong replace_len)
3117 {
3118 DYNAMIC_STRING ds_tmp;
3119 const char *start= strstr(ds_str->str, search_str);
3120 if (!start)
3121 return 1;
3122 init_dynamic_string(&ds_tmp, "",
3123 ds_str->length + replace_len, 256);
3124 dynstr_append_mem(&ds_tmp, ds_str->str, start - ds_str->str);
3125 dynstr_append_mem(&ds_tmp, replace_str, replace_len);
3126 dynstr_append(&ds_tmp, start + search_len);
3127 dynstr_set(ds_str, ds_tmp.str);
3128 dynstr_free(&ds_tmp);
3129 return 0;
3130 }
3131
3132
3133 /*
3134 Execute given command.
3135
3136 SYNOPSIS
3137 do_exec()
3138 query called command
3139
3140 DESCRIPTION
3141 exec <command>
3142
3143 Execute the text between exec and end of line in a subprocess.
3144 The error code returned from the subprocess is checked against the
3145 expected error array, previously set with the --error command.
3146 It can thus be used to execute a command that shall fail.
3147
3148 NOTE
3149 Although mysqltest is executed from cygwin shell, the command will be
3150 executed in "cmd.exe". Thus commands like "rm" etc can NOT be used, use
3151 mysqltest commmand(s) like "remove_file" for that
3152 */
3153
do_exec(struct st_command * command)3154 void do_exec(struct st_command *command)
3155 {
3156 int error;
3157 char buf[512];
3158 FILE *res_file;
3159 char *cmd= command->first_argument;
3160 DYNAMIC_STRING ds_cmd;
3161 DBUG_ENTER("do_exec");
3162 DBUG_PRINT("enter", ("cmd: '%s'", cmd));
3163
3164 /* Skip leading space */
3165 while (*cmd && my_isspace(charset_info, *cmd))
3166 cmd++;
3167 if (!*cmd)
3168 die("Missing argument in exec");
3169 command->last_argument= command->end;
3170
3171 init_dynamic_string(&ds_cmd, 0, command->query_len+256, 256);
3172 /* Eval the command, thus replacing all environment variables */
3173 do_eval(&ds_cmd, cmd, command->end, !is_windows);
3174
3175 /* Check if echo should be replaced with "builtin" echo */
3176 if (builtin_echo[0] && strncmp(cmd, "echo", 4) == 0)
3177 {
3178 /* Replace echo with our "builtin" echo */
3179 replace(&ds_cmd, "echo", 4, builtin_echo, strlen(builtin_echo));
3180 }
3181
3182 #ifdef __WIN__
3183 #ifndef USE_CYGWIN
3184 /* Replace /dev/null with NUL */
3185 while(replace(&ds_cmd, "/dev/null", 9, "NUL", 3) == 0)
3186 ;
3187 /* Replace "closed stdout" with non existing output fd */
3188 while(replace(&ds_cmd, ">&-", 3, ">&4", 3) == 0)
3189 ;
3190 #endif
3191 #endif
3192
3193 /* exec command is interpreted externally and will not take newlines */
3194 while(replace(&ds_cmd, "\n", 1, " ", 1) == 0)
3195 ;
3196
3197 DBUG_PRINT("info", ("Executing '%s' as '%s'",
3198 command->first_argument, ds_cmd.str));
3199
3200 if (!(res_file= my_popen(&ds_cmd, "r", command)) && command->abort_on_error)
3201 {
3202 dynstr_free(&ds_cmd);
3203 die("popen(\"%s\", \"r\") failed", command->first_argument);
3204 }
3205
3206 while (fgets(buf, sizeof(buf), res_file))
3207 {
3208 if (disable_result_log)
3209 {
3210 buf[strlen(buf)-1]=0;
3211 DBUG_PRINT("exec_result",("%s", buf));
3212 }
3213 else
3214 {
3215 replace_dynstr_append(&ds_res, buf);
3216 }
3217 }
3218 error= pclose(res_file);
3219 if (error > 0)
3220 {
3221 uint status= WEXITSTATUS(error);
3222 int i;
3223
3224 if (command->abort_on_error)
3225 {
3226 log_msg("exec of '%s' failed, error: %d, status: %d, errno: %d",
3227 ds_cmd.str, error, status, errno);
3228 dynstr_free(&ds_cmd);
3229 die("command \"%s\" failed\n\nOutput from before failure:\n%s\n",
3230 command->first_argument, ds_res.str);
3231 }
3232
3233 DBUG_PRINT("info",
3234 ("error: %d, status: %d", error, status));
3235
3236 i= match_expected_error(command, status, NULL);
3237
3238 if (i >= 0)
3239 DBUG_PRINT("info", ("command \"%s\" failed with expected error: %d",
3240 command->first_argument, status));
3241 else
3242 {
3243 dynstr_free(&ds_cmd);
3244 if (command->expected_errors.count > 0)
3245 die("command \"%s\" failed with wrong error: %d",
3246 command->first_argument, status);
3247 }
3248 }
3249 else if (command->expected_errors.err[0].type == ERR_ERRNO &&
3250 command->expected_errors.err[0].code.errnum != 0)
3251 {
3252 /* Error code we wanted was != 0, i.e. not an expected success */
3253 log_msg("exec of '%s failed, error: %d, errno: %d",
3254 ds_cmd.str, error, errno);
3255 dynstr_free(&ds_cmd);
3256 die("command \"%s\" succeeded - should have failed with errno %d...",
3257 command->first_argument, command->expected_errors.err[0].code.errnum);
3258 }
3259
3260 dynstr_free(&ds_cmd);
3261 DBUG_VOID_RETURN;
3262 }
3263
3264 enum enum_operator
3265 {
3266 DO_DEC,
3267 DO_INC
3268 };
3269
3270
3271 /*
3272 Decrease or increase the value of a variable
3273
3274 SYNOPSIS
3275 do_modify_var()
3276 query called command
3277 op operation to perform on the var
3278
3279 DESCRIPTION
3280 dec $var_name
3281 inc $var_name
3282
3283 */
3284
do_modify_var(struct st_command * command,enum enum_operator op)3285 int do_modify_var(struct st_command *command,
3286 enum enum_operator op)
3287 {
3288 const char *p= command->first_argument;
3289 VAR* v;
3290 if (!*p)
3291 die("Missing argument to %.*s", command->first_word_len, command->query);
3292 if (*p != '$')
3293 die("The argument to %.*s must be a variable (start with $)",
3294 command->first_word_len, command->query);
3295 v= var_get(p, &p, 1, 0);
3296 if (! v->is_int)
3297 die("Cannot perform inc/dec on a non-numeric value");
3298 switch (op) {
3299 case DO_DEC:
3300 v->int_val--;
3301 break;
3302 case DO_INC:
3303 v->int_val++;
3304 break;
3305 default:
3306 die("Invalid operator to do_modify_var");
3307 break;
3308 }
3309 v->int_dirty= true;
3310 command->last_argument= (char*)++p;
3311 return 0;
3312 }
3313
3314
3315 /*
3316 SYNOPSIS
3317 set_wild_chars
3318 set true to set * etc. as wild char, false to reset
3319
3320 DESCRIPTION
3321 Auxiliary function to set "our" wild chars before calling wild_compare
3322 This is needed because the default values are changed to SQL syntax
3323 in mysqltest_embedded.
3324 */
3325
set_wild_chars(my_bool set)3326 void set_wild_chars (my_bool set)
3327 {
3328 static char old_many= 0, old_one, old_prefix;
3329
3330 if (set)
3331 {
3332 if (wild_many == '*') return; // No need
3333 old_many= wild_many;
3334 old_one= wild_one;
3335 old_prefix= wild_prefix;
3336 wild_many= '*';
3337 wild_one= '?';
3338 wild_prefix= 0;
3339 }
3340 else
3341 {
3342 if (! old_many) return; // Was not set
3343 wild_many= old_many;
3344 wild_one= old_one;
3345 wild_prefix= old_prefix;
3346 }
3347 }
3348
3349
3350 /*
3351 SYNOPSIS
3352 do_remove_file
3353 command called command
3354
3355 DESCRIPTION
3356 remove_file <file_name>
3357 Remove the file <file_name>
3358 */
3359
do_remove_file(struct st_command * command)3360 void do_remove_file(struct st_command *command)
3361 {
3362 int error;
3363 static DYNAMIC_STRING ds_filename;
3364 const struct command_arg rm_args[] = {
3365 { "filename", ARG_STRING, TRUE, &ds_filename, "File to delete" }
3366 };
3367 DBUG_ENTER("do_remove_file");
3368
3369 check_command_args(command, command->first_argument,
3370 rm_args, sizeof(rm_args)/sizeof(struct command_arg),
3371 ' ');
3372
3373 DBUG_PRINT("info", ("removing file: %s", ds_filename.str));
3374 error= my_delete(ds_filename.str, MYF(0)) != 0;
3375 handle_command_error(command, error);
3376 dynstr_free(&ds_filename);
3377 DBUG_VOID_RETURN;
3378 }
3379
3380
3381 /*
3382 SYNOPSIS
3383 do_remove_files_wildcard
3384 command called command
3385
3386 DESCRIPTION
3387 remove_files_wildcard <directory> [<file_name_pattern>]
3388 Remove the files in <directory> optionally matching <file_name_pattern>
3389 */
3390
do_remove_files_wildcard(struct st_command * command)3391 void do_remove_files_wildcard(struct st_command *command)
3392 {
3393 int error= 0;
3394 uint i;
3395 MY_DIR *dir_info;
3396 FILEINFO *file;
3397 char dir_separator[2];
3398 static DYNAMIC_STRING ds_directory;
3399 static DYNAMIC_STRING ds_wild;
3400 static DYNAMIC_STRING ds_file_to_remove;
3401 char dirname[FN_REFLEN];
3402
3403 const struct command_arg rm_args[] = {
3404 { "directory", ARG_STRING, TRUE, &ds_directory,
3405 "Directory containing files to delete" },
3406 { "filename", ARG_STRING, FALSE, &ds_wild, "File pattern to delete" }
3407 };
3408 DBUG_ENTER("do_remove_files_wildcard");
3409
3410 check_command_args(command, command->first_argument,
3411 rm_args, sizeof(rm_args)/sizeof(struct command_arg),
3412 ' ');
3413 fn_format(dirname, ds_directory.str, "", "", MY_UNPACK_FILENAME);
3414
3415 DBUG_PRINT("info", ("listing directory: %s", dirname));
3416 /* Note that my_dir sorts the list if not given any flags */
3417 if (!(dir_info= my_dir(dirname, MYF(MY_DONT_SORT | MY_WANT_STAT))))
3418 {
3419 error= 1;
3420 goto end;
3421 }
3422 init_dynamic_string(&ds_file_to_remove, dirname, 1024, 1024);
3423 dir_separator[0]= FN_LIBCHAR;
3424 dir_separator[1]= 0;
3425 dynstr_append(&ds_file_to_remove, dir_separator);
3426
3427 /* Set default wild chars for wild_compare, is changed in embedded mode */
3428 set_wild_chars(1);
3429
3430 uint length;
3431 /* Storing the length of the path to the file, so it can be reused */
3432 length= ds_file_to_remove.length;
3433 for (i= 0; i < (uint) dir_info->number_off_files; i++)
3434 {
3435 ds_file_to_remove.length= length;
3436 file= dir_info->dir_entry + i;
3437 /* Remove only regular files, i.e. no directories etc. */
3438 /* if (!MY_S_ISREG(file->mystat->st_mode)) */
3439 /* MY_S_ISREG does not work here on Windows, just skip directories */
3440 if (MY_S_ISDIR(file->mystat->st_mode))
3441 continue;
3442 if (ds_wild.length &&
3443 wild_compare(file->name, ds_wild.str, 0))
3444 continue;
3445 /* Not required as the var ds_file_to_remove.length already has the
3446 length in canonnicalized form */
3447 /* ds_file_to_remove.length= ds_directory.length + 1;
3448 ds_file_to_remove.str[ds_directory.length + 1]= 0; */
3449 dynstr_append(&ds_file_to_remove, file->name);
3450 DBUG_PRINT("info", ("removing file: %s", ds_file_to_remove.str));
3451 error= my_delete(ds_file_to_remove.str, MYF(0)) != 0;
3452 if (error)
3453 break;
3454 }
3455 set_wild_chars(0);
3456 my_dirend(dir_info);
3457
3458 end:
3459 handle_command_error(command, error);
3460 dynstr_free(&ds_directory);
3461 dynstr_free(&ds_wild);
3462 dynstr_free(&ds_file_to_remove);
3463 DBUG_VOID_RETURN;
3464 }
3465
3466
3467 /*
3468 SYNOPSIS
3469 do_copy_file
3470 command command handle
3471
3472 DESCRIPTION
3473 copy_file <from_file> <to_file>
3474 Copy <from_file> to <to_file>
3475
3476 NOTE! Will fail if <to_file> exists
3477 */
3478
do_copy_file(struct st_command * command)3479 void do_copy_file(struct st_command *command)
3480 {
3481 int error;
3482 static DYNAMIC_STRING ds_from_file;
3483 static DYNAMIC_STRING ds_to_file;
3484 const struct command_arg copy_file_args[] = {
3485 { "from_file", ARG_STRING, TRUE, &ds_from_file, "Filename to copy from" },
3486 { "to_file", ARG_STRING, TRUE, &ds_to_file, "Filename to copy to" }
3487 };
3488 DBUG_ENTER("do_copy_file");
3489
3490 check_command_args(command, command->first_argument,
3491 copy_file_args,
3492 sizeof(copy_file_args)/sizeof(struct command_arg),
3493 ' ');
3494
3495 DBUG_PRINT("info", ("Copy %s to %s", ds_from_file.str, ds_to_file.str));
3496 /* MY_HOLD_ORIGINAL_MODES prevents attempts to chown the file */
3497 error= (my_copy(ds_from_file.str, ds_to_file.str,
3498 MYF(MY_DONT_OVERWRITE_FILE | MY_HOLD_ORIGINAL_MODES)) != 0);
3499 handle_command_error(command, error);
3500 dynstr_free(&ds_from_file);
3501 dynstr_free(&ds_to_file);
3502 DBUG_VOID_RETURN;
3503 }
3504
3505
3506 /*
3507 SYNOPSIS
3508 do_move_file
3509 command command handle
3510
3511 DESCRIPTION
3512 move_file <from_file> <to_file>
3513 Move <from_file> to <to_file>
3514 */
3515
do_move_file(struct st_command * command)3516 void do_move_file(struct st_command *command)
3517 {
3518 int error;
3519 static DYNAMIC_STRING ds_from_file;
3520 static DYNAMIC_STRING ds_to_file;
3521 const struct command_arg move_file_args[] = {
3522 { "from_file", ARG_STRING, TRUE, &ds_from_file, "Filename to move from" },
3523 { "to_file", ARG_STRING, TRUE, &ds_to_file, "Filename to move to" }
3524 };
3525 DBUG_ENTER("do_move_file");
3526
3527 check_command_args(command, command->first_argument,
3528 move_file_args,
3529 sizeof(move_file_args)/sizeof(struct command_arg),
3530 ' ');
3531
3532 DBUG_PRINT("info", ("Move %s to %s", ds_from_file.str, ds_to_file.str));
3533 error= (my_rename(ds_from_file.str, ds_to_file.str,
3534 MYF(0)) != 0);
3535 handle_command_error(command, error);
3536 dynstr_free(&ds_from_file);
3537 dynstr_free(&ds_to_file);
3538 DBUG_VOID_RETURN;
3539 }
3540
3541
3542 /*
3543 SYNOPSIS
3544 do_chmod_file
3545 command command handle
3546
3547 DESCRIPTION
3548 chmod <octal> <file_name>
3549 Change file permission of <file_name>
3550
3551 */
3552
do_chmod_file(struct st_command * command)3553 void do_chmod_file(struct st_command *command)
3554 {
3555 long mode= 0;
3556 int err_code;
3557 static DYNAMIC_STRING ds_mode;
3558 static DYNAMIC_STRING ds_file;
3559 const struct command_arg chmod_file_args[] = {
3560 { "mode", ARG_STRING, TRUE, &ds_mode, "Mode of file(octal) ex. 0660"},
3561 { "filename", ARG_STRING, TRUE, &ds_file, "Filename of file to modify" }
3562 };
3563 DBUG_ENTER("do_chmod_file");
3564
3565 check_command_args(command, command->first_argument,
3566 chmod_file_args,
3567 sizeof(chmod_file_args)/sizeof(struct command_arg),
3568 ' ');
3569
3570 /* Parse what mode to set */
3571 if (ds_mode.length != 4 ||
3572 str2int(ds_mode.str, 8, 0, INT_MAX, &mode) == NullS)
3573 die("You must write a 4 digit octal number for mode");
3574
3575 DBUG_PRINT("info", ("chmod %o %s", (uint)mode, ds_file.str));
3576 err_code= chmod(ds_file.str, mode);
3577 if (err_code < 0)
3578 err_code= 1;
3579 handle_command_error(command, err_code);
3580 dynstr_free(&ds_mode);
3581 dynstr_free(&ds_file);
3582 DBUG_VOID_RETURN;
3583 }
3584
3585
3586 /*
3587 SYNOPSIS
3588 do_file_exists
3589 command called command
3590
3591 DESCRIPTION
3592 fiile_exist <file_name>
3593 Check if file <file_name> exists
3594 */
3595
do_file_exist(struct st_command * command)3596 void do_file_exist(struct st_command *command)
3597 {
3598 int error;
3599 static DYNAMIC_STRING ds_filename;
3600 const struct command_arg file_exist_args[] = {
3601 { "filename", ARG_STRING, TRUE, &ds_filename, "File to check if it exist" }
3602 };
3603 DBUG_ENTER("do_file_exist");
3604
3605 check_command_args(command, command->first_argument,
3606 file_exist_args,
3607 sizeof(file_exist_args)/sizeof(struct command_arg),
3608 ' ');
3609
3610 DBUG_PRINT("info", ("Checking for existence of file: %s", ds_filename.str));
3611 error= (access(ds_filename.str, F_OK) != 0);
3612 handle_command_error(command, error);
3613 dynstr_free(&ds_filename);
3614 DBUG_VOID_RETURN;
3615 }
3616
3617
3618 /*
3619 SYNOPSIS
3620 do_mkdir
3621 command called command
3622
3623 DESCRIPTION
3624 mkdir <dir_name>
3625 Create the directory <dir_name>
3626 */
3627
do_mkdir(struct st_command * command)3628 void do_mkdir(struct st_command *command)
3629 {
3630 int error;
3631 static DYNAMIC_STRING ds_dirname;
3632 const struct command_arg mkdir_args[] = {
3633 {"dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to create"}
3634 };
3635 DBUG_ENTER("do_mkdir");
3636
3637 check_command_args(command, command->first_argument,
3638 mkdir_args, sizeof(mkdir_args)/sizeof(struct command_arg),
3639 ' ');
3640
3641 DBUG_PRINT("info", ("creating directory: %s", ds_dirname.str));
3642 error= my_mkdir(ds_dirname.str, 0777, MYF(0)) != 0;
3643 handle_command_error(command, error);
3644 dynstr_free(&ds_dirname);
3645 DBUG_VOID_RETURN;
3646 }
3647
3648 /*
3649 SYNOPSIS
3650 do_rmdir
3651 command called command
3652
3653 DESCRIPTION
3654 rmdir <dir_name>
3655 Remove the empty directory <dir_name>
3656 */
3657
do_rmdir(struct st_command * command)3658 void do_rmdir(struct st_command *command)
3659 {
3660 int error;
3661 static DYNAMIC_STRING ds_dirname;
3662 const struct command_arg rmdir_args[] = {
3663 {"dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to remove"}
3664 };
3665 DBUG_ENTER("do_rmdir");
3666
3667 check_command_args(command, command->first_argument,
3668 rmdir_args, sizeof(rmdir_args)/sizeof(struct command_arg),
3669 ' ');
3670
3671 DBUG_PRINT("info", ("removing directory: %s", ds_dirname.str));
3672 error= rmdir(ds_dirname.str) != 0;
3673 handle_command_error(command, error);
3674 dynstr_free(&ds_dirname);
3675 DBUG_VOID_RETURN;
3676 }
3677
3678
3679 /*
3680 SYNOPSIS
3681 get_list_files
3682 ds output
3683 ds_dirname dir to list
3684 ds_wild wild-card file pattern (can be empty)
3685
3686 DESCRIPTION
3687 list all entries in directory (matching ds_wild if given)
3688 */
3689
get_list_files(DYNAMIC_STRING * ds,const DYNAMIC_STRING * ds_dirname,const DYNAMIC_STRING * ds_wild)3690 static int get_list_files(DYNAMIC_STRING *ds, const DYNAMIC_STRING *ds_dirname,
3691 const DYNAMIC_STRING *ds_wild)
3692 {
3693 uint i;
3694 MY_DIR *dir_info;
3695 FILEINFO *file;
3696 DBUG_ENTER("get_list_files");
3697
3698 DBUG_PRINT("info", ("listing directory: %s", ds_dirname->str));
3699 /* Note that my_dir sorts the list if not given any flags */
3700 if (!(dir_info= my_dir(ds_dirname->str, MYF(0))))
3701 DBUG_RETURN(1);
3702 set_wild_chars(1);
3703 for (i= 0; i < (uint) dir_info->number_off_files; i++)
3704 {
3705 file= dir_info->dir_entry + i;
3706 if (file->name[0] == '.' &&
3707 (file->name[1] == '\0' ||
3708 (file->name[1] == '.' && file->name[2] == '\0')))
3709 continue; /* . or .. */
3710 if (ds_wild && ds_wild->length &&
3711 wild_compare(file->name, ds_wild->str, 0))
3712 continue;
3713 replace_dynstr_append(ds, file->name);
3714 dynstr_append(ds, "\n");
3715 }
3716 set_wild_chars(0);
3717 my_dirend(dir_info);
3718 DBUG_RETURN(0);
3719 }
3720
3721
3722 /*
3723 SYNOPSIS
3724 do_list_files
3725 command called command
3726
3727 DESCRIPTION
3728 list_files <dir_name> [<file_name>]
3729 List files and directories in directory <dir_name> (like `ls`)
3730 [Matching <file_name>, where wild-cards are allowed]
3731 */
3732
do_list_files(struct st_command * command)3733 static void do_list_files(struct st_command *command)
3734 {
3735 int error;
3736 static DYNAMIC_STRING ds_dirname;
3737 static DYNAMIC_STRING ds_wild;
3738 const struct command_arg list_files_args[] = {
3739 {"dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to list"},
3740 {"file", ARG_STRING, FALSE, &ds_wild, "Filename (incl. wildcard)"}
3741 };
3742 DBUG_ENTER("do_list_files");
3743 command->used_replace= 1;
3744
3745 check_command_args(command, command->first_argument,
3746 list_files_args,
3747 sizeof(list_files_args)/sizeof(struct command_arg), ' ');
3748
3749 error= get_list_files(&ds_res, &ds_dirname, &ds_wild);
3750 handle_command_error(command, error);
3751 dynstr_free(&ds_dirname);
3752 dynstr_free(&ds_wild);
3753 DBUG_VOID_RETURN;
3754 }
3755
3756
3757 /*
3758 SYNOPSIS
3759 do_list_files_write_file_command
3760 command called command
3761 append append file, or create new
3762
3763 DESCRIPTION
3764 list_files_{write|append}_file <filename> <dir_name> [<match_file>]
3765 List files and directories in directory <dir_name> (like `ls`)
3766 [Matching <match_file>, where wild-cards are allowed]
3767
3768 Note: File will be truncated if exists and append is not true.
3769 */
3770
do_list_files_write_file_command(struct st_command * command,my_bool append)3771 static void do_list_files_write_file_command(struct st_command *command,
3772 my_bool append)
3773 {
3774 int error;
3775 static DYNAMIC_STRING ds_content;
3776 static DYNAMIC_STRING ds_filename;
3777 static DYNAMIC_STRING ds_dirname;
3778 static DYNAMIC_STRING ds_wild;
3779 const struct command_arg list_files_args[] = {
3780 {"filename", ARG_STRING, TRUE, &ds_filename, "Filename for write"},
3781 {"dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to list"},
3782 {"file", ARG_STRING, FALSE, &ds_wild, "Filename (incl. wildcard)"}
3783 };
3784 DBUG_ENTER("do_list_files_write_file");
3785 command->used_replace= 1;
3786
3787 check_command_args(command, command->first_argument,
3788 list_files_args,
3789 sizeof(list_files_args)/sizeof(struct command_arg), ' ');
3790
3791 init_dynamic_string(&ds_content, "", 1024, 1024);
3792 error= get_list_files(&ds_content, &ds_dirname, &ds_wild);
3793 handle_command_error(command, error);
3794 str_to_file2(ds_filename.str, ds_content.str, ds_content.length, append);
3795 dynstr_free(&ds_content);
3796 dynstr_free(&ds_filename);
3797 dynstr_free(&ds_dirname);
3798 dynstr_free(&ds_wild);
3799 DBUG_VOID_RETURN;
3800 }
3801
3802
3803 /*
3804 Read characters from line buffer or file. This is needed to allow
3805 my_ungetc() to buffer MAX_DELIMITER_LENGTH characters for a file
3806
3807 NOTE:
3808 This works as long as one doesn't change files (with 'source file_name')
3809 when there is things pushed into the buffer. This should however not
3810 happen for any tests in the test suite.
3811 */
3812
my_getc(FILE * file)3813 int my_getc(FILE *file)
3814 {
3815 if (line_buffer_pos == line_buffer)
3816 return fgetc(file);
3817 return *--line_buffer_pos;
3818 }
3819
3820
my_ungetc(int c)3821 void my_ungetc(int c)
3822 {
3823 *line_buffer_pos++= (char) c;
3824 }
3825
3826
read_until_delimiter(DYNAMIC_STRING * ds,DYNAMIC_STRING * ds_delimiter)3827 void read_until_delimiter(DYNAMIC_STRING *ds,
3828 DYNAMIC_STRING *ds_delimiter)
3829 {
3830 char c;
3831 DBUG_ENTER("read_until_delimiter");
3832 DBUG_PRINT("enter", ("delimiter: %s, length: %u",
3833 ds_delimiter->str, (uint) ds_delimiter->length));
3834
3835 if (ds_delimiter->length > MAX_DELIMITER_LENGTH)
3836 die("Max delimiter length(%d) exceeded", MAX_DELIMITER_LENGTH);
3837
3838 /* Read from file until delimiter is found */
3839 while (1)
3840 {
3841 c= my_getc(cur_file->file);
3842
3843 if (c == '\n')
3844 {
3845 cur_file->lineno++;
3846
3847 /* Skip newline from the same line as the command */
3848 if (start_lineno == (cur_file->lineno - 1))
3849 continue;
3850 }
3851 else if (start_lineno == cur_file->lineno)
3852 {
3853 /*
3854 No characters except \n are allowed on
3855 the same line as the command
3856 */
3857 die("Trailing characters found after command");
3858 }
3859
3860 if (feof(cur_file->file))
3861 die("End of file encountered before '%s' delimiter was found",
3862 ds_delimiter->str);
3863
3864 if (match_delimiter(c, ds_delimiter->str, ds_delimiter->length))
3865 {
3866 DBUG_PRINT("exit", ("Found delimiter '%s'", ds_delimiter->str));
3867 break;
3868 }
3869 dynstr_append_mem(ds, (const char*)&c, 1);
3870 }
3871 DBUG_PRINT("exit", ("ds: %s", ds->str));
3872 DBUG_VOID_RETURN;
3873 }
3874
3875
do_write_file_command(struct st_command * command,my_bool append)3876 void do_write_file_command(struct st_command *command, my_bool append)
3877 {
3878 static DYNAMIC_STRING ds_content;
3879 static DYNAMIC_STRING ds_filename;
3880 static DYNAMIC_STRING ds_delimiter;
3881 const struct command_arg write_file_args[] = {
3882 { "filename", ARG_STRING, TRUE, &ds_filename, "File to write to" },
3883 { "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until" }
3884 };
3885 DBUG_ENTER("do_write_file");
3886
3887 check_command_args(command,
3888 command->first_argument,
3889 write_file_args,
3890 sizeof(write_file_args)/sizeof(struct command_arg),
3891 ' ');
3892
3893 if (!append && access(ds_filename.str, F_OK) == 0)
3894 {
3895 /* The file should not be overwritten */
3896 die("File already exist: '%s'", ds_filename.str);
3897 }
3898
3899 ds_content= command->content;
3900 /* If it hasn't been done already by a loop iteration, fill it in */
3901 if (! ds_content.str)
3902 {
3903 /* If no delimiter was provided, use EOF */
3904 if (ds_delimiter.length == 0)
3905 dynstr_set(&ds_delimiter, "EOF");
3906
3907 init_dynamic_string(&ds_content, "", 1024, 1024);
3908 read_until_delimiter(&ds_content, &ds_delimiter);
3909 command->content= ds_content;
3910 }
3911 /* This function could be called even if "false", so check before printing */
3912 if (cur_block->ok)
3913 {
3914 DBUG_PRINT("info", ("Writing to file: %s", ds_filename.str));
3915 str_to_file2(ds_filename.str, ds_content.str, ds_content.length, append);
3916 }
3917 dynstr_free(&ds_filename);
3918 dynstr_free(&ds_delimiter);
3919 DBUG_VOID_RETURN;
3920 }
3921
3922
3923 /*
3924 SYNOPSIS
3925 do_write_file
3926 command called command
3927
3928 DESCRIPTION
3929 write_file <file_name> [<delimiter>];
3930 <what to write line 1>
3931 <...>
3932 < what to write line n>
3933 EOF
3934
3935 --write_file <file_name>;
3936 <what to write line 1>
3937 <...>
3938 < what to write line n>
3939 EOF
3940
3941 Write everything between the "write_file" command and 'delimiter'
3942 to "file_name"
3943
3944 NOTE! Will fail if <file_name> exists
3945
3946 Default <delimiter> is EOF
3947
3948 */
3949
do_write_file(struct st_command * command)3950 void do_write_file(struct st_command *command)
3951 {
3952 do_write_file_command(command, FALSE);
3953 }
3954
3955
3956 /*
3957 SYNOPSIS
3958 do_append_file
3959 command called command
3960
3961 DESCRIPTION
3962 append_file <file_name> [<delimiter>];
3963 <what to write line 1>
3964 <...>
3965 < what to write line n>
3966 EOF
3967
3968 --append_file <file_name>;
3969 <what to write line 1>
3970 <...>
3971 < what to write line n>
3972 EOF
3973
3974 Append everything between the "append_file" command
3975 and 'delimiter' to "file_name"
3976
3977 Default <delimiter> is EOF
3978
3979 */
3980
do_append_file(struct st_command * command)3981 void do_append_file(struct st_command *command)
3982 {
3983 do_write_file_command(command, TRUE);
3984 }
3985
3986
3987 /*
3988 SYNOPSIS
3989 do_cat_file
3990 command called command
3991
3992 DESCRIPTION
3993 cat_file <file_name>;
3994
3995 Print the given file to result log
3996
3997 */
3998
do_cat_file(struct st_command * command)3999 void do_cat_file(struct st_command *command)
4000 {
4001 int error;
4002 static DYNAMIC_STRING ds_filename;
4003 const struct command_arg cat_file_args[] = {
4004 { "filename", ARG_STRING, TRUE, &ds_filename, "File to read from" }
4005 };
4006 DBUG_ENTER("do_cat_file");
4007
4008 check_command_args(command,
4009 command->first_argument,
4010 cat_file_args,
4011 sizeof(cat_file_args)/sizeof(struct command_arg),
4012 ' ');
4013
4014 DBUG_PRINT("info", ("Reading from, file: %s", ds_filename.str));
4015
4016 error= cat_file(&ds_res, ds_filename.str);
4017 handle_command_error(command, error);
4018 dynstr_free(&ds_filename);
4019 DBUG_VOID_RETURN;
4020 }
4021
4022
4023 /*
4024 SYNOPSIS
4025 do_diff_files
4026 command called command
4027
4028 DESCRIPTION
4029 diff_files <file1> <file2>;
4030
4031 Fails if the two files differ.
4032
4033 */
4034
do_diff_files(struct st_command * command)4035 void do_diff_files(struct st_command *command)
4036 {
4037 int error= 0;
4038 static DYNAMIC_STRING ds_filename;
4039 static DYNAMIC_STRING ds_filename2;
4040 const struct command_arg diff_file_args[] = {
4041 { "file1", ARG_STRING, TRUE, &ds_filename, "First file to diff" },
4042 { "file2", ARG_STRING, TRUE, &ds_filename2, "Second file to diff" }
4043 };
4044 DBUG_ENTER("do_diff_files");
4045
4046 check_command_args(command,
4047 command->first_argument,
4048 diff_file_args,
4049 sizeof(diff_file_args)/sizeof(struct command_arg),
4050 ' ');
4051
4052 if (access(ds_filename.str, F_OK) != 0)
4053 die("command \"diff_files\" failed, file '%s' does not exist",
4054 ds_filename.str);
4055
4056 if (access(ds_filename2.str, F_OK) != 0)
4057 die("command \"diff_files\" failed, file '%s' does not exist",
4058 ds_filename2.str);
4059
4060 if ((error= compare_files(ds_filename.str, ds_filename2.str)) &&
4061 match_expected_error(command, error, NULL) < 0)
4062 {
4063 /* Compare of the two files failed, append them to output
4064 so the failure can be analyzed, but only if it was not
4065 expected to fail.
4066 */
4067 show_diff(&ds_res, ds_filename.str, ds_filename2.str);
4068 log_file.write(&ds_res);
4069 log_file.flush();
4070 dynstr_set(&ds_res, 0);
4071 }
4072
4073 dynstr_free(&ds_filename);
4074 dynstr_free(&ds_filename2);
4075 handle_command_error(command, error);
4076 DBUG_VOID_RETURN;
4077 }
4078
4079
find_connection_by_name(const char * name)4080 struct st_connection * find_connection_by_name(const char *name)
4081 {
4082 struct st_connection *con;
4083 for (con= connections; con < next_con; con++)
4084 {
4085 if (!strcmp(con->name, name))
4086 {
4087 return con;
4088 }
4089 }
4090 return 0; /* Connection not found */
4091 }
4092
4093
4094 /*
4095 SYNOPSIS
4096 do_send_quit
4097 command called command
4098
4099 DESCRIPTION
4100 Sends a simple quit command to the server for the named connection.
4101
4102 */
4103
do_send_quit(struct st_command * command)4104 void do_send_quit(struct st_command *command)
4105 {
4106 char *p= command->first_argument, *name;
4107 struct st_connection *con;
4108
4109 DBUG_ENTER("do_send_quit");
4110 DBUG_PRINT("enter",("name: '%s'",p));
4111
4112 if (!*p)
4113 die("Missing connection name in send_quit");
4114 name= p;
4115 while (*p && !my_isspace(charset_info,*p))
4116 p++;
4117
4118 if (*p)
4119 *p++= 0;
4120 command->last_argument= p;
4121
4122 if (!(con= find_connection_by_name(name)))
4123 die("connection '%s' not found in connection pool", name);
4124
4125 simple_command(&con->mysql,COM_QUIT,0,0,1);
4126
4127 DBUG_VOID_RETURN;
4128 }
4129
4130
4131 /*
4132 SYNOPSIS
4133 do_change_user
4134 command called command
4135
4136 DESCRIPTION
4137 change_user [<user>], [<passwd>], [<db>]
4138 <user> - user to change to
4139 <passwd> - user password
4140 <db> - default database
4141
4142 Changes the user and causes the database specified by db to become
4143 the default (current) database for the the current connection.
4144
4145 */
4146
do_change_user(struct st_command * command)4147 void do_change_user(struct st_command *command)
4148 {
4149 MYSQL *mysql = &cur_con->mysql;
4150 static DYNAMIC_STRING ds_user, ds_passwd, ds_db;
4151 const struct command_arg change_user_args[] = {
4152 { "user", ARG_STRING, FALSE, &ds_user, "User to connect as" },
4153 { "password", ARG_STRING, FALSE, &ds_passwd, "Password used when connecting" },
4154 { "database", ARG_STRING, FALSE, &ds_db, "Database to select after connect" },
4155 };
4156
4157 DBUG_ENTER("do_change_user");
4158
4159 check_command_args(command, command->first_argument,
4160 change_user_args,
4161 sizeof(change_user_args)/sizeof(struct command_arg),
4162 ',');
4163
4164 if (cur_con->stmt)
4165 {
4166 mysql_stmt_close(cur_con->stmt);
4167 cur_con->stmt= NULL;
4168 }
4169
4170 if (!ds_user.length)
4171 {
4172 dynstr_set(&ds_user, mysql->user);
4173
4174 if (!ds_passwd.length)
4175 dynstr_set(&ds_passwd, mysql->passwd);
4176
4177 if (!ds_db.length)
4178 dynstr_set(&ds_db, mysql->db);
4179 }
4180
4181 DBUG_PRINT("info",("connection: '%s' user: '%s' password: '%s' database: '%s'",
4182 cur_con->name, ds_user.str, ds_passwd.str, ds_db.str));
4183
4184 if (mysql_change_user(mysql, ds_user.str, ds_passwd.str, ds_db.str))
4185 handle_error(command, mysql_errno(mysql), mysql_error(mysql),
4186 mysql_sqlstate(mysql), &ds_res);
4187 else
4188 handle_no_error(command);
4189
4190 dynstr_free(&ds_user);
4191 dynstr_free(&ds_passwd);
4192 dynstr_free(&ds_db);
4193
4194 DBUG_VOID_RETURN;
4195 }
4196
4197
4198 /*
4199 SYNOPSIS
4200 do_perl
4201 command command handle
4202
4203 DESCRIPTION
4204 perl [<delimiter>];
4205 <perlscript line 1>
4206 <...>
4207 <perlscript line n>
4208 EOF
4209
4210 Execute everything after "perl" until <delimiter> as perl.
4211 Useful for doing more advanced things
4212 but still being able to execute it on all platforms.
4213
4214 Default <delimiter> is EOF
4215 */
4216
do_perl(struct st_command * command)4217 void do_perl(struct st_command *command)
4218 {
4219 int error;
4220 File fd;
4221 FILE *res_file;
4222 char buf[FN_REFLEN];
4223 char temp_file_path[FN_REFLEN];
4224 static DYNAMIC_STRING ds_script;
4225 static DYNAMIC_STRING ds_delimiter;
4226 const struct command_arg perl_args[] = {
4227 { "delimiter", ARG_STRING, FALSE, &ds_delimiter, "Delimiter to read until" }
4228 };
4229 DBUG_ENTER("do_perl");
4230
4231 check_command_args(command,
4232 command->first_argument,
4233 perl_args,
4234 sizeof(perl_args)/sizeof(struct command_arg),
4235 ' ');
4236
4237 ds_script= command->content;
4238 /* If it hasn't been done already by a loop iteration, fill it in */
4239 if (! ds_script.str)
4240 {
4241 /* If no delimiter was provided, use EOF */
4242 if (ds_delimiter.length == 0)
4243 dynstr_set(&ds_delimiter, "EOF");
4244
4245 init_dynamic_string(&ds_script, "", 1024, 1024);
4246 read_until_delimiter(&ds_script, &ds_delimiter);
4247 command->content= ds_script;
4248 }
4249
4250 /* This function could be called even if "false", so check before doing */
4251 if (cur_block->ok)
4252 {
4253 DBUG_PRINT("info", ("Executing perl: %s", ds_script.str));
4254
4255 /* Create temporary file name */
4256 if ((fd= create_temp_file(temp_file_path, getenv("MYSQLTEST_VARDIR"),
4257 "tmp", O_CREAT | O_SHARE | O_RDWR,
4258 MYF(MY_WME))) < 0)
4259 die("Failed to create temporary file for perl command");
4260 my_close(fd, MYF(0));
4261
4262 /* Compatibility for Perl 5.24 and newer. */
4263 string script = "push @INC, \".\";\n";
4264 script.append(ds_script.str, ds_script.length);
4265
4266 str_to_file(temp_file_path, &script[0], script.size());
4267
4268 /* Format the "perl <filename>" command */
4269 my_snprintf(buf, sizeof(buf), "perl %s", temp_file_path);
4270
4271 if (!(res_file= popen(buf, "r")) && command->abort_on_error)
4272 die("popen(\"%s\", \"r\") failed", buf);
4273
4274 while (fgets(buf, sizeof(buf), res_file))
4275 {
4276 if (disable_result_log)
4277 {
4278 buf[strlen(buf)-1]=0;
4279 DBUG_PRINT("exec_result",("%s", buf));
4280 }
4281 else
4282 {
4283 replace_dynstr_append(&ds_res, buf);
4284 }
4285 }
4286 error= pclose(res_file);
4287
4288 /* Remove the temporary file, but keep it if perl failed */
4289 if (!error)
4290 my_delete(temp_file_path, MYF(0));
4291
4292 /* Check for error code that indicates perl could not be started */
4293 int exstat= WEXITSTATUS(error);
4294 #ifdef __WIN__
4295 if (exstat == 1)
4296 /* Text must begin 'perl not found' as mtr looks for it */
4297 abort_not_supported_test("perl not found in path or did not start");
4298 #else
4299 if (exstat == 127)
4300 abort_not_supported_test("perl not found in path");
4301 #endif
4302 else
4303 handle_command_error(command, exstat);
4304 }
4305 dynstr_free(&ds_delimiter);
4306 DBUG_VOID_RETURN;
4307 }
4308
4309
4310 /*
4311 Print the content between echo and <delimiter> to result file.
4312 Evaluate all variables in the string before printing, allow
4313 for variable names to be escaped using \
4314
4315 SYNOPSIS
4316 do_echo()
4317 command called command
4318
4319 DESCRIPTION
4320 echo text
4321 Print the text after echo until end of command to result file
4322
4323 echo $<var_name>
4324 Print the content of the variable <var_name> to result file
4325
4326 echo Some text $<var_name>
4327 Print "Some text" plus the content of the variable <var_name> to
4328 result file
4329
4330 echo Some text \$<var_name>
4331 Print "Some text" plus $<var_name> to result file
4332 */
4333
do_echo(struct st_command * command)4334 int do_echo(struct st_command *command)
4335 {
4336 DYNAMIC_STRING ds_echo;
4337 DBUG_ENTER("do_echo");
4338
4339 init_dynamic_string(&ds_echo, "", command->query_len, 256);
4340 do_eval(&ds_echo, command->first_argument, command->end, FALSE);
4341 dynstr_append_mem(&ds_res, ds_echo.str, ds_echo.length);
4342 dynstr_append_mem(&ds_res, "\n", 1);
4343 dynstr_free(&ds_echo);
4344 command->last_argument= command->end;
4345 DBUG_RETURN(0);
4346 }
4347
4348
do_wait_for_slave_to_stop(struct st_command * c MY_ATTRIBUTE ((unused)))4349 void do_wait_for_slave_to_stop(struct st_command *c MY_ATTRIBUTE((unused)))
4350 {
4351 static int SLAVE_POLL_INTERVAL= 300000;
4352 MYSQL* mysql = &cur_con->mysql;
4353 for (;;)
4354 {
4355 MYSQL_RES *UNINIT_VAR(res);
4356 MYSQL_ROW row;
4357 int done;
4358
4359 if (mysql_query(mysql,"show status like 'Slave_running'") ||
4360 !(res=mysql_store_result(mysql)))
4361 die("Query failed while probing slave for stop: %s",
4362 mysql_error(mysql));
4363 if (!(row=mysql_fetch_row(res)) || !row[1])
4364 {
4365 mysql_free_result(res);
4366 die("Strange result from query while probing slave for stop");
4367 }
4368 done = !strcmp(row[1],"OFF");
4369 mysql_free_result(res);
4370 if (done)
4371 break;
4372 my_sleep(SLAVE_POLL_INTERVAL);
4373 }
4374 return;
4375 }
4376
4377
do_sync_with_master2(struct st_command * command,long offset)4378 void do_sync_with_master2(struct st_command *command, long offset)
4379 {
4380 MYSQL_RES *res;
4381 MYSQL_ROW row;
4382 MYSQL *mysql= &cur_con->mysql;
4383 char query_buf[FN_REFLEN+128];
4384 int timeout= 300; /* seconds */
4385
4386 if (!master_pos.file[0])
4387 die("Calling 'sync_with_master' without calling 'save_master_pos'");
4388
4389 sprintf(query_buf, "select master_pos_wait('%s', %ld, %d)",
4390 master_pos.file, master_pos.pos + offset, timeout);
4391
4392 if (mysql_query(mysql, query_buf))
4393 die("failed in '%s': %d: %s", query_buf, mysql_errno(mysql),
4394 mysql_error(mysql));
4395
4396 if (!(res= mysql_store_result(mysql)))
4397 die("mysql_store_result() returned NULL for '%s'", query_buf);
4398 if (!(row= mysql_fetch_row(res)))
4399 {
4400 mysql_free_result(res);
4401 die("empty result in %s", query_buf);
4402 }
4403
4404 int result= -99;
4405 const char* result_str= row[0];
4406 if (result_str)
4407 result= atoi(result_str);
4408
4409 mysql_free_result(res);
4410
4411 if (!result_str || result < 0)
4412 {
4413 /* master_pos_wait returned NULL or < 0 */
4414 show_query(mysql, "SHOW MASTER STATUS");
4415 show_query(mysql, "SHOW SLAVE STATUS");
4416 show_query(mysql, "SHOW PROCESSLIST");
4417 fprintf(stderr, "analyze: sync_with_master\n");
4418
4419 if (!result_str)
4420 {
4421 /*
4422 master_pos_wait returned NULL. This indicates that
4423 slave SQL thread is not started, the slave's master
4424 information is not initialized, the arguments are
4425 incorrect, or an error has occured
4426 */
4427 die("%.*s failed: '%s' returned NULL "\
4428 "indicating slave SQL thread failure",
4429 command->first_word_len, command->query, query_buf);
4430
4431 }
4432
4433 if (result == -1)
4434 die("%.*s failed: '%s' returned -1 "\
4435 "indicating timeout after %d seconds",
4436 command->first_word_len, command->query, query_buf, timeout);
4437 else
4438 die("%.*s failed: '%s' returned unknown result :%d",
4439 command->first_word_len, command->query, query_buf, result);
4440 }
4441
4442 return;
4443 }
4444
do_sync_with_master(struct st_command * command)4445 void do_sync_with_master(struct st_command *command)
4446 {
4447 long offset= 0;
4448 char *p= command->first_argument;
4449 const char *offset_start= p;
4450 if (*offset_start)
4451 {
4452 for (; my_isdigit(charset_info, *p); p++)
4453 offset = offset * 10 + *p - '0';
4454
4455 if(*p && !my_isspace(charset_info, *p))
4456 die("Invalid integer argument \"%s\"", offset_start);
4457 command->last_argument= p;
4458 }
4459 do_sync_with_master2(command, offset);
4460 return;
4461 }
4462
4463
4464 /*
4465 when ndb binlog is on, this call will wait until last updated epoch
4466 (locally in the mysqld) has been received into the binlog
4467 */
do_save_master_pos()4468 int do_save_master_pos()
4469 {
4470 MYSQL_RES *res;
4471 MYSQL_ROW row;
4472 MYSQL *mysql = &cur_con->mysql;
4473 const char *query;
4474 DBUG_ENTER("do_save_master_pos");
4475
4476 #ifdef HAVE_NDB_BINLOG
4477 /*
4478 Wait for ndb binlog to be up-to-date with all changes
4479 done on the local mysql server
4480 */
4481 {
4482 bool have_ndbcluster;
4483 if (mysql_query(mysql, query=
4484 "select count(*) from information_schema.engines"
4485 " where engine = 'ndbcluster' and"
4486 " support in ('YES', 'DEFAULT')"))
4487 die("'%s' failed: %d %s", query,
4488 mysql_errno(mysql), mysql_error(mysql));
4489 if (!(res= mysql_store_result(mysql)))
4490 die("mysql_store_result() returned NULL for '%s'", query);
4491 if (!(row= mysql_fetch_row(res)))
4492 die("Query '%s' returned empty result", query);
4493
4494 have_ndbcluster= strcmp(row[0], "1") == 0;
4495 mysql_free_result(res);
4496
4497 if (have_ndbcluster)
4498 {
4499 ulonglong start_epoch= 0, handled_epoch= 0,
4500 latest_trans_epoch=0,
4501 latest_handled_binlog_epoch= 0;
4502 int count= 0;
4503 int do_continue= 1;
4504 while (do_continue)
4505 {
4506 const char binlog[]= "binlog";
4507 const char latest_trans_epoch_str[]=
4508 "latest_trans_epoch=";
4509 const char latest_handled_binlog_epoch_str[]=
4510 "latest_handled_binlog_epoch=";
4511 if (count)
4512 my_sleep(100*1000); /* 100ms */
4513 if (mysql_query(mysql, query= "show engine ndb status"))
4514 die("failed in '%s': %d %s", query,
4515 mysql_errno(mysql), mysql_error(mysql));
4516 if (!(res= mysql_store_result(mysql)))
4517 die("mysql_store_result() returned NULL for '%s'", query);
4518 while ((row= mysql_fetch_row(res)))
4519 {
4520 if (strcmp(row[1], binlog) == 0)
4521 {
4522 const char *status= row[2];
4523
4524 /* latest_trans_epoch */
4525 while (*status && strncmp(status, latest_trans_epoch_str,
4526 sizeof(latest_trans_epoch_str)-1))
4527 status++;
4528 if (*status)
4529 {
4530 status+= sizeof(latest_trans_epoch_str)-1;
4531 latest_trans_epoch= strtoull(status, (char**) 0, 10);
4532 }
4533 else
4534 die("result does not contain '%s' in '%s'",
4535 latest_trans_epoch_str, query);
4536
4537 /* latest_handled_binlog */
4538 while (*status &&
4539 strncmp(status, latest_handled_binlog_epoch_str,
4540 sizeof(latest_handled_binlog_epoch_str)-1))
4541 status++;
4542 if (*status)
4543 {
4544 status+= sizeof(latest_handled_binlog_epoch_str)-1;
4545 latest_handled_binlog_epoch= strtoull(status, (char**) 0, 10);
4546 }
4547 else
4548 die("result does not contain '%s' in '%s'",
4549 latest_handled_binlog_epoch_str, query);
4550
4551 if (count == 0)
4552 start_epoch= latest_trans_epoch;
4553 break;
4554 }
4555 }
4556 if (!row)
4557 die("result does not contain '%s' in '%s'",
4558 binlog, query);
4559 if (latest_handled_binlog_epoch > handled_epoch)
4560 count= 0;
4561 handled_epoch= latest_handled_binlog_epoch;
4562 count++;
4563 if (latest_handled_binlog_epoch >= start_epoch)
4564 do_continue= 0;
4565 else if (count > 300) /* 30s */
4566 {
4567 break;
4568 }
4569 mysql_free_result(res);
4570 }
4571 }
4572 }
4573 #endif
4574 if (mysql_query(mysql, query= "show master status"))
4575 die("failed in 'show master status': %d %s",
4576 mysql_errno(mysql), mysql_error(mysql));
4577
4578 if (!(res = mysql_store_result(mysql)))
4579 die("mysql_store_result() retuned NULL for '%s'", query);
4580 if (!(row = mysql_fetch_row(res)))
4581 die("empty result in show master status");
4582 strnmov(master_pos.file, row[0], sizeof(master_pos.file)-1);
4583 master_pos.pos = strtoul(row[1], (char**) 0, 10);
4584 mysql_free_result(res);
4585 DBUG_RETURN(0);
4586 }
4587
4588
4589 /*
4590 Assign the variable <var_name> with <var_val>
4591
4592 SYNOPSIS
4593 do_let()
4594 query called command
4595
4596 DESCRIPTION
4597 let $<var_name>=<var_val><delimiter>
4598
4599 <var_name> - is the string string found between the $ and =
4600 <var_val> - is the content between the = and <delimiter>, it may span
4601 multiple line and contain any characters except <delimiter>
4602 <delimiter> - is a string containing of one or more chars, default is ;
4603
4604 RETURN VALUES
4605 Program will die if error detected
4606 */
4607
do_let(struct st_command * command)4608 void do_let(struct st_command *command)
4609 {
4610 char *p= command->first_argument;
4611 char *var_name, *var_name_end;
4612 DYNAMIC_STRING let_rhs_expr;
4613 DBUG_ENTER("do_let");
4614
4615 init_dynamic_string(&let_rhs_expr, "", 512, 2048);
4616
4617 /* Find <var_name> */
4618 if (!*p)
4619 die("Missing arguments to let");
4620 var_name= p;
4621 while (*p && (*p != '=') && !my_isspace(charset_info,*p))
4622 p++;
4623 var_name_end= p;
4624 if (var_name == var_name_end ||
4625 (var_name+1 == var_name_end && *var_name == '$'))
4626 die("Missing variable name in let");
4627 while (my_isspace(charset_info,*p))
4628 p++;
4629 if (*p++ != '=')
4630 die("Missing assignment operator in let");
4631
4632 /* Find start of <var_val> */
4633 while (*p && my_isspace(charset_info,*p))
4634 p++;
4635
4636 do_eval(&let_rhs_expr, p, command->end, FALSE);
4637
4638 command->last_argument= command->end;
4639 /* Assign var_val to var_name */
4640 var_set(var_name, var_name_end, let_rhs_expr.str,
4641 (let_rhs_expr.str + let_rhs_expr.length));
4642 dynstr_free(&let_rhs_expr);
4643 revert_properties();
4644 DBUG_VOID_RETURN;
4645 }
4646
4647
4648 /*
4649 Sleep the number of specified seconds
4650
4651 SYNOPSIS
4652 do_sleep()
4653 q called command
4654 real_sleep use the value from opt_sleep as number of seconds to sleep
4655 if real_sleep is false
4656
4657 DESCRIPTION
4658 sleep <seconds>
4659 real_sleep <seconds>
4660
4661 The difference between the sleep and real_sleep commands is that sleep
4662 uses the delay from the --sleep command-line option if there is one.
4663 (If the --sleep option is not given, the sleep command uses the delay
4664 specified by its argument.) The real_sleep command always uses the
4665 delay specified by its argument. The logic is that sometimes delays are
4666 cpu-dependent, and --sleep can be used to set this delay. real_sleep is
4667 used for cpu-independent delays.
4668 */
4669
do_sleep(struct st_command * command,my_bool real_sleep)4670 int do_sleep(struct st_command *command, my_bool real_sleep)
4671 {
4672 int error= 0;
4673 char *sleep_start, *sleep_end;
4674 double sleep_val;
4675 char *p;
4676 static DYNAMIC_STRING ds_sleep;
4677 const struct command_arg sleep_args[] = {
4678 { "sleep_delay", ARG_STRING, TRUE, &ds_sleep, "Number of seconds to sleep." }
4679 };
4680 check_command_args(command, command->first_argument, sleep_args,
4681 sizeof(sleep_args)/sizeof(struct command_arg),
4682 ' ');
4683
4684 p= ds_sleep.str;
4685 sleep_end= ds_sleep.str + ds_sleep.length;
4686 while (my_isspace(charset_info, *p))
4687 p++;
4688 if (!*p)
4689 die("Missing argument to %.*s", command->first_word_len, command->query);
4690 sleep_start= p;
4691 /* Check that arg starts with a digit, not handled by my_strtod */
4692 if (!my_isdigit(charset_info, *sleep_start))
4693 die("Invalid argument to %.*s \"%s\"", command->first_word_len,
4694 command->query, sleep_start);
4695 sleep_val= my_strtod(sleep_start, &sleep_end, &error);
4696 check_eol_junk_line(sleep_end);
4697 if (error)
4698 die("Invalid argument to %.*s \"%s\"", command->first_word_len,
4699 command->query, command->first_argument);
4700 dynstr_free(&ds_sleep);
4701
4702 /* Fixed sleep time selected by --sleep option */
4703 if (opt_sleep >= 0 && !real_sleep)
4704 sleep_val= opt_sleep;
4705
4706 DBUG_PRINT("info", ("sleep_val: %f", sleep_val));
4707 if (sleep_val)
4708 my_sleep((ulong) (sleep_val * 1000000L));
4709 return 0;
4710 }
4711
4712
do_get_file_name(struct st_command * command,char * dest,uint dest_max_len)4713 void do_get_file_name(struct st_command *command,
4714 char* dest, uint dest_max_len)
4715 {
4716 char *p= command->first_argument, *name;
4717 if (!*p)
4718 die("Missing file name argument");
4719 name= p;
4720 while (*p && !my_isspace(charset_info,*p))
4721 p++;
4722 if (*p)
4723 *p++= 0;
4724 command->last_argument= p;
4725 strmake(dest, name, dest_max_len - 1);
4726 }
4727
4728
do_set_charset(struct st_command * command)4729 void do_set_charset(struct st_command *command)
4730 {
4731 char *charset_name= command->first_argument;
4732 char *p;
4733
4734 if (!charset_name || !*charset_name)
4735 die("Missing charset name in 'character_set'");
4736 /* Remove end space */
4737 p= charset_name;
4738 while (*p && !my_isspace(charset_info,*p))
4739 p++;
4740 if(*p)
4741 *p++= 0;
4742 command->last_argument= p;
4743 charset_info= get_charset_by_csname(charset_name,MY_CS_PRIMARY,MYF(MY_WME));
4744 if (!charset_info)
4745 abort_not_supported_test("Test requires charset '%s'", charset_name);
4746 }
4747
4748
4749 /*
4750 Run query and return one field in the result set from the
4751 first row and <column>
4752 */
4753
query_get_string(MYSQL * mysql,const char * query,int column,DYNAMIC_STRING * ds)4754 int query_get_string(MYSQL* mysql, const char* query,
4755 int column, DYNAMIC_STRING* ds)
4756 {
4757 MYSQL_RES *res= NULL;
4758 MYSQL_ROW row;
4759
4760 if (mysql_query(mysql, query))
4761 die("'%s' failed: %d %s", query,
4762 mysql_errno(mysql), mysql_error(mysql));
4763 if ((res= mysql_store_result(mysql)) == NULL)
4764 die("Failed to store result: %d %s",
4765 mysql_errno(mysql), mysql_error(mysql));
4766
4767 if ((row= mysql_fetch_row(res)) == NULL)
4768 {
4769 mysql_free_result(res);
4770 ds= 0;
4771 return 1;
4772 }
4773 init_dynamic_string(ds, (row[column] ? row[column] : "NULL"), ~0, 32);
4774 mysql_free_result(res);
4775 return 0;
4776 }
4777
4778
my_kill(int pid,int sig)4779 static int my_kill(int pid, int sig)
4780 {
4781 #ifdef __WIN__
4782 HANDLE proc;
4783 if ((proc= OpenProcess(PROCESS_TERMINATE, FALSE, pid)) == NULL)
4784 return -1;
4785 if (sig == 0)
4786 {
4787 CloseHandle(proc);
4788 return 0;
4789 }
4790 (void)TerminateProcess(proc, 201);
4791 CloseHandle(proc);
4792 return 1;
4793 #else
4794 int result= kill(pid, sig);
4795 if (result == -1 && errno != ESRCH)
4796 {
4797 log_msg("kill(%d, %d) returned errno %d (%s)", pid, sig, errno,
4798 strerror(errno));
4799 }
4800 return result;
4801 #endif
4802 }
4803
4804
4805
4806 /*
4807 Shutdown the server of current connection and
4808 make sure it goes away within <timeout> seconds
4809
4810 NOTE! Currently only works with local server
4811
4812 SYNOPSIS
4813 do_shutdown_server()
4814 command called command
4815
4816 DESCRIPTION
4817 shutdown_server [<timeout>]
4818
4819 */
4820
do_shutdown_server(struct st_command * command)4821 void do_shutdown_server(struct st_command *command)
4822 {
4823 long timeout=90;
4824 int pid;
4825 DYNAMIC_STRING ds_pidfile_name;
4826 MYSQL* mysql = &cur_con->mysql;
4827 static DYNAMIC_STRING ds_timeout;
4828 const struct command_arg shutdown_args[] = {
4829 {"timeout", ARG_STRING, FALSE, &ds_timeout, "Timeout before killing server"}
4830 };
4831 DBUG_ENTER("do_shutdown_server");
4832
4833 check_command_args(command, command->first_argument, shutdown_args,
4834 sizeof(shutdown_args)/sizeof(struct command_arg),
4835 ' ');
4836
4837 if (ds_timeout.length)
4838 {
4839 char* endptr;
4840 timeout= strtol(ds_timeout.str, &endptr, 10);
4841 if (*endptr != '\0')
4842 die("Illegal argument for timeout: '%s'", ds_timeout.str);
4843 }
4844 dynstr_free(&ds_timeout);
4845
4846 /* Get the servers pid_file name and use it to read pid */
4847 if (query_get_string(mysql, "SHOW VARIABLES LIKE 'pid_file'", 1,
4848 &ds_pidfile_name))
4849 die("Failed to get pid_file from server");
4850
4851 /* Read the pid from the file */
4852 {
4853 int fd;
4854 char buff[32];
4855
4856 if ((fd= my_open(ds_pidfile_name.str, O_RDONLY, MYF(0))) < 0)
4857 die("Failed to open file '%s'", ds_pidfile_name.str);
4858 dynstr_free(&ds_pidfile_name);
4859
4860 if (my_read(fd, (uchar*)&buff,
4861 sizeof(buff), MYF(0)) <= 0){
4862 my_close(fd, MYF(0));
4863 die("pid file was empty");
4864 }
4865 my_close(fd, MYF(0));
4866
4867 pid= atoi(buff);
4868 if (pid == 0)
4869 die("Pidfile didn't contain a valid number");
4870 }
4871 DBUG_PRINT("info", ("Got pid %d", pid));
4872
4873 /* Tell server to shutdown if timeout > 0*/
4874 if (timeout && mysql_shutdown(mysql, SHUTDOWN_DEFAULT))
4875 die("mysql_shutdown failed");
4876
4877 /* Check that server dies */
4878 long orig_timeout= timeout;
4879 while(timeout--){
4880 if (my_kill(pid, 0) < 0){
4881 DBUG_PRINT("info", ("Process %d does not exist anymore", pid));
4882 DBUG_VOID_RETURN;
4883 }
4884 DBUG_PRINT("info", ("Sleeping, timeout: %ld", timeout));
4885 my_sleep(1000000L);
4886 }
4887
4888 /* Kill the server */
4889 DBUG_PRINT("info", ("Killing server, pid: %d", pid));
4890 if (orig_timeout != 0)
4891 {
4892 log_msg("shutdown_server timeout %ld exceeded, "
4893 "SIGABRT sent to the server PID %d",
4894 orig_timeout, pid);
4895 }
4896 (void)my_kill(pid, (orig_timeout != 0) ? SIGABRT : SIGKILL);
4897
4898 DBUG_VOID_RETURN;
4899
4900 }
4901
4902
4903
get_errcode_from_name(char * error_name,char * error_end)4904 uint get_errcode_from_name(char *error_name, char *error_end)
4905 {
4906 /* SQL error as string */
4907 st_error *e= global_error_names;
4908
4909 DBUG_ENTER("get_errcode_from_name");
4910 DBUG_PRINT("enter", ("error_name: %s", error_name));
4911
4912 /* Loop through the array of known error names */
4913 for (; e->name; e++)
4914 {
4915 /*
4916 If we get a match, we need to check the length of the name we
4917 matched against in case it was longer than what we are checking
4918 (as in ER_WRONG_VALUE vs. ER_WRONG_VALUE_COUNT).
4919 */
4920 if (!strncmp(error_name, e->name, (int) (error_end - error_name)) &&
4921 (uint) strlen(e->name) == (uint) (error_end - error_name))
4922 {
4923 DBUG_RETURN(e->code);
4924 }
4925 }
4926 if (!e->name)
4927 die("Unknown SQL error name '%s'", error_name);
4928 DBUG_RETURN(0);
4929 }
4930
get_errname_from_code(uint error_code)4931 const char *get_errname_from_code (uint error_code)
4932 {
4933 st_error *e= global_error_names;
4934
4935 DBUG_ENTER("get_errname_from_code");
4936 DBUG_PRINT("enter", ("error_code: %d", error_code));
4937
4938 if (! error_code)
4939 {
4940 DBUG_RETURN("");
4941 }
4942 for (; e->name; e++)
4943 {
4944 if (e->code == error_code)
4945 {
4946 DBUG_RETURN(e->name);
4947 }
4948 }
4949 /* Apparently, errors without known names may occur */
4950 DBUG_RETURN("<Unknown>");
4951 }
4952
do_get_errcodes(struct st_command * command)4953 void do_get_errcodes(struct st_command *command)
4954 {
4955 struct st_match_err *to= saved_expected_errors.err;
4956 char *p= command->first_argument;
4957 uint count= 0;
4958 char *next;
4959
4960 DBUG_ENTER("do_get_errcodes");
4961
4962 if (!*p)
4963 die("Missing argument(s) to 'error'");
4964
4965 do
4966 {
4967 char *end;
4968
4969 /* Skip leading spaces */
4970 while (*p && *p == ' ')
4971 p++;
4972
4973 /* Find end */
4974 end= p;
4975 while (*end && *end != ',' && *end != ' ')
4976 end++;
4977
4978 next=end;
4979
4980 /* code to handle variables passed to mysqltest */
4981 if( *p == '$')
4982 {
4983 const char* fin= NULL;
4984 VAR *var = var_get(p,&fin,0,0);
4985 p=var->str_val;
4986 end=p+var->str_val_len;
4987 }
4988
4989 if (*p == 'S')
4990 {
4991 char *to_ptr= to->code.sqlstate;
4992
4993 /*
4994 SQLSTATE string
4995 - Must be SQLSTATE_LENGTH long
4996 - May contain only digits[0-9] and _uppercase_ letters
4997 */
4998 p++; /* Step past the S */
4999 if ((end - p) != SQLSTATE_LENGTH)
5000 die("The sqlstate must be exactly %d chars long", SQLSTATE_LENGTH);
5001
5002 /* Check sqlstate string validity */
5003 while (*p && p < end)
5004 {
5005 if (my_isdigit(charset_info, *p) || my_isupper(charset_info, *p))
5006 *to_ptr++= *p++;
5007 else
5008 die("The sqlstate may only consist of digits[0-9] " \
5009 "and _uppercase_ letters");
5010 }
5011
5012 *to_ptr= 0;
5013 to->type= ERR_SQLSTATE;
5014 DBUG_PRINT("info", ("ERR_SQLSTATE: %s", to->code.sqlstate));
5015 }
5016 else if (*p == 's')
5017 {
5018 die("The sqlstate definition must start with an uppercase S");
5019 }
5020 else if (*p == 'E')
5021 {
5022 /* Error name string */
5023
5024 DBUG_PRINT("info", ("Error name: %s", p));
5025 to->code.errnum= get_errcode_from_name(p, end);
5026 to->type= ERR_ERRNO;
5027 DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum));
5028 }
5029 else if (*p == 'e')
5030 {
5031 die("The error name definition must start with an uppercase E");
5032 }
5033 else
5034 {
5035 long val;
5036 char *start= p;
5037 /* Check that the string passed to str2int only contain digits */
5038 while (*p && p != end)
5039 {
5040 if (!my_isdigit(charset_info, *p))
5041 die("Invalid argument to error: '%s' - "\
5042 "the errno may only consist of digits[0-9]",
5043 command->first_argument);
5044 p++;
5045 }
5046
5047 /* Convert the sting to int */
5048 if (!str2int(start, 10, (long) INT_MIN, (long) INT_MAX, &val))
5049 die("Invalid argument to error: '%s'", command->first_argument);
5050
5051 to->code.errnum= (uint) val;
5052 to->type= ERR_ERRNO;
5053 DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum));
5054 }
5055 to++;
5056 count++;
5057
5058 if (count >= (sizeof(saved_expected_errors.err) /
5059 sizeof(struct st_match_err)))
5060 die("Too many errorcodes specified");
5061
5062 /* Set pointer to the end of the last error code */
5063 p= next;
5064
5065 /* Find next ',' */
5066 while (*p && *p != ',')
5067 p++;
5068
5069 if (*p)
5070 p++; /* Step past ',' */
5071
5072 } while (*p);
5073
5074 command->last_argument= p;
5075 to->type= ERR_EMPTY; /* End of data */
5076
5077 DBUG_PRINT("info", ("Expected errors: %d", count));
5078 saved_expected_errors.count= count;
5079 DBUG_VOID_RETURN;
5080 }
5081
5082
5083 /*
5084 Get a string; Return ptr to end of string
5085 Strings may be surrounded by " or '
5086
5087 If string is a '$variable', return the value of the variable.
5088 */
5089
get_string(char ** to_ptr,char ** from_ptr,struct st_command * command)5090 char *get_string(char **to_ptr, char **from_ptr,
5091 struct st_command *command)
5092 {
5093 char c, sep;
5094 char *to= *to_ptr, *from= *from_ptr, *start=to;
5095 DBUG_ENTER("get_string");
5096
5097 /* Find separator */
5098 if (*from == '"' || *from == '\'')
5099 sep= *from++;
5100 else
5101 sep=' '; /* Separated with space */
5102
5103 for ( ; (c=*from) ; from++)
5104 {
5105 if (c == '\\' && from[1])
5106 { /* Escaped character */
5107 /* We can't translate \0 -> ASCII 0 as replace can't handle ASCII 0 */
5108 switch (*++from) {
5109 case 'n':
5110 *to++= '\n';
5111 break;
5112 case 't':
5113 *to++= '\t';
5114 break;
5115 case 'r':
5116 *to++ = '\r';
5117 break;
5118 case 'b':
5119 *to++ = '\b';
5120 break;
5121 case 'Z': /* ^Z must be escaped on Win32 */
5122 *to++='\032';
5123 break;
5124 default:
5125 *to++ = *from;
5126 break;
5127 }
5128 }
5129 else if (c == sep)
5130 {
5131 if (c == ' ' || c != *++from)
5132 break; /* Found end of string */
5133 *to++=c; /* Copy duplicated separator */
5134 }
5135 else
5136 *to++=c;
5137 }
5138 if (*from != ' ' && *from)
5139 die("Wrong string argument in %s", command->query);
5140
5141 while (my_isspace(charset_info,*from)) /* Point to next string */
5142 from++;
5143
5144 *to =0; /* End of string marker */
5145 *to_ptr= to+1; /* Store pointer to end */
5146 *from_ptr= from;
5147
5148 /* Check if this was a variable */
5149 if (*start == '$')
5150 {
5151 const char *end= to;
5152 VAR *var=var_get(start, &end, 0, 1);
5153 if (var && to == (char*) end+1)
5154 {
5155 DBUG_PRINT("info",("var: '%s' -> '%s'", start, var->str_val));
5156 DBUG_RETURN(var->str_val); /* return found variable value */
5157 }
5158 }
5159 DBUG_RETURN(start);
5160 }
5161
5162
set_reconnect(MYSQL * mysql,int val)5163 void set_reconnect(MYSQL* mysql, int val)
5164 {
5165 my_bool reconnect= val;
5166 DBUG_ENTER("set_reconnect");
5167 DBUG_PRINT("info", ("val: %d", val));
5168 #if MYSQL_VERSION_ID < 50000
5169 mysql->reconnect= reconnect;
5170 #else
5171 mysql_options(mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
5172 #endif
5173 DBUG_VOID_RETURN;
5174 }
5175
5176
5177 /**
5178 Change the current connection to the given st_connection, and update
5179 $mysql_get_server_version and $CURRENT_CONNECTION accordingly.
5180 */
set_current_connection(struct st_connection * con)5181 void set_current_connection(struct st_connection *con)
5182 {
5183 cur_con= con;
5184 /* Update $mysql_get_server_version to that of current connection */
5185 var_set_int("$mysql_get_server_version",
5186 mysql_get_server_version(&con->mysql));
5187 /* Update $CURRENT_CONNECTION to the name of the current connection */
5188 var_set_string("$CURRENT_CONNECTION", con->name);
5189 }
5190
5191
select_connection_name(const char * name)5192 void select_connection_name(const char *name)
5193 {
5194 DBUG_ENTER("select_connection_name");
5195 DBUG_PRINT("enter",("name: '%s'", name));
5196 st_connection *con= find_connection_by_name(name);
5197
5198 if (!con)
5199 die("connection '%s' not found in connection pool", name);
5200
5201 set_current_connection(con);
5202
5203 /* Connection logging if enabled */
5204 if (!disable_connect_log && !disable_query_log)
5205 {
5206 DYNAMIC_STRING *ds= &ds_res;
5207
5208 dynstr_append_mem(ds, "connection ", 11);
5209 replace_dynstr_append(ds, name);
5210 dynstr_append_mem(ds, ";\n", 2);
5211 }
5212
5213 DBUG_VOID_RETURN;
5214 }
5215
5216
select_connection(struct st_command * command)5217 void select_connection(struct st_command *command)
5218 {
5219 DBUG_ENTER("select_connection");
5220 static DYNAMIC_STRING ds_connection;
5221 const struct command_arg connection_args[] = {
5222 { "connection_name", ARG_STRING, TRUE, &ds_connection, "Name of the connection that we switch to." }
5223 };
5224 check_command_args(command, command->first_argument, connection_args,
5225 sizeof(connection_args)/sizeof(struct command_arg),
5226 ' ');
5227
5228 DBUG_PRINT("info", ("changing connection: %s", ds_connection.str));
5229 select_connection_name(ds_connection.str);
5230 dynstr_free(&ds_connection);
5231 DBUG_VOID_RETURN;
5232 }
5233
5234
do_close_connection(struct st_command * command)5235 void do_close_connection(struct st_command *command)
5236 {
5237 DBUG_ENTER("close_connection");
5238
5239 struct st_connection *con;
5240 static DYNAMIC_STRING ds_connection;
5241 const struct command_arg close_connection_args[] = {
5242 { "connection_name", ARG_STRING, TRUE, &ds_connection,
5243 "Name of the connection to close." }
5244 };
5245 check_command_args(command, command->first_argument,
5246 close_connection_args,
5247 sizeof(close_connection_args)/sizeof(struct command_arg),
5248 ' ');
5249
5250 DBUG_PRINT("enter",("connection name: '%s'", ds_connection.str));
5251
5252 if (!(con= find_connection_by_name(ds_connection.str)))
5253 die("connection '%s' not found in connection pool", ds_connection.str);
5254
5255 DBUG_PRINT("info", ("Closing connection %s", con->name));
5256 #ifndef EMBEDDED_LIBRARY
5257 if (command->type == Q_DIRTY_CLOSE)
5258 {
5259 if (con->mysql.net.vio)
5260 {
5261 vio_delete(con->mysql.net.vio);
5262 con->mysql.net.vio = 0;
5263 end_server(&con->mysql);
5264 }
5265 }
5266 #else
5267 /*
5268 As query could be still executed in a separate theread
5269 we need to check if the query's thread was finished and probably wait
5270 (embedded-server specific)
5271 */
5272 emb_close_connection(con);
5273 #endif /*EMBEDDED_LIBRARY*/
5274 if (con->stmt)
5275 mysql_stmt_close(con->stmt);
5276 con->stmt= 0;
5277
5278 mysql_close(&con->mysql);
5279
5280 if (con->util_mysql)
5281 mysql_close(con->util_mysql);
5282 con->util_mysql= 0;
5283 con->pending= FALSE;
5284
5285 my_free(con->name);
5286
5287 /*
5288 When the connection is closed set name to "-closed_connection-"
5289 to make it possible to reuse the connection name.
5290 */
5291 if (!(con->name = my_strdup("-closed_connection-", MYF(MY_WME))))
5292 die("Out of memory");
5293
5294 if (con == cur_con)
5295 {
5296 /* Current connection was closed */
5297 var_set_int("$mysql_get_server_version", 0xFFFFFFFF);
5298 var_set_string("$CURRENT_CONNECTION", con->name);
5299 }
5300
5301 /* Connection logging if enabled */
5302 if (!disable_connect_log && !disable_query_log)
5303 {
5304 DYNAMIC_STRING *ds= &ds_res;
5305
5306 dynstr_append_mem(ds, "disconnect ", 11);
5307 replace_dynstr_append(ds, ds_connection.str);
5308 dynstr_append_mem(ds, ";\n", 2);
5309 }
5310
5311 dynstr_free(&ds_connection);
5312 DBUG_VOID_RETURN;
5313 }
5314
5315
5316 /*
5317 Connect to a server doing several retries if needed.
5318
5319 SYNOPSIS
5320 safe_connect()
5321 con - connection structure to be used
5322 host, user, pass, - connection parameters
5323 db, port, sock
5324
5325 NOTE
5326
5327 Sometimes in a test the client starts before
5328 the server - to solve the problem, we try again
5329 after some sleep if connection fails the first
5330 time
5331
5332 This function will try to connect to the given server
5333 "opt_max_connect_retries" times and sleep "connection_retry_sleep"
5334 seconds between attempts before finally giving up.
5335 This helps in situation when the client starts
5336 before the server (which happens sometimes).
5337 It will only ignore connection errors during these retries.
5338
5339 */
5340
safe_connect(MYSQL * mysql,const char * name,const char * host,const char * user,const char * pass,const char * db,int port,const char * sock)5341 void safe_connect(MYSQL* mysql, const char *name, const char *host,
5342 const char *user, const char *pass, const char *db,
5343 int port, const char *sock)
5344 {
5345 int failed_attempts= 0;
5346
5347 DBUG_ENTER("safe_connect");
5348
5349 verbose_msg("Connecting to server %s:%d (socket %s) as '%s'"
5350 ", connection '%s', attempt %d ...",
5351 host, port, sock, user, name, failed_attempts);
5352
5353 mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
5354 mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
5355 "program_name", "mysqltest");
5356 mysql_options(mysql, MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS,
5357 &can_handle_expired_passwords);
5358 while(!mysql_connect_ssl_check(mysql, host,user, pass, db, port, sock,
5359 CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS,
5360 opt_ssl_mode == SSL_MODE_REQUIRED))
5361 {
5362 /*
5363 Connect failed
5364
5365 Only allow retry if this was an error indicating the server
5366 could not be contacted. Error code differs depending
5367 on protocol/connection type
5368 */
5369
5370 if ((mysql_errno(mysql) == CR_CONN_HOST_ERROR ||
5371 mysql_errno(mysql) == CR_CONNECTION_ERROR) &&
5372 failed_attempts < opt_max_connect_retries)
5373 {
5374 verbose_msg("Connect attempt %d/%d failed: %d: %s", failed_attempts,
5375 opt_max_connect_retries, mysql_errno(mysql),
5376 mysql_error(mysql));
5377 my_sleep(connection_retry_sleep);
5378 }
5379 else
5380 {
5381 if (failed_attempts > 0)
5382 die("Could not open connection '%s' after %d attempts: %d %s", name,
5383 failed_attempts, mysql_errno(mysql), mysql_error(mysql));
5384 else
5385 die("Could not open connection '%s': %d %s", name,
5386 mysql_errno(mysql), mysql_error(mysql));
5387 }
5388 failed_attempts++;
5389 }
5390 verbose_msg("... Connected.");
5391 DBUG_VOID_RETURN;
5392 }
5393
5394
5395 /*
5396 Connect to a server and handle connection errors in case they occur.
5397
5398 SYNOPSIS
5399 connect_n_handle_errors()
5400 q - context of connect "query" (command)
5401 con - connection structure to be used
5402 host, user, pass, - connection parameters
5403 db, port, sock
5404
5405 DESCRIPTION
5406 This function will try to establish a connection to server and handle
5407 possible errors in the same manner as if "connect" was usual SQL-statement
5408 (If error is expected it will ignore it once it occurs and log the
5409 "statement" to the query log).
5410 Unlike safe_connect() it won't do several attempts.
5411
5412 RETURN VALUES
5413 1 - Connected
5414 0 - Not connected
5415
5416 */
5417
connect_n_handle_errors(struct st_command * command,MYSQL * con,const char * host,const char * user,const char * pass,const char * db,int port,const char * sock)5418 int connect_n_handle_errors(struct st_command *command,
5419 MYSQL* con, const char* host,
5420 const char* user, const char* pass,
5421 const char* db, int port, const char* sock)
5422 {
5423 DYNAMIC_STRING *ds;
5424 int failed_attempts= 0;
5425
5426 ds= &ds_res;
5427
5428 /* Only log if an error is expected */
5429 if (command->expected_errors.count > 0 &&
5430 !disable_query_log)
5431 {
5432 /*
5433 Log the connect to result log
5434 */
5435 dynstr_append_mem(ds, "connect(", 8);
5436 replace_dynstr_append(ds, host);
5437 dynstr_append_mem(ds, ",", 1);
5438 replace_dynstr_append(ds, user);
5439 dynstr_append_mem(ds, ",", 1);
5440 replace_dynstr_append(ds, pass);
5441 dynstr_append_mem(ds, ",", 1);
5442 if (db)
5443 replace_dynstr_append(ds, db);
5444 dynstr_append_mem(ds, ",", 1);
5445 replace_dynstr_append_uint(ds, port);
5446 dynstr_append_mem(ds, ",", 1);
5447 if (sock)
5448 replace_dynstr_append(ds, sock);
5449 dynstr_append_mem(ds, ")", 1);
5450 dynstr_append_mem(ds, delimiter, delimiter_length);
5451 dynstr_append_mem(ds, "\n", 1);
5452 }
5453 /* Simlified logging if enabled */
5454 if (!disable_connect_log && !disable_query_log)
5455 {
5456 replace_dynstr_append(ds, command->query);
5457 dynstr_append_mem(ds, ";\n", 2);
5458 }
5459
5460 mysql_options(con, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
5461 mysql_options4(con, MYSQL_OPT_CONNECT_ATTR_ADD, "program_name", "mysqltest");
5462 mysql_options(con, MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS,
5463 &can_handle_expired_passwords);
5464 while (!mysql_connect_ssl_check(con, host, user, pass, db, port,
5465 sock ? sock: 0, CLIENT_MULTI_STATEMENTS,
5466 opt_ssl_mode == SSL_MODE_REQUIRED))
5467 {
5468 /*
5469 If we have used up all our connections check whether this
5470 is expected (by --error). If so, handle the error right away.
5471 Otherwise, give it some extra time to rule out race-conditions.
5472 If extra-time doesn't help, we have an unexpected error and
5473 must abort -- just proceeding to handle_error() when second
5474 and third chances are used up will handle that for us.
5475
5476 There are various user-limits of which only max_user_connections
5477 and max_connections_per_hour apply at connect time. For the
5478 the second to create a race in our logic, we'd need a limits
5479 test that runs without a FLUSH for longer than an hour, so we'll
5480 stay clear of trying to work out which exact user-limit was
5481 exceeded.
5482 */
5483
5484 if (((mysql_errno(con) == ER_TOO_MANY_USER_CONNECTIONS) ||
5485 (mysql_errno(con) == ER_USER_LIMIT_REACHED)) &&
5486 (failed_attempts++ < opt_max_connect_retries))
5487 {
5488 int i;
5489
5490 i= match_expected_error(command, mysql_errno(con), mysql_sqlstate(con));
5491
5492 if (i >= 0)
5493 goto do_handle_error; /* expected error, handle */
5494
5495 my_sleep(connection_retry_sleep); /* unexpected error, wait */
5496 continue; /* and give it 1 more chance */
5497 }
5498
5499 do_handle_error:
5500 var_set_errno(mysql_errno(con));
5501 handle_error(command, mysql_errno(con), mysql_error(con),
5502 mysql_sqlstate(con), ds);
5503 return 0; /* Not connected */
5504 }
5505
5506 var_set_errno(0);
5507 handle_no_error(command);
5508 revert_properties();
5509 return 1; /* Connected */
5510 }
5511
5512
5513 /*
5514 Open a new connection to MySQL Server with the parameters
5515 specified. Make the new connection the current connection.
5516
5517 SYNOPSIS
5518 do_connect()
5519 q called command
5520
5521 DESCRIPTION
5522 connect(<name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]]);
5523 connect <name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]];
5524
5525 <name> - name of the new connection
5526 <host> - hostname of server
5527 <user> - user to connect as
5528 <pass> - password used when connecting
5529 <db> - initial db when connected
5530 <port> - server port
5531 <sock> - server socket
5532 <opts> - options to use for the connection
5533 * SSL - use SSL if available
5534 * COMPRESS - use compression if available
5535 * SHM - use shared memory if available
5536 * PIPE - use named pipe if available
5537
5538 */
5539
do_connect(struct st_command * command)5540 void do_connect(struct st_command *command)
5541 {
5542 int con_port= opt_port;
5543 char *con_options;
5544 my_bool con_ssl= 0, con_compress= 0;
5545 my_bool con_pipe= 0, con_shm= 0, con_cleartext_enable= 0;
5546 my_bool con_secure_auth= 1;
5547 struct st_connection* con_slot;
5548
5549 static DYNAMIC_STRING ds_connection_name;
5550 static DYNAMIC_STRING ds_host;
5551 static DYNAMIC_STRING ds_user;
5552 static DYNAMIC_STRING ds_password;
5553 static DYNAMIC_STRING ds_database;
5554 static DYNAMIC_STRING ds_port;
5555 static DYNAMIC_STRING ds_sock;
5556 static DYNAMIC_STRING ds_options;
5557 static DYNAMIC_STRING ds_default_auth;
5558 #ifdef HAVE_SMEM
5559 static DYNAMIC_STRING ds_shm;
5560 #endif
5561 const struct command_arg connect_args[] = {
5562 { "connection name", ARG_STRING, TRUE, &ds_connection_name, "Name of the connection" },
5563 { "host", ARG_STRING, TRUE, &ds_host, "Host to connect to" },
5564 { "user", ARG_STRING, FALSE, &ds_user, "User to connect as" },
5565 { "passsword", ARG_STRING, FALSE, &ds_password, "Password used when connecting" },
5566 { "database", ARG_STRING, FALSE, &ds_database, "Database to select after connect" },
5567 { "port", ARG_STRING, FALSE, &ds_port, "Port to connect to" },
5568 { "socket", ARG_STRING, FALSE, &ds_sock, "Socket to connect with" },
5569 { "options", ARG_STRING, FALSE, &ds_options, "Options to use while connecting" },
5570 { "default_auth", ARG_STRING, FALSE, &ds_default_auth, "Default authentication to use" }
5571 };
5572
5573 DBUG_ENTER("do_connect");
5574 DBUG_PRINT("enter",("connect: %s", command->first_argument));
5575
5576 strip_parentheses(command);
5577 check_command_args(command, command->first_argument, connect_args,
5578 sizeof(connect_args)/sizeof(struct command_arg),
5579 ',');
5580
5581 /* Port */
5582 if (ds_port.length)
5583 {
5584 con_port= atoi(ds_port.str);
5585 if (con_port == 0)
5586 die("Illegal argument for port: '%s'", ds_port.str);
5587 }
5588
5589 #ifdef HAVE_SMEM
5590 /* Shared memory */
5591 init_dynamic_string(&ds_shm, ds_sock.str, 0, 0);
5592 #endif
5593
5594 /* Sock */
5595 if (ds_sock.length)
5596 {
5597 /*
5598 If the socket is specified just as a name without path
5599 append tmpdir in front
5600 */
5601 if (*ds_sock.str != FN_LIBCHAR)
5602 {
5603 char buff[FN_REFLEN];
5604 fn_format(buff, ds_sock.str, TMPDIR, "", 0);
5605 dynstr_set(&ds_sock, buff);
5606 }
5607 }
5608 else
5609 {
5610 /* No socket specified, use default */
5611 dynstr_set(&ds_sock, unix_sock);
5612 }
5613 DBUG_PRINT("info", ("socket: %s", ds_sock.str));
5614
5615
5616 /* Options */
5617 con_options= ds_options.str;
5618 while (*con_options)
5619 {
5620 char* end;
5621 /* Step past any spaces in beginning of option*/
5622 while (*con_options && my_isspace(charset_info, *con_options))
5623 con_options++;
5624 /* Find end of this option */
5625 end= con_options;
5626 while (*end && !my_isspace(charset_info, *end))
5627 end++;
5628 if (!strncmp(con_options, "SSL", 3))
5629 con_ssl= 1;
5630 else if (!strncmp(con_options, "COMPRESS", 8))
5631 con_compress= 1;
5632 else if (!strncmp(con_options, "PIPE", 4))
5633 con_pipe= 1;
5634 else if (!strncmp(con_options, "SHM", 3))
5635 con_shm= 1;
5636 else if (!strncmp(con_options, "CLEARTEXT", 9))
5637 con_cleartext_enable= 1;
5638 else if (!strncmp(con_options, "SKIPSECUREAUTH",14))
5639 con_secure_auth= 0;
5640 else
5641 die("Illegal option to connect: %.*s",
5642 (int) (end - con_options), con_options);
5643 /* Process next option */
5644 con_options= end;
5645 }
5646
5647 if (find_connection_by_name(ds_connection_name.str))
5648 die("Connection %s already exists", ds_connection_name.str);
5649
5650 if (next_con != connections_end)
5651 con_slot= next_con;
5652 else
5653 {
5654 if (!(con_slot= find_connection_by_name("-closed_connection-")))
5655 die("Connection limit exhausted, you can have max %d connections",
5656 opt_max_connections);
5657 }
5658
5659 #ifdef EMBEDDED_LIBRARY
5660 init_connection_thd(con_slot);
5661 #endif /*EMBEDDED_LIBRARY*/
5662
5663 if (!mysql_init(&con_slot->mysql))
5664 die("Failed on mysql_init()");
5665
5666 if (opt_connect_timeout)
5667 mysql_options(&con_slot->mysql, MYSQL_OPT_CONNECT_TIMEOUT,
5668 (void *) &opt_connect_timeout);
5669
5670 if (opt_compress || con_compress)
5671 mysql_options(&con_slot->mysql, MYSQL_OPT_COMPRESS, NullS);
5672 mysql_options(&con_slot->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
5673 mysql_options(&con_slot->mysql, MYSQL_SET_CHARSET_NAME,
5674 charset_info->csname);
5675 if (opt_charsets_dir)
5676 mysql_options(&con_slot->mysql, MYSQL_SET_CHARSET_DIR,
5677 opt_charsets_dir);
5678
5679 #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
5680 if (opt_use_ssl)
5681 con_ssl= 1;
5682 #endif
5683
5684 if (con_ssl)
5685 {
5686 #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
5687 mysql_ssl_set(&con_slot->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
5688 opt_ssl_capath, opt_ssl_cipher);
5689 mysql_options(&con_slot->mysql, MYSQL_OPT_SSL_CRL, opt_ssl_crl);
5690 mysql_options(&con_slot->mysql, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath);
5691 #if MYSQL_VERSION_ID >= 50000
5692 /* Turn on ssl_verify_server_cert only if host is "localhost" */
5693 opt_ssl_verify_server_cert= !strcmp(ds_host.str, "localhost");
5694 mysql_options(&con_slot->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
5695 &opt_ssl_verify_server_cert);
5696 #endif
5697 #endif
5698 }
5699
5700 if (con_pipe)
5701 {
5702 #if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
5703 opt_protocol= MYSQL_PROTOCOL_PIPE;
5704 #endif
5705 }
5706
5707 #ifndef EMBEDDED_LIBRARY
5708 if (opt_protocol)
5709 mysql_options(&con_slot->mysql, MYSQL_OPT_PROTOCOL, (char*) &opt_protocol);
5710 #endif
5711
5712 if (con_shm)
5713 {
5714 #ifdef HAVE_SMEM
5715 uint protocol= MYSQL_PROTOCOL_MEMORY;
5716 if (!ds_shm.length)
5717 die("Missing shared memory base name");
5718 mysql_options(&con_slot->mysql, MYSQL_SHARED_MEMORY_BASE_NAME, ds_shm.str);
5719 mysql_options(&con_slot->mysql, MYSQL_OPT_PROTOCOL, &protocol);
5720 #endif
5721 }
5722 #ifdef HAVE_SMEM
5723 else if (shared_memory_base_name)
5724 {
5725 mysql_options(&con_slot->mysql, MYSQL_SHARED_MEMORY_BASE_NAME,
5726 shared_memory_base_name);
5727 }
5728 #endif
5729
5730 /* Use default db name */
5731 if (ds_database.length == 0)
5732 dynstr_set(&ds_database, opt_db);
5733
5734 if (opt_plugin_dir && *opt_plugin_dir)
5735 mysql_options(&con_slot->mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir);
5736
5737 if (ds_default_auth.length)
5738 mysql_options(&con_slot->mysql, MYSQL_DEFAULT_AUTH, ds_default_auth.str);
5739
5740 /* Set server public_key */
5741 if (opt_server_public_key && *opt_server_public_key)
5742 mysql_options(&con_slot->mysql, MYSQL_SERVER_PUBLIC_KEY,
5743 opt_server_public_key);
5744
5745 if (con_cleartext_enable)
5746 mysql_options(&con_slot->mysql, MYSQL_ENABLE_CLEARTEXT_PLUGIN,
5747 (char*) &con_cleartext_enable);
5748
5749 if (!con_secure_auth)
5750 mysql_options(&con_slot->mysql, MYSQL_SECURE_AUTH,
5751 (char*) &con_secure_auth);
5752
5753 /* Special database to allow one to connect without a database name */
5754 if (ds_database.length && !strcmp(ds_database.str,"*NO-ONE*"))
5755 dynstr_set(&ds_database, "");
5756
5757 if (connect_n_handle_errors(command, &con_slot->mysql,
5758 ds_host.str,ds_user.str,
5759 ds_password.str, ds_database.str,
5760 con_port, ds_sock.str))
5761 {
5762 DBUG_PRINT("info", ("Inserting connection %s in connection pool",
5763 ds_connection_name.str));
5764 my_free(con_slot->name);
5765 if (!(con_slot->name= my_strdup(ds_connection_name.str, MYF(MY_WME))))
5766 die("Out of memory");
5767 con_slot->name_len= strlen(con_slot->name);
5768 set_current_connection(con_slot);
5769
5770 if (con_slot == next_con)
5771 next_con++; /* if we used the next_con slot, advance the pointer */
5772 }
5773
5774 dynstr_free(&ds_connection_name);
5775 dynstr_free(&ds_host);
5776 dynstr_free(&ds_user);
5777 dynstr_free(&ds_password);
5778 dynstr_free(&ds_database);
5779 dynstr_free(&ds_port);
5780 dynstr_free(&ds_sock);
5781 dynstr_free(&ds_options);
5782 dynstr_free(&ds_default_auth);
5783 #ifdef HAVE_SMEM
5784 dynstr_free(&ds_shm);
5785 #endif
5786 DBUG_VOID_RETURN;
5787 }
5788
5789
do_done(struct st_command * command)5790 int do_done(struct st_command *command)
5791 {
5792 /* Check if empty block stack */
5793 if (cur_block == block_stack)
5794 {
5795 if (*command->query != '}')
5796 die("Stray 'end' command - end of block before beginning");
5797 die("Stray '}' - end of block before beginning");
5798 }
5799
5800 /* Test if inner block has been executed */
5801 if (cur_block->ok && cur_block->cmd == cmd_while)
5802 {
5803 /* Pop block from stack, re-execute outer block */
5804 cur_block--;
5805 parser.current_line = cur_block->line;
5806 }
5807 else
5808 {
5809 if (*cur_block->delim)
5810 {
5811 /* Restore "old" delimiter after false if block */
5812 strcpy (delimiter, cur_block->delim);
5813 delimiter_length= strlen(delimiter);
5814 }
5815 /* Pop block from stack, goto next line */
5816 cur_block--;
5817 parser.current_line++;
5818 }
5819 return 0;
5820 }
5821
5822 /* Operands available in if or while conditions */
5823
5824 enum block_op {
5825 EQ_OP,
5826 NE_OP,
5827 GT_OP,
5828 GE_OP,
5829 LT_OP,
5830 LE_OP,
5831 ILLEG_OP
5832 };
5833
5834
find_operand(const char * start)5835 enum block_op find_operand(const char *start)
5836 {
5837 char first= *start;
5838 char next= *(start+1);
5839
5840 if (first == '=' && next == '=')
5841 return EQ_OP;
5842 if (first == '!' && next == '=')
5843 return NE_OP;
5844 if (first == '>' && next == '=')
5845 return GE_OP;
5846 if (first == '>')
5847 return GT_OP;
5848 if (first == '<' && next == '=')
5849 return LE_OP;
5850 if (first == '<')
5851 return LT_OP;
5852
5853 return ILLEG_OP;
5854 }
5855
5856
5857 /*
5858 Process start of a "if" or "while" statement
5859
5860 SYNOPSIS
5861 do_block()
5862 cmd Type of block
5863 q called command
5864
5865 DESCRIPTION
5866 if ([!]<expr>)
5867 {
5868 <block statements>
5869 }
5870
5871 while ([!]<expr>)
5872 {
5873 <block statements>
5874 }
5875
5876 Evaluates the <expr> and if it evaluates to
5877 greater than zero executes the following code block.
5878 A '!' can be used before the <expr> to indicate it should
5879 be executed if it evaluates to zero.
5880
5881 <expr> can also be a simple comparison condition:
5882
5883 <variable> <op> <expr>
5884
5885 The left hand side must be a variable, the right hand side can be a
5886 variable, number, string or `query`. Operands are ==, !=, <, <=, >, >=.
5887 == and != can be used for strings, all can be used for numerical values.
5888 */
5889
do_block(enum block_cmd cmd,struct st_command * command)5890 void do_block(enum block_cmd cmd, struct st_command* command)
5891 {
5892 char *p= command->first_argument;
5893 const char *expr_start, *expr_end;
5894 VAR v;
5895 const char *cmd_name= (cmd == cmd_while ? "while" : "if");
5896 my_bool not_expr= FALSE;
5897 DBUG_ENTER("do_block");
5898 DBUG_PRINT("enter", ("%s", cmd_name));
5899
5900 /* Check stack overflow */
5901 if (cur_block == block_stack_end)
5902 die("Nesting too deeply");
5903
5904 /* Set way to find outer block again, increase line counter */
5905 cur_block->line= parser.current_line++;
5906
5907 /* If this block is ignored */
5908 if (!cur_block->ok)
5909 {
5910 /* Inner block should be ignored too */
5911 cur_block++;
5912 cur_block->cmd= cmd;
5913 cur_block->ok= FALSE;
5914 cur_block->delim[0]= '\0';
5915 DBUG_VOID_RETURN;
5916 }
5917
5918 /* Parse and evaluate test expression */
5919 expr_start= strchr(p, '(');
5920 if (!expr_start++)
5921 die("missing '(' in %s", cmd_name);
5922
5923 while (my_isspace(charset_info, *expr_start))
5924 expr_start++;
5925
5926 /* Check for !<expr> */
5927 if (*expr_start == '!')
5928 {
5929 not_expr= TRUE;
5930 expr_start++; /* Step past the '!', then any whitespace */
5931 while (*expr_start && my_isspace(charset_info, *expr_start))
5932 expr_start++;
5933 }
5934 /* Find ending ')' */
5935 expr_end= strrchr(expr_start, ')');
5936 if (!expr_end)
5937 die("missing ')' in %s", cmd_name);
5938 p= (char*)expr_end+1;
5939
5940 while (*p && my_isspace(charset_info, *p))
5941 p++;
5942 if (*p && *p != '{')
5943 die("Missing '{' after %s. Found \"%s\"", cmd_name, p);
5944
5945 var_init(&v,0,0,0,0);
5946
5947 /* If expression starts with a variable, it may be a compare condition */
5948
5949 if (*expr_start == '$')
5950 {
5951 const char *curr_ptr= expr_end;
5952 eval_expr(&v, expr_start, &curr_ptr, true);
5953 while (my_isspace(charset_info, *++curr_ptr))
5954 {}
5955 /* If there was nothing past the variable, skip condition part */
5956 if (curr_ptr == expr_end)
5957 goto NO_COMPARE;
5958
5959 enum block_op operand= find_operand(curr_ptr);
5960 if (operand == ILLEG_OP)
5961 die("Found junk '%.*s' after $variable in condition",
5962 (int)(expr_end - curr_ptr), curr_ptr);
5963
5964 /* We could silently allow this, but may be confusing */
5965 if (not_expr)
5966 die("Negation and comparison should not be combined, please rewrite");
5967
5968 /* Skip the 1 or 2 chars of the operand, then white space */
5969 if (operand == LT_OP || operand == GT_OP)
5970 {
5971 curr_ptr++;
5972 }
5973 else
5974 {
5975 curr_ptr+= 2;
5976 }
5977 while (my_isspace(charset_info, *curr_ptr))
5978 curr_ptr++;
5979 if (curr_ptr == expr_end)
5980 die("Missing right operand in comparison");
5981
5982 /* Strip off trailing white space */
5983 while (my_isspace(charset_info, expr_end[-1]))
5984 expr_end--;
5985 /* strip off ' or " around the string */
5986 if (*curr_ptr == '\'' || *curr_ptr == '"')
5987 {
5988 if (expr_end[-1] != *curr_ptr)
5989 die("Unterminated string value");
5990 curr_ptr++;
5991 expr_end--;
5992 }
5993 VAR v2;
5994 var_init(&v2,0,0,0,0);
5995 eval_expr(&v2, curr_ptr, &expr_end);
5996
5997 if ((operand!=EQ_OP && operand!=NE_OP) && ! (v.is_int && v2.is_int))
5998 die ("Only == and != are supported for string values");
5999
6000 /* Now we overwrite the first variable with 0 or 1 (for false or true) */
6001
6002 switch (operand)
6003 {
6004 case EQ_OP:
6005 if (v.is_int)
6006 v.int_val= (v2.is_int && v2.int_val == v.int_val);
6007 else
6008 v.int_val= !strcmp (v.str_val, v2.str_val);
6009 break;
6010
6011 case NE_OP:
6012 if (v.is_int)
6013 v.int_val= ! (v2.is_int && v2.int_val == v.int_val);
6014 else
6015 v.int_val= (strcmp (v.str_val, v2.str_val) != 0);
6016 break;
6017
6018 case LT_OP:
6019 v.int_val= (v.int_val < v2.int_val);
6020 break;
6021 case LE_OP:
6022 v.int_val= (v.int_val <= v2.int_val);
6023 break;
6024 case GT_OP:
6025 v.int_val= (v.int_val > v2.int_val);
6026 break;
6027 case GE_OP:
6028 v.int_val= (v.int_val >= v2.int_val);
6029 break;
6030 case ILLEG_OP:
6031 die("Impossible operator, this cannot happen");
6032 }
6033
6034 v.is_int= TRUE;
6035 var_free(&v2);
6036 } else
6037 {
6038 if (*expr_start != '`' && ! my_isdigit(charset_info, *expr_start))
6039 die("Expression in if/while must beging with $, ` or a number");
6040 eval_expr(&v, expr_start, &expr_end);
6041 }
6042
6043 NO_COMPARE:
6044 /* Define inner block */
6045 cur_block++;
6046 cur_block->cmd= cmd;
6047 if (v.is_int)
6048 {
6049 cur_block->ok= (v.int_val != 0);
6050 } else
6051 /* Any non-empty string which does not begin with 0 is also TRUE */
6052 {
6053 p= v.str_val;
6054 /* First skip any leading white space or unary -+ */
6055 while (*p && ((my_isspace(charset_info, *p) || *p == '-' || *p == '+')))
6056 p++;
6057
6058 cur_block->ok= (*p && *p != '0') ? TRUE : FALSE;
6059 }
6060
6061 if (not_expr)
6062 cur_block->ok = !cur_block->ok;
6063
6064 if (cur_block->ok)
6065 {
6066 cur_block->delim[0]= '\0';
6067 } else
6068 {
6069 /* Remember "old" delimiter if entering a false if block */
6070 strcpy (cur_block->delim, delimiter);
6071 }
6072
6073 DBUG_PRINT("info", ("OK: %d", cur_block->ok));
6074
6075 var_free(&v);
6076 DBUG_VOID_RETURN;
6077 }
6078
6079
do_delimiter(struct st_command * command)6080 void do_delimiter(struct st_command* command)
6081 {
6082 char* p= command->first_argument;
6083 DBUG_ENTER("do_delimiter");
6084 DBUG_PRINT("enter", ("first_argument: %s", command->first_argument));
6085
6086 while (*p && my_isspace(charset_info, *p))
6087 p++;
6088
6089 if (!(*p))
6090 die("Can't set empty delimiter");
6091
6092 strmake(delimiter, p, sizeof(delimiter) - 1);
6093 delimiter_length= strlen(delimiter);
6094
6095 DBUG_PRINT("exit", ("delimiter: %s", delimiter));
6096 command->last_argument= p + delimiter_length;
6097 DBUG_VOID_RETURN;
6098 }
6099
6100
match_delimiter(int c,const char * delim,uint length)6101 my_bool match_delimiter(int c, const char *delim, uint length)
6102 {
6103 uint i;
6104 char tmp[MAX_DELIMITER_LENGTH];
6105
6106 if (c != *delim)
6107 return 0;
6108
6109 for (i= 1; i < length &&
6110 (c= my_getc(cur_file->file)) == *(delim + i);
6111 i++)
6112 tmp[i]= c;
6113
6114 if (i == length)
6115 return 1; /* Found delimiter */
6116
6117 /* didn't find delimiter, push back things that we read */
6118 my_ungetc(c);
6119 while (i > 1)
6120 my_ungetc(tmp[--i]);
6121 return 0;
6122 }
6123
6124
end_of_query(int c)6125 my_bool end_of_query(int c)
6126 {
6127 return match_delimiter(c, delimiter, delimiter_length);
6128 }
6129
6130
6131 /*
6132 Read one "line" from the file
6133
6134 SYNOPSIS
6135 read_line
6136 buf buffer for the read line
6137 size size of the buffer i.e max size to read
6138
6139 DESCRIPTION
6140 This function actually reads several lines and adds them to the
6141 buffer buf. It continues to read until it finds what it believes
6142 is a complete query.
6143
6144 Normally that means it will read lines until it reaches the
6145 "delimiter" that marks end of query. Default delimiter is ';'
6146 The function should be smart enough not to detect delimiter's
6147 found inside strings surrounded with '"' and '\'' escaped strings.
6148
6149 If the first line in a query starts with '#' or '-' this line is treated
6150 as a comment. A comment is always terminated when end of line '\n' is
6151 reached.
6152
6153 */
6154
read_line(char * buf,int size)6155 int read_line(char *buf, int size)
6156 {
6157 char c, UNINIT_VAR(last_quote), last_char= 0;
6158 char *p= buf, *buf_end= buf + size - 1;
6159 int skip_char= 0;
6160 my_bool have_slash= FALSE;
6161
6162 enum {R_NORMAL, R_Q, R_SLASH_IN_Q,
6163 R_COMMENT, R_LINE_START} state= R_LINE_START;
6164 DBUG_ENTER("read_line");
6165
6166 start_lineno= cur_file->lineno;
6167 DBUG_PRINT("info", ("Starting to read at lineno: %d", start_lineno));
6168 for (; p < buf_end ;)
6169 {
6170 skip_char= 0;
6171 c= my_getc(cur_file->file);
6172 if (feof(cur_file->file))
6173 {
6174 found_eof:
6175 if (cur_file->file != stdin)
6176 {
6177 fclose(cur_file->file);
6178 cur_file->file= 0;
6179 }
6180 my_free(cur_file->file_name);
6181 cur_file->file_name= 0;
6182 if (cur_file == file_stack)
6183 {
6184 /* We're back at the first file, check if
6185 all { have matching }
6186 */
6187 if (cur_block != block_stack)
6188 die("Missing end of block");
6189
6190 *p= 0;
6191 DBUG_PRINT("info", ("end of file at line %d", cur_file->lineno));
6192 DBUG_RETURN(1);
6193 }
6194 cur_file--;
6195 start_lineno= cur_file->lineno;
6196 continue;
6197 }
6198
6199 if (c == '\n')
6200 {
6201 /* Line counting is independent of state */
6202 cur_file->lineno++;
6203
6204 /* Convert cr/lf to lf */
6205 if (p != buf && *(p-1) == '\r')
6206 p--;
6207 }
6208
6209 switch(state) {
6210 case R_NORMAL:
6211 if (end_of_query(c))
6212 {
6213 *p= 0;
6214 DBUG_PRINT("exit", ("Found delimiter '%s' at line %d",
6215 delimiter, cur_file->lineno));
6216 DBUG_RETURN(0);
6217 }
6218 else if ((c == '{' &&
6219 (!my_strnncoll_simple(charset_info, (const uchar*) "while", 5,
6220 (uchar*) buf, min<my_ptrdiff_t>(5, p - buf), 0) ||
6221 !my_strnncoll_simple(charset_info, (const uchar*) "if", 2,
6222 (uchar*) buf, min<my_ptrdiff_t>(2, p - buf), 0))))
6223 {
6224 /* Only if and while commands can be terminated by { */
6225 *p++= c;
6226 *p= 0;
6227 DBUG_PRINT("exit", ("Found '{' indicating start of block at line %d",
6228 cur_file->lineno));
6229 DBUG_RETURN(0);
6230 }
6231 else if (c == '\'' || c == '"' || c == '`')
6232 {
6233 if (! have_slash)
6234 {
6235 last_quote= c;
6236 state= R_Q;
6237 }
6238 }
6239 have_slash= (c == '\\');
6240 break;
6241
6242 case R_COMMENT:
6243 if (c == '\n')
6244 {
6245 /* Comments are terminated by newline */
6246 *p= 0;
6247 DBUG_PRINT("exit", ("Found newline in comment at line: %d",
6248 cur_file->lineno));
6249 DBUG_RETURN(0);
6250 }
6251 break;
6252
6253 case R_LINE_START:
6254 if (c == '#' || c == '-')
6255 {
6256 /* A # or - in the first position of the line - this is a comment */
6257 state = R_COMMENT;
6258 }
6259 else if (my_isspace(charset_info, c))
6260 {
6261 if (c == '\n')
6262 {
6263 if (last_char == '\n')
6264 {
6265 /* Two new lines in a row, return empty line */
6266 DBUG_PRINT("info", ("Found two new lines in a row"));
6267 *p++= c;
6268 *p= 0;
6269 DBUG_RETURN(0);
6270 }
6271
6272 /* Query hasn't started yet */
6273 start_lineno= cur_file->lineno;
6274 DBUG_PRINT("info", ("Query hasn't started yet, start_lineno: %d",
6275 start_lineno));
6276 }
6277
6278 /* Skip all space at begining of line */
6279 skip_char= 1;
6280 }
6281 else if (end_of_query(c))
6282 {
6283 *p= 0;
6284 DBUG_PRINT("exit", ("Found delimiter '%s' at line: %d",
6285 delimiter, cur_file->lineno));
6286 DBUG_RETURN(0);
6287 }
6288 else if (c == '}')
6289 {
6290 /* A "}" need to be by itself in the begining of a line to terminate */
6291 *p++= c;
6292 *p= 0;
6293 DBUG_PRINT("exit", ("Found '}' in begining of a line at line: %d",
6294 cur_file->lineno));
6295 DBUG_RETURN(0);
6296 }
6297 else if (c == '\'' || c == '"' || c == '`')
6298 {
6299 last_quote= c;
6300 state= R_Q;
6301 }
6302 else
6303 state= R_NORMAL;
6304 break;
6305
6306 case R_Q:
6307 if (c == last_quote)
6308 state= R_NORMAL;
6309 else if (c == '\\')
6310 state= R_SLASH_IN_Q;
6311 break;
6312
6313 case R_SLASH_IN_Q:
6314 state= R_Q;
6315 break;
6316
6317 }
6318
6319 last_char= c;
6320
6321 if (!skip_char)
6322 {
6323 /* Could be a multibyte character */
6324 /* This code is based on the code in "sql_load.cc" */
6325 #ifdef USE_MB
6326 uint charlen = my_mbcharlen(charset_info, (unsigned char) c);
6327 if (charlen == 0)
6328 DBUG_RETURN(1);
6329 /* We give up if multibyte character is started but not */
6330 /* completed before we pass buf_end */
6331 if ((charlen > 1) && (p + charlen) <= buf_end)
6332 {
6333 char* mb_start = p;
6334 *p++ = c;
6335
6336 for (uint i= 1; i < charlen; i++)
6337 {
6338 c= my_getc(cur_file->file);
6339 if (feof(cur_file->file))
6340 goto found_eof;
6341 *p++ = c;
6342 }
6343 if (! my_ismbchar(charset_info, mb_start, p))
6344 {
6345 /* It was not a multiline char, push back the characters */
6346 /* We leave first 'c', i.e. pretend it was a normal char */
6347 while (p-1 > mb_start)
6348 my_ungetc(*--p);
6349 }
6350 }
6351 else
6352 #endif
6353 *p++= c;
6354 }
6355 }
6356 die("The input buffer is too small for this query.x\n" \
6357 "check your query or increase MAX_QUERY and recompile");
6358 DBUG_RETURN(0);
6359 }
6360
6361
6362 /*
6363 Convert the read query to result format version 1
6364
6365 That is: After newline, all spaces need to be skipped
6366 unless the previous char was a quote
6367
6368 This is due to an old bug that has now been fixed, but the
6369 version 1 output format is preserved by using this function
6370
6371 */
6372
convert_to_format_v1(char * query)6373 void convert_to_format_v1(char* query)
6374 {
6375 int last_c_was_quote= 0;
6376 char *p= query, *to= query;
6377 char *end= strend(query);
6378 char last_c;
6379
6380 while (p <= end)
6381 {
6382 if (*p == '\n' && !last_c_was_quote)
6383 {
6384 *to++ = *p++; /* Save the newline */
6385
6386 /* Skip any spaces on next line */
6387 while (*p && my_isspace(charset_info, *p))
6388 p++;
6389
6390 last_c_was_quote= 0;
6391 }
6392 else if (*p == '\'' || *p == '"' || *p == '`')
6393 {
6394 last_c= *p;
6395 *to++ = *p++;
6396
6397 /* Copy anything until the next quote of same type */
6398 while (*p && *p != last_c)
6399 *to++ = *p++;
6400
6401 *to++ = *p++;
6402
6403 last_c_was_quote= 1;
6404 }
6405 else
6406 {
6407 *to++ = *p++;
6408 last_c_was_quote= 0;
6409 }
6410 }
6411 }
6412
6413
6414 /*
6415 Check for unexpected "junk" after the end of query
6416 This is normally caused by missing delimiters or when
6417 switching between different delimiters
6418 */
6419
check_eol_junk_line(const char * line)6420 void check_eol_junk_line(const char *line)
6421 {
6422 const char *p= line;
6423 DBUG_ENTER("check_eol_junk_line");
6424 DBUG_PRINT("enter", ("line: %s", line));
6425
6426 /* Check for extra delimiter */
6427 if (*p && !strncmp(p, delimiter, delimiter_length))
6428 die("Extra delimiter \"%s\" found", delimiter);
6429
6430 /* Allow trailing # comment */
6431 if (*p && *p != '#')
6432 {
6433 if (*p == '\n')
6434 die("Missing delimiter");
6435 die("End of line junk detected: \"%s\"", p);
6436 }
6437 DBUG_VOID_RETURN;
6438 }
6439
check_eol_junk(const char * eol)6440 void check_eol_junk(const char *eol)
6441 {
6442 const char *p= eol;
6443 DBUG_ENTER("check_eol_junk");
6444 DBUG_PRINT("enter", ("eol: %s", eol));
6445
6446 /* Skip past all spacing chars and comments */
6447 while (*p && (my_isspace(charset_info, *p) || *p == '#' || *p == '\n'))
6448 {
6449 /* Skip past comments started with # and ended with newline */
6450 if (*p && *p == '#')
6451 {
6452 p++;
6453 while (*p && *p != '\n')
6454 p++;
6455 }
6456
6457 /* Check this line */
6458 if (*p && *p == '\n')
6459 check_eol_junk_line(p);
6460
6461 if (*p)
6462 p++;
6463 }
6464
6465 check_eol_junk_line(p);
6466
6467 DBUG_VOID_RETURN;
6468 }
6469
6470
is_delimiter(const char * p)6471 bool is_delimiter(const char* p)
6472 {
6473 uint match= 0;
6474 char* delim= delimiter;
6475 while (*p && *p == *delim++)
6476 {
6477 match++;
6478 p++;
6479 }
6480
6481 return (match == delimiter_length);
6482 }
6483
6484
6485 /*
6486 Create a command from a set of lines
6487
6488 SYNOPSIS
6489 read_command()
6490 command_ptr pointer where to return the new query
6491
6492 DESCRIPTION
6493 Converts lines returned by read_line into a command, this involves
6494 parsing the first word in the read line to find the command type.
6495
6496 A -- comment may contain a valid query as the first word after the
6497 comment start. Thus it's always checked to see if that is the case.
6498 The advantage with this approach is to be able to execute commands
6499 terminated by new line '\n' regardless how many "delimiter" it contain.
6500 */
6501
6502 #define MAX_QUERY (256*1024*2) /* 256K -- a test in sp-big is >128K */
6503 static char read_command_buf[MAX_QUERY];
6504
read_command(struct st_command ** command_ptr)6505 int read_command(struct st_command** command_ptr)
6506 {
6507 char *p= read_command_buf;
6508 struct st_command* command;
6509 DBUG_ENTER("read_command");
6510
6511 if (parser.current_line < parser.read_lines)
6512 {
6513 get_dynamic(&q_lines, (uchar*) command_ptr, parser.current_line) ;
6514 DBUG_RETURN(0);
6515 }
6516 if (!(*command_ptr= command=
6517 (struct st_command*) my_malloc(sizeof(*command),
6518 MYF(MY_WME|MY_ZEROFILL))) ||
6519 insert_dynamic(&q_lines, &command))
6520 die("Out of memory");
6521 command->type= Q_UNKNOWN;
6522
6523 read_command_buf[0]= 0;
6524 if (read_line(read_command_buf, sizeof(read_command_buf)))
6525 {
6526 check_eol_junk(read_command_buf);
6527 DBUG_RETURN(1);
6528 }
6529
6530 if (opt_result_format_version == 1)
6531 convert_to_format_v1(read_command_buf);
6532
6533 DBUG_PRINT("info", ("query: '%s'", read_command_buf));
6534 if (*p == '#')
6535 {
6536 command->type= Q_COMMENT;
6537 }
6538 else if (p[0] == '-' && p[1] == '-')
6539 {
6540 command->type= Q_COMMENT_WITH_COMMAND;
6541 p+= 2; /* Skip past -- */
6542 }
6543 else if (*p == '\n')
6544 {
6545 command->type= Q_EMPTY_LINE;
6546 }
6547
6548 /* Skip leading spaces */
6549 while (*p && my_isspace(charset_info, *p))
6550 p++;
6551
6552 if (!(command->query_buf= command->query= my_strdup(p, MYF(MY_WME))))
6553 die("Out of memory");
6554
6555 /*
6556 Calculate first word length(the command), terminated
6557 by 'space' , '(' or 'delimiter' */
6558 p= command->query;
6559 while (*p && !my_isspace(charset_info, *p) && *p != '(' && !is_delimiter(p))
6560 p++;
6561 command->first_word_len= (uint) (p - command->query);
6562 DBUG_PRINT("info", ("first_word: %.*s",
6563 command->first_word_len, command->query));
6564
6565 /* Skip spaces between command and first argument */
6566 while (*p && my_isspace(charset_info, *p))
6567 p++;
6568 command->first_argument= p;
6569
6570 command->end= strend(command->query);
6571 command->query_len= (command->end - command->query);
6572 parser.read_lines++;
6573 DBUG_RETURN(0);
6574 }
6575
6576
6577 static struct my_option my_long_options[] =
6578 {
6579 {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG,
6580 0, 0, 0, 0, 0, 0},
6581 {"basedir", 'b', "Basedir for tests.", &opt_basedir,
6582 &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6583 {"character-sets-dir", OPT_CHARSETS_DIR,
6584 "Directory for character set files.", &opt_charsets_dir,
6585 &opt_charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6586 {"compress", 'C', "Use the compressed server/client protocol.",
6587 &opt_compress, &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
6588 0, 0, 0},
6589 {"cursor-protocol", OPT_CURSOR_PROTOCOL, "Use cursors for prepared statements.",
6590 &cursor_protocol, &cursor_protocol, 0,
6591 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
6592 {"database", 'D', "Database to use.", &opt_db, &opt_db, 0,
6593 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6594 #ifdef DBUG_OFF
6595 {"debug", '#', "This is a non-debug version. Catch this and exit",
6596 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
6597 #else
6598 {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
6599 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
6600 #endif
6601 {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.",
6602 &debug_check_flag, &debug_check_flag, 0,
6603 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
6604 {"debug-info", OPT_DEBUG_INFO, "Print some debug info at exit.",
6605 &debug_info_flag, &debug_info_flag,
6606 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
6607 {"host", 'h', "Connect to host.", &opt_host, &opt_host, 0,
6608 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6609 {"include", 'i', "Include SQL before each test case.", &opt_include,
6610 &opt_include, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6611 {"logdir", OPT_LOG_DIR, "Directory for log files", &opt_logdir,
6612 &opt_logdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6613 {"mark-progress", OPT_MARK_PROGRESS,
6614 "Write line number and elapsed time to <testname>.progress.",
6615 &opt_mark_progress, &opt_mark_progress, 0,
6616 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
6617 {"max-connect-retries", OPT_MAX_CONNECT_RETRIES,
6618 "Maximum number of attempts to connect to server.",
6619 &opt_max_connect_retries, &opt_max_connect_retries, 0,
6620 GET_INT, REQUIRED_ARG, 500, 1, 10000, 0, 0, 0},
6621 {"max-connections", OPT_MAX_CONNECTIONS,
6622 "Max number of open connections to server",
6623 &opt_max_connections, &opt_max_connections, 0,
6624 GET_INT, REQUIRED_ARG, 128, 8, 5120, 0, 0, 0},
6625 {"password", 'p', "Password to use when connecting to server.",
6626 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
6627 {"protocol", OPT_MYSQL_PROTOCOL, "The protocol of connection (tcp,socket,pipe,memory).",
6628 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6629 {"port", 'P', "Port number to use for connection or 0 for default to, in "
6630 "order of preference, my.cnf, $MYSQL_TCP_PORT, "
6631 #if MYSQL_PORT_DEFAULT == 0
6632 "/etc/services, "
6633 #endif
6634 "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
6635 &opt_port, &opt_port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6636 {"ps-protocol", OPT_PS_PROTOCOL,
6637 "Use prepared-statement protocol for communication.",
6638 &ps_protocol, &ps_protocol, 0,
6639 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
6640 {"quiet", 's', "Suppress all normal output.", &silent,
6641 &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
6642 {"record", 'r', "Record output of test_file into result file.",
6643 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
6644 {"result-file", 'R', "Read/store result from/in this file.",
6645 &result_file_name, &result_file_name, 0,
6646 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6647 {"result-format-version", OPT_RESULT_FORMAT_VERSION,
6648 "Version of the result file format to use",
6649 &opt_result_format_version,
6650 &opt_result_format_version, 0,
6651 GET_INT, REQUIRED_ARG, 1, 1, 2, 0, 0, 0},
6652 {"server-arg", 'A', "Send option value to embedded server as a parameter.",
6653 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6654 {"server-file", 'F', "Read embedded server arguments from file.",
6655 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6656 {"shared-memory-base-name", OPT_SHARED_MEMORY_BASE_NAME,
6657 "Base name of shared memory.", &shared_memory_base_name,
6658 &shared_memory_base_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0,
6659 0, 0, 0},
6660 {"silent", 's', "Suppress all normal output. Synonym for --quiet.",
6661 &silent, &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
6662 {"sleep", 'T', "Always sleep this many seconds on sleep commands.",
6663 &opt_sleep, &opt_sleep, 0, GET_INT, REQUIRED_ARG, -1, -1, 0,
6664 0, 0, 0},
6665 {"socket", 'S', "The socket file to use for connection.",
6666 &unix_sock, &unix_sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0,
6667 0, 0, 0},
6668 {"sp-protocol", OPT_SP_PROTOCOL, "Use stored procedures for select.",
6669 &sp_protocol, &sp_protocol, 0,
6670 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
6671 #include "sslopt-longopts.h"
6672 {"tail-lines", OPT_TAIL_LINES,
6673 "Number of lines of the result to include in a failure report.",
6674 &opt_tail_lines, &opt_tail_lines, 0,
6675 GET_INT, REQUIRED_ARG, 0, 0, 10000, 0, 0, 0},
6676 {"test-file", 'x', "Read test from/in this file (default stdin).",
6677 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6678 {"timer-file", 'm', "File where the timing in microseconds is stored.",
6679 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6680 {"tmpdir", 't', "Temporary directory where sockets are put.",
6681 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6682 {"user", 'u', "User for login.", &opt_user, &opt_user, 0,
6683 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6684 {"verbose", 'v', "Write more.", &verbose, &verbose, 0,
6685 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
6686 {"version", 'V', "Output version information and exit.",
6687 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
6688 {"view-protocol", OPT_VIEW_PROTOCOL, "Use views for select.",
6689 &view_protocol, &view_protocol, 0,
6690 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
6691 {"opt-trace-protocol", OPT_TRACE_PROTOCOL,
6692 "Trace DML statements with optimizer trace",
6693 &opt_trace_protocol, &opt_trace_protocol, 0,
6694 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
6695 {"explain-protocol", OPT_EXPLAIN_PROTOCOL,
6696 "Explain all SELECT/INSERT/REPLACE/UPDATE/DELETE statements",
6697 &explain_protocol, &explain_protocol, 0,
6698 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
6699 {"json-explain-protocol", OPT_JSON_EXPLAIN_PROTOCOL,
6700 "Explain all SELECT/INSERT/REPLACE/UPDATE/DELETE statements with FORMAT=JSON",
6701 &json_explain_protocol, &json_explain_protocol, 0,
6702 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
6703 {"connect_timeout", OPT_CONNECT_TIMEOUT,
6704 "Number of seconds before connection timeout.",
6705 &opt_connect_timeout, &opt_connect_timeout, 0, GET_UINT, REQUIRED_ARG,
6706 120, 0, 3600 * 12, 0, 0, 0},
6707 {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.",
6708 &opt_plugin_dir, &opt_plugin_dir, 0,
6709 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6710 {"server-public-key-path", OPT_SERVER_PUBLIC_KEY,
6711 "File path to the server public RSA key in PEM format.",
6712 &opt_server_public_key, &opt_server_public_key, 0,
6713 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
6714 { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
6715 };
6716
6717
print_version(void)6718 void print_version(void)
6719 {
6720 printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname,MTEST_VERSION,
6721 MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE);
6722 }
6723
usage()6724 void usage()
6725 {
6726 print_version();
6727 puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
6728 printf("Runs a test against the mysql server and compares output with a results file.\n\n");
6729 printf("Usage: %s [OPTIONS] [database] < test_file\n", my_progname);
6730 my_print_help(my_long_options);
6731 printf(" --no-defaults Don't read default options from any options file.\n");
6732 my_print_variables(my_long_options);
6733 }
6734
6735
6736 /*
6737 Read arguments for embedded server and put them into
6738 embedded_server_args[]
6739 */
6740
read_embedded_server_arguments(const char * name)6741 void read_embedded_server_arguments(const char *name)
6742 {
6743 char argument[1024],buff[FN_REFLEN], *str=0;
6744 FILE *file;
6745
6746 if (!test_if_hard_path(name))
6747 {
6748 strxmov(buff, opt_basedir, name, NullS);
6749 name=buff;
6750 }
6751 fn_format(buff, name, "", "", MY_UNPACK_FILENAME);
6752
6753 if (!embedded_server_arg_count)
6754 {
6755 embedded_server_arg_count=1;
6756 embedded_server_args[0]= (char*) ""; /* Progname */
6757 }
6758 if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
6759 die("Failed to open file '%s'", buff);
6760
6761 while (embedded_server_arg_count < MAX_EMBEDDED_SERVER_ARGS &&
6762 (str=fgets(argument,sizeof(argument), file)))
6763 {
6764 *(strend(str)-1)=0; /* Remove end newline */
6765 if (!(embedded_server_args[embedded_server_arg_count]=
6766 (char*) my_strdup(str,MYF(MY_WME))))
6767 {
6768 my_fclose(file,MYF(0));
6769 die("Out of memory");
6770
6771 }
6772 embedded_server_arg_count++;
6773 }
6774 my_fclose(file,MYF(0));
6775 if (str)
6776 die("Too many arguments in option file: %s",name);
6777
6778 return;
6779 }
6780
6781
6782 static my_bool
get_one_option(int optid,const struct my_option * opt,char * argument)6783 get_one_option(int optid, const struct my_option *opt, char *argument)
6784 {
6785 switch(optid) {
6786 case '#':
6787 #ifndef DBUG_OFF
6788 DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysqltest.trace");
6789 debug_check_flag= 1;
6790 #endif
6791 break;
6792 case 'r':
6793 record = 1;
6794 break;
6795 case 'x':
6796 {
6797 char buff[FN_REFLEN];
6798 if (!test_if_hard_path(argument))
6799 {
6800 strxmov(buff, opt_basedir, argument, NullS);
6801 argument= buff;
6802 }
6803 fn_format(buff, argument, "", "", MY_UNPACK_FILENAME);
6804 DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0);
6805 if (!(cur_file->file=
6806 fopen(buff, "rb")))
6807 die("Could not open '%s' for reading, errno: %d", buff, errno);
6808 cur_file->file_name= my_strdup(buff, MYF(MY_FAE));
6809 cur_file->lineno= 1;
6810 break;
6811 }
6812 case 'm':
6813 {
6814 static char buff[FN_REFLEN];
6815 if (!test_if_hard_path(argument))
6816 {
6817 strxmov(buff, opt_basedir, argument, NullS);
6818 argument= buff;
6819 }
6820 fn_format(buff, argument, "", "", MY_UNPACK_FILENAME);
6821 timer_file= buff;
6822 unlink(timer_file); /* Ignore error, may not exist */
6823 break;
6824 }
6825 case 'p':
6826 if (argument == disabled_my_option)
6827 argument= (char*) ""; // Don't require password
6828 if (argument)
6829 {
6830 my_free(opt_pass);
6831 opt_pass= my_strdup(argument, MYF(MY_FAE));
6832 while (*argument) *argument++= 'x'; /* Destroy argument */
6833 tty_password= 0;
6834 }
6835 else
6836 tty_password= 1;
6837 break;
6838 #include <sslopt-case.h>
6839 case 't':
6840 strnmov(TMPDIR, argument, sizeof(TMPDIR));
6841 break;
6842 case 'A':
6843 if (!embedded_server_arg_count)
6844 {
6845 embedded_server_arg_count=1;
6846 embedded_server_args[0]= (char*) "";
6847 }
6848 if (embedded_server_arg_count == MAX_EMBEDDED_SERVER_ARGS-1 ||
6849 !(embedded_server_args[embedded_server_arg_count++]=
6850 my_strdup(argument, MYF(MY_FAE))))
6851 {
6852 die("Can't use server argument");
6853 }
6854 break;
6855 case OPT_LOG_DIR:
6856 /* Check that the file exists */
6857 if (access(opt_logdir, F_OK) != 0)
6858 die("The specified log directory does not exist: '%s'", opt_logdir);
6859 break;
6860 case 'F':
6861 read_embedded_server_arguments(argument);
6862 break;
6863 case OPT_RESULT_FORMAT_VERSION:
6864 set_result_format_version(opt_result_format_version);
6865 break;
6866 case 'V':
6867 print_version();
6868 exit(0);
6869 case OPT_MYSQL_PROTOCOL:
6870 #ifndef EMBEDDED_LIBRARY
6871 opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib,
6872 opt->name);
6873 #endif
6874 break;
6875 case '?':
6876 usage();
6877 exit(0);
6878 }
6879 return 0;
6880 }
6881
6882
parse_args(int argc,char ** argv)6883 int parse_args(int argc, char **argv)
6884 {
6885 if (load_defaults("my",load_default_groups,&argc,&argv))
6886 exit(1);
6887
6888 default_argv= argv;
6889
6890 if ((handle_options(&argc, &argv, my_long_options, get_one_option)))
6891 exit(1);
6892
6893 if (argc > 1)
6894 {
6895 usage();
6896 exit(1);
6897 }
6898 if (argc == 1)
6899 opt_db= *argv;
6900 if (tty_password)
6901 opt_pass= get_tty_password(NullS); /* purify tested */
6902 if (debug_info_flag)
6903 my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
6904 if (debug_check_flag)
6905 my_end_arg= MY_CHECK_ERROR;
6906
6907
6908 if (!record)
6909 {
6910 /* Check that the result file exists */
6911 if (result_file_name && access(result_file_name, F_OK) != 0)
6912 die("The specified result file '%s' does not exist", result_file_name);
6913 }
6914
6915 return 0;
6916 }
6917
6918 /*
6919 Write the content of str into file
6920
6921 SYNOPSIS
6922 str_to_file2
6923 fname - name of file to truncate/create and write to
6924 str - content to write to file
6925 size - size of content witten to file
6926 append - append to file instead of overwriting old file
6927 */
6928
str_to_file2(const char * fname,char * str,int size,my_bool append)6929 void str_to_file2(const char *fname, char *str, int size, my_bool append)
6930 {
6931 int fd;
6932 char buff[FN_REFLEN];
6933 int flags= O_WRONLY | O_CREAT;
6934 if (!test_if_hard_path(fname))
6935 {
6936 strxmov(buff, opt_basedir, fname, NullS);
6937 fname= buff;
6938 }
6939 fn_format(buff, fname, "", "", MY_UNPACK_FILENAME);
6940
6941 if (!append)
6942 flags|= O_TRUNC;
6943 if ((fd= my_open(buff, flags,
6944 MYF(MY_WME | MY_FFNF))) < 0)
6945 die("Could not open '%s' for writing, errno: %d", buff, errno);
6946 if (append && my_seek(fd, 0, SEEK_END, MYF(0)) == MY_FILEPOS_ERROR)
6947 die("Could not find end of file '%s', errno: %d", buff, errno);
6948 if (my_write(fd, (uchar*)str, size, MYF(MY_WME|MY_FNABP)))
6949 die("write failed, errno: %d", errno);
6950 my_close(fd, MYF(0));
6951 }
6952
6953 /*
6954 Write the content of str into file
6955
6956 SYNOPSIS
6957 str_to_file
6958 fname - name of file to truncate/create and write to
6959 str - content to write to file
6960 size - size of content witten to file
6961 */
6962
str_to_file(const char * fname,char * str,int size)6963 void str_to_file(const char *fname, char *str, int size)
6964 {
6965 str_to_file2(fname, str, size, FALSE);
6966 }
6967
6968
check_regerr(my_regex_t * r,int err)6969 void check_regerr(my_regex_t* r, int err)
6970 {
6971 char err_buf[1024];
6972
6973 if (err)
6974 {
6975 my_regerror(err,r,err_buf,sizeof(err_buf));
6976 die("Regex error: %s\n", err_buf);
6977 }
6978 }
6979
6980
6981 #ifdef __WIN__
6982
6983 DYNAMIC_ARRAY patterns;
6984
6985 /*
6986 init_win_path_patterns
6987
6988 DESCRIPTION
6989 Setup string patterns that will be used to detect filenames that
6990 needs to be converted from Win to Unix format
6991
6992 */
6993
init_win_path_patterns()6994 void init_win_path_patterns()
6995 {
6996 /* List of string patterns to match in order to find paths */
6997 const char* paths[] = { "$MYSQL_TEST_DIR",
6998 "$MYSQL_TMP_DIR",
6999 "$MYSQLTEST_VARDIR",
7000 "$MASTER_MYSOCK",
7001 "$MYSQL_SHAREDIR",
7002 "$MYSQL_LIBDIR",
7003 "./test/" };
7004 int num_paths= sizeof(paths)/sizeof(char*);
7005 int i;
7006 char* p;
7007
7008 DBUG_ENTER("init_win_path_patterns");
7009
7010 my_init_dynamic_array(&patterns, sizeof(const char*), 16, 16);
7011
7012 /* Loop through all paths in the array */
7013 for (i= 0; i < num_paths; i++)
7014 {
7015 VAR* v;
7016 if (*(paths[i]) == '$')
7017 {
7018 v= var_get(paths[i], 0, 0, 0);
7019 p= my_strdup(v->str_val, MYF(MY_FAE));
7020 }
7021 else
7022 p= my_strdup(paths[i], MYF(MY_FAE));
7023
7024 /* Don't insert zero length strings in patterns array */
7025 if (strlen(p) == 0)
7026 {
7027 my_free(p);
7028 continue;
7029 }
7030
7031 if (insert_dynamic(&patterns, &p))
7032 die("Out of memory");
7033
7034 DBUG_PRINT("info", ("p: %s", p));
7035 while (*p)
7036 {
7037 if (*p == '/')
7038 *p='\\';
7039 p++;
7040 }
7041 }
7042 DBUG_VOID_RETURN;
7043 }
7044
free_win_path_patterns()7045 void free_win_path_patterns()
7046 {
7047 uint i= 0;
7048 for (i=0 ; i < patterns.elements ; i++)
7049 {
7050 const char** pattern= dynamic_element(&patterns, i, const char**);
7051 my_free((void *) *pattern);
7052 }
7053 delete_dynamic(&patterns);
7054 }
7055
7056 /*
7057 fix_win_paths
7058
7059 DESCRIPTION
7060 Search the string 'val' for the patterns that are known to be
7061 strings that contain filenames. Convert all \ to / in the
7062 filenames that are found.
7063
7064 Ex:
7065 val = 'Error "c:\mysql\mysql-test\var\test\t1.frm" didn't exist'
7066 => $MYSQL_TEST_DIR is found by strstr
7067 => all \ from c:\mysql\m... until next space is converted into /
7068 */
7069
fix_win_paths(const char * val,int len)7070 void fix_win_paths(const char *val, int len)
7071 {
7072 uint i;
7073 char *p;
7074
7075 DBUG_ENTER("fix_win_paths");
7076 for (i= 0; i < patterns.elements; i++)
7077 {
7078 const char** pattern= dynamic_element(&patterns, i, const char**);
7079 DBUG_PRINT("info", ("pattern: %s", *pattern));
7080
7081 /* Search for the path in string */
7082 while ((p= strstr((char*)val, *pattern)))
7083 {
7084 DBUG_PRINT("info", ("Found %s in val p: %s", *pattern, p));
7085
7086 while (*p && !my_isspace(charset_info, *p))
7087 {
7088 if (*p == '\\')
7089 *p= '/';
7090 p++;
7091 }
7092 DBUG_PRINT("info", ("Converted \\ to /, p: %s", p));
7093 }
7094 }
7095 DBUG_PRINT("exit", (" val: %s, len: %d", val, len));
7096 DBUG_VOID_RETURN;
7097 }
7098 #endif
7099
7100
7101
7102 /*
7103 Append the result for one field to the dynamic string ds
7104 */
7105
append_field(DYNAMIC_STRING * ds,uint col_idx,MYSQL_FIELD * field,char * val,ulonglong len,my_bool is_null)7106 void append_field(DYNAMIC_STRING *ds, uint col_idx, MYSQL_FIELD* field,
7107 char* val, ulonglong len, my_bool is_null)
7108 {
7109 char null[]= "NULL";
7110
7111 if (col_idx < max_replace_column && replace_column[col_idx])
7112 {
7113 val= replace_column[col_idx];
7114 len= strlen(val);
7115 }
7116 else if (is_null)
7117 {
7118 val= null;
7119 len= 4;
7120 }
7121 #ifdef __WIN__
7122 else if ((field->type == MYSQL_TYPE_DOUBLE ||
7123 field->type == MYSQL_TYPE_FLOAT ) &&
7124 field->decimals >= 31)
7125 {
7126 /* Convert 1.2e+018 to 1.2e+18 and 1.2e-018 to 1.2e-18 */
7127 char *start= strchr(val, 'e');
7128 if (start && strlen(start) >= 5 &&
7129 (start[1] == '-' || start[1] == '+') && start[2] == '0')
7130 {
7131 start+=2; /* Now points at first '0' */
7132 if (field->flags & ZEROFILL_FLAG)
7133 {
7134 /* Move all chars before the first '0' one step right */
7135 memmove(val + 1, val, start - val);
7136 *val= '0';
7137 }
7138 else
7139 {
7140 /* Move all chars after the first '0' one step left */
7141 memmove(start, start + 1, strlen(start));
7142 len--;
7143 }
7144 }
7145 }
7146 #endif
7147
7148 if (!display_result_vertically)
7149 {
7150 if (col_idx)
7151 dynstr_append_mem(ds, "\t", 1);
7152 replace_dynstr_append_mem(ds, val, (int)len);
7153 }
7154 else
7155 {
7156 dynstr_append(ds, field->name);
7157 dynstr_append_mem(ds, "\t", 1);
7158 replace_dynstr_append_mem(ds, val, (int)len);
7159 dynstr_append_mem(ds, "\n", 1);
7160 }
7161 }
7162
7163
7164 /*
7165 Append all results to the dynamic string separated with '\t'
7166 Values may be converted with 'replace_column'
7167 */
7168
append_result(DYNAMIC_STRING * ds,MYSQL_RES * res)7169 void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res)
7170 {
7171 MYSQL_ROW row;
7172 uint num_fields= mysql_num_fields(res);
7173 MYSQL_FIELD *fields= mysql_fetch_fields(res);
7174 ulong *lengths;
7175
7176 while ((row = mysql_fetch_row(res)))
7177 {
7178 uint i;
7179 lengths = mysql_fetch_lengths(res);
7180 for (i = 0; i < num_fields; i++)
7181 {
7182 /* looks ugly , but put here to convince parfait */
7183 assert(lengths);
7184 append_field(ds, i, &fields[i],
7185 row[i], lengths[i], !row[i]);
7186 }
7187 if (!display_result_vertically)
7188 dynstr_append_mem(ds, "\n", 1);
7189 }
7190 }
7191
7192
7193 /*
7194 Append all results from ps execution to the dynamic string separated
7195 with '\t'. Values may be converted with 'replace_column'
7196 */
7197
append_stmt_result(DYNAMIC_STRING * ds,MYSQL_STMT * stmt,MYSQL_FIELD * fields,uint num_fields)7198 void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt,
7199 MYSQL_FIELD *fields, uint num_fields)
7200 {
7201 MYSQL_BIND *my_bind;
7202 my_bool *is_null;
7203 ulong *length;
7204 uint i;
7205
7206 /* Allocate array with bind structs, lengths and NULL flags */
7207 my_bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND),
7208 MYF(MY_WME | MY_FAE | MY_ZEROFILL));
7209 length= (ulong*) my_malloc(num_fields * sizeof(ulong),
7210 MYF(MY_WME | MY_FAE));
7211 is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool),
7212 MYF(MY_WME | MY_FAE));
7213
7214 /* Allocate data for the result of each field */
7215 for (i= 0; i < num_fields; i++)
7216 {
7217 uint max_length= fields[i].max_length + 1;
7218 my_bind[i].buffer_type= MYSQL_TYPE_STRING;
7219 my_bind[i].buffer= my_malloc(max_length, MYF(MY_WME | MY_FAE));
7220 my_bind[i].buffer_length= max_length;
7221 my_bind[i].is_null= &is_null[i];
7222 my_bind[i].length= &length[i];
7223
7224 DBUG_PRINT("bind", ("col[%d]: buffer_type: %d, buffer_length: %lu",
7225 i, my_bind[i].buffer_type, my_bind[i].buffer_length));
7226 }
7227
7228 if (mysql_stmt_bind_result(stmt, my_bind))
7229 die("mysql_stmt_bind_result failed: %d: %s",
7230 mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
7231
7232 while (mysql_stmt_fetch(stmt) == 0)
7233 {
7234 for (i= 0; i < num_fields; i++)
7235 append_field(ds, i, &fields[i], (char*)my_bind[i].buffer,
7236 *my_bind[i].length, *my_bind[i].is_null);
7237 if (!display_result_vertically)
7238 dynstr_append_mem(ds, "\n", 1);
7239 }
7240
7241 int rc;
7242 if ((rc= mysql_stmt_fetch(stmt)) != MYSQL_NO_DATA)
7243 die("fetch didn't end with MYSQL_NO_DATA from statement: %d: %s; rc=%d",
7244 mysql_stmt_errno(stmt), mysql_stmt_error(stmt), rc);
7245
7246 for (i= 0; i < num_fields; i++)
7247 {
7248 /* Free data for output */
7249 my_free(my_bind[i].buffer);
7250 }
7251 /* Free array with bind structs, lengths and NULL flags */
7252 my_free(my_bind);
7253 my_free(length);
7254 my_free(is_null);
7255 }
7256
7257
7258 /*
7259 Append metadata for fields to output
7260 */
7261
append_metadata(DYNAMIC_STRING * ds,MYSQL_FIELD * field,uint num_fields)7262 void append_metadata(DYNAMIC_STRING *ds,
7263 MYSQL_FIELD *field,
7264 uint num_fields)
7265 {
7266 MYSQL_FIELD *field_end;
7267 dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\t"
7268 "Column_alias\tType\tLength\tMax length\tIs_null\t"
7269 "Flags\tDecimals\tCharsetnr\n");
7270
7271 for (field_end= field+num_fields ;
7272 field < field_end ;
7273 field++)
7274 {
7275 dynstr_append_mem(ds, field->catalog,
7276 field->catalog_length);
7277 dynstr_append_mem(ds, "\t", 1);
7278 dynstr_append_mem(ds, field->db, field->db_length);
7279 dynstr_append_mem(ds, "\t", 1);
7280 dynstr_append_mem(ds, field->org_table,
7281 field->org_table_length);
7282 dynstr_append_mem(ds, "\t", 1);
7283 dynstr_append_mem(ds, field->table,
7284 field->table_length);
7285 dynstr_append_mem(ds, "\t", 1);
7286 dynstr_append_mem(ds, field->org_name,
7287 field->org_name_length);
7288 dynstr_append_mem(ds, "\t", 1);
7289 dynstr_append_mem(ds, field->name, field->name_length);
7290 dynstr_append_mem(ds, "\t", 1);
7291 replace_dynstr_append_uint(ds, field->type);
7292 dynstr_append_mem(ds, "\t", 1);
7293 replace_dynstr_append_uint(ds, field->length);
7294 dynstr_append_mem(ds, "\t", 1);
7295 replace_dynstr_append_uint(ds, field->max_length);
7296 dynstr_append_mem(ds, "\t", 1);
7297 dynstr_append_mem(ds, (char*) (IS_NOT_NULL(field->flags) ?
7298 "N" : "Y"), 1);
7299 dynstr_append_mem(ds, "\t", 1);
7300 replace_dynstr_append_uint(ds, field->flags);
7301 dynstr_append_mem(ds, "\t", 1);
7302 replace_dynstr_append_uint(ds, field->decimals);
7303 dynstr_append_mem(ds, "\t", 1);
7304 replace_dynstr_append_uint(ds, field->charsetnr);
7305 dynstr_append_mem(ds, "\n", 1);
7306 }
7307 }
7308
7309
7310 /*
7311 Append affected row count and other info to output
7312 */
7313
append_info(DYNAMIC_STRING * ds,ulonglong affected_rows,const char * info)7314 void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows,
7315 const char *info)
7316 {
7317 char buf[40], buff2[21];
7318 sprintf(buf,"affected rows: %s\n", llstr(affected_rows, buff2));
7319 dynstr_append(ds, buf);
7320 if (info)
7321 {
7322 dynstr_append(ds, "info: ");
7323 dynstr_append(ds, info);
7324 dynstr_append_mem(ds, "\n", 1);
7325 }
7326 }
7327
7328
7329 /*
7330 Display the table headings with the names tab separated
7331 */
7332
append_table_headings(DYNAMIC_STRING * ds,MYSQL_FIELD * field,uint num_fields)7333 void append_table_headings(DYNAMIC_STRING *ds,
7334 MYSQL_FIELD *field,
7335 uint num_fields)
7336 {
7337 uint col_idx;
7338 for (col_idx= 0; col_idx < num_fields; col_idx++)
7339 {
7340 if (col_idx)
7341 dynstr_append_mem(ds, "\t", 1);
7342 replace_dynstr_append(ds, field[col_idx].name);
7343 }
7344 dynstr_append_mem(ds, "\n", 1);
7345 }
7346
7347 /*
7348 Fetch warnings from server and append to ds
7349
7350 RETURN VALUE
7351 Number of warnings appended to ds
7352 */
7353
append_warnings(DYNAMIC_STRING * ds,MYSQL * mysql)7354 int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql)
7355 {
7356 uint count;
7357 MYSQL_RES *warn_res;
7358 DBUG_ENTER("append_warnings");
7359
7360 if (!(count= mysql_warning_count(mysql)))
7361 DBUG_RETURN(0);
7362
7363 /*
7364 If one day we will support execution of multi-statements
7365 through PS API we should not issue SHOW WARNINGS until
7366 we have not read all results...
7367 */
7368 DBUG_ASSERT(!mysql_more_results(mysql));
7369
7370 if (mysql_real_query(mysql, "SHOW WARNINGS", 13))
7371 die("Error running query \"SHOW WARNINGS\": %s", mysql_error(mysql));
7372
7373 if (!(warn_res= mysql_store_result(mysql)))
7374 die("Warning count is %u but didn't get any warnings",
7375 count);
7376
7377 append_result(ds, warn_res);
7378 mysql_free_result(warn_res);
7379
7380 DBUG_PRINT("warnings", ("%s", ds->str));
7381
7382 DBUG_RETURN(count);
7383 }
7384
7385
7386 /*
7387 Run query using MySQL C API
7388
7389 SYNOPSIS
7390 run_query_normal()
7391 mysql mysql handle
7392 command current command pointer
7393 flags flags indicating if we should SEND and/or REAP
7394 query query string to execute
7395 query_len length query string to execute
7396 ds output buffer where to store result form query
7397 */
7398
run_query_normal(struct st_connection * cn,struct st_command * command,int flags,char * query,int query_len,DYNAMIC_STRING * ds,DYNAMIC_STRING * ds_warnings)7399 void run_query_normal(struct st_connection *cn, struct st_command *command,
7400 int flags, char *query, int query_len,
7401 DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings)
7402 {
7403 MYSQL_RES *res= 0;
7404 MYSQL *mysql= &cn->mysql;
7405 int err= 0, counter= 0;
7406 DBUG_ENTER("run_query_normal");
7407 DBUG_PRINT("enter",("flags: %d", flags));
7408 DBUG_PRINT("enter", ("query: '%-.60s'", query));
7409
7410 if (flags & QUERY_SEND_FLAG)
7411 {
7412 /*
7413 Send the query
7414 */
7415 if (do_send_query(cn, query, query_len))
7416 {
7417 handle_error(command, mysql_errno(mysql), mysql_error(mysql),
7418 mysql_sqlstate(mysql), ds);
7419 goto end;
7420 }
7421 }
7422 if (!(flags & QUERY_REAP_FLAG))
7423 {
7424 cn->pending= TRUE;
7425 DBUG_VOID_RETURN;
7426 }
7427
7428 do
7429 {
7430 /*
7431 When on first result set, call mysql_read_query_result to retrieve
7432 answer to the query sent earlier
7433 */
7434 if ((counter==0) && do_read_query_result(cn))
7435 {
7436 /* we've failed to collect the result set */
7437 cn->pending= TRUE;
7438 handle_error(command, mysql_errno(mysql), mysql_error(mysql),
7439 mysql_sqlstate(mysql), ds);
7440 goto end;
7441
7442 }
7443
7444 /*
7445 Store the result of the query if it will return any fields
7446 */
7447 if (mysql_field_count(mysql) && ((res= mysql_store_result(mysql)) == 0))
7448 {
7449 handle_error(command, mysql_errno(mysql), mysql_error(mysql),
7450 mysql_sqlstate(mysql), ds);
7451 goto end;
7452 }
7453
7454 if (!disable_result_log)
7455 {
7456 if (res)
7457 {
7458 MYSQL_FIELD *fields= mysql_fetch_fields(res);
7459 uint num_fields= mysql_num_fields(res);
7460
7461 if (display_metadata)
7462 append_metadata(ds, fields, num_fields);
7463
7464 if (!display_result_vertically)
7465 append_table_headings(ds, fields, num_fields);
7466
7467 append_result(ds, res);
7468 }
7469
7470 /*
7471 Need to call mysql_affected_rows() before the "new"
7472 query to find the warnings.
7473 */
7474 if (!disable_info)
7475 append_info(ds, mysql_affected_rows(mysql), mysql_info(mysql));
7476
7477 /*
7478 Add all warnings to the result. We can't do this if we are in
7479 the middle of processing results from multi-statement, because
7480 this will break protocol.
7481 */
7482 if (!disable_warnings && !mysql_more_results(mysql))
7483 {
7484 if (append_warnings(ds_warnings, mysql) || ds_warnings->length)
7485 {
7486 dynstr_append_mem(ds, "Warnings:\n", 10);
7487 dynstr_append_mem(ds, ds_warnings->str, ds_warnings->length);
7488 }
7489 }
7490 }
7491
7492 if (res)
7493 {
7494 mysql_free_result(res);
7495 res= 0;
7496 }
7497 counter++;
7498 } while (!(err= mysql_next_result(mysql)));
7499 if (err > 0)
7500 {
7501 /* We got an error from mysql_next_result, maybe expected */
7502 handle_error(command, mysql_errno(mysql), mysql_error(mysql),
7503 mysql_sqlstate(mysql), ds);
7504 goto end;
7505 }
7506 DBUG_ASSERT(err == -1); /* Successful and there are no more results */
7507
7508 /* If we come here the query is both executed and read successfully */
7509 handle_no_error(command);
7510 revert_properties();
7511
7512 end:
7513
7514 cn->pending= FALSE;
7515 /*
7516 We save the return code (mysql_errno(mysql)) from the last call sent
7517 to the server into the mysqltest builtin variable $mysql_errno. This
7518 variable then can be used from the test case itself.
7519 */
7520 var_set_errno(mysql_errno(mysql));
7521 DBUG_VOID_RETURN;
7522 }
7523
7524
7525 /*
7526 Check whether given error is in list of expected errors
7527
7528 SYNOPSIS
7529 match_expected_error()
7530
7531 PARAMETERS
7532 command the current command (and its expect-list)
7533 err_errno error number of the error that actually occurred
7534 err_sqlstate SQL-state that was thrown, or NULL for impossible
7535 (file-ops, diff, etc.)
7536
7537 RETURNS
7538 -1 for not in list, index in list of expected errors otherwise
7539
7540 NOTE
7541 If caller needs to know whether the list was empty, they should
7542 check command->expected_errors.count.
7543 */
7544
match_expected_error(struct st_command * command,unsigned int err_errno,const char * err_sqlstate)7545 static int match_expected_error(struct st_command *command,
7546 unsigned int err_errno,
7547 const char *err_sqlstate)
7548 {
7549 uint i;
7550
7551 for (i= 0 ; (uint) i < command->expected_errors.count ; i++)
7552 {
7553 if ((command->expected_errors.err[i].type == ERR_ERRNO) &&
7554 (command->expected_errors.err[i].code.errnum == err_errno))
7555 return i;
7556
7557 if (command->expected_errors.err[i].type == ERR_SQLSTATE)
7558 {
7559 /*
7560 NULL is quite likely, but not in conjunction with a SQL-state expect!
7561 */
7562 if (unlikely(err_sqlstate == NULL))
7563 die("expecting a SQL-state (%s) from query '%s' which cannot produce one...",
7564 command->expected_errors.err[i].code.sqlstate, command->query);
7565
7566 if (strncmp(command->expected_errors.err[i].code.sqlstate,
7567 err_sqlstate, SQLSTATE_LENGTH) == 0)
7568 return i;
7569 }
7570 }
7571 return -1;
7572 }
7573
7574
7575 /*
7576 Handle errors which occurred during execution
7577
7578 SYNOPSIS
7579 handle_error()
7580 q - query context
7581 err_errno - error number
7582 err_error - error message
7583 err_sqlstate - sql state
7584 ds - dynamic string which is used for output buffer
7585
7586 NOTE
7587 If there is an unexpected error this function will abort mysqltest
7588 immediately.
7589 */
7590
handle_error(struct st_command * command,unsigned int err_errno,const char * err_error,const char * err_sqlstate,DYNAMIC_STRING * ds)7591 void handle_error(struct st_command *command,
7592 unsigned int err_errno, const char *err_error,
7593 const char *err_sqlstate, DYNAMIC_STRING *ds)
7594 {
7595 int i;
7596
7597 DBUG_ENTER("handle_error");
7598
7599 if (command->require_file[0])
7600 {
7601 /*
7602 The query after a "--require" failed. This is fine as long the server
7603 returned a valid reponse. Don't allow 2013 or 2006 to trigger an
7604 abort_not_supported_test
7605 */
7606 if (err_errno == CR_SERVER_LOST ||
7607 err_errno == CR_SERVER_GONE_ERROR)
7608 die("require query '%s' failed: %d: %s", command->query,
7609 err_errno, err_error);
7610
7611 /* Abort the run of this test, pass the failed query as reason */
7612 abort_not_supported_test("Query '%s' failed, required functionality " \
7613 "not supported", command->query);
7614 }
7615
7616 if (command->abort_on_error)
7617 {
7618 if (err_errno == ER_NO_SUCH_THREAD)
7619 {
7620 /* No such thread id, let's dump the available ones */
7621 fprintf(stderr, "mysqltest: query '%s returned ER_NO_SUCH_THREAD, "
7622 "dumping processlist\n", command->query);
7623 show_query(&cur_con->mysql, "SHOW PROCESSLIST");
7624 }
7625
7626 die("query '%s' failed: %d: %s", command->query, err_errno, err_error);
7627 }
7628
7629 DBUG_PRINT("info", ("expected_errors.count: %d",
7630 command->expected_errors.count));
7631
7632 i= match_expected_error(command, err_errno, err_sqlstate);
7633
7634 if (i >= 0)
7635 {
7636 if (!disable_result_log)
7637 {
7638 if (command->expected_errors.count == 1)
7639 {
7640 /* Only log error if there is one possible error */
7641 dynstr_append_mem(ds, "ERROR ", 6);
7642 replace_dynstr_append(ds, err_sqlstate);
7643 dynstr_append_mem(ds, ": ", 2);
7644 replace_dynstr_append(ds, err_error);
7645 dynstr_append_mem(ds,"\n",1);
7646 }
7647 /* Don't log error if we may not get an error */
7648 else if (command->expected_errors.err[0].type == ERR_SQLSTATE ||
7649 (command->expected_errors.err[0].type == ERR_ERRNO &&
7650 command->expected_errors.err[0].code.errnum != 0))
7651 dynstr_append(ds,"Got one of the listed errors\n");
7652 }
7653 /* OK */
7654 revert_properties();
7655 DBUG_VOID_RETURN;
7656 }
7657
7658 DBUG_PRINT("info",("i: %d expected_errors: %d", i,
7659 command->expected_errors.count));
7660
7661 if (!disable_result_log)
7662 {
7663 dynstr_append_mem(ds, "ERROR ",6);
7664 replace_dynstr_append(ds, err_sqlstate);
7665 dynstr_append_mem(ds, ": ", 2);
7666 replace_dynstr_append(ds, err_error);
7667 dynstr_append_mem(ds, "\n", 1);
7668 }
7669
7670 if (command->expected_errors.count > 0)
7671 {
7672 if (command->expected_errors.err[0].type == ERR_ERRNO)
7673 {
7674 if (err_errno == ER_NO_SUCH_THREAD)
7675 {
7676 /* No such thread id, let's dump the available ones */
7677 fprintf(stderr, "mysqltest: query '%s returned ER_NO_SUCH_THREAD, "
7678 "dumping processlist\n", command->query);
7679 show_query(&cur_con->mysql, "SHOW PROCESSLIST");
7680 }
7681 die("query '%s' failed with wrong errno %d: '%s', instead of %d...",
7682 command->query, err_errno, err_error,
7683 command->expected_errors.err[0].code.errnum);
7684 }
7685 else
7686 die("query '%s' failed with wrong sqlstate %s: '%s', instead of %s...",
7687 command->query, err_sqlstate, err_error,
7688 command->expected_errors.err[0].code.sqlstate);
7689 }
7690
7691 revert_properties();
7692 DBUG_VOID_RETURN;
7693 }
7694
7695
7696 /*
7697 Handle absence of errors after execution
7698
7699 SYNOPSIS
7700 handle_no_error()
7701 q - context of query
7702
7703 RETURN VALUE
7704 error - function will not return
7705 */
7706
handle_no_error(struct st_command * command)7707 void handle_no_error(struct st_command *command)
7708 {
7709 DBUG_ENTER("handle_no_error");
7710
7711 if (command->expected_errors.err[0].type == ERR_ERRNO &&
7712 command->expected_errors.err[0].code.errnum != 0)
7713 {
7714 /* Error code we wanted was != 0, i.e. not an expected success */
7715 die("query '%s' succeeded - should have failed with errno %d...",
7716 command->query, command->expected_errors.err[0].code.errnum);
7717 }
7718 else if (command->expected_errors.err[0].type == ERR_SQLSTATE &&
7719 strcmp(command->expected_errors.err[0].code.sqlstate,"00000") != 0)
7720 {
7721 /* SQLSTATE we wanted was != "00000", i.e. not an expected success */
7722 die("query '%s' succeeded - should have failed with sqlstate %s...",
7723 command->query, command->expected_errors.err[0].code.sqlstate);
7724 }
7725 DBUG_VOID_RETURN;
7726 }
7727
7728
7729 /*
7730 Run query using prepared statement C API
7731
7732 SYNPOSIS
7733 run_query_stmt
7734 mysql - mysql handle
7735 command - currrent command pointer
7736 query - query string to execute
7737 query_len - length query string to execute
7738 ds - output buffer where to store result form query
7739
7740 RETURN VALUE
7741 error - function will not return
7742 */
7743
run_query_stmt(MYSQL * mysql,struct st_command * command,char * query,int query_len,DYNAMIC_STRING * ds,DYNAMIC_STRING * ds_warnings)7744 void run_query_stmt(MYSQL *mysql, struct st_command *command,
7745 char *query, int query_len, DYNAMIC_STRING *ds,
7746 DYNAMIC_STRING *ds_warnings)
7747 {
7748 MYSQL_RES *res= NULL; /* Note that here 'res' is meta data result set */
7749 MYSQL_STMT *stmt;
7750 DYNAMIC_STRING ds_prepare_warnings;
7751 DYNAMIC_STRING ds_execute_warnings;
7752 DBUG_ENTER("run_query_stmt");
7753 DBUG_PRINT("query", ("'%-.60s'", query));
7754
7755 /*
7756 Init a new stmt if it's not already one created for this connection
7757 */
7758 if(!(stmt= cur_con->stmt))
7759 {
7760 if (!(stmt= mysql_stmt_init(mysql)))
7761 die("unable to init stmt structure");
7762 cur_con->stmt= stmt;
7763 }
7764
7765 /* Init dynamic strings for warnings */
7766 if (!disable_warnings)
7767 {
7768 init_dynamic_string(&ds_prepare_warnings, "", 0, 256);
7769 init_dynamic_string(&ds_execute_warnings, "", 0, 256);
7770 }
7771
7772 /*
7773 Prepare the query
7774 */
7775 if (mysql_stmt_prepare(stmt, query, query_len))
7776 {
7777 handle_error(command, mysql_stmt_errno(stmt),
7778 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
7779 goto end;
7780 }
7781
7782 /*
7783 Get the warnings from mysql_stmt_prepare and keep them in a
7784 separate string
7785 */
7786 if (!disable_warnings)
7787 append_warnings(&ds_prepare_warnings, mysql);
7788
7789 /*
7790 No need to call mysql_stmt_bind_param() because we have no
7791 parameter markers.
7792 */
7793
7794 #if MYSQL_VERSION_ID >= 50000
7795 if (cursor_protocol_enabled)
7796 {
7797 /*
7798 Use cursor when retrieving result
7799 */
7800 ulong type= CURSOR_TYPE_READ_ONLY;
7801 if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type))
7802 die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s",
7803 mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
7804 }
7805 #endif
7806
7807 /*
7808 Execute the query
7809 */
7810 if (mysql_stmt_execute(stmt))
7811 {
7812 handle_error(command, mysql_stmt_errno(stmt),
7813 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
7814 goto end;
7815 }
7816
7817 /*
7818 When running in cursor_protocol get the warnings from execute here
7819 and keep them in a separate string for later.
7820 */
7821 if (cursor_protocol_enabled && !disable_warnings)
7822 append_warnings(&ds_execute_warnings, mysql);
7823
7824 /*
7825 We instruct that we want to update the "max_length" field in
7826 mysql_stmt_store_result(), this is our only way to know how much
7827 buffer to allocate for result data
7828 */
7829 {
7830 my_bool one= 1;
7831 if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one))
7832 die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s",
7833 mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
7834 }
7835
7836 /*
7837 If we got here the statement succeeded and was expected to do so,
7838 get data. Note that this can still give errors found during execution!
7839 Store the result of the query if if will return any fields
7840 */
7841 if (mysql_stmt_field_count(stmt) && mysql_stmt_store_result(stmt))
7842 {
7843 handle_error(command, mysql_stmt_errno(stmt),
7844 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
7845 goto end;
7846 }
7847
7848 /* If we got here the statement was both executed and read successfully */
7849 handle_no_error(command);
7850 if (!disable_result_log)
7851 {
7852 /*
7853 Not all statements creates a result set. If there is one we can
7854 now create another normal result set that contains the meta
7855 data. This set can be handled almost like any other non prepared
7856 statement result set.
7857 */
7858 if ((res= mysql_stmt_result_metadata(stmt)) != NULL)
7859 {
7860 /* Take the column count from meta info */
7861 MYSQL_FIELD *fields= mysql_fetch_fields(res);
7862 uint num_fields= mysql_num_fields(res);
7863
7864 if (display_metadata)
7865 append_metadata(ds, fields, num_fields);
7866
7867 if (!display_result_vertically)
7868 append_table_headings(ds, fields, num_fields);
7869
7870 append_stmt_result(ds, stmt, fields, num_fields);
7871
7872 mysql_free_result(res); /* Free normal result set with meta data */
7873 }
7874 else
7875 {
7876 /*
7877 This is a query without resultset
7878 */
7879 }
7880
7881 /*
7882 Fetch info before fetching warnings, since it will be reset
7883 otherwise.
7884 */
7885
7886 if (!disable_info)
7887 append_info(ds, mysql_stmt_affected_rows(stmt), mysql_info(mysql));
7888
7889 if (!disable_warnings)
7890 {
7891 /* Get the warnings from execute */
7892
7893 /* Append warnings to ds - if there are any */
7894 if (append_warnings(&ds_execute_warnings, mysql) ||
7895 ds_execute_warnings.length ||
7896 ds_prepare_warnings.length ||
7897 ds_warnings->length)
7898 {
7899 dynstr_append_mem(ds, "Warnings:\n", 10);
7900 if (ds_warnings->length)
7901 dynstr_append_mem(ds, ds_warnings->str,
7902 ds_warnings->length);
7903 if (ds_prepare_warnings.length)
7904 {
7905 /* Split the string to get each warning */
7906 std::stringstream prepare_warnings(ds_prepare_warnings.str);
7907 std::string prepare_warning;
7908 /*
7909 If the warning is already present in the execute phase,
7910 do not append it
7911 */
7912 while (std::getline(prepare_warnings, prepare_warning))
7913 {
7914 std::string execute_warnings(ds_execute_warnings.str);
7915 if ((execute_warnings + "\n").find(prepare_warning + "\n") ==
7916 std::string::npos)
7917 {
7918 dynstr_append_mem(ds, prepare_warning.c_str(),
7919 prepare_warning.length());
7920 dynstr_append_mem(ds, "\n", 1);
7921 }
7922 }
7923 }
7924 if (ds_execute_warnings.length)
7925 dynstr_append_mem(ds, ds_execute_warnings.str,
7926 ds_execute_warnings.length);
7927 }
7928 }
7929 }
7930
7931 end:
7932 if (!disable_warnings)
7933 {
7934 dynstr_free(&ds_prepare_warnings);
7935 dynstr_free(&ds_execute_warnings);
7936 }
7937 revert_properties();
7938
7939 /*
7940 We save the return code (mysql_stmt_errno(stmt)) from the last call sent
7941 to the server into the mysqltest builtin variable $mysql_errno. This
7942 variable then can be used from the test case itself.
7943 */
7944
7945 var_set_errno(mysql_stmt_errno(stmt));
7946
7947 /* Close the statement if - no reconnect, need new prepare */
7948 if (mysql->reconnect)
7949 {
7950 mysql_stmt_close(stmt);
7951 cur_con->stmt= NULL;
7952 }
7953
7954
7955 DBUG_VOID_RETURN;
7956 }
7957
7958
7959
7960 /*
7961 Create a util connection if one does not already exists
7962 and use that to run the query
7963 This is done to avoid implict commit when creating/dropping objects such
7964 as view, sp etc.
7965 */
7966
util_query(MYSQL * org_mysql,const char * query)7967 int util_query(MYSQL* org_mysql, const char* query){
7968
7969 MYSQL* mysql;
7970 DBUG_ENTER("util_query");
7971
7972 if(!(mysql= cur_con->util_mysql))
7973 {
7974 DBUG_PRINT("info", ("Creating util_mysql"));
7975 if (!(mysql= mysql_init(mysql)))
7976 die("Failed in mysql_init()");
7977
7978 if (opt_connect_timeout)
7979 mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT,
7980 (void *) &opt_connect_timeout);
7981
7982 /* enable local infile, in non-binary builds often disabled by default */
7983 mysql_options(mysql, MYSQL_OPT_LOCAL_INFILE, 0);
7984 safe_connect(mysql, "util", org_mysql->host, org_mysql->user,
7985 org_mysql->passwd, org_mysql->db, org_mysql->port,
7986 org_mysql->unix_socket);
7987
7988 cur_con->util_mysql= mysql;
7989 }
7990
7991 DBUG_RETURN(mysql_query(mysql, query));
7992 }
7993
7994
7995
7996 /*
7997 Run query
7998
7999 SYNPOSIS
8000 run_query()
8001 mysql mysql handle
8002 command currrent command pointer
8003
8004 flags control the phased/stages of query execution to be performed
8005 if QUERY_SEND_FLAG bit is on, the query will be sent. If QUERY_REAP_FLAG
8006 is on the result will be read - for regular query, both bits must be on
8007 */
8008
run_query(struct st_connection * cn,struct st_command * command,int flags)8009 void run_query(struct st_connection *cn, struct st_command *command, int flags)
8010 {
8011 MYSQL *mysql= &cn->mysql;
8012 DYNAMIC_STRING *ds;
8013 DYNAMIC_STRING *save_ds= NULL;
8014 DYNAMIC_STRING ds_result;
8015 DYNAMIC_STRING ds_sorted;
8016 DYNAMIC_STRING ds_warnings;
8017 DYNAMIC_STRING eval_query;
8018 char *query;
8019 int query_len;
8020 my_bool view_created= 0, sp_created= 0;
8021 my_bool complete_query= ((flags & QUERY_SEND_FLAG) &&
8022 (flags & QUERY_REAP_FLAG));
8023 DBUG_ENTER("run_query");
8024
8025 if (cn->pending && (flags & QUERY_SEND_FLAG))
8026 die ("Cannot run query on connection between send and reap");
8027
8028 if (!(flags & QUERY_SEND_FLAG) && !cn->pending)
8029 die ("Cannot reap on a connection without pending send");
8030
8031 init_dynamic_string(&ds_warnings, NULL, 0, 256);
8032 ds_warn= &ds_warnings;
8033
8034 /*
8035 Evaluate query if this is an eval command
8036 */
8037 if (command->type == Q_EVAL || command->type == Q_SEND_EVAL)
8038 {
8039 init_dynamic_string(&eval_query, "", command->query_len+256, 1024);
8040 do_eval(&eval_query, command->query, command->end, FALSE);
8041 query = eval_query.str;
8042 query_len = eval_query.length;
8043 }
8044 else
8045 {
8046 query = command->query;
8047 query_len = strlen(query);
8048 }
8049
8050 /*
8051 When command->require_file is set the output of _this_ query
8052 should be compared with an already existing file
8053 Create a temporary dynamic string to contain the output from
8054 this query.
8055 */
8056 if (command->require_file[0])
8057 {
8058 init_dynamic_string(&ds_result, "", 1024, 1024);
8059 ds= &ds_result;
8060 }
8061 else
8062 ds= &ds_res;
8063
8064 /*
8065 Log the query into the output buffer
8066 */
8067 if (!disable_query_log && (flags & QUERY_SEND_FLAG))
8068 {
8069 replace_dynstr_append_mem(ds, query, query_len);
8070 dynstr_append_mem(ds, delimiter, delimiter_length);
8071 dynstr_append_mem(ds, "\n", 1);
8072 }
8073
8074 if (view_protocol_enabled &&
8075 complete_query &&
8076 match_re(&view_re, query))
8077 {
8078 /*
8079 Create the query as a view.
8080 Use replace since view can exist from a failed mysqltest run
8081 */
8082 DYNAMIC_STRING query_str;
8083 init_dynamic_string(&query_str,
8084 "CREATE OR REPLACE VIEW mysqltest_tmp_v AS ",
8085 query_len+64, 256);
8086 dynstr_append_mem(&query_str, query, query_len);
8087 if (util_query(mysql, query_str.str))
8088 {
8089 /*
8090 Failed to create the view, this is not fatal
8091 just run the query the normal way
8092 */
8093 DBUG_PRINT("view_create_error",
8094 ("Failed to create view '%s': %d: %s", query_str.str,
8095 mysql_errno(mysql), mysql_error(mysql)));
8096
8097 /* Log error to create view */
8098 verbose_msg("Failed to create view '%s' %d: %s", query_str.str,
8099 mysql_errno(mysql), mysql_error(mysql));
8100 }
8101 else
8102 {
8103 /*
8104 Yes, it was possible to create this query as a view
8105 */
8106 view_created= 1;
8107 query= (char*)"SELECT * FROM mysqltest_tmp_v";
8108 query_len = strlen(query);
8109
8110 /*
8111 Collect warnings from create of the view that should otherwise
8112 have been produced when the SELECT was executed
8113 */
8114 append_warnings(&ds_warnings, cur_con->util_mysql);
8115 }
8116
8117 dynstr_free(&query_str);
8118
8119 }
8120
8121 if (sp_protocol_enabled &&
8122 complete_query &&
8123 match_re(&sp_re, query))
8124 {
8125 /*
8126 Create the query as a stored procedure
8127 Drop first since sp can exist from a failed mysqltest run
8128 */
8129 DYNAMIC_STRING query_str;
8130 init_dynamic_string(&query_str,
8131 "DROP PROCEDURE IF EXISTS mysqltest_tmp_sp;",
8132 query_len+64, 256);
8133 util_query(mysql, query_str.str);
8134 dynstr_set(&query_str, "CREATE PROCEDURE mysqltest_tmp_sp()\n");
8135 dynstr_append_mem(&query_str, query, query_len);
8136 if (util_query(mysql, query_str.str))
8137 {
8138 /*
8139 Failed to create the stored procedure for this query,
8140 this is not fatal just run the query the normal way
8141 */
8142 DBUG_PRINT("sp_create_error",
8143 ("Failed to create sp '%s': %d: %s", query_str.str,
8144 mysql_errno(mysql), mysql_error(mysql)));
8145
8146 /* Log error to create sp */
8147 verbose_msg("Failed to create sp '%s' %d: %s", query_str.str,
8148 mysql_errno(mysql), mysql_error(mysql));
8149
8150 }
8151 else
8152 {
8153 sp_created= 1;
8154
8155 query= (char*)"CALL mysqltest_tmp_sp()";
8156 query_len = strlen(query);
8157 }
8158 dynstr_free(&query_str);
8159 }
8160
8161 if (display_result_sorted)
8162 {
8163 /*
8164 Collect the query output in a separate string
8165 that can be sorted before it's added to the
8166 global result string
8167 */
8168 init_dynamic_string(&ds_sorted, "", 1024, 1024);
8169 save_ds= ds; /* Remember original ds */
8170 ds= &ds_sorted;
8171 }
8172
8173 /*
8174 Find out how to run this query
8175
8176 Always run with normal C API if it's not a complete
8177 SEND + REAP
8178
8179 If it is a '?' in the query it may be a SQL level prepared
8180 statement already and we can't do it twice
8181 */
8182 if (ps_protocol_enabled &&
8183 complete_query &&
8184 match_re(&ps_re, query))
8185 run_query_stmt(mysql, command, query, query_len, ds, &ds_warnings);
8186 else
8187 run_query_normal(cn, command, flags, query, query_len,
8188 ds, &ds_warnings);
8189
8190 dynstr_free(&ds_warnings);
8191 ds_warn= 0;
8192 if (command->type == Q_EVAL || command->type == Q_SEND_EVAL)
8193 dynstr_free(&eval_query);
8194
8195 if (display_result_sorted)
8196 {
8197 /* Sort the result set and append it to result */
8198 dynstr_append_sorted(save_ds, &ds_sorted);
8199 ds= save_ds;
8200 dynstr_free(&ds_sorted);
8201 }
8202
8203 if (sp_created)
8204 {
8205 if (util_query(mysql, "DROP PROCEDURE mysqltest_tmp_sp "))
8206 die("Failed to drop sp: %d: %s", mysql_errno(mysql), mysql_error(mysql));
8207 }
8208
8209 if (view_created)
8210 {
8211 if (util_query(mysql, "DROP VIEW mysqltest_tmp_v "))
8212 die("Failed to drop view: %d: %s",
8213 mysql_errno(mysql), mysql_error(mysql));
8214 }
8215
8216 if (command->require_file[0])
8217 {
8218 /* A result file was specified for _this_ query
8219 and the output should be checked against an already
8220 existing file which has been specified using --require or --result
8221 */
8222 check_require(ds, command->require_file);
8223 }
8224
8225 if (ds == &ds_result)
8226 dynstr_free(&ds_result);
8227 DBUG_VOID_RETURN;
8228 }
8229
8230 /**
8231 Display the optimizer trace produced by the last executed statement.
8232 */
display_opt_trace(struct st_connection * cn,struct st_command * command,int flags)8233 void display_opt_trace(struct st_connection *cn,
8234 struct st_command *command,
8235 int flags)
8236 {
8237 if (!disable_query_log &&
8238 opt_trace_protocol_enabled &&
8239 !cn->pending &&
8240 !command->expected_errors.count &&
8241 match_re(&opt_trace_re, command->query))
8242 {
8243 st_command save_command= *command;
8244 DYNAMIC_STRING query_str;
8245 init_dynamic_string(&query_str,
8246 "SELECT trace FROM information_schema.optimizer_trace"
8247 " /* injected by --opt-trace-protocol */",
8248 128, 128);
8249
8250 command->query= query_str.str;
8251 command->query_len= query_str.length;
8252 command->end= strend(command->query);
8253
8254 /* Sorted trace is not readable at all, don't bother to lower case */
8255 /* No need to keep old values, will be reset anyway */
8256 display_result_sorted= FALSE;
8257 display_result_lower= FALSE;
8258 run_query(cn, command, flags);
8259
8260 dynstr_free(&query_str);
8261 *command= save_command;
8262 }
8263 }
8264
8265
run_explain(struct st_connection * cn,struct st_command * command,int flags,bool json)8266 void run_explain(struct st_connection *cn, struct st_command *command,
8267 int flags, bool json)
8268 {
8269 if ((flags & QUERY_REAP_FLAG) &&
8270 !command->expected_errors.count &&
8271 match_re(&explain_re, command->query))
8272 {
8273 st_command save_command= *command;
8274 DYNAMIC_STRING query_str;
8275 DYNAMIC_STRING ds_warning_messages;
8276
8277 init_dynamic_string(&ds_warning_messages, "", 0, 2048);
8278 init_dynamic_string(&query_str, json ? "EXPLAIN FORMAT=JSON "
8279 : "EXPLAIN EXTENDED ", 256, 256);
8280 dynstr_append_mem(&query_str, command->query,
8281 command->end - command->query);
8282
8283 command->query= query_str.str;
8284 command->query_len= query_str.length;
8285 command->end= strend(command->query);
8286
8287 run_query(cn, command, flags);
8288
8289 dynstr_free(&query_str);
8290 dynstr_free(&ds_warning_messages);
8291
8292 *command= save_command;
8293 }
8294 }
8295
8296
8297 /****************************************************************************/
8298 /*
8299 Functions to detect different SQL statements
8300 */
8301
re_eprint(int err)8302 char *re_eprint(int err)
8303 {
8304 static char epbuf[100];
8305 size_t len MY_ATTRIBUTE((unused))= my_regerror(MY_REG_ITOA | err, NULL, epbuf, sizeof(epbuf));
8306 assert(len <= sizeof(epbuf));
8307 return(epbuf);
8308 }
8309
init_re_comp(my_regex_t * re,const char * str)8310 void init_re_comp(my_regex_t *re, const char* str)
8311 {
8312 int err= my_regcomp(re, str, (MY_REG_EXTENDED | MY_REG_ICASE | MY_REG_NOSUB),
8313 &my_charset_latin1);
8314 if (err)
8315 {
8316 char erbuf[100];
8317 int len= my_regerror(err, re, erbuf, sizeof(erbuf));
8318 die("error %s, %d/%d `%s'\n",
8319 re_eprint(err), (int)len, (int)sizeof(erbuf), erbuf);
8320 }
8321 }
8322
init_re(void)8323 void init_re(void)
8324 {
8325 /*
8326 Filter for queries that can be run using the
8327 MySQL Prepared Statements C API
8328 */
8329 const char *ps_re_str =
8330 "^("
8331 "[[:space:]]*REPLACE[[:space:]]|"
8332 "[[:space:]]*INSERT[[:space:]]|"
8333 "[[:space:]]*UPDATE[[:space:]]|"
8334 "[[:space:]]*DELETE[[:space:]]|"
8335 "[[:space:]]*SELECT[[:space:]]|"
8336 "[[:space:]]*CREATE[[:space:]]+TABLE[[:space:]]|"
8337 "[[:space:]]*DO[[:space:]]|"
8338 "[[:space:]]*SET[[:space:]]+OPTION[[:space:]]|"
8339 "[[:space:]]*DELETE[[:space:]]+MULTI[[:space:]]|"
8340 "[[:space:]]*UPDATE[[:space:]]+MULTI[[:space:]]|"
8341 "[[:space:]]*INSERT[[:space:]]+SELECT[[:space:]])";
8342
8343 /*
8344 Filter for queries that can be run using the
8345 Stored procedures
8346 */
8347 const char *sp_re_str =ps_re_str;
8348
8349 /*
8350 Filter for queries that can be run as views
8351 */
8352 const char *view_re_str =
8353 "^("
8354 "[[:space:]]*SELECT[[:space:]])";
8355
8356 const char *opt_trace_re_str =
8357 "^("
8358 "[[:space:]]*INSERT[[:space:]]|"
8359 "[[:space:]]*UPDATE[[:space:]]|"
8360 "[[:space:]]*DELETE[[:space:]]|"
8361 "[[:space:]]*EXPLAIN[[:space:]]|"
8362 "[[:space:]]*SELECT[[:space:]])";
8363
8364 /* Filter for queries that can be converted to EXPLAIN */
8365 const char *explain_re_str =
8366 "^("
8367 "[[:space:]]*(SELECT|DELETE|UPDATE|INSERT|REPLACE)[[:space:]])";
8368
8369 init_re_comp(&ps_re, ps_re_str);
8370 init_re_comp(&sp_re, sp_re_str);
8371 init_re_comp(&view_re, view_re_str);
8372 init_re_comp(&opt_trace_re, opt_trace_re_str);
8373 init_re_comp(&explain_re, explain_re_str);
8374 }
8375
8376
match_re(my_regex_t * re,char * str)8377 int match_re(my_regex_t *re, char *str)
8378 {
8379 while (my_isspace(charset_info, *str))
8380 str++;
8381 if (str[0] == '/' && str[1] == '*')
8382 {
8383 char *comm_end= strstr (str, "*/");
8384 if (! comm_end)
8385 die("Statement is unterminated comment");
8386 str= comm_end + 2;
8387 }
8388
8389 int err= my_regexec(re, str, (size_t)0, NULL, 0);
8390
8391 if (err == 0)
8392 return 1;
8393 else if (err == MY_REG_NOMATCH)
8394 return 0;
8395
8396 {
8397 char erbuf[100];
8398 int len= my_regerror(err, re, erbuf, sizeof(erbuf));
8399 die("error %s, %d/%d `%s'\n",
8400 re_eprint(err), (int)len, (int)sizeof(erbuf), erbuf);
8401 }
8402 return 0;
8403 }
8404
free_re(void)8405 void free_re(void)
8406 {
8407 my_regfree(&ps_re);
8408 my_regfree(&sp_re);
8409 my_regfree(&view_re);
8410 my_regfree(&opt_trace_re);
8411 my_regfree(&explain_re);
8412 my_regex_end();
8413 }
8414
8415 /****************************************************************************/
8416
get_command_type(struct st_command * command)8417 void get_command_type(struct st_command* command)
8418 {
8419 char save;
8420 uint type;
8421 DBUG_ENTER("get_command_type");
8422
8423 if (*command->query == '}')
8424 {
8425 command->type = Q_END_BLOCK;
8426 DBUG_VOID_RETURN;
8427 }
8428
8429 save= command->query[command->first_word_len];
8430 command->query[command->first_word_len]= 0;
8431 type= find_type(command->query, &command_typelib, FIND_TYPE_NO_PREFIX);
8432 command->query[command->first_word_len]= save;
8433 if (type > 0)
8434 {
8435 command->type=(enum enum_commands) type; /* Found command */
8436
8437 /*
8438 Look for case where "query" was explicitly specified to
8439 force command being sent to server
8440 */
8441 if (type == Q_QUERY)
8442 {
8443 /* Skip the "query" part */
8444 command->query= command->first_argument;
8445 }
8446 }
8447 else
8448 {
8449 /* No mysqltest command matched */
8450
8451 if (command->type != Q_COMMENT_WITH_COMMAND)
8452 {
8453 /* A query that will sent to mysqld */
8454 command->type= Q_QUERY;
8455 }
8456 else
8457 {
8458 /* -- "comment" that didn't contain a mysqltest command */
8459 die("Found line beginning with -- that didn't contain "\
8460 "a valid mysqltest command, check your syntax or "\
8461 "use # if you intended to write a comment");
8462 }
8463 }
8464 DBUG_VOID_RETURN;
8465 }
8466
8467
update_expected_errors(struct st_command * command)8468 void update_expected_errors(struct st_command* command)
8469 {
8470 DBUG_ENTER("update_expected_errors");
8471
8472 /* Set expected error on command */
8473 memcpy(&command->expected_errors, &saved_expected_errors,
8474 sizeof(saved_expected_errors));
8475 DBUG_PRINT("info", ("There are %d expected errors",
8476 command->expected_errors.count));
8477 DBUG_VOID_RETURN;
8478 }
8479
8480
8481
8482 /*
8483 Record how many milliseconds it took to execute the test file
8484 up until the current line and write it to .progress file
8485
8486 */
8487
mark_progress(struct st_command * command MY_ATTRIBUTE ((unused)),int line)8488 void mark_progress(struct st_command* command MY_ATTRIBUTE((unused)),
8489 int line)
8490 {
8491 static ulonglong progress_start= 0; // < Beware
8492 DYNAMIC_STRING ds_progress;
8493
8494 char buf[32], *end;
8495 ulonglong timer= timer_now();
8496 if (!progress_start)
8497 progress_start= timer;
8498 timer-= progress_start;
8499
8500 if (init_dynamic_string(&ds_progress, "", 256, 256))
8501 die("Out of memory");
8502
8503 /* Milliseconds since start */
8504 end= longlong2str(timer, buf, 10);
8505 dynstr_append_mem(&ds_progress, buf, (int)(end-buf));
8506 dynstr_append_mem(&ds_progress, "\t", 1);
8507
8508 /* Parser line number */
8509 end= int10_to_str(line, buf, 10);
8510 dynstr_append_mem(&ds_progress, buf, (int)(end-buf));
8511 dynstr_append_mem(&ds_progress, "\t", 1);
8512
8513 /* Filename */
8514 dynstr_append(&ds_progress, cur_file->file_name);
8515 dynstr_append_mem(&ds_progress, ":", 1);
8516
8517 /* Line in file */
8518 end= int10_to_str(cur_file->lineno, buf, 10);
8519 dynstr_append_mem(&ds_progress, buf, (int)(end-buf));
8520
8521
8522 dynstr_append_mem(&ds_progress, "\n", 1);
8523
8524 progress_file.write(&ds_progress);
8525
8526 dynstr_free(&ds_progress);
8527
8528 }
8529
8530 #ifdef HAVE_STACKTRACE
8531
dump_backtrace(void)8532 static void dump_backtrace(void)
8533 {
8534 struct st_connection *conn= cur_con;
8535
8536 fprintf(stderr, "read_command_buf (%p): ", read_command_buf);
8537 my_safe_print_str(read_command_buf, sizeof(read_command_buf));
8538
8539 if (conn)
8540 {
8541 fprintf(stderr, "conn->name (%p): ", conn->name);
8542 my_safe_print_str(conn->name, conn->name_len);
8543 #ifdef EMBEDDED_LIBRARY
8544 fprintf(stderr, "conn->cur_query (%p): ", conn->cur_query);
8545 my_safe_print_str(conn->cur_query, conn->cur_query_len);
8546 #endif
8547 }
8548 fputs("Attempting backtrace...\n", stderr);
8549 my_print_stacktrace(NULL, my_thread_stack_size);
8550 }
8551
8552 #else
8553
dump_backtrace(void)8554 static void dump_backtrace(void)
8555 {
8556 fputs("Backtrace not available.\n", stderr);
8557 }
8558
8559 #endif
8560
signal_handler(int sig)8561 static sig_handler signal_handler(int sig)
8562 {
8563 fprintf(stderr, "mysqltest got " SIGNAL_FMT "\n", sig);
8564 dump_backtrace();
8565
8566 fprintf(stderr, "Writing a core file...\n");
8567 fflush(stderr);
8568 my_write_core(sig);
8569 #ifndef __WIN__
8570 exit(1); // Shouldn't get here but just in case
8571 #endif
8572 }
8573
8574 #ifdef __WIN__
8575
exception_filter(EXCEPTION_POINTERS * exp)8576 LONG WINAPI exception_filter(EXCEPTION_POINTERS *exp)
8577 {
8578 __try
8579 {
8580 my_set_exception_pointers(exp);
8581 signal_handler(exp->ExceptionRecord->ExceptionCode);
8582 }
8583 __except(EXCEPTION_EXECUTE_HANDLER)
8584 {
8585 fputs("Got exception in exception handler!\n", stderr);
8586 }
8587
8588 return EXCEPTION_CONTINUE_SEARCH;
8589 }
8590
8591
init_signal_handling(void)8592 static void init_signal_handling(void)
8593 {
8594 UINT mode;
8595
8596 /* Set output destination of messages to the standard error stream. */
8597 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
8598 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
8599 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
8600 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
8601 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
8602 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
8603
8604 /* Do not not display the a error message box. */
8605 mode= SetErrorMode(0) | SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX;
8606 SetErrorMode(mode);
8607
8608 SetUnhandledExceptionFilter(exception_filter);
8609 }
8610
8611 #else /* __WIN__ */
8612
init_signal_handling(void)8613 static void init_signal_handling(void)
8614 {
8615 struct sigaction sa;
8616 DBUG_ENTER("init_signal_handling");
8617
8618 #ifdef HAVE_STACKTRACE
8619 my_init_stacktrace();
8620 #endif
8621
8622 sa.sa_flags = SA_RESETHAND | SA_NODEFER;
8623 sigemptyset(&sa.sa_mask);
8624 sigprocmask(SIG_SETMASK, &sa.sa_mask, NULL);
8625
8626 sa.sa_handler= signal_handler;
8627
8628 sigaction(SIGSEGV, &sa, NULL);
8629 sigaction(SIGABRT, &sa, NULL);
8630 #ifdef SIGBUS
8631 sigaction(SIGBUS, &sa, NULL);
8632 #endif
8633 sigaction(SIGILL, &sa, NULL);
8634 sigaction(SIGFPE, &sa, NULL);
8635
8636 DBUG_VOID_RETURN;
8637 }
8638
8639 #endif /* !__WIN__ */
8640
main(int argc,char ** argv)8641 int main(int argc, char **argv)
8642 {
8643 struct st_command *command;
8644 my_bool q_send_flag= 0, abort_flag= 0;
8645 uint command_executed= 0, last_command_executed= 0;
8646 char save_file[FN_REFLEN];
8647 MY_INIT(argv[0]);
8648
8649 save_file[0]= 0;
8650 TMPDIR[0]= 0;
8651
8652 init_signal_handling();
8653
8654 /* Init expected errors */
8655 memset(&saved_expected_errors, 0, sizeof(saved_expected_errors));
8656
8657 #ifdef EMBEDDED_LIBRARY
8658 /* set appropriate stack for the 'query' threads */
8659 (void) pthread_attr_init(&cn_thd_attrib);
8660 pthread_attr_setstacksize(&cn_thd_attrib, DEFAULT_THREAD_STACK);
8661 #endif /*EMBEDDED_LIBRARY*/
8662
8663 /* Init file stack */
8664 memset(file_stack, 0, sizeof(file_stack));
8665 file_stack_end=
8666 file_stack + (sizeof(file_stack)/sizeof(struct st_test_file)) - 1;
8667 cur_file= file_stack;
8668
8669 /* Init block stack */
8670 memset(block_stack, 0, sizeof(block_stack));
8671 block_stack_end=
8672 block_stack + (sizeof(block_stack)/sizeof(struct st_block)) - 1;
8673 cur_block= block_stack;
8674 cur_block->ok= TRUE; /* Outer block should always be executed */
8675 cur_block->cmd= cmd_none;
8676
8677 my_init_dynamic_array(&q_lines, sizeof(struct st_command*), 1024, 1024);
8678
8679 if (my_hash_init(&var_hash, charset_info,
8680 1024, 0, 0, get_var_key, var_free, MYF(0)))
8681 die("Variable hash initialization failed");
8682
8683 {
8684 char path_separator[]= { FN_LIBCHAR, 0 };
8685 var_set_string("SYSTEM_PATH_SEPARATOR", path_separator);
8686 }
8687 var_set_string("MYSQL_SERVER_VERSION", MYSQL_SERVER_VERSION);
8688 var_set_string("MYSQL_SYSTEM_TYPE", SYSTEM_TYPE);
8689 var_set_string("MYSQL_MACHINE_TYPE", MACHINE_TYPE);
8690 if (sizeof(void *) == 8) {
8691 var_set_string("MYSQL_SYSTEM_ARCHITECTURE", "64");
8692 } else {
8693 var_set_string("MYSQL_SYSTEM_ARCHITECTURE", "32");
8694 }
8695
8696 memset(&master_pos, 0, sizeof(master_pos));
8697
8698 parser.current_line= parser.read_lines= 0;
8699 memset(&var_reg, 0, sizeof(var_reg));
8700
8701 init_builtin_echo();
8702 #ifdef __WIN__
8703 #ifndef USE_CYGWIN
8704 is_windows= 1;
8705 #endif
8706 init_tmp_sh_file();
8707 init_win_path_patterns();
8708 #endif
8709
8710 init_dynamic_string(&ds_res, "", 2048, 2048);
8711
8712 parse_args(argc, argv);
8713
8714 log_file.open(opt_logdir, result_file_name, ".log");
8715 verbose_msg("Logging to '%s'.", log_file.file_name());
8716 if (opt_mark_progress)
8717 {
8718 progress_file.open(opt_logdir, result_file_name, ".progress");
8719 verbose_msg("Tracing progress in '%s'.", progress_file.file_name());
8720 }
8721
8722 /* Init connections, allocate 1 extra as buffer + 1 for default */
8723 connections= (struct st_connection*)
8724 my_malloc((opt_max_connections+2) * sizeof(struct st_connection),
8725 MYF(MY_WME | MY_ZEROFILL));
8726 connections_end= connections + opt_max_connections +1;
8727 next_con= connections + 1;
8728
8729 var_set_int("$PS_PROTOCOL", ps_protocol);
8730 var_set_int("$SP_PROTOCOL", sp_protocol);
8731 var_set_int("$VIEW_PROTOCOL", view_protocol);
8732 var_set_int("$OPT_TRACE_PROTOCOL", opt_trace_protocol);
8733 var_set_int("$EXPLAIN_PROTOCOL", explain_protocol);
8734 var_set_int("$JSON_EXPLAIN_PROTOCOL", json_explain_protocol);
8735 var_set_int("$CURSOR_PROTOCOL", cursor_protocol);
8736
8737 var_set_int("$ENABLED_QUERY_LOG", 1);
8738 var_set_int("$ENABLED_ABORT_ON_ERROR", 1);
8739 var_set_int("$ENABLED_RESULT_LOG", 1);
8740 var_set_int("$ENABLED_CONNECT_LOG", 0);
8741 var_set_int("$ENABLED_WARNINGS", 1);
8742 var_set_int("$ENABLED_INFO", 0);
8743 var_set_int("$ENABLED_METADATA", 0);
8744
8745 DBUG_PRINT("info",("result_file: '%s'",
8746 result_file_name ? result_file_name : ""));
8747 verbose_msg("Results saved in '%s'.",
8748 result_file_name ? result_file_name : "");
8749 if (mysql_server_init(embedded_server_arg_count,
8750 embedded_server_args,
8751 (char**) embedded_server_groups))
8752 die("Can't initialize MySQL server");
8753 server_initialized= 1;
8754 if (cur_file == file_stack && cur_file->file == 0)
8755 {
8756 cur_file->file= stdin;
8757 cur_file->file_name= my_strdup("<stdin>", MYF(MY_WME));
8758 cur_file->lineno= 1;
8759 }
8760 var_set_string("MYSQLTEST_FILE", cur_file->file_name);
8761 init_re();
8762
8763 /* Cursor protcol implies ps protocol */
8764 if (cursor_protocol)
8765 ps_protocol= 1;
8766
8767 ps_protocol_enabled= ps_protocol;
8768 sp_protocol_enabled= sp_protocol;
8769 view_protocol_enabled= view_protocol;
8770 opt_trace_protocol_enabled= opt_trace_protocol;
8771 explain_protocol_enabled= explain_protocol;
8772 json_explain_protocol_enabled= json_explain_protocol;
8773 cursor_protocol_enabled= cursor_protocol;
8774
8775 st_connection *con= connections;
8776 #ifdef EMBEDDED_LIBRARY
8777 init_connection_thd(con);
8778 #endif /*EMBEDDED_LIBRARY*/
8779 if (!( mysql_init(&con->mysql)))
8780 die("Failed in mysql_init()");
8781 if (opt_connect_timeout)
8782 mysql_options(&con->mysql, MYSQL_OPT_CONNECT_TIMEOUT,
8783 (void *) &opt_connect_timeout);
8784 if (opt_compress)
8785 mysql_options(&con->mysql,MYSQL_OPT_COMPRESS,NullS);
8786 mysql_options(&con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
8787 mysql_options(&con->mysql, MYSQL_SET_CHARSET_NAME,
8788 charset_info->csname);
8789 if (opt_charsets_dir)
8790 mysql_options(&con->mysql, MYSQL_SET_CHARSET_DIR,
8791 opt_charsets_dir);
8792
8793 #ifndef EMBEDDED_LIBRARY
8794 if (opt_protocol)
8795 mysql_options(&con->mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
8796 #endif
8797
8798 #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
8799
8800 if (opt_use_ssl)
8801 {
8802 mysql_ssl_set(&con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
8803 opt_ssl_capath, opt_ssl_cipher);
8804 mysql_options(&con->mysql, MYSQL_OPT_SSL_CRL, opt_ssl_crl);
8805 mysql_options(&con->mysql, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath);
8806 #if MYSQL_VERSION_ID >= 50000
8807 /* Turn on ssl_verify_server_cert only if host is "localhost" */
8808 opt_ssl_verify_server_cert= opt_host && !strcmp(opt_host, "localhost");
8809 mysql_options(&con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
8810 &opt_ssl_verify_server_cert);
8811 #endif
8812 }
8813 #endif
8814
8815 #ifdef HAVE_SMEM
8816 if (shared_memory_base_name)
8817 mysql_options(&con->mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name);
8818 #endif
8819
8820 if (!(con->name = my_strdup("default", MYF(MY_WME))))
8821 die("Out of memory");
8822
8823 safe_connect(&con->mysql, con->name, opt_host, opt_user, opt_pass,
8824 opt_db, opt_port, unix_sock);
8825
8826 /* Use all time until exit if no explicit 'start_timer' */
8827 timer_start= timer_now();
8828
8829 /*
8830 Initialize $mysql_errno with -1, so we can
8831 - distinguish it from valid values ( >= 0 ) and
8832 - detect if there was never a command sent to the server
8833 */
8834 var_set_errno(-1);
8835
8836 set_current_connection(con);
8837
8838 if (opt_include)
8839 {
8840 open_file(opt_include);
8841 }
8842
8843 verbose_msg("Start processing test commands from '%s' ...", cur_file->file_name);
8844 while (!read_command(&command) && !abort_flag)
8845 {
8846 int current_line_inc = 1, processed = 0;
8847 if (command->type == Q_UNKNOWN || command->type == Q_COMMENT_WITH_COMMAND)
8848 get_command_type(command);
8849
8850 if(saved_expected_errors.count > 0)
8851 update_expected_errors(command);
8852
8853 if (parsing_disabled &&
8854 command->type != Q_ENABLE_PARSING &&
8855 command->type != Q_DISABLE_PARSING)
8856 {
8857 /* Parsing is disabled, silently convert this line to a comment */
8858 command->type= Q_COMMENT;
8859 }
8860
8861 /* (Re-)set abort_on_error for this command */
8862 command->abort_on_error= (command->expected_errors.count == 0 &&
8863 abort_on_error);
8864
8865 /* delimiter needs to be executed so we can continue to parse */
8866 my_bool ok_to_do= cur_block->ok || command->type == Q_DELIMITER;
8867 /*
8868 Some commands need to be "done" the first time if they may get
8869 re-iterated over in a true context. This can only happen if there's
8870 a while loop at some level above the current block.
8871 */
8872 if (!ok_to_do)
8873 {
8874 if (command->type == Q_SOURCE ||
8875 command->type == Q_ERROR ||
8876 command->type == Q_WRITE_FILE ||
8877 command->type == Q_APPEND_FILE ||
8878 command->type == Q_PERL)
8879 {
8880 for (struct st_block *stb= cur_block-1; stb >= block_stack; stb--)
8881 {
8882 if (stb->cmd == cmd_while)
8883 {
8884 ok_to_do= 1;
8885 break;
8886 }
8887 }
8888 }
8889 }
8890
8891 if (ok_to_do)
8892 {
8893 command->last_argument= command->first_argument;
8894 processed = 1;
8895 /* Need to remember this for handle_error() */
8896 curr_command= command;
8897 switch (command->type) {
8898 case Q_CONNECT:
8899 do_connect(command);
8900 break;
8901 case Q_CONNECTION: select_connection(command); break;
8902 case Q_DISCONNECT:
8903 case Q_DIRTY_CLOSE:
8904 do_close_connection(command); break;
8905 case Q_ENABLE_QUERY_LOG:
8906 set_property(command, P_QUERY, 0);
8907 break;
8908 case Q_DISABLE_QUERY_LOG:
8909 set_property(command, P_QUERY, 1);
8910 break;
8911 case Q_ENABLE_ABORT_ON_ERROR:
8912 set_property(command, P_ABORT, 1);
8913 break;
8914 case Q_DISABLE_ABORT_ON_ERROR:
8915 set_property(command, P_ABORT, 0);
8916 break;
8917 case Q_ENABLE_RESULT_LOG:
8918 set_property(command, P_RESULT, 0);
8919 break;
8920 case Q_DISABLE_RESULT_LOG:
8921 set_property(command, P_RESULT, 1);
8922 break;
8923 case Q_ENABLE_CONNECT_LOG:
8924 set_property(command, P_CONNECT, 0);
8925 break;
8926 case Q_DISABLE_CONNECT_LOG:
8927 set_property(command, P_CONNECT, 1);
8928 break;
8929 case Q_ENABLE_WARNINGS:
8930 set_property(command, P_WARN, 0);
8931 break;
8932 case Q_DISABLE_WARNINGS:
8933 set_property(command, P_WARN, 1);
8934 break;
8935 case Q_ENABLE_INFO:
8936 set_property(command, P_INFO, 0);
8937 break;
8938 case Q_DISABLE_INFO:
8939 set_property(command, P_INFO, 1);
8940 break;
8941 case Q_ENABLE_METADATA:
8942 set_property(command, P_META, 1);
8943 break;
8944 case Q_DISABLE_METADATA:
8945 set_property(command, P_META, 0);
8946 break;
8947 case Q_SOURCE: do_source(command); break;
8948 case Q_SLEEP: do_sleep(command, 0); break;
8949 case Q_REAL_SLEEP: do_sleep(command, 1); break;
8950 case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(command); break;
8951 case Q_INC: do_modify_var(command, DO_INC); break;
8952 case Q_DEC: do_modify_var(command, DO_DEC); break;
8953 case Q_ECHO: do_echo(command); command_executed++; break;
8954 case Q_SYSTEM:
8955 die("'system' command is deprecated, use exec or\n"\
8956 " see the manual for portable commands to use");
8957 break;
8958 case Q_REMOVE_FILE: do_remove_file(command); break;
8959 case Q_REMOVE_FILES_WILDCARD: do_remove_files_wildcard(command); break;
8960 case Q_MKDIR: do_mkdir(command); break;
8961 case Q_RMDIR: do_rmdir(command); break;
8962 case Q_LIST_FILES: do_list_files(command); break;
8963 case Q_LIST_FILES_WRITE_FILE:
8964 do_list_files_write_file_command(command, FALSE);
8965 break;
8966 case Q_LIST_FILES_APPEND_FILE:
8967 do_list_files_write_file_command(command, TRUE);
8968 break;
8969 case Q_FILE_EXIST: do_file_exist(command); break;
8970 case Q_WRITE_FILE: do_write_file(command); break;
8971 case Q_APPEND_FILE: do_append_file(command); break;
8972 case Q_DIFF_FILES: do_diff_files(command); break;
8973 case Q_SEND_QUIT: do_send_quit(command); break;
8974 case Q_CHANGE_USER: do_change_user(command); break;
8975 case Q_CAT_FILE: do_cat_file(command); break;
8976 case Q_COPY_FILE: do_copy_file(command); break;
8977 case Q_MOVE_FILE: do_move_file(command); break;
8978 case Q_CHMOD_FILE: do_chmod_file(command); break;
8979 case Q_PERL: do_perl(command); break;
8980 case Q_RESULT_FORMAT_VERSION: do_result_format_version(command); break;
8981 case Q_DELIMITER:
8982 do_delimiter(command);
8983 break;
8984 case Q_DISPLAY_VERTICAL_RESULTS:
8985 display_result_vertically= TRUE;
8986 break;
8987 case Q_DISPLAY_HORIZONTAL_RESULTS:
8988 display_result_vertically= FALSE;
8989 break;
8990 case Q_SORTED_RESULT:
8991 /*
8992 Turn on sorting of result set, will be reset after next
8993 command
8994 */
8995 display_result_sorted= TRUE;
8996 break;
8997 case Q_LOWERCASE:
8998 /*
8999 Turn on lowercasing of result, will be reset after next
9000 command
9001 */
9002 display_result_lower= TRUE;
9003 break;
9004 case Q_LET: do_let(command); break;
9005 case Q_EVAL_RESULT:
9006 die("'eval_result' command is deprecated");
9007 case Q_EVAL:
9008 case Q_QUERY_VERTICAL:
9009 case Q_QUERY_HORIZONTAL:
9010 if (command->query == command->query_buf)
9011 {
9012 /* Skip the first part of command, i.e query_xxx */
9013 command->query= command->first_argument;
9014 command->first_word_len= 0;
9015 }
9016 /* fall through */
9017 case Q_QUERY:
9018 case Q_REAP:
9019 {
9020 my_bool old_display_result_vertically= display_result_vertically;
9021 /* Default is full query, both reap and send */
9022 int flags= QUERY_REAP_FLAG | QUERY_SEND_FLAG;
9023
9024 if (q_send_flag)
9025 {
9026 /* Last command was an empty 'send' */
9027 flags= QUERY_SEND_FLAG;
9028 q_send_flag= 0;
9029 }
9030 else if (command->type == Q_REAP)
9031 {
9032 flags= QUERY_REAP_FLAG;
9033 }
9034
9035 /* Check for special property for this query */
9036 display_result_vertically|= (command->type == Q_QUERY_VERTICAL);
9037
9038 /*
9039 We run EXPLAIN _before_ the query. If query is UPDATE/DELETE is
9040 matters: a DELETE may delete rows, and then EXPLAIN DELETE will
9041 usually terminate quickly with "no matching rows". To make it more
9042 interesting, EXPLAIN is now first.
9043 */
9044 if (explain_protocol_enabled)
9045 run_explain(cur_con, command, flags, 0);
9046 if (json_explain_protocol_enabled)
9047 run_explain(cur_con, command, flags, 1);
9048 /* Check for 'require' */
9049 if (*save_file)
9050 {
9051 strmake(command->require_file, save_file, sizeof(save_file) - 1);
9052 *save_file= 0;
9053 }
9054 run_query(cur_con, command, flags);
9055 display_opt_trace(cur_con, command, flags);
9056 command_executed++;
9057 command->last_argument= command->end;
9058
9059 /* Restore settings */
9060 display_result_vertically= old_display_result_vertically;
9061
9062 break;
9063 }
9064 case Q_SEND:
9065 case Q_SEND_EVAL:
9066 if (!*command->first_argument)
9067 {
9068 /*
9069 This is a send without arguments, it indicates that _next_ query
9070 should be send only
9071 */
9072 q_send_flag= 1;
9073 break;
9074 }
9075
9076 /* Remove "send" if this is first iteration */
9077 if (command->query == command->query_buf)
9078 command->query= command->first_argument;
9079
9080 /*
9081 run_query() can execute a query partially, depending on the flags.
9082 QUERY_SEND_FLAG flag without QUERY_REAP_FLAG tells it to just send
9083 the query and read the result some time later when reap instruction
9084 is given on this connection.
9085 */
9086 run_query(cur_con, command, QUERY_SEND_FLAG);
9087 command_executed++;
9088 command->last_argument= command->end;
9089 break;
9090 case Q_REQUIRE:
9091 do_get_file_name(command, save_file, sizeof(save_file));
9092 break;
9093 case Q_ERROR:
9094 do_get_errcodes(command);
9095 break;
9096 case Q_REPLACE:
9097 do_get_replace(command);
9098 break;
9099 case Q_REPLACE_REGEX:
9100 do_get_replace_regex(command);
9101 break;
9102 case Q_REPLACE_COLUMN:
9103 do_get_replace_column(command);
9104 break;
9105 case Q_SAVE_MASTER_POS: do_save_master_pos(); break;
9106 case Q_SYNC_WITH_MASTER: do_sync_with_master(command); break;
9107 case Q_SYNC_SLAVE_WITH_MASTER:
9108 {
9109 do_save_master_pos();
9110 if (*command->first_argument)
9111 select_connection(command);
9112 else
9113 select_connection_name("slave");
9114 do_sync_with_master2(command, 0);
9115 break;
9116 }
9117 case Q_COMMENT:
9118 {
9119 command->last_argument= command->end;
9120
9121 /* Don't output comments in v1 */
9122 if (opt_result_format_version == 1)
9123 break;
9124
9125 /* Don't output comments if query logging is off */
9126 if (disable_query_log)
9127 break;
9128
9129 /* Write comment's with two starting #'s to result file */
9130 const char* p= command->query;
9131 if (p && *p == '#' && *(p+1) == '#')
9132 {
9133 dynstr_append_mem(&ds_res, command->query, command->query_len);
9134 dynstr_append(&ds_res, "\n");
9135 }
9136 break;
9137 }
9138 case Q_EMPTY_LINE:
9139 /* Don't output newline in v1 */
9140 if (opt_result_format_version == 1)
9141 break;
9142
9143 /* Don't output newline if query logging is off */
9144 if (disable_query_log)
9145 break;
9146
9147 dynstr_append(&ds_res, "\n");
9148 break;
9149 case Q_PING:
9150 handle_command_error(command, mysql_ping(&cur_con->mysql));
9151 break;
9152 case Q_SEND_SHUTDOWN:
9153 handle_command_error(command,
9154 mysql_shutdown(&cur_con->mysql,
9155 SHUTDOWN_DEFAULT));
9156 break;
9157 case Q_SHUTDOWN_SERVER:
9158 do_shutdown_server(command);
9159 break;
9160 case Q_EXEC:
9161 case Q_EXECW:
9162 do_exec(command);
9163 command_executed++;
9164 break;
9165 case Q_START_TIMER:
9166 /* Overwrite possible earlier start of timer */
9167 timer_start= timer_now();
9168 break;
9169 case Q_END_TIMER:
9170 /* End timer before ending mysqltest */
9171 timer_output();
9172 break;
9173 case Q_CHARACTER_SET:
9174 do_set_charset(command);
9175 break;
9176 case Q_DISABLE_PS_PROTOCOL:
9177 set_property(command, P_PS, 0);
9178 /* Close any open statements */
9179 close_statements();
9180 break;
9181 case Q_ENABLE_PS_PROTOCOL:
9182 set_property(command, P_PS, ps_protocol);
9183 break;
9184 case Q_DISABLE_RECONNECT:
9185 set_reconnect(&cur_con->mysql, 0);
9186 break;
9187 case Q_ENABLE_RECONNECT:
9188 set_reconnect(&cur_con->mysql, 1);
9189 /* Close any open statements - no reconnect, need new prepare */
9190 close_statements();
9191 break;
9192 case Q_DISABLE_PARSING:
9193 if (parsing_disabled == 0)
9194 parsing_disabled= 1;
9195 else
9196 die("Parsing is already disabled");
9197 break;
9198 case Q_ENABLE_PARSING:
9199 /*
9200 Ensure we don't get parsing_disabled < 0 as this would accidentally
9201 disable code we don't want to have disabled
9202 */
9203 if (parsing_disabled == 1)
9204 parsing_disabled= 0;
9205 else
9206 die("Parsing is already enabled");
9207 break;
9208 case Q_DIE:
9209 /* Abort test with error code and error message */
9210 die("%s", command->first_argument);
9211 break;
9212 case Q_EXIT:
9213 /* Stop processing any more commands */
9214 abort_flag= 1;
9215 break;
9216 case Q_SKIP:
9217 abort_not_supported_test("%s", command->first_argument);
9218 break;
9219
9220 case Q_RESULT:
9221 die("result, deprecated command");
9222 break;
9223
9224 default:
9225 processed= 0;
9226 break;
9227 }
9228 }
9229
9230 if (!processed)
9231 {
9232 current_line_inc= 0;
9233 switch (command->type) {
9234 case Q_WHILE: do_block(cmd_while, command); break;
9235 case Q_IF: do_block(cmd_if, command); break;
9236 case Q_END_BLOCK: do_done(command); break;
9237 default: current_line_inc = 1; break;
9238 }
9239 }
9240 else
9241 check_eol_junk(command->last_argument);
9242
9243 if (command->type != Q_ERROR &&
9244 command->type != Q_COMMENT)
9245 {
9246 /*
9247 As soon as any non "error" command or comment has been executed,
9248 the array with expected errors should be cleared
9249 */
9250 memset(&saved_expected_errors, 0, sizeof(saved_expected_errors));
9251 }
9252
9253 if (command_executed != last_command_executed || command->used_replace)
9254 {
9255 /*
9256 As soon as any command has been executed,
9257 the replace structures should be cleared
9258 */
9259 free_all_replace();
9260
9261 /* Also reset "sorted_result" and "lowercase"*/
9262 display_result_sorted= FALSE;
9263 display_result_lower= FALSE;
9264 }
9265 last_command_executed= command_executed;
9266
9267 parser.current_line += current_line_inc;
9268 if ( opt_mark_progress )
9269 mark_progress(command, parser.current_line);
9270
9271 /* Write result from command to log file immediately */
9272 log_file.write(&ds_res);
9273 log_file.flush();
9274 dynstr_set(&ds_res, 0);
9275 }
9276
9277 log_file.close();
9278
9279 start_lineno= 0;
9280 verbose_msg("... Done processing test commands.");
9281
9282 if (parsing_disabled)
9283 die("Test ended with parsing disabled");
9284
9285 my_bool empty_result= FALSE;
9286
9287 /*
9288 The whole test has been executed _sucessfully_.
9289 Time to compare result or save it to record file.
9290 The entire output from test is in the log file
9291 */
9292 if (log_file.bytes_written())
9293 {
9294 if (result_file_name)
9295 {
9296 /* A result file has been specified */
9297
9298 if (record)
9299 {
9300 /* Recording */
9301
9302 /* save a copy of the log to result file */
9303 if (my_copy(log_file.file_name(), result_file_name, MYF(0)) != 0)
9304 die("Failed to copy '%s' to '%s', errno: %d",
9305 log_file.file_name(), result_file_name, errno);
9306
9307 }
9308 else
9309 {
9310 /* Check that the output from test is equal to result file */
9311 check_result();
9312 }
9313 }
9314 }
9315 else
9316 {
9317 /* Empty output is an error *unless* we also have an empty result file */
9318 if (! result_file_name || record ||
9319 compare_files (log_file.file_name(), result_file_name))
9320 {
9321 die("The test didn't produce any output");
9322 }
9323 else
9324 {
9325 empty_result= TRUE; /* Meaning empty was expected */
9326 }
9327 }
9328
9329 if (!command_executed && result_file_name && !empty_result)
9330 die("No queries executed but non-empty result file found!");
9331
9332 verbose_msg("Test has succeeded!");
9333 timer_output();
9334 /* Yes, if we got this far the test has suceeded! Sakila smiles */
9335 cleanup_and_exit(0);
9336 return 0; /* Keep compiler happy too */
9337 }
9338
9339
9340 /*
9341 A primitive timer that give results in milliseconds if the
9342 --timer-file=<filename> is given. The timer result is written
9343 to that file when the result is available. To not confuse
9344 mysql-test-run with an old obsolete result, we remove the file
9345 before executing any commands. The time we measure is
9346
9347 - If no explicit 'start_timer' or 'end_timer' is given in the
9348 test case, the timer measure how long we execute in mysqltest.
9349
9350 - If only 'start_timer' is given we measure how long we execute
9351 from that point until we terminate mysqltest.
9352
9353 - If only 'end_timer' is given we measure how long we execute
9354 from that we enter mysqltest to the 'end_timer' is command is
9355 executed.
9356
9357 - If both 'start_timer' and 'end_timer' are given we measure
9358 the time between executing the two commands.
9359 */
9360
timer_output(void)9361 void timer_output(void)
9362 {
9363 if (timer_file)
9364 {
9365 char buf[32], *end;
9366 ulonglong timer= timer_now() - timer_start;
9367 end= longlong2str(timer, buf, 10);
9368 str_to_file(timer_file,buf, (int) (end-buf));
9369 /* Timer has been written to the file, don't use it anymore */
9370 timer_file= 0;
9371 }
9372 }
9373
9374
timer_now(void)9375 ulonglong timer_now(void)
9376 {
9377 return my_micro_time() / 1000;
9378 }
9379
9380
9381 /*
9382 Get arguments for replace_columns. The syntax is:
9383 replace-column column_number to_string [column_number to_string ...]
9384 Where each argument may be quoted with ' or "
9385 A argument may also be a variable, in which case the value of the
9386 variable is replaced.
9387 */
9388
do_get_replace_column(struct st_command * command)9389 void do_get_replace_column(struct st_command *command)
9390 {
9391 char *from= command->first_argument;
9392 char *buff, *start;
9393 DBUG_ENTER("get_replace_columns");
9394
9395 free_replace_column();
9396 if (!*from)
9397 die("Missing argument in %s", command->query);
9398
9399 /* Allocate a buffer for results */
9400 start= buff= (char*)my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
9401 while (*from)
9402 {
9403 char *to;
9404 uint column_number;
9405 to= get_string(&buff, &from, command);
9406 if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS)
9407 die("Wrong column number to replace_column in '%s'", command->query);
9408 if (!*from)
9409 die("Wrong number of arguments to replace_column in '%s'", command->query);
9410 to= get_string(&buff, &from, command);
9411 my_free(replace_column[column_number-1]);
9412 replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE));
9413 set_if_bigger(max_replace_column, column_number);
9414 }
9415 my_free(start);
9416 command->last_argument= command->end;
9417
9418 DBUG_VOID_RETURN;
9419 }
9420
9421
free_replace_column()9422 void free_replace_column()
9423 {
9424 uint i;
9425 for (i=0 ; i < max_replace_column ; i++)
9426 {
9427 if (replace_column[i])
9428 {
9429 my_free(replace_column[i]);
9430 replace_column[i]= 0;
9431 }
9432 }
9433 max_replace_column= 0;
9434 }
9435
9436
9437 /****************************************************************************/
9438 /*
9439 Replace functions
9440 */
9441
9442 /* Definitions for replace result */
9443
9444 typedef struct st_pointer_array { /* when using array-strings */
9445 TYPELIB typelib; /* Pointer to strings */
9446 uchar *str; /* Strings is here */
9447 uint8 *flag; /* Flag about each var. */
9448 uint array_allocs,max_count,length,max_length;
9449 } POINTER_ARRAY;
9450
9451 struct st_replace *init_replace(char * *from, char * *to, uint count,
9452 char * word_end_chars);
9453 int insert_pointer_name(POINTER_ARRAY *pa,char * name);
9454 void free_pointer_array(POINTER_ARRAY *pa);
9455
9456 /*
9457 Get arguments for replace. The syntax is:
9458 replace from to [from to ...]
9459 Where each argument may be quoted with ' or "
9460 A argument may also be a variable, in which case the value of the
9461 variable is replaced.
9462 */
9463
do_get_replace(struct st_command * command)9464 void do_get_replace(struct st_command *command)
9465 {
9466 uint i;
9467 char *from= command->first_argument;
9468 char *buff, *start;
9469 char word_end_chars[256], *pos;
9470 POINTER_ARRAY to_array, from_array;
9471 DBUG_ENTER("get_replace");
9472
9473 free_replace();
9474
9475 memset(&to_array, 0, sizeof(to_array));
9476 memset(&from_array, 0, sizeof(from_array));
9477 if (!*from)
9478 die("Missing argument in %s", command->query);
9479 start= buff= (char*)my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
9480 while (*from)
9481 {
9482 char *to= buff;
9483 to= get_string(&buff, &from, command);
9484 if (!*from)
9485 die("Wrong number of arguments to replace_result in '%s'",
9486 command->query);
9487 #ifdef __WIN__
9488 fix_win_paths(to, from - to);
9489 #endif
9490 insert_pointer_name(&from_array,to);
9491 to= get_string(&buff, &from, command);
9492 insert_pointer_name(&to_array,to);
9493 }
9494 for (i= 1,pos= word_end_chars ; i < 256 ; i++)
9495 if (my_isspace(charset_info,i))
9496 *pos++= i;
9497 *pos=0; /* End pointer */
9498 if (!(glob_replace= init_replace((char**) from_array.typelib.type_names,
9499 (char**) to_array.typelib.type_names,
9500 (uint) from_array.typelib.count,
9501 word_end_chars)))
9502 die("Can't initialize replace from '%s'", command->query);
9503 free_pointer_array(&from_array);
9504 free_pointer_array(&to_array);
9505 my_free(start);
9506 command->last_argument= command->end;
9507 DBUG_VOID_RETURN;
9508 }
9509
9510
free_replace()9511 void free_replace()
9512 {
9513 DBUG_ENTER("free_replace");
9514 my_free(glob_replace);
9515 glob_replace= NULL;
9516 DBUG_VOID_RETURN;
9517 }
9518
9519
9520 typedef struct st_replace {
9521 my_bool found;
9522 struct st_replace *next[256];
9523 } REPLACE;
9524
9525 typedef struct st_replace_found {
9526 my_bool found;
9527 char *replace_string;
9528 uint to_offset;
9529 int from_offset;
9530 } REPLACE_STRING;
9531
9532
replace_strings_append(REPLACE * rep,DYNAMIC_STRING * ds,const char * str,int len MY_ATTRIBUTE ((unused)))9533 void replace_strings_append(REPLACE *rep, DYNAMIC_STRING* ds,
9534 const char *str,
9535 int len MY_ATTRIBUTE((unused)))
9536 {
9537 REPLACE *rep_pos;
9538 REPLACE_STRING *rep_str;
9539 const char *start, *from;
9540 DBUG_ENTER("replace_strings_append");
9541
9542 start= from= str;
9543 rep_pos=rep+1;
9544 for (;;)
9545 {
9546 /* Loop through states */
9547 DBUG_PRINT("info", ("Looping through states"));
9548 while (!rep_pos->found)
9549 rep_pos= rep_pos->next[(uchar) *from++];
9550
9551 /* Does this state contain a string to be replaced */
9552 if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string)
9553 {
9554 /* No match found */
9555 dynstr_append_mem(ds, start, from - start - 1);
9556 DBUG_PRINT("exit", ("Found no more string to replace, appended: %s", start));
9557 DBUG_VOID_RETURN;
9558 }
9559
9560 /* Found a string that needs to be replaced */
9561 DBUG_PRINT("info", ("found: %d, to_offset: %d, from_offset: %d, string: %s",
9562 rep_str->found, rep_str->to_offset,
9563 rep_str->from_offset, rep_str->replace_string));
9564
9565 /* Append part of original string before replace string */
9566 dynstr_append_mem(ds, start, (from - rep_str->to_offset) - start);
9567
9568 /* Append replace string */
9569 dynstr_append_mem(ds, rep_str->replace_string,
9570 strlen(rep_str->replace_string));
9571
9572 if (!*(from-=rep_str->from_offset) && rep_pos->found != 2)
9573 {
9574 /* End of from string */
9575 DBUG_PRINT("exit", ("Found end of from string"));
9576 DBUG_VOID_RETURN;
9577 }
9578 DBUG_ASSERT(from <= str+len);
9579 start= from;
9580 rep_pos=rep;
9581 }
9582 }
9583
9584
9585 /*
9586 Regex replace functions
9587 */
9588
9589
9590 /* Stores regex substitutions */
9591
9592 struct st_regex
9593 {
9594 char* pattern; /* Pattern to be replaced */
9595 char* replace; /* String or expression to replace the pattern with */
9596 int icase; /* true if the match is case insensitive */
9597 };
9598
9599 int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace,
9600 char *string, int icase);
9601
9602
9603
9604 /*
9605 Finds the next (non-escaped) '/' in the expression.
9606 (If the character '/' is needed, it can be escaped using '\'.)
9607 */
9608
9609 #define PARSE_REGEX_ARG \
9610 while (p < expr_end) \
9611 { \
9612 char c= *p; \
9613 if (c == '/') \
9614 { \
9615 if (last_c == '\\') \
9616 { \
9617 buf_p[-1]= '/'; \
9618 } \
9619 else \
9620 { \
9621 *buf_p++ = 0; \
9622 break; \
9623 } \
9624 } \
9625 else \
9626 *buf_p++ = c; \
9627 \
9628 last_c= c; \
9629 p++; \
9630 } \
9631 \
9632 /*
9633 Initializes the regular substitution expression to be used in the
9634 result output of test.
9635
9636 Returns: st_replace_regex struct with pairs of substitutions
9637 */
9638
init_replace_regex(char * expr)9639 struct st_replace_regex* init_replace_regex(char* expr)
9640 {
9641 struct st_replace_regex* res;
9642 char* buf,*expr_end;
9643 char* p;
9644 char* buf_p;
9645 uint expr_len= strlen(expr);
9646 char last_c = 0;
9647 struct st_regex reg;
9648
9649 /* my_malloc() will die on fail with MY_FAE */
9650 res=(struct st_replace_regex*)my_malloc(
9651 sizeof(*res)+expr_len ,MYF(MY_FAE+MY_WME));
9652 my_init_dynamic_array(&res->regex_arr,sizeof(struct st_regex),128,128);
9653
9654 buf= (char*)res + sizeof(*res);
9655 expr_end= expr + expr_len;
9656 p= expr;
9657 buf_p= buf;
9658
9659 /* for each regexp substitution statement */
9660 while (p < expr_end)
9661 {
9662 memset(®, 0, sizeof(reg));
9663 /* find the start of the statement */
9664 while (p < expr_end)
9665 {
9666 if (*p == '/')
9667 break;
9668 p++;
9669 }
9670
9671 if (p == expr_end || ++p == expr_end)
9672 {
9673 if (res->regex_arr.elements)
9674 break;
9675 else
9676 goto err;
9677 }
9678 /* we found the start */
9679 reg.pattern= buf_p;
9680
9681 /* Find first argument -- pattern string to be removed */
9682 PARSE_REGEX_ARG
9683
9684 if (p == expr_end || ++p == expr_end)
9685 goto err;
9686
9687 /* buf_p now points to the replacement pattern terminated with \0 */
9688 reg.replace= buf_p;
9689
9690 /* Find second argument -- replace string to replace pattern */
9691 PARSE_REGEX_ARG
9692
9693 if (p == expr_end)
9694 goto err;
9695
9696 /* skip the ending '/' in the statement */
9697 p++;
9698
9699 /* Check if we should do matching case insensitive */
9700 if (p < expr_end && *p == 'i')
9701 reg.icase= 1;
9702
9703 /* done parsing the statement, now place it in regex_arr */
9704 if (insert_dynamic(&res->regex_arr, ®))
9705 die("Out of memory");
9706 }
9707 res->odd_buf_len= res->even_buf_len= 8192;
9708 res->even_buf= (char*)my_malloc(res->even_buf_len,MYF(MY_WME+MY_FAE));
9709 res->odd_buf= (char*)my_malloc(res->odd_buf_len,MYF(MY_WME+MY_FAE));
9710 res->buf= res->even_buf;
9711
9712 return res;
9713
9714 err:
9715 delete_dynamic(&res->regex_arr);
9716 my_free(res);
9717 die("Error parsing replace_regex \"%s\"", expr);
9718 return 0;
9719 }
9720
9721 /*
9722 Execute all substitutions on val.
9723
9724 Returns: true if substituition was made, false otherwise
9725 Side-effect: Sets r->buf to be the buffer with all substitutions done.
9726
9727 IN:
9728 struct st_replace_regex* r
9729 char* val
9730 Out:
9731 struct st_replace_regex* r
9732 r->buf points at the resulting buffer
9733 r->even_buf and r->odd_buf might have been reallocated
9734 r->even_buf_len and r->odd_buf_len might have been changed
9735
9736 TODO: at some point figure out if there is a way to do everything
9737 in one pass
9738 */
9739
multi_reg_replace(struct st_replace_regex * r,char * val)9740 int multi_reg_replace(struct st_replace_regex* r,char* val)
9741 {
9742 uint i;
9743 char* in_buf, *out_buf;
9744 int* buf_len_p;
9745
9746 in_buf= val;
9747 out_buf= r->even_buf;
9748 buf_len_p= &r->even_buf_len;
9749 r->buf= 0;
9750
9751 /* For each substitution, do the replace */
9752 for (i= 0; i < r->regex_arr.elements; i++)
9753 {
9754 struct st_regex re;
9755 char* save_out_buf= out_buf;
9756
9757 get_dynamic(&r->regex_arr,(uchar*)&re,i);
9758
9759 if (!reg_replace(&out_buf, buf_len_p, re.pattern, re.replace,
9760 in_buf, re.icase))
9761 {
9762 /* if the buffer has been reallocated, make adjustements */
9763 if (save_out_buf != out_buf)
9764 {
9765 if (save_out_buf == r->even_buf)
9766 r->even_buf= out_buf;
9767 else
9768 r->odd_buf= out_buf;
9769 }
9770
9771 r->buf= out_buf;
9772 if (in_buf == val)
9773 in_buf= r->odd_buf;
9774
9775 swap_variables(char*,in_buf,out_buf);
9776
9777 buf_len_p= (out_buf == r->even_buf) ? &r->even_buf_len :
9778 &r->odd_buf_len;
9779 }
9780 }
9781
9782 return (r->buf == 0);
9783 }
9784
9785 /*
9786 Parse the regular expression to be used in all result files
9787 from now on.
9788
9789 The syntax is --replace_regex /from/to/i /from/to/i ...
9790 i means case-insensitive match. If omitted, the match is
9791 case-sensitive
9792
9793 */
do_get_replace_regex(struct st_command * command)9794 void do_get_replace_regex(struct st_command *command)
9795 {
9796 char *expr= command->first_argument;
9797 free_replace_regex();
9798 /* Allow variable for the *entire* list of replacements */
9799 if (*expr == '$')
9800 {
9801 VAR *val= var_get(expr, NULL, 0, 1);
9802 expr= val ? val->str_val : NULL;
9803 }
9804 if (expr && *expr && !(glob_replace_regex=init_replace_regex(expr)))
9805 die("Could not init replace_regex");
9806 command->last_argument= command->end;
9807 }
9808
free_replace_regex()9809 void free_replace_regex()
9810 {
9811 if (glob_replace_regex)
9812 {
9813 delete_dynamic(&glob_replace_regex->regex_arr);
9814 my_free(glob_replace_regex->even_buf);
9815 my_free(glob_replace_regex->odd_buf);
9816 my_free(glob_replace_regex);
9817 glob_replace_regex=0;
9818 }
9819 }
9820
9821
9822
9823 /*
9824 auxiluary macro used by reg_replace
9825 makes sure the result buffer has sufficient length
9826 */
9827 #define SECURE_REG_BUF if (buf_len < need_buf_len) \
9828 { \
9829 int off= res_p - buf; \
9830 buf= (char*)my_realloc(buf,need_buf_len,MYF(MY_WME+MY_FAE)); \
9831 res_p= buf + off; \
9832 buf_len= need_buf_len; \
9833 } \
9834 \
9835 /*
9836 Performs a regex substitution
9837
9838 IN:
9839
9840 buf_p - result buffer pointer. Will change if reallocated
9841 buf_len_p - result buffer length. Will change if the buffer is reallocated
9842 pattern - regexp pattern to match
9843 replace - replacement expression
9844 string - the string to perform substituions in
9845 icase - flag, if set to 1 the match is case insensitive
9846 */
reg_replace(char ** buf_p,int * buf_len_p,char * pattern,char * replace,char * string,int icase)9847 int reg_replace(char** buf_p, int* buf_len_p, char *pattern,
9848 char *replace, char *string, int icase)
9849 {
9850 my_regex_t r;
9851 my_regmatch_t *subs;
9852 char *replace_end;
9853 char *buf= *buf_p;
9854 int len;
9855 int buf_len, need_buf_len;
9856 int cflags= MY_REG_EXTENDED;
9857 int err_code;
9858 char *res_p,*str_p,*str_end;
9859
9860 buf_len= *buf_len_p;
9861 len= strlen(string);
9862 str_end= string + len;
9863
9864 /* start with a buffer of a reasonable size that hopefully will not
9865 need to be reallocated
9866 */
9867 need_buf_len= len * 2 + 1;
9868 res_p= buf;
9869
9870 SECURE_REG_BUF
9871
9872 if (icase)
9873 cflags|= MY_REG_ICASE;
9874
9875 if ((err_code= my_regcomp(&r,pattern,cflags,&my_charset_latin1)))
9876 {
9877 check_regerr(&r,err_code);
9878 return 1;
9879 }
9880
9881 subs= (my_regmatch_t*)my_malloc(sizeof(my_regmatch_t) * (r.re_nsub+1),
9882 MYF(MY_WME+MY_FAE));
9883
9884 *res_p= 0;
9885 str_p= string;
9886 replace_end= replace + strlen(replace);
9887
9888 /* for each pattern match instance perform a replacement */
9889 while (!err_code)
9890 {
9891 /* find the match */
9892 err_code= my_regexec(&r,str_p, r.re_nsub+1, subs,
9893 (str_p == string) ? MY_REG_NOTBOL : 0);
9894
9895 /* if regular expression error (eg. bad syntax, or out of memory) */
9896 if (err_code && err_code != MY_REG_NOMATCH)
9897 {
9898 check_regerr(&r,err_code);
9899 my_regfree(&r);
9900 return 1;
9901 }
9902
9903 /* if match found */
9904 if (!err_code)
9905 {
9906 char* expr_p= replace;
9907 int c;
9908
9909 /*
9910 we need at least what we have so far in the buffer + the part
9911 before this match
9912 */
9913 need_buf_len= (res_p - buf) + (int) subs[0].rm_so;
9914
9915 /* on this pass, calculate the memory for the result buffer */
9916 while (expr_p < replace_end)
9917 {
9918 int back_ref_num= -1;
9919 c= *expr_p;
9920
9921 if (c == '\\' && expr_p + 1 < replace_end)
9922 {
9923 back_ref_num= (int) (expr_p[1] - '0');
9924 }
9925
9926 /* found a valid back_ref (eg. \1)*/
9927 if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub)
9928 {
9929 my_regoff_t start_off, end_off;
9930 if ((start_off=subs[back_ref_num].rm_so) > -1 &&
9931 (end_off=subs[back_ref_num].rm_eo) > -1)
9932 {
9933 need_buf_len += (int) (end_off - start_off);
9934 }
9935 expr_p += 2;
9936 }
9937 else
9938 {
9939 expr_p++;
9940 need_buf_len++;
9941 }
9942 }
9943 need_buf_len++;
9944 /*
9945 now that we know the size of the buffer,
9946 make sure it is big enough
9947 */
9948 SECURE_REG_BUF
9949
9950 /* copy the pre-match part */
9951 if (subs[0].rm_so)
9952 {
9953 memcpy(res_p, str_p, (size_t) subs[0].rm_so);
9954 res_p+= subs[0].rm_so;
9955 }
9956
9957 expr_p= replace;
9958
9959 /* copy the match and expand back_refs */
9960 while (expr_p < replace_end)
9961 {
9962 int back_ref_num= -1;
9963 c= *expr_p;
9964
9965 if (c == '\\' && expr_p + 1 < replace_end)
9966 {
9967 back_ref_num= expr_p[1] - '0';
9968 }
9969
9970 if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub)
9971 {
9972 my_regoff_t start_off, end_off;
9973 if ((start_off=subs[back_ref_num].rm_so) > -1 &&
9974 (end_off=subs[back_ref_num].rm_eo) > -1)
9975 {
9976 int block_len= (int) (end_off - start_off);
9977 memcpy(res_p,str_p + start_off, block_len);
9978 res_p += block_len;
9979 }
9980 expr_p += 2;
9981 }
9982 else
9983 {
9984 *res_p++ = *expr_p++;
9985 }
9986 }
9987
9988 /* handle the post-match part */
9989 if (subs[0].rm_so == subs[0].rm_eo)
9990 {
9991 if (str_p + subs[0].rm_so >= str_end)
9992 break;
9993 str_p += subs[0].rm_eo ;
9994 *res_p++ = *str_p++;
9995 }
9996 else
9997 {
9998 str_p += subs[0].rm_eo;
9999 }
10000 }
10001 else /* no match this time, just copy the string as is */
10002 {
10003 int left_in_str= str_end-str_p;
10004 need_buf_len= (res_p-buf) + left_in_str;
10005 SECURE_REG_BUF
10006 memcpy(res_p,str_p,left_in_str);
10007 res_p += left_in_str;
10008 str_p= str_end;
10009 }
10010 }
10011 my_free(subs);
10012 my_regfree(&r);
10013 *res_p= 0;
10014 *buf_p= buf;
10015 *buf_len_p= buf_len;
10016 return 0;
10017 }
10018
10019
10020 #ifndef WORD_BIT
10021 #define WORD_BIT (8*sizeof(uint))
10022 #endif
10023
10024 #define SET_MALLOC_HUNC 64
10025 #define LAST_CHAR_CODE 259
10026
10027 typedef struct st_rep_set {
10028 uint *bits; /* Pointer to used sets */
10029 short next[LAST_CHAR_CODE]; /* Pointer to next sets */
10030 uint found_len; /* Best match to date */
10031 int found_offset;
10032 uint table_offset;
10033 uint size_of_bits; /* For convinience */
10034 } REP_SET;
10035
10036 typedef struct st_rep_sets {
10037 uint count; /* Number of sets */
10038 uint extra; /* Extra sets in buffer */
10039 uint invisible; /* Sets not chown */
10040 uint size_of_bits;
10041 REP_SET *set,*set_buffer;
10042 uint *bit_buffer;
10043 } REP_SETS;
10044
10045 typedef struct st_found_set {
10046 uint table_offset;
10047 int found_offset;
10048 } FOUND_SET;
10049
10050 typedef struct st_follow {
10051 int chr;
10052 uint table_offset;
10053 uint len;
10054 } FOLLOWS;
10055
10056
10057 int init_sets(REP_SETS *sets,uint states);
10058 REP_SET *make_new_set(REP_SETS *sets);
10059 void make_sets_invisible(REP_SETS *sets);
10060 void free_last_set(REP_SETS *sets);
10061 void free_sets(REP_SETS *sets);
10062 void internal_set_bit(REP_SET *set, uint bit);
10063 void internal_clear_bit(REP_SET *set, uint bit);
10064 void or_bits(REP_SET *to,REP_SET *from);
10065 void copy_bits(REP_SET *to,REP_SET *from);
10066 int cmp_bits(REP_SET *set1,REP_SET *set2);
10067 int get_next_bit(REP_SET *set,uint lastpos);
10068 int find_set(REP_SETS *sets,REP_SET *find);
10069 int find_found(FOUND_SET *found_set,uint table_offset,
10070 int found_offset);
10071 uint start_at_word(char * pos);
10072 uint end_of_word(char * pos);
10073
10074 static uint found_sets=0;
10075
10076
replace_len(char * str)10077 uint replace_len(char * str)
10078 {
10079 uint len=0;
10080 while (*str)
10081 {
10082 str++;
10083 len++;
10084 }
10085 return len;
10086 }
10087
10088 /* Init a replace structure for further calls */
10089
init_replace(char ** from,char ** to,uint count,char * word_end_chars)10090 REPLACE *init_replace(char * *from, char * *to,uint count,
10091 char * word_end_chars)
10092 {
10093 static const int SPACE_CHAR= 256;
10094 static const int END_OF_LINE= 258;
10095
10096 uint i,j,states,set_nr,len,result_len,max_length,found_end,bits_set,bit_nr;
10097 int used_sets,chr,default_state;
10098 char used_chars[LAST_CHAR_CODE],is_word_end[256];
10099 char * pos, *to_pos, **to_array;
10100 REP_SETS sets;
10101 REP_SET *set,*start_states,*word_states,*new_set;
10102 FOLLOWS *follow,*follow_ptr;
10103 REPLACE *replace;
10104 FOUND_SET *found_set;
10105 REPLACE_STRING *rep_str;
10106 DBUG_ENTER("init_replace");
10107
10108 /* Count number of states */
10109 for (i=result_len=max_length=0 , states=2 ; i < count ; i++)
10110 {
10111 len=replace_len(from[i]);
10112 if (!len)
10113 {
10114 errno=EINVAL;
10115 DBUG_RETURN(0);
10116 }
10117 states+=len+1;
10118 result_len+=(uint) strlen(to[i])+1;
10119 if (len > max_length)
10120 max_length=len;
10121 }
10122 memset(is_word_end, 0, sizeof(is_word_end));
10123 for (i=0 ; word_end_chars[i] ; i++)
10124 is_word_end[(uchar) word_end_chars[i]]=1;
10125
10126 if (init_sets(&sets,states))
10127 DBUG_RETURN(0);
10128 found_sets=0;
10129 if (!(found_set= (FOUND_SET*) my_malloc(sizeof(FOUND_SET)*max_length*count,
10130 MYF(MY_WME))))
10131 {
10132 free_sets(&sets);
10133 DBUG_RETURN(0);
10134 }
10135 (void) make_new_set(&sets); /* Set starting set */
10136 make_sets_invisible(&sets); /* Hide previus sets */
10137 used_sets=-1;
10138 word_states=make_new_set(&sets); /* Start of new word */
10139 start_states=make_new_set(&sets); /* This is first state */
10140 if (!(follow=(FOLLOWS*) my_malloc((states+2)*sizeof(FOLLOWS),MYF(MY_WME))))
10141 {
10142 free_sets(&sets);
10143 my_free(found_set);
10144 DBUG_RETURN(0);
10145 }
10146
10147 /* Init follow_ptr[] */
10148 for (i=0, states=1, follow_ptr=follow+1 ; i < count ; i++)
10149 {
10150 if (from[i][0] == '\\' && from[i][1] == '^')
10151 {
10152 internal_set_bit(start_states,states+1);
10153 if (!from[i][2])
10154 {
10155 start_states->table_offset=i;
10156 start_states->found_offset=1;
10157 }
10158 }
10159 else if (from[i][0] == '\\' && from[i][1] == '$')
10160 {
10161 internal_set_bit(start_states,states);
10162 internal_set_bit(word_states,states);
10163 if (!from[i][2] && start_states->table_offset == (uint) ~0)
10164 {
10165 start_states->table_offset=i;
10166 start_states->found_offset=0;
10167 }
10168 }
10169 else
10170 {
10171 internal_set_bit(word_states,states);
10172 if (from[i][0] == '\\' && (from[i][1] == 'b' && from[i][2]))
10173 internal_set_bit(start_states,states+1);
10174 else
10175 internal_set_bit(start_states,states);
10176 }
10177 for (pos=from[i], len=0; *pos ; pos++)
10178 {
10179 follow_ptr->chr= (uchar) *pos;
10180 follow_ptr->table_offset=i;
10181 follow_ptr->len= ++len;
10182 follow_ptr++;
10183 }
10184 follow_ptr->chr=0;
10185 follow_ptr->table_offset=i;
10186 follow_ptr->len=len;
10187 follow_ptr++;
10188 states+=(uint) len+1;
10189 }
10190
10191
10192 for (set_nr=0,pos=0 ; set_nr < sets.count ; set_nr++)
10193 {
10194 set=sets.set+set_nr;
10195 default_state= 0; /* Start from beginning */
10196
10197 /* If end of found-string not found or start-set with current set */
10198
10199 for (i= (uint) ~0; (i=get_next_bit(set,i)) ;)
10200 {
10201 if (!follow[i].chr)
10202 {
10203 if (! default_state)
10204 default_state= find_found(found_set,set->table_offset,
10205 set->found_offset+1);
10206 }
10207 }
10208 copy_bits(sets.set+used_sets,set); /* Save set for changes */
10209 if (!default_state)
10210 or_bits(sets.set+used_sets,sets.set); /* Can restart from start */
10211
10212 /* Find all chars that follows current sets */
10213 memset(used_chars, 0, sizeof(used_chars));
10214 for (i= (uint) ~0; (i=get_next_bit(sets.set+used_sets,i)) ;)
10215 {
10216 used_chars[follow[i].chr]=1;
10217 if ((follow[i].chr == SPACE_CHAR && !follow[i+1].chr &&
10218 follow[i].len > 1) || follow[i].chr == END_OF_LINE)
10219 used_chars[0]=1;
10220 }
10221
10222 /* Mark word_chars used if \b is in state */
10223 if (used_chars[SPACE_CHAR])
10224 for (pos= word_end_chars ; *pos ; pos++)
10225 used_chars[(int) (uchar) *pos] = 1;
10226
10227 /* Handle other used characters */
10228 for (chr= 0 ; chr < 256 ; chr++)
10229 {
10230 if (! used_chars[chr])
10231 set->next[chr]= chr ? default_state : -1;
10232 else
10233 {
10234 new_set=make_new_set(&sets);
10235 assert(new_set);
10236 set=sets.set+set_nr; /* if realloc */
10237 new_set->table_offset=set->table_offset;
10238 new_set->found_len=set->found_len;
10239 new_set->found_offset=set->found_offset+1;
10240 found_end=0;
10241
10242 for (i= (uint) ~0 ; (i=get_next_bit(sets.set+used_sets,i)) ; )
10243 {
10244 if (!follow[i].chr || follow[i].chr == chr ||
10245 (follow[i].chr == SPACE_CHAR &&
10246 (is_word_end[chr] ||
10247 (!chr && follow[i].len > 1 && ! follow[i+1].chr))) ||
10248 (follow[i].chr == END_OF_LINE && ! chr))
10249 {
10250 if ((! chr || (follow[i].chr && !follow[i+1].chr)) &&
10251 follow[i].len > found_end)
10252 found_end=follow[i].len;
10253 if (chr && follow[i].chr)
10254 internal_set_bit(new_set,i+1); /* To next set */
10255 else
10256 internal_set_bit(new_set,i);
10257 }
10258 }
10259 if (found_end)
10260 {
10261 new_set->found_len=0; /* Set for testing if first */
10262 bits_set=0;
10263 for (i= (uint) ~0; (i=get_next_bit(new_set,i)) ;)
10264 {
10265 if ((follow[i].chr == SPACE_CHAR ||
10266 follow[i].chr == END_OF_LINE) && ! chr)
10267 bit_nr=i+1;
10268 else
10269 bit_nr=i;
10270 if (follow[bit_nr-1].len < found_end ||
10271 (new_set->found_len &&
10272 (chr == 0 || !follow[bit_nr].chr)))
10273 internal_clear_bit(new_set,i);
10274 else
10275 {
10276 if (chr == 0 || !follow[bit_nr].chr)
10277 { /* best match */
10278 new_set->table_offset=follow[bit_nr].table_offset;
10279 if (chr || (follow[i].chr == SPACE_CHAR ||
10280 follow[i].chr == END_OF_LINE))
10281 new_set->found_offset=found_end; /* New match */
10282 new_set->found_len=found_end;
10283 }
10284 bits_set++;
10285 }
10286 }
10287 if (bits_set == 1)
10288 {
10289 set->next[chr] = find_found(found_set,
10290 new_set->table_offset,
10291 new_set->found_offset);
10292 free_last_set(&sets);
10293 }
10294 else
10295 set->next[chr] = find_set(&sets,new_set);
10296 }
10297 else
10298 set->next[chr] = find_set(&sets,new_set);
10299 }
10300 }
10301 }
10302
10303 /* Alloc replace structure for the replace-state-machine */
10304
10305 if ((replace=(REPLACE*) my_malloc(sizeof(REPLACE)*(sets.count)+
10306 sizeof(REPLACE_STRING)*(found_sets+1)+
10307 sizeof(char *)*count+result_len,
10308 MYF(MY_WME | MY_ZEROFILL))))
10309 {
10310 rep_str=(REPLACE_STRING*) (replace+sets.count);
10311 to_array= (char **) (rep_str+found_sets+1);
10312 to_pos=(char *) (to_array+count);
10313 for (i=0 ; i < count ; i++)
10314 {
10315 to_array[i]=to_pos;
10316 to_pos=strmov(to_pos,to[i])+1;
10317 }
10318 rep_str[0].found=1;
10319 rep_str[0].replace_string=0;
10320 for (i=1 ; i <= found_sets ; i++)
10321 {
10322 pos=from[found_set[i-1].table_offset];
10323 rep_str[i].found= !memcmp(pos, "\\^", 3) ? 2 : 1;
10324 rep_str[i].replace_string=to_array[found_set[i-1].table_offset];
10325 rep_str[i].to_offset=found_set[i-1].found_offset-start_at_word(pos);
10326 rep_str[i].from_offset=found_set[i-1].found_offset-replace_len(pos)+
10327 end_of_word(pos);
10328 }
10329 for (i=0 ; i < sets.count ; i++)
10330 {
10331 for (j=0 ; j < 256 ; j++)
10332 if (sets.set[i].next[j] >= 0)
10333 replace[i].next[j]=replace+sets.set[i].next[j];
10334 else
10335 replace[i].next[j]=(REPLACE*) (rep_str+(-sets.set[i].next[j]-1));
10336 }
10337 }
10338 my_free(follow);
10339 free_sets(&sets);
10340 my_free(found_set);
10341 DBUG_PRINT("exit",("Replace table has %d states",sets.count));
10342 DBUG_RETURN(replace);
10343 }
10344
10345
init_sets(REP_SETS * sets,uint states)10346 int init_sets(REP_SETS *sets,uint states)
10347 {
10348 memset(sets, 0, sizeof(*sets));
10349 sets->size_of_bits=((states+7)/8);
10350 if (!(sets->set_buffer=(REP_SET*) my_malloc(sizeof(REP_SET)*SET_MALLOC_HUNC,
10351 MYF(MY_WME))))
10352 return 1;
10353 if (!(sets->bit_buffer=(uint*) my_malloc(sizeof(uint)*sets->size_of_bits*
10354 SET_MALLOC_HUNC,MYF(MY_WME))))
10355 {
10356 my_free(sets->set);
10357 return 1;
10358 }
10359 return 0;
10360 }
10361
10362 /* Make help sets invisible for nicer codeing */
10363
make_sets_invisible(REP_SETS * sets)10364 void make_sets_invisible(REP_SETS *sets)
10365 {
10366 sets->invisible=sets->count;
10367 sets->set+=sets->count;
10368 sets->count=0;
10369 }
10370
make_new_set(REP_SETS * sets)10371 REP_SET *make_new_set(REP_SETS *sets)
10372 {
10373 uint i,count,*bit_buffer;
10374 REP_SET *set;
10375 if (sets->extra)
10376 {
10377 sets->extra--;
10378 set=sets->set+ sets->count++;
10379 memset(set->bits, 0, sizeof(uint)*sets->size_of_bits);
10380 memset(&set->next[0], 0, sizeof(set->next[0])*LAST_CHAR_CODE);
10381 set->found_offset=0;
10382 set->found_len=0;
10383 set->table_offset= (uint) ~0;
10384 set->size_of_bits=sets->size_of_bits;
10385 return set;
10386 }
10387 count=sets->count+sets->invisible+SET_MALLOC_HUNC;
10388 if (!(set=(REP_SET*) my_realloc((uchar*) sets->set_buffer,
10389 sizeof(REP_SET)*count,
10390 MYF(MY_WME))))
10391 return 0;
10392 sets->set_buffer=set;
10393 sets->set=set+sets->invisible;
10394 if (!(bit_buffer=(uint*) my_realloc((uchar*) sets->bit_buffer,
10395 (sizeof(uint)*sets->size_of_bits)*count,
10396 MYF(MY_WME))))
10397 return 0;
10398 sets->bit_buffer=bit_buffer;
10399 for (i=0 ; i < count ; i++)
10400 {
10401 sets->set_buffer[i].bits=bit_buffer;
10402 bit_buffer+=sets->size_of_bits;
10403 }
10404 sets->extra=SET_MALLOC_HUNC;
10405 return make_new_set(sets);
10406 }
10407
free_last_set(REP_SETS * sets)10408 void free_last_set(REP_SETS *sets)
10409 {
10410 sets->count--;
10411 sets->extra++;
10412 return;
10413 }
10414
free_sets(REP_SETS * sets)10415 void free_sets(REP_SETS *sets)
10416 {
10417 my_free(sets->set_buffer);
10418 my_free(sets->bit_buffer);
10419 return;
10420 }
10421
internal_set_bit(REP_SET * set,uint bit)10422 void internal_set_bit(REP_SET *set, uint bit)
10423 {
10424 set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT);
10425 return;
10426 }
10427
internal_clear_bit(REP_SET * set,uint bit)10428 void internal_clear_bit(REP_SET *set, uint bit)
10429 {
10430 set->bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT));
10431 return;
10432 }
10433
10434
or_bits(REP_SET * to,REP_SET * from)10435 void or_bits(REP_SET *to,REP_SET *from)
10436 {
10437 uint i;
10438 for (i=0 ; i < to->size_of_bits ; i++)
10439 to->bits[i]|=from->bits[i];
10440 return;
10441 }
10442
copy_bits(REP_SET * to,REP_SET * from)10443 void copy_bits(REP_SET *to,REP_SET *from)
10444 {
10445 memcpy((uchar*) to->bits,(uchar*) from->bits,
10446 (size_t) (sizeof(uint) * to->size_of_bits));
10447 }
10448
cmp_bits(REP_SET * set1,REP_SET * set2)10449 int cmp_bits(REP_SET *set1,REP_SET *set2)
10450 {
10451 return memcmp(set1->bits, set2->bits,
10452 sizeof(uint) * set1->size_of_bits);
10453 }
10454
10455
10456 /* Get next set bit from set. */
10457
get_next_bit(REP_SET * set,uint lastpos)10458 int get_next_bit(REP_SET *set,uint lastpos)
10459 {
10460 uint pos,*start,*end,bits;
10461
10462 start=set->bits+ ((lastpos+1) / WORD_BIT);
10463 end=set->bits + set->size_of_bits;
10464 bits=start[0] & ~((1 << ((lastpos+1) % WORD_BIT)) -1);
10465
10466 while (! bits && ++start < end)
10467 bits=start[0];
10468 if (!bits)
10469 return 0;
10470 pos=(uint) (start-set->bits)*WORD_BIT;
10471 while (! (bits & 1))
10472 {
10473 bits>>=1;
10474 pos++;
10475 }
10476 return pos;
10477 }
10478
10479 /* find if there is a same set in sets. If there is, use it and
10480 free given set, else put in given set in sets and return its
10481 position */
10482
find_set(REP_SETS * sets,REP_SET * find)10483 int find_set(REP_SETS *sets,REP_SET *find)
10484 {
10485 uint i;
10486 for (i=0 ; i < sets->count-1 ; i++)
10487 {
10488 if (!cmp_bits(sets->set+i,find))
10489 {
10490 free_last_set(sets);
10491 return i;
10492 }
10493 }
10494 return i; /* return new position */
10495 }
10496
10497 /* find if there is a found_set with same table_offset & found_offset
10498 If there is return offset to it, else add new offset and return pos.
10499 Pos returned is -offset-2 in found_set_structure because it is
10500 saved in set->next and set->next[] >= 0 points to next set and
10501 set->next[] == -1 is reserved for end without replaces.
10502 */
10503
find_found(FOUND_SET * found_set,uint table_offset,int found_offset)10504 int find_found(FOUND_SET *found_set,uint table_offset, int found_offset)
10505 {
10506 int i;
10507 for (i=0 ; (uint) i < found_sets ; i++)
10508 if (found_set[i].table_offset == table_offset &&
10509 found_set[i].found_offset == found_offset)
10510 return -i-2;
10511 found_set[i].table_offset=table_offset;
10512 found_set[i].found_offset=found_offset;
10513 found_sets++;
10514 return -i-2; /* return new position */
10515 }
10516
10517 /* Return 1 if regexp starts with \b or ends with \b*/
10518
start_at_word(char * pos)10519 uint start_at_word(char * pos)
10520 {
10521 return (((!memcmp(pos, "\\b",2) && pos[2]) ||
10522 !memcmp(pos, "\\^", 2)) ? 1 : 0);
10523 }
10524
end_of_word(char * pos)10525 uint end_of_word(char * pos)
10526 {
10527 char * end=strend(pos);
10528 return ((end > pos+2 && !memcmp(end-2, "\\b", 2)) ||
10529 (end >= pos+2 && !memcmp(end-2, "\\$",2))) ? 1 : 0;
10530 }
10531
10532 /****************************************************************************
10533 * Handle replacement of strings
10534 ****************************************************************************/
10535
10536 #define PC_MALLOC 256 /* Bytes for pointers */
10537 #define PS_MALLOC 512 /* Bytes for data */
10538
insert_pointer_name(POINTER_ARRAY * pa,char * name)10539 int insert_pointer_name(POINTER_ARRAY *pa,char * name)
10540 {
10541 uint i,length,old_count;
10542 uchar *new_pos;
10543 const char **new_array;
10544 DBUG_ENTER("insert_pointer_name");
10545
10546 if (! pa->typelib.count)
10547 {
10548 if (!(pa->typelib.type_names=(const char **)
10549 my_malloc(((PC_MALLOC-MALLOC_OVERHEAD)/
10550 (sizeof(char *)+sizeof(*pa->flag))*
10551 (sizeof(char *)+sizeof(*pa->flag))),MYF(MY_WME | MY_ZEROFILL))))
10552 DBUG_RETURN(-1);
10553 if (!(pa->str= (uchar*) my_malloc((uint) (PS_MALLOC-MALLOC_OVERHEAD),
10554 MYF(MY_WME | MY_ZEROFILL))))
10555 {
10556 my_free(pa->typelib.type_names);
10557 DBUG_RETURN (-1);
10558 }
10559 pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(uchar*)+
10560 sizeof(*pa->flag));
10561 pa->flag= (uint8*) (pa->typelib.type_names+pa->max_count);
10562 pa->length=0;
10563 pa->max_length=PS_MALLOC-MALLOC_OVERHEAD;
10564 pa->array_allocs=1;
10565 }
10566 length=(uint) strlen(name)+1;
10567 if (pa->length+length >= pa->max_length)
10568 {
10569 if (!(new_pos= (uchar*) my_realloc((uchar*) pa->str,
10570 (uint) (pa->length+length+PS_MALLOC),
10571 MYF(MY_WME))))
10572 DBUG_RETURN(1);
10573 if (new_pos != pa->str)
10574 {
10575 my_ptrdiff_t diff=PTR_BYTE_DIFF(new_pos,pa->str);
10576 for (i=0 ; i < pa->typelib.count ; i++)
10577 pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff,
10578 char*);
10579 pa->str=new_pos;
10580 }
10581 pa->max_length= pa->length+length+PS_MALLOC;
10582 }
10583 if (pa->typelib.count >= pa->max_count-1)
10584 {
10585 int len;
10586 pa->array_allocs++;
10587 len=(PC_MALLOC*pa->array_allocs - MALLOC_OVERHEAD);
10588 if (!(new_array=(const char **) my_realloc((uchar*) pa->typelib.type_names,
10589 (uint) len/
10590 (sizeof(uchar*)+sizeof(*pa->flag))*
10591 (sizeof(uchar*)+sizeof(*pa->flag)),
10592 MYF(MY_WME))))
10593 DBUG_RETURN(1);
10594 pa->typelib.type_names=new_array;
10595 old_count=pa->max_count;
10596 pa->max_count=len/(sizeof(uchar*) + sizeof(*pa->flag));
10597 pa->flag= (uint8*) (pa->typelib.type_names+pa->max_count);
10598 memcpy((uchar*) pa->flag,(char *) (pa->typelib.type_names+old_count),
10599 old_count*sizeof(*pa->flag));
10600 }
10601 pa->flag[pa->typelib.count]=0; /* Reset flag */
10602 pa->typelib.type_names[pa->typelib.count++]= (char*) pa->str+pa->length;
10603 pa->typelib.type_names[pa->typelib.count]= NullS; /* Put end-mark */
10604 (void) strmov((char*) pa->str+pa->length,name);
10605 pa->length+=length;
10606 DBUG_RETURN(0);
10607 } /* insert_pointer_name */
10608
10609
10610 /* free pointer array */
10611
free_pointer_array(POINTER_ARRAY * pa)10612 void free_pointer_array(POINTER_ARRAY *pa)
10613 {
10614 if (pa->typelib.count)
10615 {
10616 pa->typelib.count=0;
10617 my_free(pa->typelib.type_names);
10618 pa->typelib.type_names=0;
10619 my_free(pa->str);
10620 }
10621 } /* free_pointer_array */
10622
10623
10624 /* Functions that uses replace and replace_regex */
10625
10626 /* Append the string to ds, with optional replace */
replace_dynstr_append_mem(DYNAMIC_STRING * ds,const char * val,int len)10627 void replace_dynstr_append_mem(DYNAMIC_STRING *ds,
10628 const char *val, int len)
10629 {
10630 char lower[512];
10631 #ifdef __WIN__
10632 fix_win_paths(val, len);
10633 #endif
10634
10635 if (display_result_lower)
10636 {
10637 /* Convert to lower case, and do this first */
10638 char *c= lower;
10639 for (const char *v= val; *v; v++)
10640 *c++= my_tolower(charset_info, *v);
10641 *c= '\0';
10642 /* Copy from this buffer instead */
10643 val= lower;
10644 }
10645
10646 if (glob_replace_regex)
10647 {
10648 /* Regex replace */
10649 if (!multi_reg_replace(glob_replace_regex, (char*)val))
10650 {
10651 val= glob_replace_regex->buf;
10652 len= strlen(val);
10653 }
10654 }
10655
10656 if (glob_replace)
10657 {
10658 /* Normal replace */
10659 replace_strings_append(glob_replace, ds, val, len);
10660 }
10661 else
10662 dynstr_append_mem(ds, val, len);
10663 }
10664
10665
10666 /* Append zero-terminated string to ds, with optional replace */
replace_dynstr_append(DYNAMIC_STRING * ds,const char * val)10667 void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val)
10668 {
10669 replace_dynstr_append_mem(ds, val, strlen(val));
10670 }
10671
10672 /* Append uint to ds, with optional replace */
replace_dynstr_append_uint(DYNAMIC_STRING * ds,uint val)10673 void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val)
10674 {
10675 char buff[22]; /* This should be enough for any int */
10676 char *end= longlong10_to_str(val, buff, 10);
10677 replace_dynstr_append_mem(ds, buff, end - buff);
10678 }
10679
10680
10681
10682 /*
10683 Build a list of pointer to each line in ds_input, sort
10684 the list and use the sorted list to append the strings
10685 sorted to the output ds
10686
10687 SYNOPSIS
10688 dynstr_append_sorted
10689 ds - string where the sorted output will be appended
10690 ds_input - string to be sorted
10691
10692 */
10693
comp_lines(const char ** a,const char ** b)10694 static int comp_lines(const char **a, const char **b)
10695 {
10696 return (strcmp(*a,*b));
10697 }
10698
dynstr_append_sorted(DYNAMIC_STRING * ds,DYNAMIC_STRING * ds_input)10699 void dynstr_append_sorted(DYNAMIC_STRING* ds, DYNAMIC_STRING *ds_input)
10700 {
10701 unsigned i;
10702 char *start= ds_input->str;
10703 DYNAMIC_ARRAY lines;
10704 DBUG_ENTER("dynstr_append_sorted");
10705
10706 if (!*start)
10707 DBUG_VOID_RETURN; /* No input */
10708
10709 my_init_dynamic_array(&lines, sizeof(const char*), 32, 32);
10710
10711 /* First line is result header, skip past it */
10712 while (*start && *start != '\n')
10713 start++;
10714 start++; /* Skip past \n */
10715 dynstr_append_mem(ds, ds_input->str, start - ds_input->str);
10716
10717 /* Insert line(s) in array */
10718 while (*start)
10719 {
10720 char* line_end= (char*)start;
10721
10722 /* Find end of line */
10723 while (*line_end && *line_end != '\n')
10724 line_end++;
10725 *line_end= 0;
10726
10727 /* Insert pointer to the line in array */
10728 if (insert_dynamic(&lines, &start))
10729 die("Out of memory inserting lines to sort");
10730
10731 start= line_end+1;
10732 }
10733
10734 /* Sort array */
10735 qsort(lines.buffer, lines.elements,
10736 sizeof(char**), (qsort_cmp)comp_lines);
10737
10738 /* Create new result */
10739 for (i= 0; i < lines.elements ; i++)
10740 {
10741 const char **line= dynamic_element(&lines, i, const char**);
10742 dynstr_append(ds, *line);
10743 dynstr_append(ds, "\n");
10744 }
10745
10746 delete_dynamic(&lines);
10747 DBUG_VOID_RETURN;
10748 }
10749
10750 #ifndef HAVE_SETENV
setenv(const char * name,const char * value,int overwrite)10751 static int setenv(const char *name, const char *value, int overwrite)
10752 {
10753 size_t buflen= strlen(name) + strlen(value) + 2;
10754 char *envvar= (char *)malloc(buflen);
10755 if(!envvar)
10756 return ENOMEM;
10757 strcpy(envvar, name);
10758 strcat(envvar, "=");
10759 strcat(envvar, value);
10760 putenv(envvar);
10761 return 0;
10762 }
10763 #endif
10764