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