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