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(&reg, 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, &reg))
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