Drizzled Public API Documentation

drizzletest.cc
1 /* - mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3  *
4  * Copyright (C) 2010 Vijay Samuel
5  * Copyright (C) 2008 MySQL
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 /*
23  drizzletest
24 
25  Tool used for executing a .test file
26 
27  See the "MySQL Test framework manual" for more information
28  http://dev.mysql.com/doc/mysqltest/en/index.html
29 
30  Please keep the test framework tools identical in all versions!
31 
32  Written by:
33  Sasha Pachev <sasha@mysql.com>
34  Matt Wagner <matt@mysql.com>
35  Monty
36  Jani
37  Holyfoot
38 */
39 
40 #define MTEST_VERSION "3.3"
41 
42 #include <config.h>
43 #include <client/get_password.h>
44 #include <libdrizzle-2.0/libdrizzle.hpp>
45 
46 #include <queue>
47 #include <map>
48 #include <string>
49 #include <sstream>
50 #include <fstream>
51 #include <iostream>
52 #include <vector>
53 #include <algorithm>
54 #ifdef HAVE_SYS_WAIT_H
55 #include <sys/wait.h>
56 #endif
57 #include <cassert>
58 #include <sys/stat.h>
59 #include <sys/types.h>
60 #include <fcntl.h>
61 #include <boost/array.hpp>
62 #include <boost/foreach.hpp>
63 #include <boost/program_options.hpp>
64 #include <boost/smart_ptr.hpp>
65 
66 #include PCRE_HEADER
67 
68 #include <stdarg.h>
69 #include <boost/unordered_map.hpp>
70 
71 /* Added this for string translation. */
72 #include <drizzled/gettext.h>
73 
74 #include <drizzled/definitions.h>
75 #include <drizzled/internal/my_sys.h>
76 #include <drizzled/type/time.h>
77 #include <drizzled/charset.h>
78 #include <drizzled/typelib.h>
79 #include <drizzled/configmake.h>
80 #include <drizzled/util/find_ptr.h>
81 
82 #define PTR_BYTE_DIFF(A,B) (ptrdiff_t) (reinterpret_cast<const unsigned char*>(A) - reinterpret_cast<const unsigned char*>(B))
83 
84 namespace po= boost::program_options;
85 using namespace std;
86 using namespace drizzled;
87 
88 unsigned char *get_var_key(const unsigned char* var, size_t *len, bool);
89 
90 int get_one_option(int optid, const struct option *, char *argument);
91 
92 #define MAX_VAR_NAME_LENGTH 256
93 #define MAX_COLUMNS 256
94 #define MAX_DELIMITER_LENGTH 16
95 /* Flags controlling send and reap */
96 #define QUERY_SEND_FLAG 1
97 #define QUERY_REAP_FLAG 2
98 
99 typedef boost::unordered_map<std::string, uint32_t> ErrorCodes;
100 ErrorCodes global_error_names;
101 
102 enum {
103  OPT_PS_PROTOCOL, OPT_SP_PROTOCOL, OPT_CURSOR_PROTOCOL, OPT_VIEW_PROTOCOL,
104  OPT_MAX_CONNECT_RETRIES, OPT_MARK_PROGRESS, OPT_LOG_DIR, OPT_TAIL_LINES,
105  OPT_TESTDIR
106 };
107 
108 static int record= 0, opt_sleep= -1;
109 static char *opt_pass= NULL;
110 const char *unix_sock= NULL;
111 static uint32_t opt_port= 0;
112 static uint32_t opt_max_connect_retries;
113 static bool silent= false, verbose= false;
114 static bool opt_mark_progress= false;
115 static bool parsing_disabled= false;
116 static bool display_result_vertically= false,
117  display_metadata= false, display_result_sorted= false;
118 static bool disable_query_log= false, disable_result_log= false;
119 static bool disable_warnings= false;
120 static bool disable_info= true;
121 static bool abort_on_error= true;
122 static bool server_initialized= false;
123 static bool is_windows= false;
124 static bool use_drizzle_protocol= false;
125 static char line_buffer[MAX_DELIMITER_LENGTH], *line_buffer_pos= line_buffer;
126 static void free_all_replace();
127 
128 std::string opt_basedir,
129  opt_charsets_dir,
130  opt_db,
131  opt_host,
132  opt_include,
133  opt_testdir,
134  opt_logdir,
135  password,
136  opt_password,
137  result_file_name,
138  opt_user,
139  opt_protocol;
140 
141 static uint32_t start_lineno= 0; /* Start line of current command */
142 
143 /* Number of lines of the result to include in failure report */
144 static uint32_t opt_tail_lines= 0;
145 
146 static char delimiter[MAX_DELIMITER_LENGTH]= ";";
147 static uint32_t delimiter_length= 1;
148 
149 static char TMPDIR[FN_REFLEN];
150 
151 /* Block stack */
152 enum block_cmd {
153  cmd_none,
154  cmd_if,
155  cmd_while
156 };
157 
158 struct st_block
159 {
160  int line; /* Start line of block */
161  bool ok; /* Should block be executed */
162  enum block_cmd cmd; /* Command owning the block */
163 };
164 
165 static struct st_block block_stack[32];
166 static struct st_block *cur_block, *block_stack_end;
167 
168 /* Open file stack */
170 {
171  FILE* file;
172  const char *file_name;
173  uint32_t lineno; /* Current line in file */
174 };
175 
176 static boost::array<st_test_file, 16> file_stack;
177 static st_test_file* cur_file;
178 
179 static const charset_info_st *charset_info= &my_charset_utf8_general_ci; /* Default charset */
180 
181 /*
182  Timer related variables
183  See the timer_output() definition for details
184 */
185 static char *timer_file = NULL;
186 static uint64_t timer_start;
187 static void timer_output();
188 static uint64_t timer_now();
189 
190 static uint64_t progress_start= 0;
191 
192 vector<struct st_command*> q_lines;
193 
194 struct parser_st
195 {
196  int read_lines;
197  int current_line;
198 } parser;
199 
201 {
202  char file[FN_REFLEN];
203  uint32_t pos;
204 };
205 
206 master_pos_st master_pos;
207 
208 /* if set, all results are concated and compared against this file */
209 
210 class VAR
211 {
212 public:
213  char *name;
214  int name_len;
215  char *str_val;
216  int str_val_len;
217  int int_val;
218  int alloced_len;
219  int int_dirty; /* do not update string if int is updated until first read */
220  int alloced;
221  char *env_s;
222 };
223 
224 /*Perl/shell-like variable registers */
225 boost::array<VAR, 10> var_reg;
226 
227 typedef boost::unordered_map<string, VAR *> var_hash_t;
228 var_hash_t var_hash;
229 
231 {
232 public:
233  st_connection() : con(drizzle)
234  {
235  drizzle_con_add_options(*this, use_drizzle_protocol ? DRIZZLE_CON_EXPERIMENTAL : DRIZZLE_CON_MYSQL);
236  }
237 
238  operator drizzle::connection_c&()
239  {
240  return con;
241  }
242 
243  operator drizzle_con_st*()
244  {
245  return con;
246  }
247 
248  drizzle::drizzle_c drizzle;
249  drizzle::connection_c con;
250 };
251 
252 typedef map<string, st_connection*> connections_t;
253 connections_t g_connections;
254 st_connection* cur_con= NULL;
255 
256 /*
257  List of commands in drizzletest
258  Must match the "command_names" array
259  Add new commands before Q_UNKNOWN!
260 */
261 enum enum_commands
262 {
263  Q_CONNECTION=1, Q_QUERY,
264  Q_CONNECT, Q_SLEEP, Q_REAL_SLEEP,
265  Q_INC, Q_DEC,
266  Q_SOURCE, Q_DISCONNECT,
267  Q_LET, Q_ECHO,
268  Q_WHILE, Q_END_BLOCK,
269  Q_SYSTEM, Q_RESULT,
270  Q_REQUIRE,
271  Q_ERROR,
272  Q_SEND, Q_REAP,
273  Q_DIRTY_CLOSE, Q_REPLACE, Q_REPLACE_COLUMN,
274  Q_PING, Q_EVAL,
275  Q_EVAL_RESULT,
276  Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
277  Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
278  Q_WAIT_FOR_SLAVE_TO_STOP,
279  Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
280  Q_ENABLE_INFO, Q_DISABLE_INFO,
281  Q_ENABLE_METADATA, Q_DISABLE_METADATA,
282  Q_EXEC, Q_DELIMITER,
283  Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR,
284  Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS,
285  Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, Q_SORTED_RESULT,
286  Q_START_TIMER, Q_END_TIMER,
287  Q_CHARACTER_SET,
288  Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT,
289  Q_IF,
290  Q_DISABLE_PARSING, Q_ENABLE_PARSING,
291  Q_REPLACE_REGEX, Q_REMOVE_FILE, Q_FILE_EXIST,
292  Q_WRITE_FILE, Q_COPY_FILE, Q_PERL, Q_DIE, Q_EXIT, Q_SKIP,
293  Q_CHMOD_FILE, Q_APPEND_FILE, Q_CAT_FILE, Q_DIFF_FILES,
294  Q_SEND_QUIT, Q_CHANGE_USER, Q_MKDIR, Q_RMDIR,
295 
296  Q_UNKNOWN, /* Unknown command. */
297  Q_COMMENT, /* Comments, ignored. */
298  Q_COMMENT_WITH_COMMAND
299 };
300 
301 
302 const char *command_names[]=
303 {
304  "connection",
305  "query",
306  "connect",
307  "sleep",
308  "real_sleep",
309  "inc",
310  "dec",
311  "source",
312  "disconnect",
313  "let",
314  "echo",
315  "while",
316  "end",
317  "system",
318  "result",
319  "require",
320  "error",
321  "send",
322  "reap",
323  "dirty_close",
324  "replace_result",
325  "replace_column",
326  "ping",
327  "eval",
328  "eval_result",
329  /* Enable/disable that the _query_ is logged to result file */
330  "enable_query_log",
331  "disable_query_log",
332  /* Enable/disable that the _result_ from a query is logged to result file */
333  "enable_result_log",
334  "disable_result_log",
335  "wait_for_slave_to_stop",
336  "enable_warnings",
337  "disable_warnings",
338  "enable_info",
339  "disable_info",
340  "enable_metadata",
341  "disable_metadata",
342  "exec",
343  "delimiter",
344  "disable_abort_on_error",
345  "enable_abort_on_error",
346  "vertical_results",
347  "horizontal_results",
348  "query_vertical",
349  "query_horizontal",
350  "sorted_result",
351  "start_timer",
352  "end_timer",
353  "character_set",
354  "disable_reconnect",
355  "enable_reconnect",
356  "if",
357  "disable_parsing",
358  "enable_parsing",
359  "replace_regex",
360  "remove_file",
361  "file_exists",
362  "write_file",
363  "copy_file",
364  "perl",
365  "die",
366 
367  /* Don't execute any more commands, compare result */
368  "exit",
369  "skip",
370  "chmod",
371  "append_file",
372  "cat_file",
373  "diff_files",
374  "send_quit",
375  "change_user",
376  "mkdir",
377  "rmdir",
378 
379  0
380 };
381 
382 
383 /*
384  The list of error codes to --error are stored in an internal array of
385  structs. This struct can hold numeric SQL error codes, error names or
386  SQLSTATE codes as strings. The element next to the last active element
387  in the list is set to type ERR_EMPTY. When an SQL statement returns an
388  error, we use this list to check if this is an expected error.
389 */
390 enum match_err_type
391 {
392  ERR_EMPTY= 0,
393  ERR_ERRNO,
394  ERR_SQLSTATE
395 };
396 
398 {
399  enum match_err_type type;
400  union
401  {
402  uint32_t errnum;
403  char sqlstate[DRIZZLE_MAX_SQLSTATE_SIZE+1]; /* \0 terminated string */
404  } code;
405 };
406 
408 {
409  struct st_match_err err[10];
410  uint32_t count;
411 };
412 
413 static st_expected_errors saved_expected_errors;
414 
416 {
417 public:
418  char *query, *query_buf,*first_argument,*last_argument,*end;
419  int first_word_len, query_len;
420  bool abort_on_error;
421  st_expected_errors expected_errors;
422  string require_file;
423  enum_commands type;
424 
425  st_command()
426  : query(NULL), query_buf(NULL), first_argument(NULL), last_argument(NULL),
427  end(NULL), first_word_len(0), query_len(0), abort_on_error(false),
428  require_file(""), type(Q_CONNECTION)
429  {
430  memset(&expected_errors, 0, sizeof(st_expected_errors));
431  }
432 
433  ~st_command()
434  {
435  free(query_buf);
436  }
437 };
438 
439 TYPELIB command_typelib= {array_elements(command_names),"",
440  command_names, 0};
441 
442 string ds_res, ds_progress, ds_warning_messages;
443 
444 char builtin_echo[FN_REFLEN];
445 
446 void die(const char *fmt, ...)
447  __attribute__((format(printf, 1, 2)));
448 void abort_not_supported_test(const char *fmt, ...)
449  __attribute__((format(printf, 1, 2)));
450 void verbose_msg(const char *fmt, ...)
451  __attribute__((format(printf, 1, 2)));
452 void warning_msg(const char *fmt, ...)
453  __attribute__((format(printf, 1, 2)));
454 void log_msg(const char *fmt, ...)
455  __attribute__((format(printf, 1, 2)));
456 
457 VAR* var_from_env(const char *, const char *);
458 VAR* var_init(VAR* v, const char *name, int name_len, const char *val,
459  int val_len);
460 VAR* var_get(const char *var_name, const char** var_name_end,
461  bool raw, bool ignore_not_existing);
462 void eval_expr(VAR* v, const char *p, const char** p_end);
463 bool match_delimiter(int c, const char *delim, uint32_t length);
464 void dump_result_to_reject_file(char *buf, int size);
465 void dump_result_to_log_file(const char *buf, int size);
466 void dump_warning_messages();
467 void dump_progress();
468 
469 void do_eval(string *query_eval, const char *query,
470  const char *query_end, bool pass_through_escape_chars);
471 void str_to_file(const char *fname, const char *str, int size);
472 void str_to_file2(const char *fname, const char *str, int size, bool append);
473 
474 /* For replace_column */
475 static char *replace_column[MAX_COLUMNS];
476 static uint32_t max_replace_column= 0;
477 void do_get_replace_column(st_command*);
478 void free_replace_column();
479 
480 /* For replace */
481 void do_get_replace(st_command* command);
482 void free_replace();
483 
484 /* For replace_regex */
485 void do_get_replace_regex(st_command* command);
486 
487 void replace_append_mem(string& ds, const char *val, int len);
488 void replace_append(string *ds, const char *val);
489 void replace_append_uint(string& ds, uint32_t val);
490 void append_sorted(string& ds, const string& ds_input);
491 
492 void handle_error(st_command*,
493  unsigned int err_errno, const char *err_error,
494  const char *err_sqlstate, string *ds);
495 void handle_no_error(st_command*);
496 
497 
498 void do_eval(string *query_eval, const char *query,
499  const char *query_end, bool pass_through_escape_chars)
500 {
501  char c, next_c;
502  int escaped = 0;
503 
504  for (const char *p= query; (c= *p) && p < query_end; ++p)
505  {
506  switch(c)
507  {
508  case '$':
509  if (escaped)
510  {
511  escaped= 0;
512  query_eval->append(p, 1);
513  }
514  else
515  {
516  VAR* v= var_get(p, &p, 0, 0);
517  if (not v)
518  die("Bad variable in eval");
519  query_eval->append(v->str_val, v->str_val_len);
520  }
521  break;
522  case '\\':
523  next_c= *(p+1);
524  if (escaped)
525  {
526  escaped= 0;
527  query_eval->append(p, 1);
528  }
529  else if (next_c == '\\' || next_c == '$' || next_c == '"')
530  {
531  /* Set escaped only if next char is \, " or $ */
532  escaped= 1;
533 
534  if (pass_through_escape_chars)
535  {
536  /* The escape char should be added to the output string. */
537  query_eval->append(p, 1);
538  }
539  }
540  else
541  query_eval->append(p, 1);
542  break;
543  default:
544  escaped= 0;
545  query_eval->append(p, 1);
546  }
547  }
548 }
549 
550 
551 /*
552  Concatenates any number of strings, escapes any OS quote in the result then
553  surround the whole affair in another set of quotes which is finally appended
554  to specified string. This function is especially useful when
555  building strings to be executed with the system() function.
556 
557  @param str string which will have addtional strings appended.
558  @param append string to be appended.
559  @param ... Optional. Additional string(s) to be appended.
560 
561  @note The final argument in the list must be NULL even if no additional
562  options are passed.
563 */
564 
565 static void append_os_quoted(string *str, const char *append, ...)
566 {
567  const char *quote_str= "\'";
568  const uint32_t quote_len= 1;
569 
570  va_list dirty_text;
571 
572  str->append(quote_str, quote_len); /* Leading quote */
573  va_start(dirty_text, append);
574  while (append != NULL)
575  {
576  const char *cur_pos= append;
577  const char *next_pos= cur_pos;
578 
579  /* Search for quote in each string and replace with escaped quote */
580  while ((next_pos= strrchr(cur_pos, quote_str[0])) != NULL)
581  {
582  str->append(cur_pos, next_pos - cur_pos);
583  str->append("\\", 1);
584  str->append(quote_str, quote_len);
585  cur_pos= next_pos + 1;
586  }
587  str->append(cur_pos);
588  append= va_arg(dirty_text, char *);
589  }
590  va_end(dirty_text);
591  str->append(quote_str, quote_len); /* Trailing quote */
592 }
593 
594 
595 /*
596  Run query and dump the result to stdout in vertical format
597 
598  NOTE! This function should be safe to call when an error
599  has occured and thus any further errors will be ignored(although logged)
600 
601  SYNOPSIS
602  show_query
603  drizzle - connection to use
604  query - query to run
605 
606 */
607 
608 static int dt_query_log(drizzle::connection_c& con, drizzle::result_c& res, const std::string& query)
609 {
610  if (drizzle_return_t ret= con.query(res, query))
611  {
612  if (ret == DRIZZLE_RETURN_ERROR_CODE)
613  {
614  log_msg("Error running query '%s': %d %s", query.c_str(), res.error_code(), res.error());
615  }
616  else
617  {
618  log_msg("Error running query '%s': %d %s", query.c_str(), ret, con.error());
619  }
620  return 1;
621  }
622  return res.column_count() == 0;
623 }
624 
625 /*
626  Show any warnings just before the error. Since the last error
627  is added to the warning stack, only print @@warning_count-1 warnings.
628 
629  NOTE! This function should be safe to call when an error
630  has occured and this any further errors will be ignored(although logged)
631 
632  SYNOPSIS
633  show_warnings_before_error
634  drizzle - connection to use
635 
636 */
637 
638 static void show_warnings_before_error(drizzle::connection_c& con)
639 {
640  drizzle::result_c res;
641  if (dt_query_log(con, res, "show warnings"))
642  return;
643 
644  if (res.row_count() >= 2) /* Don't display the last row, it's "last error" */
645  {
646  unsigned int row_num= 0;
647  unsigned int num_fields= res.column_count();
648 
649  fprintf(stderr, "\nWarnings from just before the error:\n");
650  while (drizzle_row_t row= res.row_next())
651  {
652  size_t *lengths= res.row_field_sizes();
653 
654  if (++row_num >= res.row_count())
655  {
656  /* Don't display the last row, it's "last error" */
657  break;
658  }
659 
660  for (uint32_t i= 0; i < num_fields; i++)
661  {
662  fprintf(stderr, "%.*s ", (int)lengths[i], row[i] ? row[i] : "NULL");
663  }
664  fprintf(stderr, "\n");
665  }
666  }
667 }
668 
669 enum arg_type
670 {
671  ARG_STRING,
672  ARG_REST
673 };
674 
675 struct command_arg
676 {
677  const char *argname; /* Name of argument */
678  enum arg_type type; /* Type of argument */
679  bool required; /* Argument required */
680  string *ds; /* Storage for argument */
681  const char *description; /* Description of the argument */
682 };
683 
684 
685 static void check_command_args(st_command* command,
686  const char *arguments,
687  const struct command_arg *args,
688  int num_args, const char delimiter_arg)
689 {
690  const char *ptr= arguments;
691  const char *start;
692 
693  for (int i= 0; i < num_args; i++)
694  {
695  const struct command_arg *arg= &args[i];
696  arg->ds->clear();
697 
698  bool known_arg_type= true;
699  switch (arg->type) {
700  /* A string */
701  case ARG_STRING:
702  /* Skip leading spaces */
703  while (*ptr && *ptr == ' ')
704  ptr++;
705  start= ptr;
706  /* Find end of arg, terminated by "delimiter_arg" */
707  while (*ptr && *ptr != delimiter_arg)
708  ptr++;
709  if (ptr > start)
710  {
711  do_eval(arg->ds, start, ptr, false);
712  }
713  else
714  {
715  /* Empty string */
716  arg->ds->erase();
717  }
718  command->last_argument= (char*)ptr;
719 
720  /* Step past the delimiter */
721  if (*ptr && *ptr == delimiter_arg)
722  ptr++;
723  break;
724 
725  /* Rest of line */
726  case ARG_REST:
727  start= ptr;
728  do_eval(arg->ds, start, command->end, false);
729  command->last_argument= command->end;
730  break;
731 
732  default:
733  known_arg_type= false;
734  break;
735  }
736  assert(known_arg_type);
737  if (known_arg_type == false)
738  {
739  die("Bad argument");
740  }
741 
742  /* Check required arg */
743  if (arg->ds->length() == 0 && arg->required)
744  die("Missing required argument '%s' to command '%.*s'", arg->argname,
745  command->first_word_len, command->query);
746 
747  }
748  /* Check for too many arguments passed */
749  ptr= command->last_argument;
750  while (ptr <= command->end)
751  {
752  if (*ptr && *ptr != ' ')
753  die("Extra argument '%s' passed to '%.*s'",
754  ptr, command->first_word_len, command->query);
755  ptr++;
756  }
757  return;
758 }
759 
760 
761 static void handle_command_error(st_command* command, uint32_t error)
762 {
763  if (error != 0)
764  {
765  if (command->abort_on_error)
766  die("command \"%.*s\" failed with error %d", command->first_word_len, command->query, error);
767  for (uint32_t i= 0; i < command->expected_errors.count; i++)
768  {
769  if (command->expected_errors.err[i].type == ERR_ERRNO &&
770  command->expected_errors.err[i].code.errnum == error)
771  {
772  return;
773  }
774  }
775  die("command \"%.*s\" failed with wrong error: %d",
776  command->first_word_len, command->query, error);
777  }
778  else if (command->expected_errors.err[0].type == ERR_ERRNO &&
779  command->expected_errors.err[0].code.errnum != 0)
780  {
781  /* Error code we wanted was != 0, i.e. not an expected success */
782  die("command \"%.*s\" succeeded - should have failed with errno %d...",
783  command->first_word_len, command->query,
784  command->expected_errors.err[0].code.errnum);
785  }
786 }
787 
788 static void cleanup_and_exit(int exit_code)
789 {
790  if (!silent)
791  {
792  switch (exit_code)
793  {
794  case 1:
795  printf("not ok\n");
796  break;
797  case 0:
798  printf("ok\n");
799  break;
800  case 62:
801  printf("skipped\n");
802  break;
803  default:
804  printf("unknown exit code: %d\n", exit_code);
805  assert(false);
806  }
807  }
808  exit(exit_code);
809 }
810 
811 void die(const char *fmt, ...)
812 {
813  /*
814  Protect against dying twice
815  first time 'die' is called, try to write log files
816  second time, just exit
817  */
818  static bool dying= false;
819  if (dying)
820  cleanup_and_exit(1);
821  dying= true;
822 
823  /* Print the error message */
824  fprintf(stderr, "drizzletest: ");
825  if (cur_file && cur_file != file_stack.data())
826  fprintf(stderr, "In included file \"%s\": ", cur_file->file_name);
827  if (start_lineno > 0)
828  fprintf(stderr, "At line %u: ", start_lineno);
829  if (fmt)
830  {
831  va_list args;
832  va_start(args, fmt);
833  vfprintf(stderr, fmt, args);
834  va_end(args);
835  }
836  else
837  fprintf(stderr, "unknown error");
838  fprintf(stderr, "\n");
839  fflush(stderr);
840 
841  /* Show results from queries just before failure */
842  if (ds_res.length() && opt_tail_lines)
843  {
844  int tail_lines= opt_tail_lines;
845  const char* show_from= ds_res.c_str() + ds_res.length() - 1;
846  while (show_from > ds_res.c_str() && tail_lines > 0 )
847  {
848  show_from--;
849  if (*show_from == '\n')
850  tail_lines--;
851  }
852  fprintf(stderr, "\nThe result from queries just before the failure was:\n");
853  if (show_from > ds_res.c_str())
854  fprintf(stderr, "< snip >");
855  fprintf(stderr, "%s", show_from);
856  fflush(stderr);
857  }
858 
859  /* Dump the result that has been accumulated so far to .log file */
860  if (! result_file_name.empty() && ds_res.length())
861  dump_result_to_log_file(ds_res.c_str(), ds_res.length());
862 
863  /* Dump warning messages */
864  if (! result_file_name.empty() && ds_warning_messages.length())
865  dump_warning_messages();
866 
867  /*
868  Help debugging by displaying any warnings that might have
869  been produced prior to the error
870  */
871  if (cur_con)
872  {
873  show_warnings_before_error(*cur_con);
874  }
875 
876  cleanup_and_exit(1);
877 }
878 
879 
880 void abort_not_supported_test(const char *fmt, ...)
881 {
882  va_list args;
883  st_test_file* err_file= cur_file;
884 
885 
886  /* Print include filestack */
887  fprintf(stderr, "The test '%s' is not supported by this installation\n",
888  file_stack[0].file_name);
889  fprintf(stderr, "Detected in file %s at line %d\n",
890  err_file->file_name, err_file->lineno);
891  while (err_file != file_stack.data())
892  {
893  err_file--;
894  fprintf(stderr, "included from %s at line %d\n",
895  err_file->file_name, err_file->lineno);
896  }
897 
898  /* Print error message */
899  va_start(args, fmt);
900  if (fmt)
901  {
902  fprintf(stderr, "reason: ");
903  vfprintf(stderr, fmt, args);
904  fprintf(stderr, "\n");
905  fflush(stderr);
906  }
907  va_end(args);
908 
909  cleanup_and_exit(62);
910 }
911 
912 
913 void verbose_msg(const char *fmt, ...)
914 {
915  if (!verbose)
916  return;
917  va_list args;
918  va_start(args, fmt);
919  fprintf(stderr, "drizzletest: ");
920  if (cur_file && cur_file != file_stack.data())
921  fprintf(stderr, "In included file \"%s\": ", cur_file->file_name);
922  if (start_lineno != 0)
923  fprintf(stderr, "At line %u: ", start_lineno);
924  vfprintf(stderr, fmt, args);
925  fprintf(stderr, "\n");
926  va_end(args);
927 }
928 
929 
930 void warning_msg(const char *fmt, ...)
931 {
932  va_list args;
933  char buff[512];
934  size_t len;
935 
936  va_start(args, fmt);
937  ds_warning_messages += "drizzletest: ";
938  if (start_lineno != 0)
939  {
940  ds_warning_messages += "Warning detected ";
941  if (cur_file && cur_file != file_stack.data())
942  {
943  len= snprintf(buff, sizeof(buff), "in included file %s ", cur_file->file_name);
944  ds_warning_messages.append(buff, len);
945  }
946  len= snprintf(buff, sizeof(buff), "at line %d: ", start_lineno);
947  ds_warning_messages.append(buff, len);
948  }
949 
950  len= vsnprintf(buff, sizeof(buff), fmt, args);
951  ds_warning_messages.append(buff, len);
952 
953  ds_warning_messages += "\n";
954  va_end(args);
955 
956  return;
957 }
958 
959 
960 void log_msg(const char *fmt, ...)
961 {
962  va_list args;
963  char buff[1024];
964 
965  va_start(args, fmt);
966  size_t len= vsnprintf(buff, sizeof(buff)-1, fmt, args);
967  va_end(args);
968 
969  ds_res.append(buff, len);
970  ds_res += "\n";
971 }
972 
973 
974 /*
975  Read a file and append it to ds
976 
977  SYNOPSIS
978  cat_file
979  ds - pointer to dynamic string where to add the files content
980  filename - name of the file to read
981 
982 */
983 
984 static void cat_file(string& ds, const char* filename)
985 {
986  int fd= internal::my_open(filename, O_RDONLY, MYF(0));
987  if (fd < 0)
988  die("Failed to open file '%s'", filename);
989  char buff[512];
990  while (uint32_t len= internal::my_read(fd, (unsigned char*)&buff, sizeof(buff), MYF(0)))
991  {
992  char *p= buff, *start= buff;
993  while (p < buff+len)
994  {
995  /* Convert cr/lf to lf */
996  if (*p == '\r' && *(p+1) && *(p+1)== '\n')
997  {
998  /* Add fake newline instead of cr and output the line */
999  *p= '\n';
1000  p++; /* Step past the "fake" newline */
1001  ds.append(start, p - start);
1002  p++; /* Step past the "fake" newline */
1003  start= p;
1004  }
1005  else
1006  p++;
1007  }
1008  /* Output any chars that might be left */
1009  ds.append(start, p - start);
1010  }
1011  internal::my_close(fd, MYF(0));
1012 }
1013 
1014 
1015 /*
1016  Run the specified command with popen
1017 
1018  SYNOPSIS
1019  run_command
1020  cmd - command to execute(should be properly quoted
1021  result - pointer to string where to store the result
1022 
1023 */
1024 
1025 static int run_command(const char* cmd, string& result)
1026 {
1027  FILE* res_file= popen(cmd, "r");
1028  if (not res_file)
1029  die("popen(\"%s\", \"r\") failed", cmd);
1030 
1031  char buf[512]= {0};
1032  while (fgets(buf, sizeof(buf), res_file))
1033  {
1034  /* Save the output of this command in the supplied string */
1035  result.append(buf);
1036  }
1037  int error= pclose(res_file);
1038  return WEXITSTATUS(error);
1039 }
1040 
1041 
1042 /*
1043  Run the specified tool with variable number of arguments
1044 
1045  SYNOPSIS
1046  run_tool
1047  tool_path - the name of the tool to run
1048  result - pointer to dynamic string where to store the result
1049  ... - variable number of arguments that will be properly
1050  quoted and appended after the tool's name
1051 
1052 */
1053 
1054 static int run_tool(const char *tool_path, string& result, ...)
1055 {
1056  string ds_cmdline;
1057  append_os_quoted(&ds_cmdline, tool_path, NULL);
1058  ds_cmdline += " ";
1059 
1060  va_list args;
1061  va_start(args, result);
1062  while (const char* arg= va_arg(args, char *))
1063  {
1064  /* Options should be os quoted */
1065  if (strncmp(arg, "--", 2) == 0)
1066  append_os_quoted(&ds_cmdline, arg, NULL);
1067  else
1068  ds_cmdline += arg;
1069  ds_cmdline += " ";
1070  }
1071 
1072  va_end(args);
1073 
1074  return run_command(ds_cmdline.c_str(), result);
1075 }
1076 
1077 
1078 /*
1079  Show the diff of two files using the systems builtin diff
1080  command. If no such diff command exist, just dump the content
1081  of the two files and inform about how to get "diff"
1082 
1083  SYNOPSIS
1084  show_diff
1085  ds - pointer to dynamic string where to add the diff(may be NULL)
1086  filename1 - name of first file
1087  filename2 - name of second file
1088 
1089 */
1090 
1091 static void show_diff(string* ds, const char* filename1, const char* filename2)
1092 {
1093  string ds_tmp;
1094 
1095  /* First try with unified diff */
1096  if (run_tool("diff",
1097  ds_tmp, /* Get output from diff in ds_tmp */
1098  "-u",
1099  filename1,
1100  filename2,
1101  "2>&1",
1102  NULL) > 1) /* Most "diff" tools return >1 if error */
1103  {
1104 
1105  /* Fallback to context diff with "diff -c" */
1106  if (run_tool("diff",
1107  ds_tmp, /* Get output from diff in ds_tmp */
1108  "-c",
1109  filename1,
1110  filename2,
1111  "2>&1",
1112  NULL) > 1) /* Most "diff" tools return >1 if error */
1113  {
1114  /*
1115  Fallback to dump both files to result file and inform
1116  about installing "diff"
1117  */
1118  ds_tmp=
1119  "\n"
1120  "The two files differ but it was not possible to execute 'diff' in\n"
1121  "order to show only the difference, tried both 'diff -u' or 'diff -c'.\n"
1122  "Instead the whole content of the two files was shown for you to diff manually. ;)\n"
1123  "\n"
1124  "To get a better report you should install 'diff' on your system, which you\n"
1125  "for example can get from http://www.gnu.org/software/diffutils/diffutils.html\n"
1126  "\n";
1127 
1128  ds_tmp += " --- ";
1129  ds_tmp += filename1;
1130  ds_tmp += " >>>\n";
1131  cat_file(ds_tmp, filename1);
1132  ds_tmp += "<<<\n --- ";
1133  ds_tmp += filename1;
1134  ds_tmp += " >>>\n";
1135  cat_file(ds_tmp, filename2);
1136  ds_tmp += "<<<<\n";
1137  }
1138  }
1139 
1140  if (ds)
1141  {
1142  /* Add the diff to output */
1143  *ds += ds_tmp;
1144  }
1145  else
1146  {
1147  /* Print diff directly to stdout */
1148  fprintf(stderr, "%s\n", ds_tmp.c_str());
1149  }
1150 
1151 }
1152 
1153 enum compare_files_result_enum
1154 {
1155  RESULT_OK= 0,
1156  RESULT_CONTENT_MISMATCH= 1,
1157  RESULT_LENGTH_MISMATCH= 2
1158 };
1159 
1160 /*
1161  Compare two files, given a fd to the first file and
1162  name of the second file
1163 
1164  SYNOPSIS
1165  compare_files2
1166  fd - Open file descriptor of the first file
1167  filename2 - Name of second file
1168 
1169  RETURN VALUES
1170  According to the values in "compare_files_result_enum"
1171 
1172 */
1173 
1174 static int compare_files2(int fd, const char* filename2)
1175 {
1176  int error= RESULT_OK;
1177  uint32_t len, len2;
1178  char buff[512], buff2[512];
1179  const char *fname= filename2;
1180  string tmpfile;
1181 
1182  int fd2= internal::my_open(fname, O_RDONLY, MYF(0));
1183  if (fd2 < 0)
1184  {
1185  internal::my_close(fd, MYF(0));
1186  if (! opt_testdir.empty())
1187  {
1188  tmpfile= opt_testdir;
1189  if (tmpfile[tmpfile.length()] != '/')
1190  tmpfile += "/";
1191  tmpfile += filename2;
1192  fname= tmpfile.c_str();
1193  }
1194  if ((fd2= internal::my_open(fname, O_RDONLY, MYF(0))) < 0)
1195  {
1196  internal::my_close(fd, MYF(0));
1197 
1198  die("Failed to open second file: '%s'", fname);
1199  }
1200  }
1201  while ((len= internal::my_read(fd, (unsigned char*)&buff,
1202  sizeof(buff), MYF(0))) > 0)
1203  {
1204  if ((len2= internal::my_read(fd2, (unsigned char*)&buff2,
1205  sizeof(buff2), MYF(0))) < len)
1206  {
1207  /* File 2 was smaller */
1208  error= RESULT_LENGTH_MISMATCH;
1209  break;
1210  }
1211  if (len2 > len)
1212  {
1213  /* File 1 was smaller */
1214  error= RESULT_LENGTH_MISMATCH;
1215  break;
1216  }
1217  if ((memcmp(buff, buff2, len)))
1218  {
1219  /* Content of this part differed */
1220  error= RESULT_CONTENT_MISMATCH;
1221  break;
1222  }
1223  }
1224  if (!error && internal::my_read(fd2, (unsigned char*)&buff2,
1225  sizeof(buff2), MYF(0)) > 0)
1226  {
1227  /* File 1 was smaller */
1228  error= RESULT_LENGTH_MISMATCH;
1229  }
1230 
1231  internal::my_close(fd2, MYF(0));
1232 
1233  return error;
1234 }
1235 
1236 
1237 /*
1238  Compare two files, given their filenames
1239 
1240  SYNOPSIS
1241  compare_files
1242  filename1 - Name of first file
1243  filename2 - Name of second file
1244 
1245  RETURN VALUES
1246  See 'compare_files2'
1247 
1248 */
1249 
1250 static int compare_files(const char* filename1, const char* filename2)
1251 {
1252  int fd= internal::my_open(filename1, O_RDONLY, MYF(0));
1253  if (fd < 0)
1254  die("Failed to open first file: '%s'", filename1);
1255  int error= compare_files2(fd, filename2);
1256  internal::my_close(fd, MYF(0));
1257  return error;
1258 }
1259 
1260 
1261 /*
1262  Compare content of the string in ds to content of file fname
1263 
1264  SYNOPSIS
1265  string_cmp
1266  ds - Dynamic string containing the string o be compared
1267  fname - Name of file to compare with
1268 
1269  RETURN VALUES
1270  See 'compare_files2'
1271 */
1272 
1273 static int string_cmp(const string& ds, const char *fname)
1274 {
1275  char temp_file_path[FN_REFLEN];
1276 
1277  int fd= internal::create_temp_file(temp_file_path, TMPDIR, "tmp", MYF(MY_WME));
1278  if (fd < 0)
1279  die("Failed to create temporary file for ds");
1280 
1281  /* Write ds to temporary file and set file pos to beginning*/
1282  if (internal::my_write(fd, (unsigned char *) ds.data(), ds.length(), MYF(MY_FNABP | MY_WME)) ||
1283  lseek(fd, 0, SEEK_SET) == MY_FILEPOS_ERROR)
1284  {
1285  internal::my_close(fd, MYF(0));
1286  /* Remove the temporary file */
1287  internal::my_delete(temp_file_path, MYF(0));
1288  die("Failed to write file '%s'", temp_file_path);
1289  }
1290 
1291  int error= compare_files2(fd, fname);
1292 
1293  internal::my_close(fd, MYF(0));
1294  /* Remove the temporary file */
1295  internal::my_delete(temp_file_path, MYF(0));
1296 
1297  return error;
1298 }
1299 
1300 
1301 /*
1302  Check the content of ds against result file
1303 
1304  SYNOPSIS
1305  check_result
1306  ds - content to be checked
1307 
1308  RETURN VALUES
1309  error - the function will not return
1310 
1311 */
1312 
1313 static void check_result(string& ds)
1314 {
1315  const char* mess= "Result content mismatch\n";
1316 
1317  if (access(result_file_name.c_str(), F_OK) != 0)
1318  die("The specified result file does not exist: '%s'", result_file_name.c_str());
1319 
1320  switch (string_cmp(ds, result_file_name.c_str()))
1321  {
1322  case RESULT_OK:
1323  break; /* ok */
1324  case RESULT_LENGTH_MISMATCH:
1325  mess= "Result length mismatch\n";
1326  /* Fallthrough */
1327  case RESULT_CONTENT_MISMATCH:
1328  {
1329  /*
1330  Result mismatched, dump results to .reject file
1331  and then show the diff
1332  */
1333  char reject_file[FN_REFLEN];
1334  size_t reject_length;
1335  internal::dirname_part(reject_file, result_file_name.c_str(), &reject_length);
1336 
1337  if (access(reject_file, W_OK) == 0)
1338  {
1339  /* Result file directory is writable, save reject file there */
1340  internal::fn_format(reject_file, result_file_name.c_str(), NULL, ".reject", MY_REPLACE_EXT);
1341  }
1342  else
1343  {
1344  /* Put reject file in opt_logdir */
1345  internal::fn_format(reject_file, result_file_name.c_str(), opt_logdir.c_str(), ".reject", MY_REPLACE_DIR | MY_REPLACE_EXT);
1346  }
1347  str_to_file(reject_file, ds.data(), ds.length());
1348 
1349  ds.erase(); /* Don't create a .log file */
1350 
1351  show_diff(NULL, result_file_name.c_str(), reject_file);
1352  die("%s",mess);
1353  break;
1354  }
1355  default: /* impossible */
1356  die("Unknown error code from dyn_string_cmp()");
1357  }
1358 }
1359 
1360 
1361 /*
1362  Check the content of ds against a require file
1363  If match fails, abort the test with special error code
1364  indicating that test is not supported
1365 
1366  SYNOPSIS
1367  check_require
1368  ds - content to be checked
1369  fname - name of file to check against
1370 
1371  RETURN VALUES
1372  error - the function will not return
1373 
1374 */
1375 
1376 static void check_require(const string& ds, const string& fname)
1377 {
1378  if (string_cmp(ds, fname.c_str()))
1379  {
1380  char reason[FN_REFLEN];
1381  internal::fn_format(reason, fname.c_str(), "", "", MY_REPLACE_EXT | MY_REPLACE_DIR);
1382  abort_not_supported_test("Test requires: '%s'", reason);
1383  }
1384 }
1385 
1386 
1387 /*
1388  Remove surrounding chars from string
1389 
1390  Return 1 if first character is found but not last
1391 */
1392 static int strip_surrounding(char* str, char c1, char c2)
1393 {
1394  char* ptr= str;
1395 
1396  /* Check if the first non space character is c1 */
1397  while (*ptr && charset_info->isspace(*ptr))
1398  ptr++;
1399  if (*ptr == c1)
1400  {
1401  /* Replace it with a space */
1402  *ptr= ' ';
1403 
1404  /* Last non space charecter should be c2 */
1405  ptr= strchr(str, '\0')-1;
1406  while (*ptr && charset_info->isspace(*ptr))
1407  ptr--;
1408  if (*ptr == c2)
1409  {
1410  /* Replace it with \0 */
1411  *ptr= 0;
1412  }
1413  else
1414  {
1415  /* Mismatch detected */
1416  return 1;
1417  }
1418  }
1419  return 0;
1420 }
1421 
1422 
1423 static void strip_parentheses(st_command* command)
1424 {
1425  if (strip_surrounding(command->first_argument, '(', ')'))
1426  die("%.*s - argument list started with '%c' must be ended with '%c'",
1427  command->first_word_len, command->query, '(', ')');
1428 }
1429 
1430 
1431 
1432 VAR *var_init(VAR *v, const char *name, int name_len, const char *val,
1433  int val_len)
1434 {
1435  if (!name_len && name)
1436  name_len = strlen(name);
1437  if (!val_len && val)
1438  val_len = strlen(val) ;
1439  VAR *tmp_var = v ? v : (VAR*)malloc(sizeof(*tmp_var) + name_len+1);
1440 
1441  tmp_var->name = name ? (char*)&tmp_var[1] : 0;
1442  tmp_var->alloced = (v == 0);
1443 
1444  int val_alloc_len = val_len + 16; /* room to grow */
1445  tmp_var->str_val = (char*)malloc(val_alloc_len+1);
1446 
1447  memcpy(tmp_var->name, name, name_len);
1448  if (val)
1449  {
1450  memcpy(tmp_var->str_val, val, val_len);
1451  tmp_var->str_val[val_len]= 0;
1452  }
1453  tmp_var->name_len = name_len;
1454  tmp_var->str_val_len = val_len;
1455  tmp_var->alloced_len = val_alloc_len;
1456  tmp_var->int_val = val ? atoi(val) : 0;
1457  tmp_var->int_dirty = false;
1458  tmp_var->env_s = 0;
1459  return tmp_var;
1460 }
1461 
1462 VAR* var_from_env(const char *name, const char *def_val)
1463 {
1464  const char *tmp= getenv(name);
1465  if (!tmp)
1466  tmp = def_val;
1467  return var_hash[name] = var_init(0, name, strlen(name), tmp, strlen(tmp));
1468 }
1469 
1470 VAR* var_get(const char *var_name, const char **var_name_end, bool raw,
1471  bool ignore_not_existing)
1472 {
1473  int digit;
1474  VAR *v;
1475  if (*var_name != '$')
1476  goto err;
1477  digit = *++var_name - '0';
1478  if (digit < 0 || digit >= 10)
1479  {
1480  const char *save_var_name = var_name, *end;
1481  uint32_t length;
1482  end = (var_name_end) ? *var_name_end : 0;
1483  while (charset_info->isvar(*var_name) && var_name != end)
1484  var_name++;
1485  if (var_name == save_var_name)
1486  {
1487  if (ignore_not_existing)
1488  return(0);
1489  die("Empty variable");
1490  }
1491  length= (uint32_t) (var_name - save_var_name);
1492  if (length >= MAX_VAR_NAME_LENGTH)
1493  die("Too long variable name: %s", save_var_name);
1494 
1495  string save_var_name_str(save_var_name, length);
1496  if (var_hash_t::mapped_type* ptr= find_ptr(var_hash, save_var_name_str))
1497  v= *ptr;
1498  else
1499  {
1500  char buff[MAX_VAR_NAME_LENGTH+1];
1501  strncpy(buff, save_var_name, length);
1502  buff[length]= '\0';
1503  v= var_from_env(buff, "");
1504  }
1505  var_name--; /* Point at last character */
1506  }
1507  else
1508  v = &var_reg[digit];
1509 
1510  if (!raw && v->int_dirty)
1511  {
1512  sprintf(v->str_val, "%d", v->int_val);
1513  v->int_dirty = 0;
1514  v->str_val_len = strlen(v->str_val);
1515  }
1516  if (var_name_end)
1517  *var_name_end = var_name ;
1518  return(v);
1519 err:
1520  if (var_name_end)
1521  *var_name_end = 0;
1522  die("Unsupported variable name: %s", var_name);
1523  return(0);
1524 }
1525 
1526 
1527 static VAR *var_obtain(const char *name, int len)
1528 {
1529  string var_name(name, len);
1530  if (var_hash_t::mapped_type* ptr= find_ptr(var_hash, var_name))
1531  return *ptr;
1532  return var_hash[var_name] = var_init(0, name, len, "", 0);
1533 }
1534 
1535 
1536 /*
1537  - if variable starts with a $ it is regarded as a local test varable
1538  - if not it is treated as a environment variable, and the corresponding
1539  environment variable will be updated
1540 */
1541 
1542 static void var_set(const char *var_name, const char *var_name_end,
1543  const char *var_val, const char *var_val_end)
1544 {
1545  int digit, env_var= 0;
1546  VAR *v;
1547 
1548  if (*var_name != '$')
1549  env_var= 1;
1550  else
1551  var_name++;
1552 
1553  digit= *var_name - '0';
1554  if (!(digit < 10 && digit >= 0))
1555  {
1556  v= var_obtain(var_name, (uint32_t) (var_name_end - var_name));
1557  }
1558  else
1559  v= &var_reg[digit];
1560 
1561  eval_expr(v, var_val, (const char**) &var_val_end);
1562 
1563  if (env_var)
1564  {
1565  char buf[1024], *old_env_s= v->env_s;
1566  if (v->int_dirty)
1567  {
1568  sprintf(v->str_val, "%d", v->int_val);
1569  v->int_dirty= 0;
1570  v->str_val_len= strlen(v->str_val);
1571  }
1572  snprintf(buf, sizeof(buf), "%.*s=%.*s",
1573  v->name_len, v->name,
1574  v->str_val_len, v->str_val);
1575  v->env_s= strdup(buf);
1576  putenv(v->env_s);
1577  free(old_env_s);
1578  }
1579  return;
1580 }
1581 
1582 
1583 static void var_set_string(const char* name, const char* value)
1584 {
1585  var_set(name, name + strlen(name), value, value + strlen(value));
1586 }
1587 
1588 
1589 static void var_set_int(const char* name, int value)
1590 {
1591  char buf[21];
1592  snprintf(buf, sizeof(buf), "%d", value);
1593  var_set_string(name, buf);
1594 }
1595 
1596 
1597 /*
1598  Store an integer (typically the returncode of the last SQL)
1599  statement in the drizzletest builtin variable $drizzleclient_errno
1600 */
1601 
1602 static void var_set_errno(int sql_errno)
1603 {
1604  var_set_int("$drizzleclient_errno", sql_errno);
1605 }
1606 
1607 
1608 /*
1609  Update $drizzleclient_get_server_version variable with version
1610  of the currently connected server
1611 */
1612 
1613 static void var_set_drizzleclient_get_server_version(drizzle_con_st *con)
1614 {
1615  var_set_int("$drizzle_con_server_version", drizzle_con_server_version_number(con));
1616 }
1617 
1618 
1619 /*
1620  Set variable from the result of a query
1621 
1622  SYNOPSIS
1623  var_query_set()
1624  var variable to set from query
1625  query start of query string to execute
1626  query_end end of the query string to execute
1627 
1628 
1629  DESCRIPTION
1630  let @<var_name> = `<query>`
1631 
1632  Execute the query and assign the first row of result to var as
1633  a tab separated strings
1634 
1635  Also assign each column of the result set to
1636  variable "$<var_name>_<column_name>"
1637  Thus the tab separated output can be read from $<var_name> and
1638  and each individual column can be read as $<var_name>_<col_name>
1639 
1640 */
1641 
1642 static void dt_query(drizzle::connection_c& con, drizzle::result_c& res, const std::string& query)
1643 {
1644  if (drizzle_return_t ret= con.query(res, query))
1645  {
1646  if (ret == DRIZZLE_RETURN_ERROR_CODE)
1647  {
1648  die("Error running query '%s': %d %s", query.c_str(), res.error_code(), res.error());
1649  }
1650  else
1651  {
1652  die("Error running query '%s': %d %s", query.c_str(), ret, con.error());
1653  }
1654  }
1655 
1656  if (res.column_count() == 0)
1657  {
1658  die("Query '%s' didn't return a result set", query.c_str());
1659  }
1660 }
1661 
1662 static void var_query_set(VAR *var, const char *query, const char** query_end)
1663 {
1664  const char *end = ((query_end && *query_end) ? *query_end : query + strlen(query));
1665  drizzle::connection_c& con= *cur_con;
1666 
1667  while (end > query && *end != '`')
1668  --end;
1669  if (query == end)
1670  die("Syntax error in query, missing '`'");
1671  ++query;
1672 
1673  string ds_query;
1674  /* Eval the query, thus replacing all environment variables */
1675  do_eval(&ds_query, query, end, false);
1676 
1677  drizzle::result_c res;
1678  dt_query(con, res, ds_query);
1679 
1680  drizzle_row_t row= res.row_next();
1681  if (row && row[0])
1682  {
1683  /*
1684  Concatenate all fields in the first row with tab in between
1685  and assign that string to the $variable
1686  */
1687  string result;
1688  size_t* lengths= res.row_field_sizes();
1689  for (uint32_t i= 0; i < res.column_count(); i++)
1690  {
1691  if (row[i])
1692  {
1693  /* Add column to tab separated string */
1694  result.append(row[i], lengths[i]);
1695  }
1696  result += "\t";
1697  }
1698  end= result.c_str() + result.length() - 1;
1699  eval_expr(var, result.c_str(), (const char**) &end);
1700  }
1701  else
1702  eval_expr(var, "", 0);
1703 }
1704 
1705 
1706 /*
1707  Set variable from the result of a field in a query
1708 
1709  This function is useful when checking for a certain value
1710  in the output from a query that can't be restricted to only
1711  return some values. A very good example of that is most SHOW
1712  commands.
1713 
1714  SYNOPSIS
1715  var_set_query_get_value()
1716 
1717  DESCRIPTION
1718  let $variable= query_get_value(<query to run>,<column name>,<row no>);
1719 
1720  <query to run> - The query that should be sent to the server
1721  <column name> - Name of the column that holds the field be compared
1722  against the expected value
1723  <row no> - Number of the row that holds the field to be
1724  compared against the expected value
1725 
1726 */
1727 
1728 static void var_set_query_get_value(st_command* command, VAR *var)
1729 {
1730  int col_no= -1;
1731  drizzle::connection_c& con= *cur_con;
1732 
1733  string ds_query;
1734  string ds_col;
1735  string ds_row;
1736  const struct command_arg query_get_value_args[] = {
1737  {"query", ARG_STRING, true, &ds_query, "Query to run"},
1738  {"column name", ARG_STRING, true, &ds_col, "Name of column"},
1739  {"row number", ARG_STRING, true, &ds_row, "Number for row"}
1740  };
1741 
1742 
1743 
1744  strip_parentheses(command);
1745  check_command_args(command, command->first_argument, query_get_value_args,
1746  sizeof(query_get_value_args)/sizeof(struct command_arg),
1747  ',');
1748 
1749  /* Convert row number to int */
1750  long row_no= atoi(ds_row.c_str());
1751 
1752  istringstream buff(ds_row);
1753  if ((buff >> row_no).fail())
1754  die("Invalid row number: '%s'", ds_row.c_str());
1755 
1756  /* Remove any surrounding "'s from the query - if there is any */
1757  // (Don't get me started on this)
1758  char* unstripped_query= strdup(ds_query.c_str());
1759  if (strip_surrounding(unstripped_query, '"', '"'))
1760  die("Mismatched \"'s around query '%s'", ds_query.c_str());
1761  ds_query= unstripped_query;
1762 
1763  drizzle::result_c res;
1764  dt_query(con, res, ds_query);
1765 
1766  {
1767  /* Find column number from the given column name */
1768  uint32_t num_fields= res.column_count();
1769  for (uint32_t i= 0; i < num_fields; i++)
1770  {
1771  drizzle_column_st* column= res.column_next();
1772  if (strcmp(drizzle_column_name(column), ds_col.c_str()) == 0 &&
1773  strlen(drizzle_column_name(column)) == ds_col.length())
1774  {
1775  col_no= i;
1776  break;
1777  }
1778  }
1779  if (col_no == -1)
1780  {
1781  die("Could not find column '%s' in the result of '%s'", ds_col.c_str(), ds_query.c_str());
1782  }
1783  }
1784 
1785  {
1786  /* Get the value */
1787  long rows= 0;
1788  const char* value= "No such row";
1789 
1790  while (drizzle_row_t row= res.row_next())
1791  {
1792  if (++rows == row_no)
1793  {
1794  /* Found the row to get */
1795  value= row[col_no] ? row[col_no] : "NULL";
1796  break;
1797  }
1798  }
1799  eval_expr(var, value, 0);
1800  }
1801 }
1802 
1803 
1804 static void var_copy(VAR *dest, VAR *src)
1805 {
1806  dest->int_val= src->int_val;
1807  dest->int_dirty= src->int_dirty;
1808 
1809  /* Alloc/realloc data for str_val in dest */
1810  if (dest->alloced_len < src->alloced_len)
1811  {
1812  char *tmpptr= (char *)realloc(dest->str_val, src->alloced_len);
1813  dest->str_val= tmpptr;
1814  }
1815  else
1816  dest->alloced_len= src->alloced_len;
1817 
1818  /* Copy str_val data to dest */
1819  dest->str_val_len= src->str_val_len;
1820  if (src->str_val_len)
1821  memcpy(dest->str_val, src->str_val, src->str_val_len);
1822 }
1823 
1824 
1825 void eval_expr(VAR *v, const char *p, const char **p_end)
1826 {
1827  if (*p == '$')
1828  {
1829  VAR *vp= var_get(p, p_end, 0, 0);
1830  if (vp)
1831  var_copy(v, vp);
1832  return;
1833  }
1834 
1835  if (*p == '`')
1836  {
1837  var_query_set(v, p, p_end);
1838  return;
1839  }
1840 
1841  {
1842  /* Check if this is a "let $var= query_get_value()" */
1843  const char* get_value_str= "query_get_value";
1844  const size_t len= strlen(get_value_str);
1845  if (strncmp(p, get_value_str, len)==0)
1846  {
1847  st_command command;
1848  command.query= (char*)p;
1849  command.first_word_len= len;
1850  command.first_argument= command.query + len;
1851  command.end= (char*)*p_end;
1852  var_set_query_get_value(&command, v);
1853  return;
1854  }
1855  }
1856 
1857  {
1858  int new_val_len = (p_end && *p_end) ?
1859  (int) (*p_end - p) : (int) strlen(p);
1860  if (new_val_len + 1 >= v->alloced_len)
1861  {
1862  static int MIN_VAR_ALLOC= 32;
1863  v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ?
1864  MIN_VAR_ALLOC : new_val_len + 1;
1865  char *tmpptr= (char *)realloc(v->str_val, v->alloced_len+1);
1866  v->str_val= tmpptr;
1867  }
1868  v->str_val_len = new_val_len;
1869  memcpy(v->str_val, p, new_val_len);
1870  v->str_val[new_val_len] = 0;
1871  v->int_val=atoi(p);
1872  v->int_dirty=0;
1873  }
1874  return;
1875 }
1876 
1877 
1878 static void open_file(const char *name)
1879 {
1880  char buff[FN_REFLEN];
1881 
1882  if (!internal::test_if_hard_path(name))
1883  {
1884  snprintf(buff, sizeof(buff), "%s%s",opt_basedir.c_str(),name);
1885  name=buff;
1886  }
1887  internal::fn_format(buff, name, "", "", MY_UNPACK_FILENAME);
1888 
1889  cur_file++;
1890  if (cur_file == &*file_stack.end())
1891  die("Source directives are nesting too deep");
1892  if (!(cur_file->file= fopen(buff, "r")))
1893  {
1894  cur_file--;
1895  die("Could not open '%s' for reading", buff);
1896  }
1897  cur_file->file_name= strdup(buff);
1898  cur_file->lineno=1;
1899 }
1900 
1901 
1902 /*
1903  Source and execute the given file
1904 
1905  SYNOPSIS
1906  do_source()
1907  query called command
1908 
1909  DESCRIPTION
1910  source <file_name>
1911 
1912  Open the file <file_name> and execute it
1913 
1914 */
1915 
1916 static void do_source(st_command* command)
1917 {
1918  string ds_filename;
1919  const struct command_arg source_args[] = {
1920  { "filename", ARG_STRING, true, &ds_filename, "File to source" }
1921  };
1922 
1923 
1924  check_command_args(command, command->first_argument, source_args,
1925  sizeof(source_args)/sizeof(struct command_arg),
1926  ' ');
1927 
1928  /*
1929  If this file has already been sourced, don't source it again.
1930  It's already available in the q_lines cache.
1931  */
1932  if (parser.current_line < (parser.read_lines - 1))
1933  ; /* Do nothing */
1934  else
1935  {
1936  if (! opt_testdir.empty())
1937  {
1938  string testdir(opt_testdir);
1939  if (testdir[testdir.length()] != '/')
1940  testdir += "/";
1941  testdir += ds_filename;
1942  ds_filename.swap(testdir);
1943  }
1944  open_file(ds_filename.c_str());
1945  }
1946 }
1947 
1948 
1949 static void init_builtin_echo()
1950 {
1951  builtin_echo[0]= 0;
1952 }
1953 
1954 
1955 /*
1956  Replace a substring
1957 
1958  SYNOPSIS
1959  replace
1960  ds_str The string to search and perform the replace in
1961  search_str The string to search for
1962  search_len Length of the string to search for
1963  replace_str The string to replace with
1964  replace_len Length of the string to replace with
1965 
1966  RETURN
1967  0 String replaced
1968  1 Could not find search_str in str
1969 */
1970 
1971 static int replace(string *ds_str,
1972  const char *search_str, uint32_t search_len,
1973  const char *replace_str, uint32_t replace_len)
1974 {
1975  string ds_tmp;
1976  const char *start= strstr(ds_str->c_str(), search_str);
1977  if (!start)
1978  return 1;
1979  ds_tmp.append(ds_str->c_str(), start - ds_str->c_str());
1980  ds_tmp.append(replace_str, replace_len);
1981  ds_tmp.append(start + search_len);
1982  *ds_str= ds_tmp;
1983  return 0;
1984 }
1985 
1986 
1987 /*
1988  Execute given command.
1989 
1990  SYNOPSIS
1991  do_exec()
1992  query called command
1993 
1994  DESCRIPTION
1995  exec <command>
1996 
1997  Execute the text between exec and end of line in a subprocess.
1998  The error code returned from the subprocess is checked against the
1999  expected error array, previously set with the --error command.
2000  It can thus be used to execute a command that shall fail.
2001 
2002  NOTE
2003  Although drizzletest is executed from cygwin shell, the command will be
2004  executed in "cmd.exe". Thus commands like "rm" etc can NOT be used, use
2005  drizzletest commmand(s) like "remove_file" for that
2006 */
2007 
2008 static void do_exec(st_command* command)
2009 {
2010  int error;
2011  char buf[512];
2012  FILE *res_file;
2013  char *cmd= command->first_argument;
2014  string ds_cmd;
2015 
2016  /* Skip leading space */
2017  while (*cmd && charset_info->isspace(*cmd))
2018  cmd++;
2019  if (!*cmd)
2020  die("Missing argument in exec");
2021  command->last_argument= command->end;
2022 
2023  /* Eval the command, thus replacing all environment variables */
2024  do_eval(&ds_cmd, cmd, command->end, !is_windows);
2025 
2026  /* Check if echo should be replaced with "builtin" echo */
2027  if (builtin_echo[0] && strncmp(cmd, "echo", 4) == 0)
2028  {
2029  /* Replace echo with our "builtin" echo */
2030  replace(&ds_cmd, "echo", 4, builtin_echo, strlen(builtin_echo));
2031  }
2032 
2033  if (!(res_file= popen(ds_cmd.c_str(), "r")) && command->abort_on_error)
2034  {
2035  die("popen(\"%s\", \"r\") failed", command->first_argument);
2036  }
2037 
2038  while (fgets(buf, sizeof(buf), res_file))
2039  {
2040  if (disable_result_log)
2041  {
2042  buf[strlen(buf)-1]=0;
2043  }
2044  else
2045  {
2046  replace_append(&ds_res, buf);
2047  }
2048  }
2049  error= pclose(res_file);
2050  if (error > 0)
2051  {
2052  uint32_t status= WEXITSTATUS(error), i;
2053  bool ok= 0;
2054 
2055  if (command->abort_on_error)
2056  {
2057  log_msg("exec of '%s' failed, error: %d, status: %d, errno: %d", ds_cmd.c_str(), error, status, errno);
2058  die("command \"%s\" failed", command->first_argument);
2059  }
2060 
2061  for (i= 0; i < command->expected_errors.count; i++)
2062  {
2063  if ((command->expected_errors.err[i].type == ERR_ERRNO) &&
2064  (command->expected_errors.err[i].code.errnum == status))
2065  {
2066  ok= 1;
2067  }
2068  }
2069  if (!ok)
2070  {
2071  die("command \"%s\" failed with wrong error: %d",
2072  command->first_argument, status);
2073  }
2074  }
2075  else if (command->expected_errors.err[0].type == ERR_ERRNO &&
2076  command->expected_errors.err[0].code.errnum != 0)
2077  {
2078  /* Error code we wanted was != 0, i.e. not an expected success */
2079  log_msg("exec of '%s failed, error: %d, errno: %d", ds_cmd.c_str(), error, errno);
2080  die("command \"%s\" succeeded - should have failed with errno %d...",
2081  command->first_argument, command->expected_errors.err[0].code.errnum);
2082  }
2083 
2084  return;
2085 }
2086 
2087 enum enum_operator
2088 {
2089  DO_DEC,
2090  DO_INC
2091 };
2092 
2093 
2094 /*
2095  Decrease or increase the value of a variable
2096 
2097  SYNOPSIS
2098  do_modify_var()
2099  query called command
2100  operator operation to perform on the var
2101 
2102  DESCRIPTION
2103  dec $var_name
2104  inc $var_name
2105 
2106 */
2107 
2108 static int do_modify_var(st_command* command,
2109  enum enum_operator op)
2110 {
2111  const char *p= command->first_argument;
2112  VAR* v;
2113  if (!*p)
2114  die("Missing argument to %.*s", command->first_word_len, command->query);
2115  if (*p != '$')
2116  die("The argument to %.*s must be a variable (start with $)",
2117  command->first_word_len, command->query);
2118  v= var_get(p, &p, 1, 0);
2119  switch (op) {
2120  case DO_DEC:
2121  v->int_val--;
2122  break;
2123  case DO_INC:
2124  v->int_val++;
2125  break;
2126  default:
2127  die("Invalid operator to do_modify_var");
2128  break;
2129  }
2130  v->int_dirty= 1;
2131  command->last_argument= (char*)++p;
2132  return 0;
2133 }
2134 
2135 
2136 /*
2137  SYNOPSIS
2138  do_system
2139  command called command
2140 
2141  DESCRIPTION
2142  system <command>
2143 
2144  Eval the query to expand any $variables in the command.
2145  Execute the command with the "system" command.
2146 
2147 */
2148 
2149 static void do_system(st_command* command)
2150 {
2151  string ds_cmd;
2152 
2153 
2154  if (strlen(command->first_argument) == 0)
2155  die("Missing arguments to system, nothing to do!");
2156 
2157  /* Eval the system command, thus replacing all environment variables */
2158  do_eval(&ds_cmd, command->first_argument, command->end, !is_windows);
2159 
2160  if (system(ds_cmd.c_str()))
2161  {
2162  if (command->abort_on_error)
2163  die("system command '%s' failed", command->first_argument);
2164 
2165  /* If ! abort_on_error, log message and continue */
2166  ds_res += "system command '";
2167  replace_append(&ds_res, command->first_argument);
2168  ds_res += "' failed\n";
2169  }
2170 
2171  command->last_argument= command->end;
2172  return;
2173 }
2174 
2175 
2176 /*
2177  SYNOPSIS
2178  do_remove_file
2179  command called command
2180 
2181  DESCRIPTION
2182  remove_file <file_name>
2183  Remove the file <file_name>
2184 */
2185 
2186 static void do_remove_file(st_command* command)
2187 {
2188  string ds_filename;
2189  const struct command_arg rm_args[] = {
2190  { "filename", ARG_STRING, true, &ds_filename, "File to delete" }
2191  };
2192 
2193 
2194  check_command_args(command, command->first_argument,
2195  rm_args, sizeof(rm_args)/sizeof(struct command_arg),
2196  ' ');
2197 
2198  int error= internal::my_delete(ds_filename.c_str(), MYF(0)) != 0;
2199  handle_command_error(command, error);
2200 }
2201 
2202 
2203 /*
2204  SYNOPSIS
2205  do_copy_file
2206  command command handle
2207 
2208  DESCRIPTION
2209  copy_file <from_file> <to_file>
2210  Copy <from_file> to <to_file>
2211 
2212  NOTE! Will fail if <to_file> exists
2213 */
2214 
2215 static void do_copy_file(st_command* command)
2216 {
2217  string ds_from_file;
2218  string ds_to_file;
2219  const struct command_arg copy_file_args[] = {
2220  { "from_file", ARG_STRING, true, &ds_from_file, "Filename to copy from" },
2221  { "to_file", ARG_STRING, true, &ds_to_file, "Filename to copy to" }
2222  };
2223 
2224 
2225  check_command_args(command, command->first_argument,
2226  copy_file_args,
2227  sizeof(copy_file_args)/sizeof(struct command_arg),
2228  ' ');
2229 
2230  int error= (internal::my_copy(ds_from_file.c_str(), ds_to_file.c_str(),
2231  MYF(MY_DONT_OVERWRITE_FILE)) != 0);
2232  handle_command_error(command, error);
2233 }
2234 
2235 
2236 /*
2237  SYNOPSIS
2238  do_chmod_file
2239  command command handle
2240 
2241  DESCRIPTION
2242  chmod <octal> <file_name>
2243  Change file permission of <file_name>
2244 
2245 */
2246 
2247 static void do_chmod_file(st_command* command)
2248 {
2249  long mode= 0;
2250  string ds_mode;
2251  string ds_file;
2252  const struct command_arg chmod_file_args[] = {
2253  { "mode", ARG_STRING, true, &ds_mode, "Mode of file(octal) ex. 0660"},
2254  { "filename", ARG_STRING, true, &ds_file, "Filename of file to modify" }
2255  };
2256 
2257 
2258  check_command_args(command, command->first_argument,
2259  chmod_file_args,
2260  sizeof(chmod_file_args)/sizeof(struct command_arg),
2261  ' ');
2262 
2263  /* Parse what mode to set */
2264  istringstream buff(ds_mode);
2265  if (ds_mode.length() != 4 ||
2266  (buff >> oct >> mode).fail())
2267  die("You must write a 4 digit octal number for mode");
2268 
2269  handle_command_error(command, chmod(ds_file.c_str(), mode));
2270 }
2271 
2272 
2273 /*
2274  SYNOPSIS
2275  do_file_exists
2276  command called command
2277 
2278  DESCRIPTION
2279  fiile_exist <file_name>
2280  Check if file <file_name> exists
2281 */
2282 
2283 static void do_file_exist(st_command* command)
2284 {
2285  string ds_filename;
2286  const struct command_arg file_exist_args[] = {
2287  { "filename", ARG_STRING, true, &ds_filename, "File to check if it exist" }
2288  };
2289 
2290 
2291  check_command_args(command, command->first_argument,
2292  file_exist_args,
2293  sizeof(file_exist_args)/sizeof(struct command_arg),
2294  ' ');
2295 
2296  int error= access(ds_filename.c_str(), F_OK) != 0;
2297  handle_command_error(command, error);
2298 }
2299 
2300 
2301 /*
2302  SYNOPSIS
2303  do_mkdir
2304  command called command
2305 
2306  DESCRIPTION
2307  mkdir <dir_name>
2308  Create the directory <dir_name>
2309 */
2310 
2311 static void do_mkdir(st_command* command)
2312 {
2313  string ds_dirname;
2314  const struct command_arg mkdir_args[] = {
2315  {"dirname", ARG_STRING, true, &ds_dirname, "Directory to create"}
2316  };
2317 
2318 
2319  check_command_args(command, command->first_argument,
2320  mkdir_args, sizeof(mkdir_args)/sizeof(struct command_arg),
2321  ' ');
2322 
2323  int error= mkdir(ds_dirname.c_str(), (0777 & internal::my_umask_dir)) != 0;
2324  handle_command_error(command, error);
2325 }
2326 
2327 /*
2328  SYNOPSIS
2329  do_rmdir
2330  command called command
2331 
2332  DESCRIPTION
2333  rmdir <dir_name>
2334  Remove the empty directory <dir_name>
2335 */
2336 
2337 static void do_rmdir(st_command* command)
2338 {
2339  string ds_dirname;
2340  const struct command_arg rmdir_args[] = {
2341  {"dirname", ARG_STRING, true, &ds_dirname, "Directory to remove"}
2342  };
2343 
2344 
2345  check_command_args(command, command->first_argument,
2346  rmdir_args, sizeof(rmdir_args)/sizeof(struct command_arg),
2347  ' ');
2348 
2349  int error= rmdir(ds_dirname.c_str()) != 0;
2350  handle_command_error(command, error);
2351 }
2352 
2353 
2354 /*
2355  Read characters from line buffer or file. This is needed to allow
2356  my_ungetc() to buffer MAX_DELIMITER_LENGTH characters for a file
2357 
2358  NOTE:
2359  This works as long as one doesn't change files (with 'source file_name')
2360  when there is things pushed into the buffer. This should however not
2361  happen for any tests in the test suite.
2362 */
2363 
2364 static int my_getc(FILE *file)
2365 {
2366  if (line_buffer_pos == line_buffer)
2367  return fgetc(file);
2368  return *--line_buffer_pos;
2369 }
2370 
2371 
2372 static void my_ungetc(int c)
2373 {
2374  *line_buffer_pos++= (char) c;
2375 }
2376 
2377 
2378 static void read_until_delimiter(string *ds,
2379  string *ds_delimiter)
2380 {
2381  if (ds_delimiter->length() > MAX_DELIMITER_LENGTH)
2382  die("Max delimiter length(%d) exceeded", MAX_DELIMITER_LENGTH);
2383 
2384  /* Read from file until delimiter is found */
2385  while (1)
2386  {
2387  char c= my_getc(cur_file->file);
2388 
2389  if (c == '\n')
2390  {
2391  cur_file->lineno++;
2392 
2393  /* Skip newline from the same line as the command */
2394  if (start_lineno == (cur_file->lineno - 1))
2395  continue;
2396  }
2397  else if (start_lineno == cur_file->lineno)
2398  {
2399  /*
2400  No characters except \n are allowed on
2401  the same line as the command
2402  */
2403  die("Trailing characters found after command");
2404  }
2405 
2406  if (feof(cur_file->file))
2407  die("End of file encountered before '%s' delimiter was found",
2408  ds_delimiter->c_str());
2409 
2410  if (match_delimiter(c, ds_delimiter->c_str(), ds_delimiter->length()))
2411  break;
2412 
2413  ds->push_back(c);
2414  }
2415 }
2416 
2417 
2418 static void do_write_file_command(st_command* command, bool append)
2419 {
2420  string ds_content;
2421  string ds_filename;
2422  string ds_delimiter;
2423  const struct command_arg write_file_args[] = {
2424  { "filename", ARG_STRING, true, &ds_filename, "File to write to" },
2425  { "delimiter", ARG_STRING, false, &ds_delimiter, "Delimiter to read until" }
2426  };
2427 
2428 
2429  check_command_args(command,
2430  command->first_argument,
2431  write_file_args,
2432  sizeof(write_file_args)/sizeof(struct command_arg),
2433  ' ');
2434 
2435  /* If no delimiter was provided, use EOF */
2436  if (ds_delimiter.length() == 0)
2437  ds_delimiter += "EOF";
2438 
2439  if (!append && access(ds_filename.c_str(), F_OK) == 0)
2440  {
2441  /* The file should not be overwritten */
2442  die("File already exist: '%s'", ds_filename.c_str());
2443  }
2444 
2445  read_until_delimiter(&ds_content, &ds_delimiter);
2446  str_to_file2(ds_filename.c_str(), ds_content.c_str(), ds_content.length(), append);
2447 }
2448 
2449 
2450 /*
2451  SYNOPSIS
2452  do_write_file
2453  command called command
2454 
2455  DESCRIPTION
2456  write_file <file_name> [<delimiter>];
2457  <what to write line 1>
2458  <...>
2459  < what to write line n>
2460  EOF
2461 
2462  --write_file <file_name>;
2463  <what to write line 1>
2464  <...>
2465  < what to write line n>
2466  EOF
2467 
2468  Write everything between the "write_file" command and 'delimiter'
2469  to "file_name"
2470 
2471  NOTE! Will fail if <file_name> exists
2472 
2473  Default <delimiter> is EOF
2474 
2475 */
2476 
2477 static void do_write_file(st_command* command)
2478 {
2479  do_write_file_command(command, false);
2480 }
2481 
2482 
2483 /*
2484  SYNOPSIS
2485  do_append_file
2486  command called command
2487 
2488  DESCRIPTION
2489  append_file <file_name> [<delimiter>];
2490  <what to write line 1>
2491  <...>
2492  < what to write line n>
2493  EOF
2494 
2495  --append_file <file_name>;
2496  <what to write line 1>
2497  <...>
2498  < what to write line n>
2499  EOF
2500 
2501  Append everything between the "append_file" command
2502  and 'delimiter' to "file_name"
2503 
2504  Default <delimiter> is EOF
2505 
2506 */
2507 
2508 static void do_append_file(st_command* command)
2509 {
2510  do_write_file_command(command, true);
2511 }
2512 
2513 
2514 /*
2515  SYNOPSIS
2516  do_cat_file
2517  command called command
2518 
2519  DESCRIPTION
2520  cat_file <file_name>;
2521 
2522  Print the given file to result log
2523 
2524 */
2525 
2526 static void do_cat_file(st_command* command)
2527 {
2528  static string ds_filename;
2529  const struct command_arg cat_file_args[] = {
2530  { "filename", ARG_STRING, true, &ds_filename, "File to read from" }
2531  };
2532 
2533 
2534  check_command_args(command,
2535  command->first_argument,
2536  cat_file_args,
2537  sizeof(cat_file_args)/sizeof(struct command_arg),
2538  ' ');
2539 
2540  cat_file(ds_res, ds_filename.c_str());
2541 }
2542 
2543 
2544 /*
2545  SYNOPSIS
2546  do_diff_files
2547  command called command
2548 
2549  DESCRIPTION
2550  diff_files <file1> <file2>;
2551 
2552  Fails if the two files differ.
2553 
2554 */
2555 
2556 static void do_diff_files(st_command* command)
2557 {
2558  string ds_filename;
2559  string ds_filename2;
2560  const struct command_arg diff_file_args[] = {
2561  { "file1", ARG_STRING, true, &ds_filename, "First file to diff" },
2562  { "file2", ARG_STRING, true, &ds_filename2, "Second file to diff" }
2563  };
2564 
2565 
2566  check_command_args(command,
2567  command->first_argument,
2568  diff_file_args,
2569  sizeof(diff_file_args)/sizeof(struct command_arg),
2570  ' ');
2571 
2572  int error= compare_files(ds_filename.c_str(), ds_filename2.c_str());
2573  if (error)
2574  {
2575  /* Compare of the two files failed, append them to output
2576  so the failure can be analyzed
2577  */
2578  show_diff(&ds_res, ds_filename.c_str(), ds_filename2.c_str());
2579  }
2580 
2581  handle_command_error(command, error);
2582 }
2583 
2584 /*
2585  SYNOPSIS
2586  do_send_quit
2587  command called command
2588 
2589  DESCRIPTION
2590  Sends a simple quit command to the server for the named connection.
2591 
2592 */
2593 
2594 static void do_send_quit(st_command* command)
2595 {
2596  char* p= command->first_argument;
2597 
2598  if (not *p)
2599  die("Missing connection name in send_quit");
2600  char* name= p;
2601  while (*p && !charset_info->isspace(*p))
2602  p++;
2603 
2604  if (*p)
2605  *p++= 0;
2606  command->last_argument= p;
2607 
2608  st_connection* con= find_ptr2(g_connections, name);
2609  if (not con)
2610  die("connection '%s' not found in connection pool", name);
2611 
2612  drizzle::result_c result;
2613  drizzle_return_t ret;
2614  drizzle_quit(*con, result, &ret);
2615 }
2616 
2617 
2618 /*
2619  SYNOPSIS
2620  do_change_user
2621  command called command
2622 
2623  DESCRIPTION
2624  change_user [<user>], [<passwd>], [<db>]
2625  <user> - user to change to
2626  <passwd> - user password
2627  <db> - default database
2628 
2629  Changes the user and causes the database specified by db to become
2630  the default (current) database for the the current connection.
2631 
2632 */
2633 
2634 static void do_change_user(st_command *)
2635 {
2636  assert(0);
2637 }
2638 
2639 /*
2640  SYNOPSIS
2641  do_perl
2642  command command handle
2643 
2644  DESCRIPTION
2645  perl [<delimiter>];
2646  <perlscript line 1>
2647  <...>
2648  <perlscript line n>
2649  EOF
2650 
2651  Execute everything after "perl" until <delimiter> as perl.
2652  Useful for doing more advanced things
2653  but still being able to execute it on all platforms.
2654 
2655  Default <delimiter> is EOF
2656 */
2657 
2658 static void do_perl(st_command* command)
2659 {
2660  char buf[FN_REFLEN];
2661  char temp_file_path[FN_REFLEN];
2662  string ds_script;
2663  string ds_delimiter;
2664  const command_arg perl_args[] = {
2665  { "delimiter", ARG_STRING, false, &ds_delimiter, "Delimiter to read until" }
2666  };
2667 
2668 
2669  check_command_args(command,
2670  command->first_argument,
2671  perl_args,
2672  sizeof(perl_args)/sizeof(struct command_arg),
2673  ' ');
2674 
2675  /* If no delimiter was provided, use EOF */
2676  if (ds_delimiter.length() == 0)
2677  ds_delimiter += "EOF";
2678 
2679  read_until_delimiter(&ds_script, &ds_delimiter);
2680 
2681  /* Create temporary file name */
2682  int fd= internal::create_temp_file(temp_file_path, getenv("MYSQLTEST_VARDIR"), "tmp", MYF(MY_WME));
2683  if (fd < 0)
2684  die("Failed to create temporary file for perl command");
2685  internal::my_close(fd, MYF(0));
2686 
2687  str_to_file(temp_file_path, ds_script.c_str(), ds_script.length());
2688 
2689  /* Format the "perl <filename>" command */
2690  snprintf(buf, sizeof(buf), "perl %s", temp_file_path);
2691 
2692  FILE* res_file= popen(buf, "r");
2693  if (not res_file && command->abort_on_error)
2694  die("popen(\"%s\", \"r\") failed", buf);
2695 
2696  while (fgets(buf, sizeof(buf), res_file))
2697  {
2698  if (disable_result_log)
2699  buf[strlen(buf)-1]=0;
2700  else
2701  replace_append(&ds_res, buf);
2702  }
2703  int error= pclose(res_file);
2704 
2705  /* Remove the temporary file */
2706  internal::my_delete(temp_file_path, MYF(0));
2707 
2708  handle_command_error(command, WEXITSTATUS(error));
2709 }
2710 
2711 
2712 /*
2713  Print the content between echo and <delimiter> to result file.
2714  Evaluate all variables in the string before printing, allow
2715  for variable names to be escaped using \
2716 
2717  SYNOPSIS
2718  do_echo()
2719  command called command
2720 
2721  DESCRIPTION
2722  echo text
2723  Print the text after echo until end of command to result file
2724 
2725  echo $<var_name>
2726  Print the content of the variable <var_name> to result file
2727 
2728  echo Some text $<var_name>
2729  Print "Some text" plus the content of the variable <var_name> to
2730  result file
2731 
2732  echo Some text \$<var_name>
2733  Print "Some text" plus $<var_name> to result file
2734 */
2735 
2736 static void do_echo(st_command* command)
2737 {
2738  string ds_echo;
2739  do_eval(&ds_echo, command->first_argument, command->end, false);
2740  ds_res += ds_echo;
2741  ds_res += "\n";
2742  command->last_argument= command->end;
2743 }
2744 
2745 static void do_wait_for_slave_to_stop()
2746 {
2747  static int SLAVE_POLL_INTERVAL= 300000;
2748  drizzle::connection_c& con= *cur_con;
2749  for (;;)
2750  {
2751  drizzle::result_c res;
2752  dt_query(con, res, "show status like 'Slave_running'");
2753  drizzle_row_t row= res.row_next();
2754  if (!row || !row[1])
2755  {
2756  die("Strange result from query while probing slave for stop");
2757  }
2758  if (!strcmp(row[1], "OFF"))
2759  break;
2760  usleep(SLAVE_POLL_INTERVAL);
2761  }
2762 }
2763 
2764 /*
2765  Assign the variable <var_name> with <var_val>
2766 
2767  SYNOPSIS
2768  do_let()
2769  query called command
2770 
2771  DESCRIPTION
2772  let $<var_name>=<var_val><delimiter>
2773 
2774  <var_name> - is the string string found between the $ and =
2775  <var_val> - is the content between the = and <delimiter>, it may span
2776  multiple line and contain any characters except <delimiter>
2777  <delimiter> - is a string containing of one or more chars, default is ;
2778 
2779  RETURN VALUES
2780  Program will die if error detected
2781 */
2782 
2783 static void do_let(st_command* command)
2784 {
2785  char *p= command->first_argument;
2786  char *var_name, *var_name_end;
2787  string let_rhs_expr;
2788 
2789 
2790  /* Find <var_name> */
2791  if (!*p)
2792  die("Missing arguments to let");
2793  var_name= p;
2794  while (*p && (*p != '=') && !charset_info->isspace(*p))
2795  p++;
2796  var_name_end= p;
2797  if (var_name == var_name_end ||
2798  (var_name+1 == var_name_end && *var_name == '$'))
2799  die("Missing variable name in let");
2800  while (charset_info->isspace(*p))
2801  p++;
2802  if (*p++ != '=')
2803  die("Missing assignment operator in let");
2804 
2805  /* Find start of <var_val> */
2806  while (*p && charset_info->isspace(*p))
2807  p++;
2808 
2809  do_eval(&let_rhs_expr, p, command->end, false);
2810 
2811  command->last_argument= command->end;
2812  /* Assign var_val to var_name */
2813  var_set(var_name, var_name_end, let_rhs_expr.c_str(),
2814  (let_rhs_expr.c_str() + let_rhs_expr.length()));
2815  return;
2816 }
2817 
2818 
2819 /*
2820  Sleep the number of specified seconds
2821 
2822  SYNOPSIS
2823  do_sleep()
2824  q called command
2825  real_sleep use the value from opt_sleep as number of seconds to sleep
2826  if real_sleep is false
2827 
2828  DESCRIPTION
2829  sleep <seconds>
2830  real_sleep <seconds>
2831 
2832  The difference between the sleep and real_sleep commands is that sleep
2833  uses the delay from the --sleep command-line option if there is one.
2834  (If the --sleep option is not given, the sleep command uses the delay
2835  specified by its argument.) The real_sleep command always uses the
2836  delay specified by its argument. The logic is that sometimes delays are
2837  cpu-dependent, and --sleep can be used to set this delay. real_sleep is
2838  used for cpu-independent delays.
2839 */
2840 
2841 static void do_sleep(st_command* command, bool real_sleep)
2842 {
2843  char *p= command->first_argument;
2844  char *sleep_start, *sleep_end= command->end;
2845  double sleep_val= 0;
2846 
2847  while (charset_info->isspace(*p))
2848  p++;
2849  if (!*p)
2850  die("Missing argument to %.*s", command->first_word_len, command->query);
2851  sleep_start= p;
2852  /* Check that arg starts with a digit, not handled by internal::my_strtod */
2853  if (!charset_info->isdigit(*sleep_start))
2854  die("Invalid argument to %.*s \"%s\"", command->first_word_len,
2855  command->query,command->first_argument);
2856  string buff_str(sleep_start, sleep_end-sleep_start);
2857  istringstream buff(buff_str);
2858  buff >> sleep_val;
2859  if (buff.fail())
2860  die("Invalid argument to %.*s \"%s\"", command->first_word_len, command->query, command->first_argument);
2861 
2862  /* Fixed sleep time selected by --sleep option */
2863  if (opt_sleep >= 0 && !real_sleep)
2864  sleep_val= opt_sleep;
2865 
2866  if (sleep_val)
2867  usleep(sleep_val * 1000000);
2868  command->last_argument= sleep_end;
2869 }
2870 
2871 
2872 static void do_get_file_name(st_command* command, string &dest)
2873 {
2874  char *p= command->first_argument;
2875  if (!*p)
2876  die("Missing file name argument");
2877  char *name= p;
2878  while (*p && !charset_info->isspace(*p))
2879  p++;
2880  if (*p)
2881  *p++= 0;
2882  command->last_argument= p;
2883  if (! opt_testdir.empty())
2884  {
2885  dest= opt_testdir;
2886  if (dest[dest.length()] != '/')
2887  dest += "/";
2888  }
2889  dest.append(name);
2890 }
2891 
2892 
2893 static void do_set_charset(st_command* command)
2894 {
2895  char *charset_name= command->first_argument;
2896  char *p;
2897 
2898  if (!charset_name || !*charset_name)
2899  die("Missing charset name in 'character_set'");
2900  /* Remove end space */
2901  p= charset_name;
2902  while (*p && !charset_info->isspace(*p))
2903  p++;
2904  if(*p)
2905  *p++= 0;
2906  command->last_argument= p;
2907  charset_info= get_charset_by_csname(charset_name, MY_CS_PRIMARY);
2908  if (!charset_info)
2909  abort_not_supported_test("Test requires charset '%s'", charset_name);
2910 }
2911 
2912 static void fill_global_error_names()
2913 {
2914  drizzle::connection_c& con= *cur_con;
2915 
2916  global_error_names.clear();
2917 
2918  drizzle::result_c res;
2919  dt_query(con, res, "select error_name, error_code from data_dictionary.errors");
2920  while (drizzle_row_t row= res.row_next())
2921  {
2922  if (not row[0])
2923  break;
2924  /*
2925  Concatenate all fields in the first row with tab in between
2926  and assign that string to the $variable
2927  */
2928  size_t *lengths= res.row_field_sizes();
2929  try
2930  {
2931  global_error_names[string(row[0], lengths[0])] = boost::lexical_cast<uint32_t>(string(row[1], lengths[1]));
2932  }
2933  catch (boost::bad_lexical_cast &ex)
2934  {
2935  die("Invalid error_code from Drizzle: %s", ex.what());
2936  }
2937  }
2938 }
2939 
2940 static uint32_t get_errcode_from_name(const char *error_name, const char *error_end)
2941 {
2942  string error_name_s(error_name, error_end);
2943 
2944  if (ErrorCodes::mapped_type* ptr= find_ptr(global_error_names, error_name_s))
2945  return *ptr;
2946 
2947  die("Unknown SQL error name '%s'", error_name_s.c_str());
2948  return 0;
2949 }
2950 
2951 static void do_get_errcodes(st_command* command)
2952 {
2953  struct st_match_err *to= saved_expected_errors.err;
2954  char *p= command->first_argument;
2955  uint32_t count= 0;
2956 
2957 
2958 
2959  if (!*p)
2960  die("Missing argument(s) to 'error'");
2961 
2962  do
2963  {
2964  char *end;
2965 
2966  /* Skip leading spaces */
2967  while (*p && *p == ' ')
2968  p++;
2969 
2970  /* Find end */
2971  end= p;
2972  while (*end && *end != ',' && *end != ' ')
2973  end++;
2974 
2975  if (*p == 'S')
2976  {
2977  char *to_ptr= to->code.sqlstate;
2978 
2979  /*
2980  SQLSTATE string
2981  - Must be DRIZZLE_MAX_SQLSTATE_SIZE long
2982  - May contain only digits[0-9] and _uppercase_ letters
2983  */
2984  p++; /* Step past the S */
2985  if ((end - p) != DRIZZLE_MAX_SQLSTATE_SIZE)
2986  die("The sqlstate must be exactly %d chars long", DRIZZLE_MAX_SQLSTATE_SIZE);
2987 
2988  /* Check sqlstate string validity */
2989  while (*p && p < end)
2990  {
2991  if (charset_info->isdigit(*p) || charset_info->isupper(*p))
2992  *to_ptr++= *p++;
2993  else
2994  die("The sqlstate may only consist of digits[0-9] and _uppercase_ letters");
2995  }
2996 
2997  *to_ptr= 0;
2998  to->type= ERR_SQLSTATE;
2999  }
3000  else if (*p == 's')
3001  {
3002  die("The sqlstate definition must start with an uppercase S");
3003  }
3004  else if (*p == 'E')
3005  {
3006  /* Error name string */
3007 
3008  to->code.errnum= get_errcode_from_name(p, end);
3009  to->type= ERR_ERRNO;
3010  }
3011  else if (*p == 'e')
3012  {
3013  die("The error name definition must start with an uppercase E");
3014  }
3015  else if (*p == 'H')
3016  {
3017  /* Error name string */
3018 
3019  to->code.errnum= get_errcode_from_name(p, end);
3020  to->type= ERR_ERRNO;
3021  }
3022  else
3023  {
3024  die ("You must either use the SQLSTATE or built in drizzle error label, numbers are not accepted");
3025  }
3026  to++;
3027  count++;
3028 
3029  if (count >= (sizeof(saved_expected_errors.err) /
3030  sizeof(struct st_match_err)))
3031  die("Too many errorcodes specified");
3032 
3033  /* Set pointer to the end of the last error code */
3034  p= end;
3035 
3036  /* Find next ',' */
3037  while (*p && *p != ',')
3038  p++;
3039 
3040  if (*p)
3041  p++; /* Step past ',' */
3042 
3043  } while (*p);
3044 
3045  command->last_argument= p;
3046  to->type= ERR_EMPTY; /* End of data */
3047 
3048  saved_expected_errors.count= count;
3049  return;
3050 }
3051 
3052 
3053 /*
3054  Get a string; Return ptr to end of string
3055  Strings may be surrounded by " or '
3056 
3057  If string is a '$variable', return the value of the variable.
3058 */
3059 
3060 static char *get_string(char **to_ptr, char **from_ptr,
3061  st_command* command)
3062 {
3063  char c, sep;
3064  char *to= *to_ptr, *from= *from_ptr, *start=to;
3065 
3066 
3067  /* Find separator */
3068  if (*from == '"' || *from == '\'')
3069  sep= *from++;
3070  else
3071  sep=' '; /* Separated with space */
3072 
3073  for ( ; (c=*from) ; from++)
3074  {
3075  if (c == '\\' && from[1])
3076  { /* Escaped character */
3077  /* We can't translate \0 -> ASCII 0 as replace can't handle ASCII 0 */
3078  switch (*++from) {
3079  case 'n':
3080  *to++= '\n';
3081  break;
3082  case 't':
3083  *to++= '\t';
3084  break;
3085  case 'r':
3086  *to++ = '\r';
3087  break;
3088  case 'b':
3089  *to++ = '\b';
3090  break;
3091  case 'Z': /* ^Z must be escaped on Win32 */
3092  *to++='\032';
3093  break;
3094  default:
3095  *to++ = *from;
3096  break;
3097  }
3098  }
3099  else if (c == sep)
3100  {
3101  if (c == ' ' || c != *++from)
3102  break; /* Found end of string */
3103  *to++=c; /* Copy duplicated separator */
3104  }
3105  else
3106  *to++=c;
3107  }
3108  if (*from != ' ' && *from)
3109  die("Wrong string argument in %s", command->query);
3110 
3111  while (charset_info->isspace(*from)) /* Point to next string */
3112  from++;
3113 
3114  *to =0; /* End of string marker */
3115  *to_ptr= to+1; /* Store pointer to end */
3116  *from_ptr= from;
3117 
3118  /* Check if this was a variable */
3119  if (*start == '$')
3120  {
3121  const char *end= to;
3122  VAR *var=var_get(start, &end, 0, 1);
3123  if (var && to == (char*) end+1)
3124  return(var->str_val); /* return found variable value */
3125  }
3126  return(start);
3127 }
3128 
3129 
3130 static void set_reconnect(drizzle_con_st *con, int val)
3131 {
3132  (void) con;
3133  (void) val;
3134 /* XXX
3135  bool reconnect= val;
3136 
3137  drizzleclient_options(drizzle, DRIZZLE_OPT_RECONNECT, (char *)&reconnect);
3138 */
3139 }
3140 
3141 
3142 static void select_connection_name(const char *name)
3143 {
3144  if (!(cur_con= find_ptr2(g_connections, name)))
3145  die("connection '%s' not found in connection pool", name);
3146 
3147  /* Update $drizzleclient_get_server_version to that of current connection */
3148  var_set_drizzleclient_get_server_version(*cur_con);
3149 }
3150 
3151 
3152 static void select_connection(st_command* command)
3153 {
3154  char *p= command->first_argument;
3155  if (!*p)
3156  die("Missing connection name in connect");
3157  char* name= p;
3158  while (*p && !charset_info->isspace(*p))
3159  p++;
3160  if (*p)
3161  *p++= 0;
3162  command->last_argument= p;
3163  select_connection_name(name);
3164 }
3165 
3166 
3167 static void do_close_connection(st_command* command)
3168 {
3169  char* p= command->first_argument;
3170  if (!*p)
3171  die("Missing connection name in disconnect");
3172  char* name= p;
3173  while (*p && !charset_info->isspace(*p))
3174  p++;
3175 
3176  if (*p)
3177  *p++= 0;
3178  command->last_argument= p;
3179 
3180  st_connection* con= find_ptr2(g_connections, name);
3181  if (!con)
3182  die("connection '%s' not found in connection pool", name);
3183  g_connections.erase(name);
3184  delete con;
3185 }
3186 
3187 
3188 /*
3189  Connect to a server doing several retries if needed.
3190 
3191  SYNOPSIS
3192  safe_connect()
3193  con - connection structure to be used
3194  host, user, pass, - connection parameters
3195  db, port, sock
3196 
3197  NOTE
3198 
3199  Sometimes in a test the client starts before
3200  the server - to solve the problem, we try again
3201  after some sleep if connection fails the first
3202  time
3203 
3204  This function will try to connect to the given server
3205  "opt_max_connect_retries" times and sleep "connection_retry_sleep"
3206  seconds between attempts before finally giving up.
3207  This helps in situation when the client starts
3208  before the server (which happens sometimes).
3209  It will only ignore connection errors during these retries.
3210 
3211 */
3212 
3213 static st_connection* safe_connect(const char *name, const string host, const string user, const char *pass, const string db, uint32_t port)
3214 {
3215  uint32_t failed_attempts= 0;
3216  st_connection* con0= new st_connection;
3217  drizzle_con_st* con= *con0;
3218  drizzle_con_set_tcp(con, host.c_str(), port);
3219  drizzle_con_set_auth(con, user.c_str(), pass);
3220  while (drizzle_return_t ret= drizzle_con_connect(con))
3221  {
3222  /*
3223  Connect failed
3224 
3225  Only allow retry if this was an error indicating the server
3226  could not be contacted. Error code differs depending
3227  on protocol/connection type
3228  */
3229 
3230  if ((ret == DRIZZLE_RETURN_GETADDRINFO ||
3231  ret == DRIZZLE_RETURN_COULD_NOT_CONNECT) &&
3232  failed_attempts < opt_max_connect_retries)
3233  {
3234  verbose_msg("Connect attempt %d/%d failed: %d: %s", failed_attempts, opt_max_connect_retries, ret, drizzle_con_error(con));
3235  usleep(100000);
3236  }
3237  else
3238  {
3239  if (failed_attempts > 0)
3240  die("Could not open connection '%s' after %d attempts: %d %s", name, failed_attempts, ret, drizzle_con_error(con));
3241  else
3242  die("Could not open connection '%s': %d %s", name, ret, drizzle_con_error(con));
3243  }
3244  failed_attempts++;
3245  }
3246 
3247  {
3248  std::string sql_string("CREATE SCHEMA IF NOT EXISTS mysql");
3249  drizzle_return_t ret;
3250  drizzle_result_st *result= drizzle_query(con, NULL, sql_string.c_str(), sql_string.size(), &ret);
3251  if (ret != DRIZZLE_RETURN_OK)
3252  {
3253  die("Failed to create schema '%s': %d %s", db.c_str(), ret, drizzle_con_error(con));
3254  }
3255 
3256  if (result)
3257  {
3258  drizzle_result_free(result);
3259  }
3260  }
3261 
3262  {
3263  std::string sql_string("CREATE SCHEMA IF NOT EXISTS ");
3264  sql_string+= db;
3265  drizzle_return_t ret;
3266  drizzle_result_st *result= drizzle_query(con, NULL, sql_string.c_str(), sql_string.size(), &ret);
3267  if (ret != DRIZZLE_RETURN_OK)
3268  {
3269  die("Failed to create schema '%s': %d %s", db.c_str(), ret, drizzle_con_error(con));
3270  }
3271 
3272  if (result)
3273  {
3274  drizzle_result_free(result);
3275  }
3276  }
3277 
3278  {
3279  std::string sql_string("USE ");
3280  sql_string+= db;
3281  drizzle_return_t ret;
3282  drizzle_result_st *result= drizzle_query(con, NULL, sql_string.c_str(), sql_string.size(), &ret);
3283  if (ret != DRIZZLE_RETURN_OK)
3284  {
3285  die("Failed to use schema '%s': %d %s", db.c_str(), ret, drizzle_con_error(con));
3286  }
3287 
3288  if (result)
3289  {
3290  drizzle_result_free(result);
3291  }
3292  }
3293 
3294  return con0;
3295 }
3296 
3297 
3298 /*
3299  Connect to a server and handle connection errors in case they occur.
3300 
3301  SYNOPSIS
3302  connect_n_handle_errors()
3303  q - context of connect "query" (command)
3304  con - connection structure to be used
3305  host, user, pass, - connection parameters
3306  db, port, sock
3307 
3308  DESCRIPTION
3309  This function will try to establish a connection to server and handle
3310  possible errors in the same manner as if "connect" was usual SQL-statement
3311  (If error is expected it will ignore it once it occurs and log the
3312  "statement" to the query log).
3313  Unlike safe_connect() it won't do several attempts.
3314 
3315  RETURN VALUES
3316  1 - Connected
3317  0 - Not connected
3318 
3319 */
3320 
3321 static int connect_n_handle_errors(st_command* command,
3322  drizzle_con_st *con, const char* host,
3323  const char* user, const char* pass,
3324  const char* db, int port, const char* sock)
3325 {
3326  /* Only log if an error is expected */
3327  if (!command->abort_on_error &&
3328  !disable_query_log)
3329  {
3330  /*
3331  Log the connect to result log
3332  */
3333  ds_res += "connect(";
3334  replace_append(&ds_res, host);
3335  ds_res += ",";
3336  replace_append(&ds_res, user);
3337  ds_res += ",";
3338  replace_append(&ds_res, pass);
3339  ds_res += ",";
3340  if (db)
3341  replace_append(&ds_res, db);
3342  ds_res += ",";
3343  replace_append_uint(ds_res, port);
3344  ds_res += ",";
3345  if (sock)
3346  replace_append(&ds_res, sock);
3347  ds_res += ")";
3348  ds_res += delimiter;
3349  ds_res += "\n";
3350  }
3351  drizzle_con_set_tcp(con, host, port);
3352  drizzle_con_set_auth(con, user, pass);
3353  drizzle_con_set_db(con, db);
3354  if (drizzle_return_t ret= drizzle_con_connect(con))
3355  {
3356  if (ret == DRIZZLE_RETURN_HANDSHAKE_FAILED)
3357  {
3358  var_set_errno(drizzle_con_error_code(con));
3359  handle_error(command, drizzle_con_error_code(con), drizzle_con_error(con), drizzle_con_sqlstate(con), &ds_res);
3360  }
3361  else
3362  {
3363  var_set_errno(ret);
3364  handle_error(command, ret, drizzle_con_error(con), "", &ds_res);
3365  }
3366  return 0; /* Not connected */
3367  }
3368  var_set_errno(0);
3369  handle_no_error(command);
3370  return 1; /* Connected */
3371 }
3372 
3373 
3374 /*
3375  Open a new connection to DRIZZLE Server with the parameters
3376  specified. Make the new connection the current connection.
3377 
3378  SYNOPSIS
3379  do_connect()
3380  q called command
3381 
3382  DESCRIPTION
3383  connect(<name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]]);
3384  connect <name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]];
3385 
3386  <name> - name of the new connection
3387  <host> - hostname of server
3388  <user> - user to connect as
3389  <pass> - password used when connecting
3390  <db> - initial db when connected
3391  <port> - server port
3392  <sock> - server socket
3393  <opts> - options to use for the connection
3394  * SSL - use SSL if available
3395  * COMPRESS - use compression if available
3396 
3397  */
3398 
3399 static void do_connect(st_command* command)
3400 {
3401  uint32_t con_port= opt_port;
3402 
3403  string ds_connection_name;
3404  string ds_host;
3405  string ds_user;
3406  string ds_password;
3407  string ds_database;
3408  string ds_port;
3409  string ds_sock;
3410  string ds_options;
3411  const struct command_arg connect_args[] = {
3412  { "connection name", ARG_STRING, true, &ds_connection_name, "Name of the connection" },
3413  { "host", ARG_STRING, true, &ds_host, "Host to connect to" },
3414  { "user", ARG_STRING, false, &ds_user, "User to connect as" },
3415  { "passsword", ARG_STRING, false, &ds_password, "Password used when connecting" },
3416  { "database", ARG_STRING, false, &ds_database, "Database to select after connect" },
3417  { "port", ARG_STRING, false, &ds_port, "Port to connect to" },
3418  { "socket", ARG_STRING, false, &ds_sock, "Socket to connect with" },
3419  { "options", ARG_STRING, false, &ds_options, "Options to use while connecting" }
3420  };
3421 
3422  strip_parentheses(command);
3423  check_command_args(command, command->first_argument, connect_args,
3424  sizeof(connect_args)/sizeof(struct command_arg),
3425  ',');
3426 
3427  /* Port */
3428  if (ds_port.length())
3429  {
3430  con_port= atoi(ds_port.c_str());
3431  if (con_port == 0)
3432  die("Illegal argument for port: '%s'", ds_port.c_str());
3433  }
3434 
3435  /* Sock */
3436  if (!ds_sock.empty())
3437  {
3438  /*
3439  If the socket is specified just as a name without path
3440  append tmpdir in front
3441  */
3442  if (*ds_sock.c_str() != FN_LIBCHAR)
3443  {
3444  char buff[FN_REFLEN];
3445  internal::fn_format(buff, ds_sock.c_str(), TMPDIR, "", 0);
3446  ds_sock= buff;
3447  }
3448  }
3449 
3450  /* Options */
3451  const char* con_options= ds_options.c_str();
3452  while (*con_options)
3453  {
3454  /* Step past any spaces in beginning of option*/
3455  while (*con_options && charset_info->isspace(*con_options))
3456  con_options++;
3457  /* Find end of this option */
3458  const char* end= con_options;
3459  while (*end && !charset_info->isspace(*end))
3460  end++;
3461  die("Illegal option to connect: %.*s", (int) (end - con_options), con_options);
3462  /* Process next option */
3463  con_options= end;
3464  }
3465 
3466  if (find_ptr2(g_connections, ds_connection_name))
3467  die("Connection %s already exists", ds_connection_name.c_str());
3468 
3469  st_connection* con_slot= new st_connection;
3470 
3471  /* Use default db name */
3472  if (ds_database.empty())
3473  {
3474  ds_database= opt_db;
3475  }
3476 
3477  /* Special database to allow one to connect without a database name */
3478  if (ds_database == "*NO-ONE*")
3479  {
3480  ds_database.clear();
3481  }
3482 
3483  if (connect_n_handle_errors(command, *con_slot, ds_host.c_str(), ds_user.c_str(),
3484  ds_password.c_str(), ds_database.c_str(), con_port, ds_sock.c_str()))
3485  {
3486  g_connections[ds_connection_name]= con_slot;
3487  cur_con= con_slot;
3488  }
3489 
3490  /* Update $drizzleclient_get_server_version to that of current connection */
3491  var_set_drizzleclient_get_server_version(*cur_con);
3492 }
3493 
3494 
3495 static void do_done(st_command* command)
3496 {
3497  /* Check if empty block stack */
3498  if (cur_block == block_stack)
3499  {
3500  if (*command->query != '}')
3501  die("Stray 'end' command - end of block before beginning");
3502  die("Stray '}' - end of block before beginning");
3503  }
3504 
3505  /* Test if inner block has been executed */
3506  if (cur_block->ok && cur_block->cmd == cmd_while)
3507  {
3508  /* Pop block from stack, re-execute outer block */
3509  cur_block--;
3510  parser.current_line = cur_block->line;
3511  }
3512  else
3513  {
3514  /* Pop block from stack, goto next line */
3515  cur_block--;
3516  parser.current_line++;
3517  }
3518 }
3519 
3520 
3521 /*
3522  Process start of a "if" or "while" statement
3523 
3524  SYNOPSIS
3525  do_block()
3526  cmd Type of block
3527  q called command
3528 
3529  DESCRIPTION
3530  if ([!]<expr>)
3531  {
3532  <block statements>
3533  }
3534 
3535  while ([!]<expr>)
3536  {
3537  <block statements>
3538  }
3539 
3540  Evaluates the <expr> and if it evaluates to
3541  greater than zero executes the following code block.
3542  A '!' can be used before the <expr> to indicate it should
3543  be executed if it evaluates to zero.
3544 
3545 */
3546 
3547 static void do_block(enum block_cmd cmd, st_command* command)
3548 {
3549  char *p= command->first_argument;
3550  const char *expr_start, *expr_end;
3551  const char *cmd_name= (cmd == cmd_while ? "while" : "if");
3552  bool not_expr= false;
3553 
3554  /* Check stack overflow */
3555  if (cur_block == block_stack_end)
3556  die("Nesting too deeply");
3557 
3558  /* Set way to find outer block again, increase line counter */
3559  cur_block->line= parser.current_line++;
3560 
3561  /* If this block is ignored */
3562  if (!cur_block->ok)
3563  {
3564  /* Inner block should be ignored too */
3565  cur_block++;
3566  cur_block->cmd= cmd;
3567  cur_block->ok= false;
3568  return;
3569  }
3570 
3571  /* Parse and evaluate test expression */
3572  expr_start= strchr(p, '(');
3573  if (!expr_start++)
3574  die("missing '(' in %s", cmd_name);
3575 
3576  /* Check for !<expr> */
3577  if (*expr_start == '!')
3578  {
3579  not_expr= true;
3580  expr_start++; /* Step past the '!' */
3581  }
3582  /* Find ending ')' */
3583  expr_end= strrchr(expr_start, ')');
3584  if (!expr_end)
3585  die("missing ')' in %s", cmd_name);
3586  p= (char*)expr_end+1;
3587 
3588  while (*p && charset_info->isspace(*p))
3589  p++;
3590  if (*p && *p != '{')
3591  die("Missing '{' after %s. Found \"%s\"", cmd_name, p);
3592 
3593  VAR v;
3594  var_init(&v,0,0,0,0);
3595  eval_expr(&v, expr_start, &expr_end);
3596 
3597  /* Define inner block */
3598  cur_block++;
3599  cur_block->cmd= cmd;
3600  cur_block->ok= (v.int_val ? true : false);
3601 
3602  if (not_expr)
3603  cur_block->ok = !cur_block->ok;
3604 
3605  free(v.str_val);
3606  free(v.env_s);
3607 }
3608 
3609 
3610 static void do_delimiter(st_command* command)
3611 {
3612  char* p= command->first_argument;
3613 
3614  while (*p && charset_info->isspace(*p))
3615  p++;
3616 
3617  if (!(*p))
3618  {
3619  die("Can't set empty delimiter");
3620  }
3621 
3622  strncpy(delimiter, p, sizeof(delimiter) - 1);
3623  delimiter_length= strlen(delimiter);
3624 
3625  command->last_argument= p + delimiter_length;
3626 }
3627 
3628 
3629 bool match_delimiter(int c, const char *delim, uint32_t length)
3630 {
3631  uint32_t i;
3632  char tmp[MAX_DELIMITER_LENGTH];
3633 
3634  if (c != *delim)
3635  return 0;
3636 
3637  for (i= 1; i < length &&
3638  (c= my_getc(cur_file->file)) == *(delim + i);
3639  i++)
3640  tmp[i]= c;
3641 
3642  if (i == length)
3643  return 1; /* Found delimiter */
3644 
3645  /* didn't find delimiter, push back things that we read */
3646  my_ungetc(c);
3647  while (i > 1)
3648  my_ungetc(tmp[--i]);
3649  return 0;
3650 }
3651 
3652 
3653 static bool end_of_query(int c)
3654 {
3655  return match_delimiter(c, delimiter, delimiter_length);
3656 }
3657 
3658 
3659 /*
3660  Read one "line" from the file
3661 
3662  SYNOPSIS
3663  read_line
3664  buf buffer for the read line
3665  size size of the buffer i.e max size to read
3666 
3667  DESCRIPTION
3668  This function actually reads several lines and adds them to the
3669  buffer buf. It continues to read until it finds what it believes
3670  is a complete query.
3671 
3672  Normally that means it will read lines until it reaches the
3673  "delimiter" that marks end of query. Default delimiter is ';'
3674  The function should be smart enough not to detect delimiter's
3675  found inside strings surrounded with '"' and '\'' escaped strings.
3676 
3677  If the first line in a query starts with '#' or '-' this line is treated
3678  as a comment. A comment is always terminated when end of line '\n' is
3679  reached.
3680 
3681 */
3682 
3683 
3684 static int my_strnncoll_simple(const charset_info_st * const cs, const unsigned char *s, size_t slen,
3685  const unsigned char *t, size_t tlen,
3686  bool t_is_prefix)
3687 {
3688  size_t len = ( slen > tlen ) ? tlen : slen;
3689  unsigned char *map= cs->sort_order;
3690  if (t_is_prefix && slen > tlen)
3691  slen=tlen;
3692  while (len--)
3693  {
3694  if (map[*s++] != map[*t++])
3695  return ((int) map[s[-1]] - (int) map[t[-1]]);
3696  }
3697  /*
3698  We can't use (slen - tlen) here as the result may be outside of the
3699  precision of a signed int
3700  */
3701  return slen > tlen ? 1 : slen < tlen ? -1 : 0 ;
3702 }
3703 
3704 static int read_line(char *buf, int size)
3705 {
3706  char c, last_quote= 0;
3707  char *p= buf, *buf_end= buf + size - 1;
3708  int skip_char= 0;
3709  enum {R_NORMAL, R_Q, R_SLASH_IN_Q,
3710  R_COMMENT, R_LINE_START} state= R_LINE_START;
3711 
3712 
3713  start_lineno= cur_file->lineno;
3714  for (; p < buf_end ;)
3715  {
3716  skip_char= 0;
3717  c= my_getc(cur_file->file);
3718  if (feof(cur_file->file))
3719  {
3720  found_eof:
3721  if (cur_file->file != stdin)
3722  {
3723  fclose(cur_file->file);
3724  cur_file->file= 0;
3725  }
3726  free((unsigned char*) cur_file->file_name);
3727  cur_file->file_name= 0;
3728  if (cur_file == file_stack.data())
3729  {
3730  /* We're back at the first file, check if
3731  all { have matching }
3732  */
3733  if (cur_block != block_stack)
3734  die("Missing end of block");
3735 
3736  *p= 0;
3737  return(1);
3738  }
3739  cur_file--;
3740  start_lineno= cur_file->lineno;
3741  continue;
3742  }
3743 
3744  if (c == '\n')
3745  {
3746  /* Line counting is independent of state */
3747  cur_file->lineno++;
3748 
3749  /* Convert cr/lf to lf */
3750  if (p != buf && *(p-1) == '\r')
3751  p--;
3752  }
3753 
3754  switch(state) {
3755  case R_NORMAL:
3756  if (end_of_query(c))
3757  {
3758  *p= 0;
3759  return(0);
3760  }
3761  else if ((c == '{' &&
3762  (!my_strnncoll_simple(charset_info, (const unsigned char*) "while", 5,
3763  (unsigned char*) buf, min((ptrdiff_t)5, p - buf), 0) ||
3764  !my_strnncoll_simple(charset_info, (const unsigned char*) "if", 2,
3765  (unsigned char*) buf, min((ptrdiff_t)2, p - buf), 0))))
3766  {
3767  /* Only if and while commands can be terminated by { */
3768  *p++= c;
3769  *p= 0;
3770  return(0);
3771  }
3772  else if (c == '\'' || c == '"' || c == '`')
3773  {
3774  last_quote= c;
3775  state= R_Q;
3776  }
3777  break;
3778 
3779  case R_COMMENT:
3780  if (c == '\n')
3781  {
3782  /* Comments are terminated by newline */
3783  *p= 0;
3784  return(0);
3785  }
3786  break;
3787 
3788  case R_LINE_START:
3789  if (c == '#' || c == '-')
3790  {
3791  /* A # or - in the first position of the line - this is a comment */
3792  state = R_COMMENT;
3793  }
3794  else if (charset_info->isspace(c))
3795  {
3796  /* Skip all space at begining of line */
3797  if (c == '\n')
3798  {
3799  /* Query hasn't started yet */
3800  start_lineno= cur_file->lineno;
3801  }
3802  skip_char= 1;
3803  }
3804  else if (end_of_query(c))
3805  {
3806  *p= 0;
3807  return(0);
3808  }
3809  else if (c == '}')
3810  {
3811  /* A "}" need to be by itself in the begining of a line to terminate */
3812  *p++= c;
3813  *p= 0;
3814  return(0);
3815  }
3816  else if (c == '\'' || c == '"' || c == '`')
3817  {
3818  last_quote= c;
3819  state= R_Q;
3820  }
3821  else
3822  state= R_NORMAL;
3823  break;
3824 
3825  case R_Q:
3826  if (c == last_quote)
3827  state= R_NORMAL;
3828  else if (c == '\\')
3829  state= R_SLASH_IN_Q;
3830  break;
3831 
3832  case R_SLASH_IN_Q:
3833  state= R_Q;
3834  break;
3835 
3836  }
3837 
3838  if (!skip_char)
3839  {
3840  /* Could be a multibyte character */
3841  /* This code is based on the code in "sql_load.cc" */
3842  int charlen = my_mbcharlen(charset_info, c);
3843  /* We give up if multibyte character is started but not */
3844  /* completed before we pass buf_end */
3845  if ((charlen > 1) && (p + charlen) <= buf_end)
3846  {
3847  int i;
3848  char* mb_start = p;
3849 
3850  *p++ = c;
3851 
3852  for (i= 1; i < charlen; i++)
3853  {
3854  if (feof(cur_file->file))
3855  goto found_eof;
3856  c= my_getc(cur_file->file);
3857  *p++ = c;
3858  }
3859  if (! my_ismbchar(charset_info, mb_start, p))
3860  {
3861  /* It was not a multiline char, push back the characters */
3862  /* We leave first 'c', i.e. pretend it was a normal char */
3863  while (p > mb_start)
3864  my_ungetc(*--p);
3865  }
3866  }
3867  else
3868  *p++= c;
3869  }
3870  }
3871  die("The input buffer is too small for this query.x\n" \
3872  "check your query or increase MAX_QUERY and recompile");
3873  return(0);
3874 }
3875 
3876 
3877 /*
3878  Convert the read query to result format version 1
3879 
3880  That is: After newline, all spaces need to be skipped
3881  unless the previous char was a quote
3882 
3883  This is due to an old bug that has now been fixed, but the
3884  version 1 output format is preserved by using this function
3885 
3886 */
3887 
3888 static void convert_to_format_v1(char* query)
3889 {
3890  int last_c_was_quote= 0;
3891  char *p= query, *to= query;
3892  char *end= strchr(query, '\0');
3893  char last_c;
3894 
3895  while (p <= end)
3896  {
3897  if (*p == '\n' && !last_c_was_quote)
3898  {
3899  *to++ = *p++; /* Save the newline */
3900 
3901  /* Skip any spaces on next line */
3902  while (*p && charset_info->isspace(*p))
3903  p++;
3904 
3905  last_c_was_quote= 0;
3906  }
3907  else if (*p == '\'' || *p == '"' || *p == '`')
3908  {
3909  last_c= *p;
3910  *to++ = *p++;
3911 
3912  /* Copy anything until the next quote of same type */
3913  while (*p && *p != last_c)
3914  *to++ = *p++;
3915 
3916  *to++ = *p++;
3917 
3918  last_c_was_quote= 1;
3919  }
3920  else
3921  {
3922  *to++ = *p++;
3923  last_c_was_quote= 0;
3924  }
3925  }
3926 }
3927 
3928 
3929 /*
3930  Check a command that is about to be sent (or should have been
3931  sent if parsing was enabled) to DRIZZLE server for
3932  suspicious things and generate warnings.
3933 */
3934 
3935 static void scan_command_for_warnings(st_command* command)
3936 {
3937  const char *ptr= command->query;
3938 
3939  while (*ptr)
3940  {
3941  /*
3942  Look for query's that lines that start with a -- comment
3943  and has a drizzletest command
3944  */
3945  if (ptr[0] == '\n' &&
3946  ptr[1] && ptr[1] == '-' &&
3947  ptr[2] && ptr[2] == '-' &&
3948  ptr[3])
3949  {
3950  uint32_t type;
3951  char save;
3952  char *end, *start= (char*)ptr+3;
3953  /* Skip leading spaces */
3954  while (*start && charset_info->isspace(*start))
3955  start++;
3956  end= start;
3957  /* Find end of command(next space) */
3958  while (*end && !charset_info->isspace(*end))
3959  end++;
3960  save= *end;
3961  *end= 0;
3962  type= command_typelib.find_type(start, TYPELIB::e_default);
3963  if (type)
3964  warning_msg("Embedded drizzletest command '--%s' detected in query '%s' was this intentional? ", start, command->query);
3965  *end= save;
3966  }
3967  ptr++;
3968  }
3969 }
3970 
3971 /*
3972  Check for unexpected "junk" after the end of query
3973  This is normally caused by missing delimiters or when
3974  switching between different delimiters
3975 */
3976 
3977 static void check_eol_junk_line(const char *line)
3978 {
3979  const char *p= line;
3980 
3981  /* Check for extra delimiter */
3982  if (*p && !strncmp(p, delimiter, delimiter_length))
3983  die("Extra delimiter \"%s\" found", delimiter);
3984 
3985  /* Allow trailing # comment */
3986  if (*p && *p != '#')
3987  {
3988  if (*p == '\n')
3989  die("Missing delimiter");
3990  die("End of line junk detected: \"%s\"", p);
3991  }
3992  return;
3993 }
3994 
3995 static void check_eol_junk(const char *eol)
3996 {
3997  const char *p= eol;
3998 
3999  /* Skip past all spacing chars and comments */
4000  while (*p && (charset_info->isspace(*p) || *p == '#' || *p == '\n'))
4001  {
4002  /* Skip past comments started with # and ended with newline */
4003  if (*p && *p == '#')
4004  {
4005  p++;
4006  while (*p && *p != '\n')
4007  p++;
4008  }
4009 
4010  /* Check this line */
4011  if (*p && *p == '\n')
4012  check_eol_junk_line(p);
4013 
4014  if (*p)
4015  p++;
4016  }
4017 
4018  check_eol_junk_line(p);
4019 
4020  return;
4021 }
4022 
4023 /*
4024  Create a command from a set of lines
4025 
4026  SYNOPSIS
4027  read_command()
4028  command_ptr pointer where to return the new query
4029 
4030  DESCRIPTION
4031  Converts lines returned by read_line into a command, this involves
4032  parsing the first word in the read line to find the command type.
4033 
4034  A -- comment may contain a valid query as the first word after the
4035  comment start. Thus it's always checked to see if that is the case.
4036  The advantage with this approach is to be able to execute commands
4037  terminated by new line '\n' regardless how many "delimiter" it contain.
4038 */
4039 
4040 #define MAX_QUERY (768*1024*2) /* 256K -- a test in sp-big is >128K */
4041 static char read_command_buf[MAX_QUERY];
4042 
4043 static int read_command(st_command** command_ptr)
4044 {
4045  char *p= read_command_buf;
4046  st_command* command;
4047 
4048 
4049  if (parser.current_line < parser.read_lines)
4050  {
4051  *command_ptr= q_lines[parser.current_line];
4052  return(0);
4053  }
4054  *command_ptr= command= new st_command;
4055  q_lines.push_back(command);
4056  command->type= Q_UNKNOWN;
4057 
4058  read_command_buf[0]= 0;
4059  if (read_line(read_command_buf, sizeof(read_command_buf)))
4060  {
4061  check_eol_junk(read_command_buf);
4062  return(1);
4063  }
4064 
4065  convert_to_format_v1(read_command_buf);
4066 
4067  if (*p == '#')
4068  {
4069  command->type= Q_COMMENT;
4070  }
4071  else if (p[0] == '-' && p[1] == '-')
4072  {
4073  command->type= Q_COMMENT_WITH_COMMAND;
4074  p+= 2; /* Skip past -- */
4075  }
4076 
4077  /* Skip leading spaces */
4078  while (*p && charset_info->isspace(*p))
4079  p++;
4080 
4081  command->query_buf= command->query= strdup(p);
4082 
4083  /* Calculate first word length(the command), terminated by space or ( */
4084  p= command->query;
4085  while (*p && !charset_info->isspace(*p) && *p != '(')
4086  p++;
4087  command->first_word_len= (uint32_t) (p - command->query);
4088 
4089  /* Skip spaces between command and first argument */
4090  while (*p && charset_info->isspace(*p))
4091  p++;
4092  command->first_argument= p;
4093 
4094  command->end= strchr(command->query, '\0');
4095  command->query_len= (command->end - command->query);
4096  parser.read_lines++;
4097 
4098  return(0);
4099 }
4100 
4101 /*
4102  Write the content of str into file
4103 
4104  SYNOPSIS
4105  str_to_file2
4106  fname - name of file to truncate/create and write to
4107  str - content to write to file
4108  size - size of content witten to file
4109  append - append to file instead of overwriting old file
4110 */
4111 
4112 void str_to_file2(const char *fname, const char *str, int size, bool append)
4113 {
4114  char buff[FN_REFLEN];
4115  if (!internal::test_if_hard_path(fname))
4116  {
4117  snprintf(buff, sizeof(buff), "%s%s",opt_basedir.c_str(),fname);
4118  fname= buff;
4119  }
4120  internal::fn_format(buff, fname, "", "", MY_UNPACK_FILENAME);
4121 
4122  int flags= O_WRONLY | O_CREAT;
4123  if (!append)
4124  flags|= O_TRUNC;
4125  int fd= internal::my_open(buff, flags, MYF(MY_WME | MY_FFNF));
4126  if (fd < 0)
4127  die("Could not open '%s' for writing: errno = %d", buff, errno);
4128  if (append && lseek(fd, 0, SEEK_END) == MY_FILEPOS_ERROR)
4129  die("Could not find end of file '%s': errno = %d", buff, errno);
4130  if (internal::my_write(fd, (unsigned char*)str, size, MYF(MY_WME|MY_FNABP)))
4131  die("write failed");
4132  internal::my_close(fd, MYF(0));
4133 }
4134 
4135 /*
4136  Write the content of str into file
4137 
4138  SYNOPSIS
4139  str_to_file
4140  fname - name of file to truncate/create and write to
4141  str - content to write to file
4142  size - size of content witten to file
4143 */
4144 
4145 void str_to_file(const char *fname, const char *str, int size)
4146 {
4147  str_to_file2(fname, str, size, false);
4148 }
4149 
4150 
4151 void dump_result_to_log_file(const char *buf, int size)
4152 {
4153  char log_file[FN_REFLEN];
4154  str_to_file(internal::fn_format(log_file, result_file_name.c_str(), opt_logdir.c_str(), ".log",
4155  ! opt_logdir.empty() ? MY_REPLACE_DIR | MY_REPLACE_EXT :
4156  MY_REPLACE_EXT),
4157  buf, size);
4158  fprintf(stderr, "\nMore results from queries before failure can be found in %s\n", log_file);
4159 }
4160 
4161 void dump_progress()
4162 {
4163  char progress_file[FN_REFLEN];
4164  str_to_file(internal::fn_format(progress_file, result_file_name.c_str(),
4165  opt_logdir.c_str(), ".progress",
4166  ! opt_logdir.empty() ? MY_REPLACE_DIR | MY_REPLACE_EXT :
4167  MY_REPLACE_EXT),
4168  ds_progress.c_str(), ds_progress.length());
4169 }
4170 
4171 void dump_warning_messages()
4172 {
4173  char warn_file[FN_REFLEN];
4174 
4175  str_to_file(internal::fn_format(warn_file, result_file_name.c_str(), opt_logdir.c_str(), ".warnings",
4176  ! opt_logdir.empty() ? MY_REPLACE_DIR | MY_REPLACE_EXT :
4177  MY_REPLACE_EXT),
4178  ds_warning_messages.c_str(), ds_warning_messages.length());
4179 }
4180 
4181 
4182 /*
4183  Append the result for one field to the dynamic string ds
4184 */
4185 
4186 static void append_field(string *ds, uint32_t col_idx, drizzle_column_st *column,
4187  const char* val, uint64_t len, bool is_null)
4188 {
4189  if (col_idx < max_replace_column && replace_column[col_idx])
4190  {
4191  val= replace_column[col_idx];
4192  len= strlen(val);
4193  }
4194  else if (is_null)
4195  {
4196  val= "NULL";
4197  len= 4;
4198  }
4199 
4200  if (!display_result_vertically)
4201  {
4202  if (col_idx)
4203  ds->append("\t");
4204  replace_append_mem(*ds, val, (int)len);
4205  }
4206  else
4207  {
4208  ds->append(drizzle_column_name(column));
4209  ds->append("\t");
4210  replace_append_mem(*ds, val, (int)len);
4211  ds->append("\n");
4212  }
4213 }
4214 
4215 
4216 /*
4217  Append all results to the dynamic string separated with '\t'
4218  Values may be converted with 'replace_column'
4219 */
4220 
4221 static void append_result(string *ds, drizzle::result_c& res)
4222 {
4223  uint32_t num_fields= res.column_count();
4224  while (drizzle_row_t row = res.row_next())
4225  {
4226  size_t* lengths = res.row_field_sizes();
4227  res.column_seek(0);
4228  for (uint32_t i = 0; i < num_fields; i++)
4229  {
4230  drizzle_column_st* column= res.column_next();
4231  if (row[i] && drizzle_column_type(column) == DRIZZLE_COLUMN_TYPE_TINY)
4232  {
4233  if (boost::lexical_cast<uint32_t>(row[i]))
4234  {
4235  if (drizzle_column_flags(column) & DRIZZLE_COLUMN_FLAGS_UNSIGNED)
4236  {
4237  append_field(ds, i, column, "YES", 3, false);
4238  }
4239  else
4240  {
4241  append_field(ds, i, column, "TRUE", 4, false);
4242  }
4243  }
4244  else
4245  {
4246  if (drizzle_column_flags(column) & DRIZZLE_COLUMN_FLAGS_UNSIGNED)
4247  {
4248  append_field(ds, i, column, "NO", 2, false);
4249  }
4250  else
4251  {
4252  append_field(ds, i, column, "FALSE", 5, false);
4253  }
4254  }
4255  }
4256  else
4257  {
4258  append_field(ds, i, column, (const char*)row[i], lengths[i], !row[i]);
4259  }
4260  }
4261  if (!display_result_vertically)
4262  ds->append("\n");
4263  }
4264 }
4265 
4266 
4267 /*
4268  Append metadata for fields to output
4269 */
4270 
4271 static void append_metadata(string& ds, drizzle::result_c& res)
4272 {
4273  ds += "Catalog\tDatabase\tTable\tTable_alias\tColumn\tColumn_alias\tType\tLength\tMax length\tIs_null\tFlags\tDecimals\tCharsetnr\n";
4274  res.column_seek(0);
4275  while (drizzle_column_st* column= res.column_next())
4276  {
4277  ds += drizzle_column_catalog(column);
4278  ds += "\t";
4279  ds += drizzle_column_db(column);
4280  ds += "\t";
4281  ds += drizzle_column_orig_table(column);
4282  ds += "\t";
4283  ds += drizzle_column_table(column);
4284  ds += "\t";
4285  ds += drizzle_column_orig_name(column);
4286  ds += "\t";
4287  ds += drizzle_column_name(column);
4288  ds += "\t";
4289  replace_append_uint(ds, drizzle_column_type_drizzle(column));
4290  ds += "\t";
4291  replace_append_uint(ds, drizzle_column_size(column));
4292  ds += "\t";
4293  replace_append_uint(ds, drizzle_column_type(column) == DRIZZLE_COLUMN_TYPE_TINY ? 1 : drizzle_column_max_size(column));
4294  ds += "\t";
4295  ds += drizzle_column_flags(column) & DRIZZLE_COLUMN_FLAGS_NOT_NULL ? "N" : "Y";
4296  ds += "\t";
4297  replace_append_uint(ds, drizzle_column_flags(column));
4298  ds += "\t";
4299  replace_append_uint(ds, drizzle_column_decimals(column));
4300  ds += "\t";
4301  replace_append_uint(ds, drizzle_column_charset(column));
4302  ds += "\n";
4303  }
4304 }
4305 
4306 
4307 /*
4308  Append affected row count and other info to output
4309 */
4310 
4311 static void append_info(string *ds, uint64_t affected_rows,
4312  const char *info)
4313 {
4314  ostringstream buf;
4315  buf << "affected rows: " << affected_rows << endl;
4316  ds->append(buf.str());
4317  if (info && strcmp(info, ""))
4318  {
4319  ds->append("info: ");
4320  ds->append(info);
4321  ds->append("\n", 1);
4322  }
4323 }
4324 
4325 
4326 /*
4327  Display the table headings with the names tab separated
4328 */
4329 
4330 static void append_table_headings(string& ds, drizzle::result_c& res)
4331 {
4332  uint32_t col_idx= 0;
4333  res.column_seek(0);
4334  while (drizzle_column_st* column= res.column_next())
4335  {
4336  if (col_idx)
4337  ds += "\t";
4338  replace_append(&ds, drizzle_column_name(column));
4339  col_idx++;
4340  }
4341  ds += "\n";
4342 }
4343 
4344 /*
4345  Fetch warnings from server and append to ds
4346 
4347  RETURN VALUE
4348  Number of warnings appended to ds
4349 */
4350 
4351 static int append_warnings(string& ds, drizzle::connection_c& con, drizzle::result_c& res)
4352 {
4353  uint32_t count= drizzle_result_warning_count(res);
4354  if (!count)
4355  return 0;
4356 
4357  drizzle::result_c warn_res;
4358  dt_query(con, warn_res, "show warnings");
4359  append_result(&ds, warn_res);
4360  return count;
4361 }
4362 
4363 
4364 /*
4365  Run query using DRIZZLE C API
4366 
4367  SYNOPSIS
4368  run_query_normal()
4369  drizzle DRIZZLE handle
4370  command current command pointer
4371  flags flags indicating if we should SEND and/or REAP
4372  query query string to execute
4373  query_len length query string to execute
4374  ds output buffer where to store result form query
4375 */
4376 
4377 static void run_query_normal(st_connection& cn,
4378  st_command* command,
4379  int flags, char *query, int query_len,
4380  string *ds, string& ds_warnings)
4381 {
4382  drizzle_return_t ret;
4383  drizzle_con_st *con= cn;
4384  int err= 0;
4385 
4386  drizzle_con_add_options(con, DRIZZLE_CON_NO_RESULT_READ);
4387 
4388  drizzle::result_c res;
4389  if (flags & QUERY_SEND_FLAG)
4390  {
4391  /*
4392  * Send the query
4393  */
4394 
4395  (void) drizzle_query(con, res, query, query_len, &ret);
4396  if (ret != DRIZZLE_RETURN_OK)
4397  {
4398  if (ret == DRIZZLE_RETURN_ERROR_CODE ||
4399  ret == DRIZZLE_RETURN_HANDSHAKE_FAILED)
4400  {
4401  err= res.error_code();
4402  handle_error(command, err, res.error(), drizzle_result_sqlstate(res), ds);
4403  }
4404  else
4405  {
4406  handle_error(command, ret, drizzle_con_error(con), "", ds);
4407  err= ret;
4408  }
4409  goto end;
4410  }
4411  }
4412  if (!(flags & QUERY_REAP_FLAG))
4413  return;
4414 
4415  {
4416  /*
4417  * Read the result packet
4418  */
4419  if (drizzle_result_read(con, res, &ret) == NULL ||
4420  ret != DRIZZLE_RETURN_OK)
4421  {
4422  if (ret == DRIZZLE_RETURN_ERROR_CODE)
4423  {
4424  handle_error(command, res.error_code(), res.error(), drizzle_result_sqlstate(res), ds);
4425  }
4426  else
4427  handle_error(command, ret, drizzle_con_error(con), "", ds);
4428  err= ret;
4429  goto end;
4430  }
4431 
4432  /*
4433  Store the result of the query if it will return any fields
4434  */
4435  if (res.column_count() && (ret= drizzle_result_buffer(res)) != DRIZZLE_RETURN_OK)
4436  {
4437  if (ret == DRIZZLE_RETURN_ERROR_CODE)
4438  {
4439  handle_error(command, res.error_code(), res.error(), drizzle_result_sqlstate(res), ds);
4440  }
4441  else
4442  handle_error(command, ret, drizzle_con_error(con), "", ds);
4443  err= ret;
4444  goto end;
4445  }
4446 
4447  if (!disable_result_log)
4448  {
4449  uint64_t affected_rows= 0; /* Ok to be undef if 'disable_info' is set */
4450 
4451  if (res.column_count())
4452  {
4453  if (display_metadata)
4454  append_metadata(*ds, res);
4455 
4456  if (!display_result_vertically)
4457  append_table_headings(*ds, res);
4458 
4459  append_result(ds, res);
4460  }
4461 
4462  /*
4463  Need to call drizzle_result_affected_rows() before the "new"
4464  query to find the warnings
4465  */
4466  if (!disable_info)
4467  affected_rows= drizzle_result_affected_rows(res);
4468 
4469  /*
4470  Add all warnings to the result. We can't do this if we are in
4471  the middle of processing results from multi-statement, because
4472  this will break protocol.
4473  */
4474  if (!disable_warnings)
4475  {
4476  drizzle_con_remove_options(con, DRIZZLE_CON_NO_RESULT_READ);
4477  if (append_warnings(ds_warnings, cn, res) || not ds_warnings.empty())
4478  {
4479  ds->append("Warnings:\n", 10);
4480  *ds += ds_warnings;
4481  }
4482  }
4483 
4484  if (!disable_info)
4485  append_info(ds, affected_rows, drizzle_result_info(res));
4486  }
4487 
4488  }
4489 
4490  /* If we come here the query is both executed and read successfully */
4491  handle_no_error(command);
4492 
4493 end:
4494 
4495  /*
4496  We save the return code (drizzleclient_errno(drizzle)) from the last call sent
4497  to the server into the drizzletest builtin variable $drizzleclient_errno. This
4498  variable then can be used from the test case itself.
4499  */
4500  drizzle_con_remove_options(con, DRIZZLE_CON_NO_RESULT_READ);
4501  var_set_errno(err);
4502 }
4503 
4504 
4505 /*
4506  Handle errors which occurred during execution
4507 
4508  SYNOPSIS
4509  handle_error()
4510  q - query context
4511  err_errno - error number
4512  err_error - error message
4513  err_sqlstate - sql state
4514  ds - dynamic string which is used for output buffer
4515 
4516  NOTE
4517  If there is an unexpected error this function will abort drizzletest
4518  immediately.
4519 */
4520 
4521 void handle_error(st_command* command,
4522  unsigned int err_errno, const char *err_error,
4523  const char *err_sqlstate, string *ds)
4524 {
4525  if (! command->require_file.empty())
4526  {
4527  /*
4528  The query after a "--require" failed. This is fine as long the server
4529  returned a valid reponse. Don't allow 2013 or 2006 to trigger an
4530  abort_not_supported_test
4531  */
4532  if (err_errno == DRIZZLE_RETURN_LOST_CONNECTION)
4533  die("require query '%s' failed: %d: %s", command->query, err_errno, err_error);
4534 
4535  /* Abort the run of this test, pass the failed query as reason */
4536  abort_not_supported_test("Query '%s' failed, required functionality not supported", command->query);
4537  }
4538 
4539  if (command->abort_on_error)
4540  die("query '%s' failed: %d: %s", command->query, err_errno, err_error);
4541 
4542  uint32_t i= 0;
4543  for (; i < command->expected_errors.count; i++)
4544  {
4545  if (((command->expected_errors.err[i].type == ERR_ERRNO) &&
4546  (command->expected_errors.err[i].code.errnum == err_errno)) ||
4547  ((command->expected_errors.err[i].type == ERR_SQLSTATE) &&
4548  (strncmp(command->expected_errors.err[i].code.sqlstate,
4549  err_sqlstate, DRIZZLE_MAX_SQLSTATE_SIZE) == 0)))
4550  {
4551  if (!disable_result_log)
4552  {
4553  if (command->expected_errors.count == 1)
4554  {
4555  /* Only log error if there is one possible error */
4556  ds->append("ERROR ", 6);
4557  replace_append(ds, err_sqlstate);
4558  ds->append(": ", 2);
4559  replace_append(ds, err_error);
4560  ds->append("\n",1);
4561  }
4562  /* Don't log error if we may not get an error */
4563  else if (command->expected_errors.err[0].type == ERR_SQLSTATE ||
4564  (command->expected_errors.err[0].type == ERR_ERRNO &&
4565  command->expected_errors.err[0].code.errnum != 0))
4566  ds->append("Got one of the listed errors\n");
4567  }
4568  /* OK */
4569  return;
4570  }
4571  }
4572 
4573  if (!disable_result_log)
4574  {
4575  ds->append("ERROR ",6);
4576  replace_append(ds, err_sqlstate);
4577  ds->append(": ", 2);
4578  replace_append(ds, err_error);
4579  ds->append("\n", 1);
4580  }
4581 
4582  if (i)
4583  {
4584  if (command->expected_errors.err[0].type == ERR_ERRNO)
4585  die("query '%s' failed with wrong errno %d: '%s', instead of %d...",
4586  command->query, err_errno, err_error,
4587  command->expected_errors.err[0].code.errnum);
4588  else
4589  die("query '%s' failed with wrong sqlstate %s: '%s', instead of %s...",
4590  command->query, err_sqlstate, err_error,
4591  command->expected_errors.err[0].code.sqlstate);
4592  }
4593 }
4594 
4595 
4596 /*
4597  Handle absence of errors after execution
4598 
4599  SYNOPSIS
4600  handle_no_error()
4601  q - context of query
4602 
4603  RETURN VALUE
4604  error - function will not return
4605 */
4606 
4607 void handle_no_error(st_command* command)
4608 {
4609  if (command->expected_errors.err[0].type == ERR_ERRNO &&
4610  command->expected_errors.err[0].code.errnum != 0)
4611  {
4612  /* Error code we wanted was != 0, i.e. not an expected success */
4613  die("query '%s' succeeded - should have failed with errno %d...", command->query, command->expected_errors.err[0].code.errnum);
4614  }
4615  else if (command->expected_errors.err[0].type == ERR_SQLSTATE &&
4616  strcmp(command->expected_errors.err[0].code.sqlstate,"00000") != 0)
4617  {
4618  /* SQLSTATE we wanted was != "00000", i.e. not an expected success */
4619  die("query '%s' succeeded - should have failed with sqlstate %s...", command->query, command->expected_errors.err[0].code.sqlstate);
4620  }
4621 }
4622 
4623 
4624 /*
4625  Run query
4626 
4627  SYNPOSIS
4628  run_query()
4629  drizzle DRIZZLE handle
4630  command currrent command pointer
4631 
4632  flags control the phased/stages of query execution to be performed
4633  if QUERY_SEND_FLAG bit is on, the query will be sent. If QUERY_REAP_FLAG
4634  is on the result will be read - for regular query, both bits must be on
4635 */
4636 
4637 static void run_query(st_connection& cn,
4638  st_command* command,
4639  int flags)
4640 {
4641  string eval_query;
4642  char *query;
4643  int query_len;
4644 
4645 
4646  /* Scan for warning before sending to server */
4647  scan_command_for_warnings(command);
4648 
4649  /*
4650  Evaluate query if this is an eval command
4651  */
4652  if (command->type == Q_EVAL)
4653  {
4654  do_eval(&eval_query, command->query, command->end, false);
4655  query = strdup(eval_query.c_str());
4656  query_len = eval_query.length();
4657  }
4658  else
4659  {
4660  query = command->query;
4661  query_len = strlen(query);
4662  }
4663 
4664  /*
4665  When command->require_file is set the output of _this_ query
4666  should be compared with an already existing file
4667  Create a temporary dynamic string to contain the output from
4668  this query.
4669  */
4670  string ds_result;
4671  string* ds= command->require_file.empty() ? &ds_res : &ds_result;
4672  /*
4673  Log the query into the output buffer
4674  */
4675  if (!disable_query_log && (flags & QUERY_SEND_FLAG))
4676  {
4677  replace_append_mem(*ds, query, query_len);
4678  ds->append(delimiter, delimiter_length);
4679  ds->append("\n");
4680  }
4681 
4682  string* save_ds= NULL;
4683  string ds_sorted;
4684  if (display_result_sorted)
4685  {
4686  /*
4687  Collect the query output in a separate string
4688  that can be sorted before it's added to the
4689  global result string
4690  */
4691  save_ds= ds; /* Remember original ds */
4692  ds= &ds_sorted;
4693  }
4694 
4695  /*
4696  Always run with normal C API if it's not a complete
4697  SEND + REAP
4698  */
4699  string ds_warnings;
4700  run_query_normal(cn, command, flags, query, query_len, ds, ds_warnings);
4701 
4702  if (display_result_sorted)
4703  {
4704  /* Sort the result set and append it to result */
4705  append_sorted(*save_ds, ds_sorted);
4706  ds= save_ds;
4707  }
4708 
4709  if (! command->require_file.empty())
4710  {
4711  /* A result file was specified for _this_ query
4712  and the output should be checked against an already
4713  existing file which has been specified using --require or --result
4714  */
4715  check_require(*ds, command->require_file);
4716  }
4717 }
4718 
4719 
4720 /****************************************************************************/
4721 
4722 static void get_command_type(st_command* command)
4723 {
4724  if (*command->query == '}')
4725  {
4726  command->type = Q_END_BLOCK;
4727  return;
4728  }
4729 
4730  char save= command->query[command->first_word_len];
4731  command->query[command->first_word_len]= 0;
4732  uint32_t type= command_typelib.find_type(command->query, TYPELIB::e_default);
4733  command->query[command->first_word_len]= save;
4734  if (type > 0)
4735  {
4736  command->type=(enum enum_commands) type; /* Found command */
4737 
4738  /*
4739  Look for case where "query" was explicitly specified to
4740  force command being sent to server
4741  */
4742  if (type == Q_QUERY)
4743  {
4744  /* Skip the "query" part */
4745  command->query= command->first_argument;
4746  }
4747  }
4748  else
4749  {
4750  /* No drizzletest command matched */
4751 
4752  if (command->type != Q_COMMENT_WITH_COMMAND)
4753  {
4754  /* A query that will sent to drizzled */
4755  command->type= Q_QUERY;
4756  }
4757  else
4758  {
4759  /* -- comment that didn't contain a drizzletest command */
4760  command->type= Q_COMMENT;
4761  warning_msg("Suspicious command '--%s' detected, was this intentional? " \
4762  "Use # instead of -- to avoid this warning",
4763  command->query);
4764 
4765  if (command->first_word_len &&
4766  strcmp(command->query + command->first_word_len - 1, delimiter) == 0)
4767  {
4768  /*
4769  Detect comment with command using extra delimiter
4770  Ex --disable_query_log;
4771  ^ Extra delimiter causing the command
4772  to be skipped
4773  */
4774  save= command->query[command->first_word_len-1];
4775  command->query[command->first_word_len-1]= 0;
4776  if (command_typelib.find_type(command->query, TYPELIB::e_default) > 0)
4777  die("Extra delimiter \";\" found");
4778  command->query[command->first_word_len-1]= save;
4779 
4780  }
4781  }
4782  }
4783 
4784  /* Set expected error on command */
4785  memcpy(&command->expected_errors, &saved_expected_errors, sizeof(saved_expected_errors));
4786  command->abort_on_error= (command->expected_errors.count == 0 && abort_on_error);
4787 }
4788 
4789 
4790 
4791 /*
4792  Record how many milliseconds it took to execute the test file
4793  up until the current line and save it in the dynamic string ds_progress.
4794 
4795  The ds_progress will be dumped to <test_name>.progress when
4796  test run completes
4797 
4798 */
4799 
4800 static void mark_progress(st_command*, int line)
4801 {
4802  uint64_t timer= timer_now();
4803  if (!progress_start)
4804  progress_start= timer;
4805  timer-= progress_start;
4806 
4807  ostringstream buf;
4808  /* Milliseconds since start */
4809  buf << timer << "\t";
4810 
4811  /* Parser line number */
4812  buf << line << "\t";
4813 
4814  /* Filename */
4815  buf << cur_file->file_name << ":";
4816 
4817  /* Line in file */
4818  buf << cur_file->lineno << endl;
4819 
4820  ds_progress += buf.str();
4821 
4822 }
4823 
4824 static void check_retries(uint32_t in_opt_max_connect_retries)
4825 {
4826  if (in_opt_max_connect_retries > 10000 || opt_max_connect_retries<1)
4827  {
4828  cout << N_("Error: Invalid Value for opt_max_connect_retries");
4829  exit(-1);
4830  }
4831  opt_max_connect_retries= in_opt_max_connect_retries;
4832 }
4833 
4834 static void check_tail_lines(uint32_t in_opt_tail_lines)
4835 {
4836  if (in_opt_tail_lines > 10000)
4837  {
4838  cout << N_("Error: Invalid Value for opt_tail_lines");
4839  exit(-1);
4840  }
4841  opt_tail_lines= in_opt_tail_lines;
4842 }
4843 
4844 static void check_sleep(int32_t in_opt_sleep)
4845 {
4846  if (in_opt_sleep < -1)
4847  {
4848  cout << N_("Error: Invalid Value for opt_sleep");
4849  exit(-1);
4850  }
4851  opt_sleep= in_opt_sleep;
4852 }
4853 
4854 int main(int argc, char **argv)
4855 {
4856 try
4857 {
4858  bool q_send_flag= 0, abort_flag= 0;
4859  uint32_t command_executed= 0, last_command_executed= 0;
4860  string save_file;
4861 
4862  TMPDIR[0]= 0;
4863 
4864  internal::my_init();
4865 
4866  po::options_description commandline_options("Options used only in command line");
4867  commandline_options.add_options()
4868  ("help,?", "Display this help and exit.")
4869  ("mark-progress", po::value<bool>(&opt_mark_progress)->default_value(false)->zero_tokens(),
4870  "Write linenumber and elapsed time to <testname>.progress ")
4871  ("sleep,T", po::value<int32_t>(&opt_sleep)->default_value(-1)->notifier(&check_sleep),
4872  "Sleep always this many seconds on sleep commands.")
4873  ("test-file,x", po::value<string>(),
4874  "Read test from/in this file (default stdin).")
4875  ("timer-file,f", po::value<string>(),
4876  "File where the timing in micro seconds is stored.")
4877  ("tmpdir,t", po::value<string>(),
4878  "Temporary directory where sockets are put.")
4879  ("verbose,v", po::value<bool>(&verbose)->default_value(false),
4880  "Write more.")
4881  ("version,V", "Output version information and exit.")
4882  ("no-defaults", po::value<bool>()->default_value(false)->zero_tokens(),
4883  "Configuration file defaults are not used if no-defaults is set")
4884  ;
4885 
4886  po::options_description test_options("Options specific to the drizzleimport");
4887  test_options.add_options()
4888  ("basedir,b", po::value<string>(&opt_basedir)->default_value(""),
4889  "Basedir for tests.")
4890  ("character-sets-dir", po::value<string>(&opt_charsets_dir)->default_value(""),
4891  "Directory where character sets are.")
4892  ("database,D", po::value<string>(&opt_db)->default_value(""),
4893  "Database to use.")
4894  ("include,i", po::value<string>(&opt_include)->default_value(""),
4895  "Include SQL before each test case.")
4896  ("testdir", po::value<string>(&opt_testdir)->default_value(""),
4897  "Path to use to search for test files")
4898  ("logdir", po::value<string>(&opt_logdir)->default_value(""),
4899  "Directory for log files")
4900  ("max-connect-retries", po::value<uint32_t>(&opt_max_connect_retries)->default_value(500)->notifier(&check_retries),
4901  "Max number of connection attempts when connecting to server")
4902  ("quiet,s", po::value<bool>(&silent)->default_value(false)->zero_tokens(),
4903  "Suppress all normal output.")
4904  ("record,r", "Record output of test_file into result file.")
4905  ("result-file,R", po::value<string>(&result_file_name)->default_value(""),
4906  "Read/Store result from/in this file.")
4907  ("silent,s", po::value<bool>(&silent)->default_value(false)->zero_tokens(),
4908  "Suppress all normal output. Synonym for --quiet.")
4909  ("tail-lines", po::value<uint32_t>(&opt_tail_lines)->default_value(0)->notifier(&check_tail_lines),
4910  "Number of lines of the resul to include in a failure report")
4911  ;
4912 
4913  po::options_description client_options("Options specific to the client");
4914  client_options.add_options()
4915 
4916  ("host,h", po::value<string>(&opt_host)->default_value("localhost"),
4917  "Connect to host.")
4918  ("password,P", po::value<string>(&password)->default_value("PASSWORD_SENTINEL"),
4919  "Password to use when connecting to server.")
4920  ("port,p", po::value<uint32_t>(&opt_port)->default_value(0),
4921  "Port number to use for connection or 0 for default")
4922  ("protocol", po::value<string>(&opt_protocol),
4923  "The protocol of connection (mysql or drizzle).")
4924  ("user,u", po::value<string>(&opt_user)->default_value(""),
4925  "User for login.")
4926  ;
4927 
4928  po::positional_options_description p;
4929  p.add("database", 1);
4930 
4931  po::options_description long_options("Allowed Options");
4932  long_options.add(commandline_options).add(test_options).add(client_options);
4933 
4934  std::string system_config_dir_test(SYSCONFDIR);
4935  system_config_dir_test += "/drizzle/drizzletest.cnf";
4936 
4937  std::string system_config_dir_client(SYSCONFDIR);
4938  system_config_dir_client += "/drizzle/client.cnf";
4939 
4940  std::string user_config_dir((getenv("XDG_CONFIG_HOME")? getenv("XDG_CONFIG_HOME"):"~/.config"));
4941 
4942  if (user_config_dir.compare(0, 2, "~/") == 0)
4943  {
4944  if (const char *homedir= getenv("HOME"))
4945  user_config_dir.replace(0, 1, homedir);
4946  }
4947 
4948  po::variables_map vm;
4949 
4950  // Disable allow_guessing
4951  int style = po::command_line_style::default_style & ~po::command_line_style::allow_guessing;
4952 
4953  po::store(po::command_line_parser(argc, argv).options(long_options).
4954  style(style).positional(p).extra_parser(parse_password_arg).run(),
4955  vm);
4956 
4957  if (! vm["no-defaults"].as<bool>())
4958  {
4959  std::string user_config_dir_test(user_config_dir);
4960  user_config_dir_test += "/drizzle/drizzletest.cnf";
4961 
4962  std::string user_config_dir_client(user_config_dir);
4963  user_config_dir_client += "/drizzle/client.cnf";
4964 
4965  ifstream user_test_ifs(user_config_dir_test.c_str());
4966  po::store(parse_config_file(user_test_ifs, test_options), vm);
4967 
4968  ifstream user_client_ifs(user_config_dir_client.c_str());
4969  po::store(parse_config_file(user_client_ifs, client_options), vm);
4970 
4971  ifstream system_test_ifs(system_config_dir_test.c_str());
4972  store(parse_config_file(system_test_ifs, test_options), vm);
4973 
4974  ifstream system_client_ifs(system_config_dir_client.c_str());
4975  po::store(parse_config_file(system_client_ifs, client_options), vm);
4976  }
4977 
4978  po::notify(vm);
4979 
4980  /* Init expected errors */
4981  memset(&saved_expected_errors, 0, sizeof(saved_expected_errors));
4982 
4983  /* Init file stack */
4984  memset(file_stack.data(), 0, sizeof(file_stack));
4985  cur_file= file_stack.data();
4986 
4987  /* Init block stack */
4988  memset(block_stack, 0, sizeof(block_stack));
4989  block_stack_end=
4990  block_stack + (sizeof(block_stack)/sizeof(struct st_block)) - 1;
4991  cur_block= block_stack;
4992  cur_block->ok= true; /* Outer block should always be executed */
4993  cur_block->cmd= cmd_none;
4994 
4995  var_set_string("$DRIZZLE_SERVER_VERSION", drizzle_version());
4996 
4997  memset(&master_pos, 0, sizeof(master_pos));
4998 
4999  parser.current_line= parser.read_lines= 0;
5000  memset(&var_reg, 0, sizeof(var_reg));
5001 
5002  init_builtin_echo();
5003 
5004  ds_res.reserve(65536);
5005  ds_progress.reserve(2048);
5006  ds_warning_messages.reserve(2048);
5007 
5008 
5009  if (vm.count("record"))
5010  {
5011  record = 1;
5012  }
5013 
5014  if (vm.count("test-file"))
5015  {
5016  string tmp= vm["test-file"].as<string>();
5017  char buff[FN_REFLEN];
5018  if (!internal::test_if_hard_path(tmp.c_str()))
5019  {
5020  snprintf(buff, sizeof(buff), "%s%s",opt_basedir.c_str(),tmp.c_str());
5021  tmp= buff;
5022  }
5023  internal::fn_format(buff, tmp.c_str(), "", "", MY_UNPACK_FILENAME);
5024  assert(cur_file == file_stack.data() && cur_file->file == 0);
5025  if (!(cur_file->file= fopen(buff, "r")))
5026  {
5027  fprintf(stderr, _("Could not open '%s' for reading: errno = %d"), buff, errno);
5028  return EXIT_ARGUMENT_INVALID;
5029  }
5030  cur_file->file_name= strdup(buff);
5031  cur_file->lineno= 1;
5032  }
5033 
5034  if (vm.count("timer-file"))
5035  {
5036  string tmp= vm["timer-file"].as<string>().c_str();
5037  static char buff[FN_REFLEN];
5038  if (!internal::test_if_hard_path(tmp.c_str()))
5039  {
5040  snprintf(buff, sizeof(buff), "%s%s",opt_basedir.c_str(),tmp.c_str());
5041  tmp= buff;
5042  }
5043  internal::fn_format(buff, tmp.c_str(), "", "", MY_UNPACK_FILENAME);
5044  timer_file= buff;
5045  unlink(timer_file); /* Ignore error, may not exist */
5046  }
5047 
5048  if (vm.count("protocol"))
5049  {
5050  boost::to_lower(opt_protocol);
5051  if (not opt_protocol.compare("mysql"))
5052  use_drizzle_protocol=false;
5053  else if (not opt_protocol.compare("drizzle"))
5054  use_drizzle_protocol=true;
5055  else
5056  {
5057  cout << _("Error: Unknown protocol") << " '" << opt_protocol << "'" << endl;
5058  exit(-1);
5059  }
5060  }
5061 
5062  if (vm.count("port"))
5063  {
5064  /* If the port number is > 65535 it is not a valid port
5065  This also helps with potential data loss casting unsigned long to a
5066  uint32_t. */
5067  if (opt_port > 65535)
5068  {
5069  fprintf(stderr, _("Value supplied for port is not valid.\n"));
5070  exit(EXIT_ARGUMENT_INVALID);
5071  }
5072  }
5073 
5074  if( vm.count("password") )
5075  {
5076  if (!opt_password.empty())
5077  opt_password.erase();
5078  if (password == PASSWORD_SENTINEL)
5079  {
5080  opt_password= "";
5081  }
5082  else
5083  {
5084  opt_password= password;
5085  tty_password= false;
5086  }
5087  }
5088  else
5089  {
5090  tty_password= true;
5091  }
5092 
5093  if (vm.count("tmpdir"))
5094  {
5095  strncpy(TMPDIR, vm["tmpdir"].as<string>().c_str(), sizeof(TMPDIR));
5096  }
5097 
5098  if (vm.count("version"))
5099  {
5100  printf("%s Ver %s Distrib %s, for %s-%s (%s)\n",internal::my_progname,MTEST_VERSION,
5101  drizzle_version(),HOST_VENDOR,HOST_OS,HOST_CPU);
5102  exit(0);
5103  }
5104 
5105  if (vm.count("help"))
5106  {
5107  printf("%s Ver %s Distrib %s, for %s-%s (%s)\n",internal::my_progname,MTEST_VERSION,
5108  drizzle_version(),HOST_VENDOR,HOST_OS,HOST_CPU);
5109  printf("MySQL AB, by Sasha, Matt, Monty & Jani\n");
5110  printf("Drizzle version modified by Brian, Jay, Monty Taylor, PatG and Stewart\n");
5111  printf("This software comes with ABSOLUTELY NO WARRANTY\n\n");
5112  printf("Runs a test against the DRIZZLE server and compares output with a results file.\n\n");
5113  printf("Usage: %s [OPTIONS] [database] < test_file\n", internal::my_progname);
5114  exit(0);
5115  }
5116 
5117  if (tty_password)
5118  {
5119  opt_pass= client_get_tty_password(NULL); /* purify tested */
5120  }
5121 
5122  server_initialized= true;
5123  if (cur_file == file_stack.data() && cur_file->file == 0)
5124  {
5125  cur_file->file= stdin;
5126  cur_file->file_name= strdup("<stdin>");
5127  cur_file->lineno= 1;
5128  }
5129  cur_con= safe_connect("default", opt_host, opt_user, opt_pass, opt_db, opt_port);
5130  g_connections["default"] = cur_con;
5131 
5132  fill_global_error_names();
5133 
5134  /* Use all time until exit if no explicit 'start_timer' */
5135  timer_start= timer_now();
5136 
5137  /*
5138  Initialize $drizzleclient_errno with -1, so we can
5139  - distinguish it from valid values ( >= 0 ) and
5140  - detect if there was never a command sent to the server
5141  */
5142  var_set_errno(-1);
5143 
5144  /* Update $drizzleclient_get_server_version to that of current connection */
5145  var_set_drizzleclient_get_server_version(*cur_con);
5146 
5147  if (! opt_include.empty())
5148  {
5149  open_file(opt_include.c_str());
5150  }
5151 
5152  st_command* command;
5153  while (!read_command(&command) && !abort_flag)
5154  {
5155  int current_line_inc = 1, processed = 0;
5156  if (command->type == Q_UNKNOWN || command->type == Q_COMMENT_WITH_COMMAND)
5157  get_command_type(command);
5158 
5159  if (parsing_disabled &&
5160  command->type != Q_ENABLE_PARSING &&
5161  command->type != Q_DISABLE_PARSING)
5162  {
5163  command->type= Q_COMMENT;
5164  scan_command_for_warnings(command);
5165  }
5166 
5167  if (cur_block->ok)
5168  {
5169  command->last_argument= command->first_argument;
5170  processed = 1;
5171  switch (command->type) {
5172  case Q_CONNECT:
5173  do_connect(command);
5174  break;
5175  case Q_CONNECTION:
5176  select_connection(command);
5177  break;
5178  case Q_DISCONNECT:
5179  case Q_DIRTY_CLOSE:
5180  do_close_connection(command); break;
5181  case Q_ENABLE_QUERY_LOG: disable_query_log=0; break;
5182  case Q_DISABLE_QUERY_LOG: disable_query_log=1; break;
5183  case Q_ENABLE_ABORT_ON_ERROR: abort_on_error=1; break;
5184  case Q_DISABLE_ABORT_ON_ERROR: abort_on_error=0; break;
5185  case Q_ENABLE_RESULT_LOG: disable_result_log=0; break;
5186  case Q_DISABLE_RESULT_LOG: disable_result_log=1; break;
5187  case Q_ENABLE_WARNINGS: disable_warnings=0; break;
5188  case Q_DISABLE_WARNINGS: disable_warnings=1; break;
5189  case Q_ENABLE_INFO: disable_info=0; break;
5190  case Q_DISABLE_INFO: disable_info=1; break;
5191  case Q_ENABLE_METADATA: display_metadata=1; break;
5192  case Q_DISABLE_METADATA: display_metadata=0; break;
5193  case Q_SOURCE: do_source(command); break;
5194  case Q_SLEEP: do_sleep(command, 0); break;
5195  case Q_REAL_SLEEP: do_sleep(command, 1); break;
5196  case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(); break;
5197  case Q_INC: do_modify_var(command, DO_INC); break;
5198  case Q_DEC: do_modify_var(command, DO_DEC); break;
5199  case Q_ECHO: do_echo(command); command_executed++; break;
5200  case Q_SYSTEM: do_system(command); break;
5201  case Q_REMOVE_FILE: do_remove_file(command); break;
5202  case Q_MKDIR: do_mkdir(command); break;
5203  case Q_RMDIR: do_rmdir(command); break;
5204  case Q_FILE_EXIST: do_file_exist(command); break;
5205  case Q_WRITE_FILE: do_write_file(command); break;
5206  case Q_APPEND_FILE: do_append_file(command); break;
5207  case Q_DIFF_FILES: do_diff_files(command); break;
5208  case Q_SEND_QUIT: do_send_quit(command); break;
5209  case Q_CHANGE_USER: do_change_user(command); break;
5210  case Q_CAT_FILE: do_cat_file(command); break;
5211  case Q_COPY_FILE: do_copy_file(command); break;
5212  case Q_CHMOD_FILE: do_chmod_file(command); break;
5213  case Q_PERL: do_perl(command); break;
5214  case Q_DELIMITER:
5215  do_delimiter(command);
5216  break;
5217  case Q_DISPLAY_VERTICAL_RESULTS:
5218  display_result_vertically= true;
5219  break;
5220  case Q_DISPLAY_HORIZONTAL_RESULTS:
5221  display_result_vertically= false;
5222  break;
5223  case Q_SORTED_RESULT:
5224  /*
5225  Turn on sorting of result set, will be reset after next
5226  command
5227  */
5228  display_result_sorted= true;
5229  break;
5230  case Q_LET: do_let(command); break;
5231  case Q_EVAL_RESULT:
5232  die("'eval_result' command is deprecated");
5233  case Q_EVAL:
5234  case Q_QUERY_VERTICAL:
5235  case Q_QUERY_HORIZONTAL:
5236  if (command->query == command->query_buf)
5237  {
5238  /* Skip the first part of command, i.e query_xxx */
5239  command->query= command->first_argument;
5240  command->first_word_len= 0;
5241  }
5242  /* fall through */
5243  case Q_QUERY:
5244  case Q_REAP:
5245  {
5246  bool old_display_result_vertically= display_result_vertically;
5247  /* Default is full query, both reap and send */
5248  int flags= QUERY_REAP_FLAG | QUERY_SEND_FLAG;
5249 
5250  if (q_send_flag)
5251  {
5252  /* Last command was an empty 'send' */
5253  flags= QUERY_SEND_FLAG;
5254  q_send_flag= 0;
5255  }
5256  else if (command->type == Q_REAP)
5257  {
5258  flags= QUERY_REAP_FLAG;
5259  }
5260 
5261  /* Check for special property for this query */
5262  display_result_vertically|= (command->type == Q_QUERY_VERTICAL);
5263 
5264  if (! save_file.empty())
5265  {
5266  command->require_file= save_file;
5267  save_file.clear();
5268  }
5269  run_query(*cur_con, command, flags);
5270  command_executed++;
5271  command->last_argument= command->end;
5272 
5273  /* Restore settings */
5274  display_result_vertically= old_display_result_vertically;
5275 
5276  break;
5277  }
5278  case Q_SEND:
5279  if (!*command->first_argument)
5280  {
5281  /*
5282  This is a send without arguments, it indicates that _next_ query
5283  should be send only
5284  */
5285  q_send_flag= 1;
5286  break;
5287  }
5288 
5289  /* Remove "send" if this is first iteration */
5290  if (command->query == command->query_buf)
5291  command->query= command->first_argument;
5292 
5293  /*
5294  run_query() can execute a query partially, depending on the flags.
5295  QUERY_SEND_FLAG flag without QUERY_REAP_FLAG tells it to just send
5296  the query and read the result some time later when reap instruction
5297  is given on this connection.
5298  */
5299  run_query(*cur_con, command, QUERY_SEND_FLAG);
5300  command_executed++;
5301  command->last_argument= command->end;
5302  break;
5303  case Q_REQUIRE:
5304  do_get_file_name(command, save_file);
5305  break;
5306  case Q_ERROR:
5307  do_get_errcodes(command);
5308  break;
5309  case Q_REPLACE:
5310  do_get_replace(command);
5311  break;
5312  case Q_REPLACE_REGEX:
5313  do_get_replace_regex(command);
5314  break;
5315  case Q_REPLACE_COLUMN:
5316  do_get_replace_column(command);
5317  break;
5318  case Q_COMMENT: /* Ignore row */
5319  command->last_argument= command->end;
5320  break;
5321  case Q_PING:
5322  {
5323  drizzle::result_c result;
5324  drizzle_return_t ret;
5325  (void) drizzle_ping(*cur_con, result, &ret);
5326  }
5327  break;
5328  case Q_EXEC:
5329  do_exec(command);
5330  command_executed++;
5331  break;
5332  case Q_START_TIMER:
5333  /* Overwrite possible earlier start of timer */
5334  timer_start= timer_now();
5335  break;
5336  case Q_END_TIMER:
5337  /* End timer before ending drizzletest */
5338  timer_output();
5339  break;
5340  case Q_CHARACTER_SET:
5341  do_set_charset(command);
5342  break;
5343  case Q_DISABLE_RECONNECT:
5344  set_reconnect(*cur_con, 0);
5345  break;
5346  case Q_ENABLE_RECONNECT:
5347  set_reconnect(*cur_con, 1);
5348  break;
5349  case Q_DISABLE_PARSING:
5350  if (parsing_disabled == 0)
5351  parsing_disabled= 1;
5352  else
5353  die("Parsing is already disabled");
5354  break;
5355  case Q_ENABLE_PARSING:
5356  /*
5357  Ensure we don't get parsing_disabled < 0 as this would accidentally
5358  disable code we don't want to have disabled
5359  */
5360  if (parsing_disabled == 1)
5361  parsing_disabled= 0;
5362  else
5363  die("Parsing is already enabled");
5364  break;
5365  case Q_DIE:
5366  /* Abort test with error code and error message */
5367  die("%s", command->first_argument);
5368  break;
5369  case Q_EXIT:
5370  /* Stop processing any more commands */
5371  abort_flag= 1;
5372  break;
5373  case Q_SKIP:
5374  abort_not_supported_test("%s", command->first_argument);
5375  break;
5376 
5377  case Q_RESULT:
5378  die("result, deprecated command");
5379  break;
5380 
5381  default:
5382  processed= 0;
5383  break;
5384  }
5385  }
5386 
5387  if (!processed)
5388  {
5389  current_line_inc= 0;
5390  switch (command->type) {
5391  case Q_WHILE: do_block(cmd_while, command); break;
5392  case Q_IF: do_block(cmd_if, command); break;
5393  case Q_END_BLOCK: do_done(command); break;
5394  default: current_line_inc = 1; break;
5395  }
5396  }
5397  else
5398  check_eol_junk(command->last_argument);
5399 
5400  if (command->type != Q_ERROR &&
5401  command->type != Q_COMMENT)
5402  {
5403  /*
5404  As soon as any non "error" command or comment has been executed,
5405  the array with expected errors should be cleared
5406  */
5407  memset(&saved_expected_errors, 0, sizeof(saved_expected_errors));
5408  }
5409 
5410  if (command_executed != last_command_executed)
5411  {
5412  /*
5413  As soon as any command has been executed,
5414  the replace structures should be cleared
5415  */
5416  free_all_replace();
5417 
5418  /* Also reset "sorted_result" */
5419  display_result_sorted= false;
5420  }
5421  last_command_executed= command_executed;
5422 
5423  parser.current_line += current_line_inc;
5424  if ( opt_mark_progress )
5425  mark_progress(command, parser.current_line);
5426  }
5427 
5428  start_lineno= 0;
5429 
5430  if (parsing_disabled)
5431  die("Test ended with parsing disabled");
5432 
5433  /*
5434  The whole test has been executed _sucessfully_.
5435  Time to compare result or save it to record file.
5436  The entire output from test is now kept in ds_res.
5437  */
5438  if (ds_res.empty())
5439  die("The test didn't produce any output");
5440  if (result_file_name.empty())
5441  {
5442  /* No result_file_name specified to compare with, print to stdout */
5443  printf("%s", ds_res.c_str());
5444  }
5445  else if (record)
5446  {
5447  /* Recording - dump the output from test to result file */
5448  str_to_file(result_file_name.c_str(), ds_res.c_str(), ds_res.length());
5449  }
5450  else
5451  {
5452  /* Check that the output from test is equal to result file
5453  - detect missing result file
5454  - detect zero size result file
5455  */
5456  check_result(ds_res);
5457  }
5458 
5459  struct stat res_info;
5460  if (not command_executed && not result_file_name.empty() && not stat(result_file_name.c_str(), &res_info))
5461  {
5462  /*
5463  my_stat() successful on result file. Check if we have not run a
5464  single query, but we do have a result file that contains data.
5465  Note that we don't care, if my_stat() fails. For example, for a
5466  non-existing or non-readable file, we assume it's fine to have
5467  no query output from the test file, e.g. regarded as no error.
5468  */
5469  die("No queries executed but result file found!");
5470  }
5471 
5472  if ( opt_mark_progress && ! result_file_name.empty() )
5473  dump_progress();
5474 
5475  /* Dump warning messages */
5476  if (not result_file_name.empty() && ds_warning_messages.length())
5477  dump_warning_messages();
5478 
5479  timer_output();
5480  /* Yes, if we got this far the test has suceeded! Sakila smiles */
5481  cleanup_and_exit(0);
5482 }
5483 
5484  catch(exception &err)
5485  {
5486  cerr<<err.what()<<endl;
5487  }
5488 
5489  return 0;
5490 }
5491 
5492 
5493 /*
5494  A primitive timer that give results in milliseconds if the
5495  --timer-file=<filename> is given. The timer result is written
5496  to that file when the result is available. To not confuse
5497  mysql-test-run with an old obsolete result, we remove the file
5498  before executing any commands. The time we measure is
5499 
5500  - If no explicit 'start_timer' or 'end_timer' is given in the
5501  test case, the timer measure how long we execute in drizzletest.
5502 
5503  - If only 'start_timer' is given we measure how long we execute
5504  from that point until we terminate drizzletest.
5505 
5506  - If only 'end_timer' is given we measure how long we execute
5507  from that we enter drizzletest to the 'end_timer' is command is
5508  executed.
5509 
5510  - If both 'start_timer' and 'end_timer' are given we measure
5511  the time between executing the two commands.
5512 */
5513 
5514 void timer_output()
5515 {
5516  if (timer_file)
5517  {
5518  ostringstream buf;
5519  uint64_t timer= timer_now() - timer_start;
5520  buf << timer;
5521  str_to_file(timer_file,buf.str().c_str(), buf.str().size() );
5522  /* Timer has been written to the file, don't use it anymore */
5523  timer_file= 0;
5524  }
5525 }
5526 
5527 
5528 uint64_t timer_now()
5529 {
5530 #if defined(HAVE_GETHRTIME)
5531  return gethrtime()/1000/1000;
5532 #else
5533  uint64_t newtime;
5534  struct timeval t;
5535  /*
5536  The following loop is here because gettimeofday may fail on some systems
5537  */
5538  while (gettimeofday(&t, NULL) != 0)
5539  {}
5540  newtime= (uint64_t)t.tv_sec * 1000000 + t.tv_usec;
5541  return newtime/1000;
5542 #endif /* defined(HAVE_GETHRTIME) */
5543 }
5544 
5545 
5546 /*
5547  Get arguments for replace_columns. The syntax is:
5548  replace-column column_number to_string [column_number to_string ...]
5549  Where each argument may be quoted with ' or "
5550  A argument may also be a variable, in which case the value of the
5551  variable is replaced.
5552 */
5553 
5554 void do_get_replace_column(st_command* command)
5555 {
5556  char *from= command->first_argument;
5557  char *buff, *start;
5558 
5559 
5560  free_replace_column();
5561  if (!*from)
5562  die("Missing argument in %s", command->query);
5563 
5564  /* Allocate a buffer for results */
5565  start= buff= (char *)malloc(strlen(from)+1);
5566  while (*from)
5567  {
5568  uint32_t column_number;
5569 
5570  char *to= get_string(&buff, &from, command);
5571  if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS)
5572  die("Wrong column number to replace_column in '%s'", command->query);
5573  if (!*from)
5574  die("Wrong number of arguments to replace_column in '%s'", command->query);
5575  to= get_string(&buff, &from, command);
5576  free(replace_column[column_number-1]);
5577  replace_column[column_number-1]= strdup(to);
5578  set_if_bigger(max_replace_column, column_number);
5579  }
5580  free(start);
5581  command->last_argument= command->end;
5582 }
5583 
5584 
5585 void free_replace_column()
5586 {
5587  for (uint32_t i= 0; i < max_replace_column; i++)
5588  {
5589  free(replace_column[i]);
5590  replace_column[i]= 0;
5591  }
5592  max_replace_column= 0;
5593 }
5594 
5595 
5596 /****************************************************************************/
5597 /*
5598  Replace functions
5599 */
5600 
5601 /* Definitions for replace result */
5602 
5604 { /* when using array-strings */
5605 public:
5606  ~POINTER_ARRAY();
5607  int insert(char* name);
5608 
5609  POINTER_ARRAY()
5610  {
5611  memset(this, 0, sizeof(*this));
5612  }
5613 
5614  TYPELIB typelib; /* Pointer to strings */
5615  unsigned char *str; /* Strings is here */
5616  uint8_t* flag; /* Flag about each var. */
5617  uint32_t array_allocs;
5618  uint32_t max_count;
5619  uint32_t length;
5620  uint32_t max_length;
5621 };
5622 
5623 struct st_replace;
5624 struct st_replace *init_replace(const char **from, const char **to, uint32_t count,
5625  char *word_end_chars);
5626 
5627 void replace_strings_append(struct st_replace *rep, string& ds, const char *from, int len);
5628 
5629 st_replace *glob_replace= NULL;
5630 // boost::scoped_ptr<st_replace> glob_replace;
5631 
5632 /*
5633  Get arguments for replace. The syntax is:
5634  replace from to [from to ...]
5635  Where each argument may be quoted with ' or "
5636  A argument may also be a variable, in which case the value of the
5637  variable is replaced.
5638 */
5639 
5640 POINTER_ARRAY::~POINTER_ARRAY()
5641 {
5642  if (!typelib.count)
5643  return;
5644  typelib.count= 0;
5645  free((char*) typelib.type_names);
5646  typelib.type_names=0;
5647  free(str);
5648 }
5649 
5650 void do_get_replace(st_command* command)
5651 {
5652  char *from= command->first_argument;
5653  if (!*from)
5654  die("Missing argument in %s", command->query);
5655  free_replace();
5656  POINTER_ARRAY to_array, from_array;
5657  char* start= (char*)malloc(strlen(from) + 1);
5658  char* buff= start;
5659  while (*from)
5660  {
5661  char *to= get_string(&buff, &from, command);
5662  if (!*from)
5663  die("Wrong number of arguments to replace_result in '%s'", command->query);
5664  from_array.insert(to);
5665  to= get_string(&buff, &from, command);
5666  to_array.insert(to);
5667  }
5668  char word_end_chars[256];
5669  char* pos= word_end_chars;
5670  for (int i= 1; i < 256; i++)
5671  {
5672  if (charset_info->isspace(i))
5673  *pos++= i;
5674  }
5675  *pos=0; /* End pointer */
5676  if (!(glob_replace= init_replace(from_array.typelib.type_names,
5677  to_array.typelib.type_names,
5678  from_array.typelib.count,
5679  word_end_chars)))
5680  die("Can't initialize replace from '%s'", command->query);
5681  free(start);
5682  command->last_argument= command->end;
5683  return;
5684 }
5685 
5686 
5687 void free_replace()
5688 {
5689  free(glob_replace);
5690  glob_replace=0;
5691 }
5692 
5693 
5694 typedef struct st_replace {
5695  bool found;
5696  struct st_replace *next[256];
5697 } REPLACE;
5698 
5699 typedef struct st_replace_found {
5700  bool found;
5701  char *replace_string;
5702  uint32_t to_offset;
5703  int from_offset;
5704 } REPLACE_STRING;
5705 
5706 
5707 void replace_strings_append(REPLACE *rep, string& ds, const char *str, int len)
5708 {
5709  REPLACE_STRING *rep_str;
5710  const char* start= str;
5711  const char* from= str;
5712 
5713  REPLACE* rep_pos=rep+1;
5714  for (;;)
5715  {
5716  /* Loop through states */
5717  while (!rep_pos->found)
5718  rep_pos= rep_pos->next[(unsigned char) *from++];
5719 
5720  /* Does this state contain a string to be replaced */
5721  if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string)
5722  {
5723  /* No match found */
5724  ds.append(start, from - start - 1);
5725  return;
5726  }
5727 
5728  /* Append part of original string before replace string */
5729  ds.append(start, (from - rep_str->to_offset) - start);
5730 
5731  /* Append replace string */
5732  ds.append(rep_str->replace_string, strlen(rep_str->replace_string));
5733 
5734  if (!*(from-=rep_str->from_offset) && rep_pos->found != 2)
5735  return;
5736 
5737  assert(from <= str+len);
5738  start= from;
5739  rep_pos=rep;
5740  }
5741 }
5742 
5743 
5744 /*
5745  Regex replace functions
5746 */
5747 
5748 
5749 /* Stores regex substitutions */
5750 
5751 struct st_regex
5752 {
5753  char* pattern; /* Pattern to be replaced */
5754  char* replace; /* String or expression to replace the pattern with */
5755  int icase; /* true if the match is case insensitive */
5756  int global; /* true if the match should be global --
5757  i.e. repeat the matching until the end of the string */
5758 };
5759 
5761 {
5762 public:
5763  st_replace_regex(char* expr);
5764  int multi_reg_replace(char* val);
5765 
5766  /*
5767  Temporary storage areas for substitutions. To reduce unnessary copying
5768  and memory freeing/allocation, we pre-allocate two buffers, and alternate
5769  their use, one for input/one for output, the roles changing on the next
5770  st_regex substition. At the end of substitutions buf points to the
5771  one containing the final result.
5772  */
5773  typedef vector<st_regex> regex_arr_t;
5774 
5775  char* buf_;
5776  char* even_buf;
5777  char* odd_buf;
5778  int even_buf_len;
5779  int odd_buf_len;
5780  boost::array<char, 8 << 10> buf0_;
5781  boost::array<char, 8 << 10> buf1_;
5782  regex_arr_t regex_arr;
5783 };
5784 
5785 boost::scoped_ptr<st_replace_regex> glob_replace_regex;
5786 
5787 int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace,
5788  char *string, int icase, int global);
5789 
5790 
5791 
5792 /*
5793  Finds the next (non-escaped) '/' in the expression.
5794  (If the character '/' is needed, it can be escaped using '\'.)
5795 */
5796 
5797 #define PARSE_REGEX_ARG \
5798  while (p < expr_end) \
5799  { \
5800  char c= *p; \
5801  if (c == '/') \
5802  { \
5803  if (last_c == '\\') \
5804  { \
5805  buf_p[-1]= '/'; \
5806  } \
5807  else \
5808  { \
5809  *buf_p++ = 0; \
5810  break; \
5811  } \
5812  } \
5813  else \
5814  *buf_p++ = c; \
5815  \
5816  last_c= c; \
5817  p++; \
5818  } \
5819  \
5820 /*
5821  Initializes the regular substitution expression to be used in the
5822  result output of test.
5823 
5824  Returns: st_replace_regex struct with pairs of substitutions
5825 */
5826 
5827 st_replace_regex::st_replace_regex(char* expr)
5828 {
5829  uint32_t expr_len= strlen(expr);
5830  char last_c = 0;
5831  st_regex reg;
5832 
5833  char* buf= new char[expr_len];
5834  char* expr_end= expr + expr_len;
5835  char* p= expr;
5836  char* buf_p= buf;
5837 
5838  /* for each regexp substitution statement */
5839  while (p < expr_end)
5840  {
5841  memset(&reg, 0, sizeof(reg));
5842  /* find the start of the statement */
5843  while (p < expr_end)
5844  {
5845  if (*p == '/')
5846  break;
5847  p++;
5848  }
5849 
5850  if (p == expr_end || ++p == expr_end)
5851  {
5852  if (!regex_arr.empty())
5853  break;
5854  else
5855  goto err;
5856  }
5857  /* we found the start */
5858  reg.pattern= buf_p;
5859 
5860  /* Find first argument -- pattern string to be removed */
5861  PARSE_REGEX_ARG
5862 
5863  if (p == expr_end || ++p == expr_end)
5864  goto err;
5865 
5866  /* buf_p now points to the replacement pattern terminated with \0 */
5867  reg.replace= buf_p;
5868 
5869  /* Find second argument -- replace string to replace pattern */
5870  PARSE_REGEX_ARG
5871 
5872  if (p == expr_end)
5873  goto err;
5874 
5875  /* skip the ending '/' in the statement */
5876  p++;
5877 
5878  /* Check if we should do matching case insensitive */
5879  if (p < expr_end && *p == 'i')
5880  {
5881  p++;
5882  reg.icase= 1;
5883  }
5884 
5885  /* Check if we should do matching globally */
5886  if (p < expr_end && *p == 'g')
5887  {
5888  p++;
5889  reg.global= 1;
5890  }
5891  regex_arr.push_back(reg);
5892  }
5893  odd_buf_len= even_buf_len= buf0_.size();
5894  even_buf= buf0_.data();
5895  odd_buf= buf1_.data();
5896  buf_= even_buf;
5897 
5898  return;
5899 
5900 err:
5901  die("Error parsing replace_regex \"%s\"", expr);
5902 }
5903 
5904 /*
5905  Execute all substitutions on val.
5906 
5907  Returns: true if substituition was made, false otherwise
5908  Side-effect: Sets r->buf to be the buffer with all substitutions done.
5909 
5910  IN:
5911  struct st_replace_regex* r
5912  char* val
5913  Out:
5914  struct st_replace_regex* r
5915  r->buf points at the resulting buffer
5916  r->even_buf and r->odd_buf might have been reallocated
5917  r->even_buf_len and r->odd_buf_len might have been changed
5918 
5919  TODO: at some point figure out if there is a way to do everything
5920  in one pass
5921 */
5922 
5923 int st_replace_regex::multi_reg_replace(char* val)
5924 {
5925  char* in_buf= val;
5926  char* out_buf= even_buf;
5927  int* buf_len_p= &even_buf_len;
5928  buf_= 0;
5929 
5930  /* For each substitution, do the replace */
5931  BOOST_FOREACH(regex_arr_t::const_reference i, regex_arr)
5932  {
5933  char* save_out_buf= out_buf;
5934  if (!reg_replace(&out_buf, buf_len_p, i.pattern, i.replace,
5935  in_buf, i.icase, i.global))
5936  {
5937  /* if the buffer has been reallocated, make adjustements */
5938  if (save_out_buf != out_buf)
5939  {
5940  if (save_out_buf == even_buf)
5941  even_buf= out_buf;
5942  else
5943  odd_buf= out_buf;
5944  }
5945  buf_= out_buf;
5946  if (in_buf == val)
5947  in_buf= odd_buf;
5948  std::swap(in_buf, out_buf);
5949  buf_len_p= (out_buf == even_buf) ? &even_buf_len : &odd_buf_len;
5950  }
5951  }
5952  return buf_ == 0;
5953 }
5954 
5955 /*
5956  Parse the regular expression to be used in all result files
5957  from now on.
5958 
5959  The syntax is --replace_regex /from/to/i /from/to/i ...
5960  i means case-insensitive match. If omitted, the match is
5961  case-sensitive
5962 
5963 */
5964 void do_get_replace_regex(st_command* command)
5965 {
5966  char *expr= command->first_argument;
5967  glob_replace_regex.reset(new st_replace_regex(expr));
5968  command->last_argument= command->end;
5969 }
5970 
5971 /*
5972  Performs a regex substitution
5973 
5974  IN:
5975 
5976  buf_p - result buffer pointer. Will change if reallocated
5977  buf_len_p - result buffer length. Will change if the buffer is reallocated
5978  pattern - regexp pattern to match
5979  replace - replacement expression
5980  string - the string to perform substituions in
5981  icase - flag, if set to 1 the match is case insensitive
5982 */
5983 int reg_replace(char** buf_p, int* buf_len_p, char *pattern,
5984  char *replace, char *in_string, int icase, int global)
5985 {
5986  const char *error= NULL;
5987  int erroffset;
5988  int ovector[3];
5989  pcre *re= pcre_compile(pattern,
5990  icase ? PCRE_CASELESS | PCRE_MULTILINE : PCRE_MULTILINE,
5991  &error, &erroffset, NULL);
5992  if (re == NULL)
5993  return 1;
5994 
5995  if (! global)
5996  {
5997 
5998  int rc= pcre_exec(re, NULL, in_string, (int)strlen(in_string),
5999  0, 0, ovector, 3);
6000  if (rc < 0)
6001  {
6002  pcre_free(re);
6003  return 1;
6004  }
6005 
6006  char *substring_to_replace= in_string + ovector[0];
6007  int substring_length= ovector[1] - ovector[0];
6008  *buf_len_p= strlen(in_string) - substring_length + strlen(replace);
6009  char* new_buf= (char*)malloc(*buf_len_p+1);
6010 
6011  memset(new_buf, 0, *buf_len_p+1);
6012  strncpy(new_buf, in_string, substring_to_replace-in_string);
6013  strncpy(new_buf+(substring_to_replace-in_string), replace, strlen(replace));
6014  strncpy(new_buf+(substring_to_replace-in_string)+strlen(replace),
6015  substring_to_replace + substring_length,
6016  strlen(in_string)
6017  - substring_length
6018  - (substring_to_replace-in_string));
6019  *buf_p= new_buf;
6020 
6021  pcre_free(re);
6022  return 0;
6023  }
6024  else
6025  {
6026  /* Repeatedly replace the string with the matched regex */
6027  string subject(in_string);
6028  size_t replace_length= strlen(replace);
6029  size_t length_of_replacement= strlen(replace);
6030  size_t current_position= 0;
6031  int rc= 0;
6032 
6033  while (true)
6034  {
6035  rc= pcre_exec(re, NULL, subject.c_str(), subject.length(),
6036  current_position, 0, ovector, 3);
6037  if (rc < 0)
6038  {
6039  break;
6040  }
6041 
6042  current_position= static_cast<size_t>(ovector[0]);
6043  replace_length= static_cast<size_t>(ovector[1] - ovector[0]);
6044  subject.replace(current_position, replace_length, replace, length_of_replacement);
6045  current_position= current_position + length_of_replacement;
6046  }
6047 
6048  char* new_buf = (char*) malloc(subject.length() + 1);
6049  memset(new_buf, 0, subject.length() + 1);
6050  strncpy(new_buf, subject.c_str(), subject.length());
6051  *buf_len_p= subject.length() + 1;
6052  *buf_p= new_buf;
6053 
6054  pcre_free(re);
6055  return 0;
6056  }
6057 }
6058 
6059 
6060 #ifndef WORD_BIT
6061 #define WORD_BIT (8*sizeof(uint32_t))
6062 #endif
6063 
6064 #define SET_MALLOC_HUNC 64
6065 #define LAST_CHAR_CODE 259
6066 
6067 class REP_SET
6068 {
6069 public:
6070  void internal_set_bit(uint32_t bit);
6071  void internal_clear_bit(uint32_t bit);
6072  void or_bits(const REP_SET *from);
6073  void copy_bits(const REP_SET *from);
6074  int cmp_bits(const REP_SET *set2) const;
6075  int get_next_bit(uint32_t lastpos) const;
6076 
6077  uint32_t *bits; /* Pointer to used sets */
6078  short next[LAST_CHAR_CODE]; /* Pointer to next sets */
6079  uint32_t found_len; /* Best match to date */
6080  int found_offset;
6081  uint32_t table_offset;
6082  uint32_t size_of_bits; /* For convinience */
6083 };
6084 
6086 {
6087 public:
6088  int find_set(const REP_SET *find);
6089  void free_last_set();
6090  void free_sets();
6091  void make_sets_invisible();
6092 
6093  uint32_t count; /* Number of sets */
6094  uint32_t extra; /* Extra sets in buffer */
6095  uint32_t invisible; /* Sets not shown */
6096  uint32_t size_of_bits;
6097  REP_SET *set,*set_buffer;
6098  uint32_t *bit_buffer;
6099 };
6100 
6101 struct FOUND_SET
6102 {
6103  uint32_t table_offset;
6104  int found_offset;
6105 };
6106 
6107 struct FOLLOWS
6108 {
6109  int chr;
6110  uint32_t table_offset;
6111  uint32_t len;
6112 };
6113 
6114 void init_sets(REP_SETS *sets, uint32_t states);
6115 REP_SET *make_new_set(REP_SETS *sets);
6116 int find_found(FOUND_SET *found_set, uint32_t table_offset, int found_offset);
6117 
6118 static uint32_t found_sets= 0;
6119 
6120 static uint32_t replace_len(const char *str)
6121 {
6122  uint32_t len=0;
6123  while (*str)
6124  {
6125  if (str[0] == '\\' && str[1])
6126  str++;
6127  str++;
6128  len++;
6129  }
6130  return len;
6131 }
6132 
6133 /* Return 1 if regexp starts with \b or ends with \b*/
6134 
6135 static bool start_at_word(const char *pos)
6136 {
6137  return (!memcmp(pos, "\\b",2) && pos[2]) || !memcmp(pos, "\\^", 2);
6138 }
6139 
6140 static bool end_of_word(const char *pos)
6141 {
6142  const char *end= strchr(pos, '\0');
6143  return (end > pos+2 && !memcmp(end-2, "\\b", 2)) || (end >= pos+2 && !memcmp(end-2, "\\$",2));
6144 }
6145 
6146 /* Init a replace structure for further calls */
6147 
6148 REPLACE *init_replace(const char **from, const char **to, uint32_t count, char *word_end_chars)
6149 {
6150  const int SPACE_CHAR= 256;
6151  const int START_OF_LINE= 257;
6152  const int END_OF_LINE= 258;
6153 
6154  uint32_t i,j,states,set_nr,len,result_len,max_length,found_end,bits_set,bit_nr;
6155  int used_sets,chr,default_state;
6156  char used_chars[LAST_CHAR_CODE],is_word_end[256];
6157  char *to_pos, **to_array;
6158 
6159  /* Count number of states */
6160  for (i=result_len=max_length=0 , states=2; i < count; i++)
6161  {
6162  len=replace_len(from[i]);
6163  if (!len)
6164  {
6165  errno=EINVAL;
6166  return(0);
6167  }
6168  states+=len+1;
6169  result_len+=(uint32_t) strlen(to[i])+1;
6170  if (len > max_length)
6171  max_length=len;
6172  }
6173  memset(is_word_end, 0, sizeof(is_word_end));
6174  for (i=0; word_end_chars[i]; i++)
6175  is_word_end[(unsigned char) word_end_chars[i]]=1;
6176 
6177  REP_SETS sets;
6178  REP_SET *set,*start_states,*word_states,*new_set;
6179  REPLACE_STRING *rep_str;
6180  init_sets(&sets, states);
6181  found_sets=0;
6182  vector<FOUND_SET> found_set(max_length * count);
6183  make_new_set(&sets); /* Set starting set */
6184  sets.make_sets_invisible(); /* Hide previus sets */
6185  used_sets=-1;
6186  word_states=make_new_set(&sets); /* Start of new word */
6187  start_states=make_new_set(&sets); /* This is first state */
6188  vector<FOLLOWS> follow(states + 2);
6189  FOLLOWS *follow_ptr= &follow[1];
6190  /* Init follow_ptr[] */
6191  for (i=0, states=1; i < count; i++)
6192  {
6193  if (from[i][0] == '\\' && from[i][1] == '^')
6194  {
6195  start_states->internal_set_bit(states + 1);
6196  if (!from[i][2])
6197  {
6198  start_states->table_offset=i;
6199  start_states->found_offset=1;
6200  }
6201  }
6202  else if (from[i][0] == '\\' && from[i][1] == '$')
6203  {
6204  start_states->internal_set_bit(states);
6205  word_states->internal_set_bit(states);
6206  if (!from[i][2] && start_states->table_offset == UINT32_MAX)
6207  {
6208  start_states->table_offset=i;
6209  start_states->found_offset=0;
6210  }
6211  }
6212  else
6213  {
6214  word_states->internal_set_bit(states);
6215  if (from[i][0] == '\\' && (from[i][1] == 'b' && from[i][2]))
6216  start_states->internal_set_bit(states + 1);
6217  else
6218  start_states->internal_set_bit(states);
6219  }
6220  const char *pos;
6221  for (pos= from[i], len=0; *pos; pos++)
6222  {
6223  if (*pos == '\\' && *(pos+1))
6224  {
6225  pos++;
6226  switch (*pos) {
6227  case 'b':
6228  follow_ptr->chr = SPACE_CHAR;
6229  break;
6230  case '^':
6231  follow_ptr->chr = START_OF_LINE;
6232  break;
6233  case '$':
6234  follow_ptr->chr = END_OF_LINE;
6235  break;
6236  case 'r':
6237  follow_ptr->chr = '\r';
6238  break;
6239  case 't':
6240  follow_ptr->chr = '\t';
6241  break;
6242  case 'v':
6243  follow_ptr->chr = '\v';
6244  break;
6245  default:
6246  follow_ptr->chr = (unsigned char) *pos;
6247  break;
6248  }
6249  }
6250  else
6251  follow_ptr->chr= (unsigned char) *pos;
6252  follow_ptr->table_offset=i;
6253  follow_ptr->len= ++len;
6254  follow_ptr++;
6255  }
6256  follow_ptr->chr=0;
6257  follow_ptr->table_offset=i;
6258  follow_ptr->len=len;
6259  follow_ptr++;
6260  states+=(uint32_t) len+1;
6261  }
6262 
6263 
6264  for (set_nr=0; set_nr < sets.count; set_nr++)
6265  {
6266  set=sets.set+set_nr;
6267  default_state= 0; /* Start from beginning */
6268 
6269  /* If end of found-string not found or start-set with current set */
6270 
6271  for (i= UINT32_MAX; (i= set->get_next_bit(i));)
6272  {
6273  if (!follow[i].chr && !default_state)
6274  default_state= find_found(&found_set.front(), set->table_offset, set->found_offset+1);
6275  }
6276  sets.set[used_sets].copy_bits(set); /* Save set for changes */
6277  if (!default_state)
6278  sets.set[used_sets].or_bits(sets.set); /* Can restart from start */
6279 
6280  /* Find all chars that follows current sets */
6281  memset(used_chars, 0, sizeof(used_chars));
6282  for (i= UINT32_MAX; (i= sets.set[used_sets].get_next_bit(i));)
6283  {
6284  used_chars[follow[i].chr]=1;
6285  if ((follow[i].chr == SPACE_CHAR && !follow[i+1].chr &&
6286  follow[i].len > 1) || follow[i].chr == END_OF_LINE)
6287  used_chars[0]=1;
6288  }
6289 
6290  /* Mark word_chars used if \b is in state */
6291  if (used_chars[SPACE_CHAR])
6292  for (const char *pos= word_end_chars; *pos; pos++)
6293  used_chars[(int) (unsigned char) *pos] = 1;
6294 
6295  /* Handle other used characters */
6296  for (chr= 0; chr < 256; chr++)
6297  {
6298  if (! used_chars[chr])
6299  set->next[chr]= chr ? default_state : -1;
6300  else
6301  {
6302  new_set=make_new_set(&sets);
6303  set=sets.set+set_nr; /* if realloc */
6304  new_set->table_offset=set->table_offset;
6305  new_set->found_len=set->found_len;
6306  new_set->found_offset=set->found_offset+1;
6307  found_end=0;
6308 
6309  for (i= UINT32_MAX; (i= sets.set[used_sets].get_next_bit(i));)
6310  {
6311  if (!follow[i].chr || follow[i].chr == chr ||
6312  (follow[i].chr == SPACE_CHAR &&
6313  (is_word_end[chr] ||
6314  (!chr && follow[i].len > 1 && ! follow[i+1].chr))) ||
6315  (follow[i].chr == END_OF_LINE && ! chr))
6316  {
6317  if ((! chr || (follow[i].chr && !follow[i+1].chr)) &&
6318  follow[i].len > found_end)
6319  found_end=follow[i].len;
6320  if (chr && follow[i].chr)
6321  new_set->internal_set_bit(i + 1); /* To next set */
6322  else
6323  new_set->internal_set_bit(i);
6324  }
6325  }
6326  if (found_end)
6327  {
6328  new_set->found_len=0; /* Set for testing if first */
6329  bits_set=0;
6330  for (i= UINT32_MAX; (i= new_set->get_next_bit(i));)
6331  {
6332  if ((follow[i].chr == SPACE_CHAR ||
6333  follow[i].chr == END_OF_LINE) && ! chr)
6334  bit_nr=i+1;
6335  else
6336  bit_nr=i;
6337  if (follow[bit_nr-1].len < found_end ||
6338  (new_set->found_len &&
6339  (chr == 0 || !follow[bit_nr].chr)))
6340  new_set->internal_clear_bit(i);
6341  else
6342  {
6343  if (chr == 0 || !follow[bit_nr].chr)
6344  { /* best match */
6345  new_set->table_offset=follow[bit_nr].table_offset;
6346  if (chr || (follow[i].chr == SPACE_CHAR ||
6347  follow[i].chr == END_OF_LINE))
6348  new_set->found_offset=found_end; /* New match */
6349  new_set->found_len=found_end;
6350  }
6351  bits_set++;
6352  }
6353  }
6354  if (bits_set == 1)
6355  {
6356  set->next[chr] = find_found(&found_set.front(), new_set->table_offset, new_set->found_offset);
6357  sets.free_last_set();
6358  }
6359  else
6360  set->next[chr] = sets.find_set(new_set);
6361  }
6362  else
6363  set->next[chr] = sets.find_set(new_set);
6364  }
6365  }
6366  }
6367 
6368  /* Alloc replace structure for the replace-state-machine */
6369 
6370  REPLACE *replace= (REPLACE*)malloc(sizeof(REPLACE) * (sets.count)
6371  + sizeof(REPLACE_STRING) * (found_sets + 1) + sizeof(char*) * count + result_len);
6372  {
6373  memset(replace, 0, sizeof(REPLACE)*(sets.count)+
6374  sizeof(REPLACE_STRING)*(found_sets+1)+
6375  sizeof(char *)*count+result_len);
6376  rep_str=(REPLACE_STRING*) (replace+sets.count);
6377  to_array= (char **) (rep_str+found_sets+1);
6378  to_pos=(char *) (to_array+count);
6379  for (i=0; i < count; i++)
6380  {
6381  to_array[i]=to_pos;
6382  to_pos=strcpy(to_pos,to[i])+strlen(to[i])+1;
6383  }
6384  rep_str[0].found=1;
6385  rep_str[0].replace_string=0;
6386  for (i=1; i <= found_sets; i++)
6387  {
6388  const char *pos= from[found_set[i-1].table_offset];
6389  rep_str[i].found= !memcmp(pos, "\\^", 3) ? 2 : 1;
6390  rep_str[i].replace_string= to_array[found_set[i-1].table_offset];
6391  rep_str[i].to_offset= found_set[i-1].found_offset-start_at_word(pos);
6392  rep_str[i].from_offset= found_set[i-1].found_offset-replace_len(pos) + end_of_word(pos);
6393  }
6394  for (i=0; i < sets.count; i++)
6395  {
6396  for (j=0; j < 256; j++)
6397  if (sets.set[i].next[j] >= 0)
6398  replace[i].next[j]=replace+sets.set[i].next[j];
6399  else
6400  replace[i].next[j]=(REPLACE*) (rep_str+(-sets.set[i].next[j]-1));
6401  }
6402  }
6403  sets.free_sets();
6404  return replace;
6405 }
6406 
6407 
6408 void init_sets(REP_SETS *sets,uint32_t states)
6409 {
6410  memset(sets, 0, sizeof(*sets));
6411  sets->size_of_bits=((states+7)/8);
6412  sets->set_buffer=(REP_SET*) malloc(sizeof(REP_SET) * SET_MALLOC_HUNC);
6413  sets->bit_buffer=(uint*) malloc(sizeof(uint32_t) * sets->size_of_bits * SET_MALLOC_HUNC);
6414 }
6415 
6416 /* Make help sets invisible for nicer codeing */
6417 
6418 void REP_SETS::make_sets_invisible()
6419 {
6420  invisible= count;
6421  set += count;
6422  count= 0;
6423 }
6424 
6425 REP_SET *make_new_set(REP_SETS *sets)
6426 {
6427  REP_SET *set;
6428  if (sets->extra)
6429  {
6430  sets->extra--;
6431  set=sets->set+ sets->count++;
6432  memset(set->bits, 0, sizeof(uint32_t)*sets->size_of_bits);
6433  memset(&set->next[0], 0, sizeof(set->next[0])*LAST_CHAR_CODE);
6434  set->found_offset=0;
6435  set->found_len=0;
6436  set->table_offset= UINT32_MAX;
6437  set->size_of_bits=sets->size_of_bits;
6438  return set;
6439  }
6440  uint32_t count= sets->count + sets->invisible + SET_MALLOC_HUNC;
6441  set= (REP_SET*) realloc((unsigned char*) sets->set_buffer, sizeof(REP_SET)*count);
6442  sets->set_buffer=set;
6443  sets->set=set+sets->invisible;
6444  uint32_t* bit_buffer= (uint*) realloc((unsigned char*) sets->bit_buffer, (sizeof(uint32_t)*sets->size_of_bits)*count);
6445  sets->bit_buffer=bit_buffer;
6446  for (uint32_t i= 0; i < count; i++)
6447  {
6448  sets->set_buffer[i].bits=bit_buffer;
6449  bit_buffer+=sets->size_of_bits;
6450  }
6451  sets->extra=SET_MALLOC_HUNC;
6452  return make_new_set(sets);
6453 }
6454 
6455 void REP_SETS::free_last_set()
6456 {
6457  count--;
6458  extra++;
6459 }
6460 
6461 void REP_SETS::free_sets()
6462 {
6463  free(set_buffer);
6464  free(bit_buffer);
6465 }
6466 
6467 void REP_SET::internal_set_bit(uint32_t bit)
6468 {
6469  bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT);
6470 }
6471 
6472 void REP_SET::internal_clear_bit(uint32_t bit)
6473 {
6474  bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT));
6475 }
6476 
6477 
6478 void REP_SET::or_bits(const REP_SET *from)
6479 {
6480  for (uint32_t i= 0; i < size_of_bits; i++)
6481  bits[i]|=from->bits[i];
6482 }
6483 
6484 void REP_SET::copy_bits(const REP_SET *from)
6485 {
6486  memcpy(bits, from->bits, sizeof(uint32_t) * size_of_bits);
6487 }
6488 
6489 int REP_SET::cmp_bits(const REP_SET *set2) const
6490 {
6491  return memcmp(bits, set2->bits, sizeof(uint32_t) * size_of_bits);
6492 }
6493 
6494 /* Get next set bit from set. */
6495 
6496 int REP_SET::get_next_bit(uint32_t lastpos) const
6497 {
6498  uint32_t *start= bits + ((lastpos+1) / WORD_BIT);
6499  uint32_t *end= bits + size_of_bits;
6500  uint32_t bits0= start[0] & ~((1 << ((lastpos+1) % WORD_BIT)) -1);
6501 
6502  while (!bits0 && ++start < end)
6503  bits0= start[0];
6504  if (!bits0)
6505  return 0;
6506  uint32_t pos= (start - bits) * WORD_BIT;
6507  while (!(bits0 & 1))
6508  {
6509  bits0 >>=1;
6510  pos++;
6511  }
6512  return pos;
6513 }
6514 
6515 /* find if there is a same set in sets. If there is, use it and
6516  free given set, else put in given set in sets and return its
6517  position */
6518 
6519 int REP_SETS::find_set(const REP_SET *find)
6520 {
6521  uint32_t i= 0;
6522  for (; i < count - 1; i++)
6523  {
6524  if (!set[i].cmp_bits(find))
6525  {
6526  free_last_set();
6527  return i;
6528  }
6529  }
6530  return i; /* return new postion */
6531 }
6532 
6533 /* find if there is a found_set with same table_offset & found_offset
6534  If there is return offset to it, else add new offset and return pos.
6535  Pos returned is -offset-2 in found_set_structure because it is
6536  saved in set->next and set->next[] >= 0 points to next set and
6537  set->next[] == -1 is reserved for end without replaces.
6538 */
6539 
6540 int find_found(FOUND_SET *found_set, uint32_t table_offset, int found_offset)
6541 {
6542  uint32_t i= 0;
6543  for (; i < found_sets; i++)
6544  {
6545  if (found_set[i].table_offset == table_offset &&
6546  found_set[i].found_offset == found_offset)
6547  return - i - 2;
6548  }
6549  found_set[i].table_offset= table_offset;
6550  found_set[i].found_offset= found_offset;
6551  found_sets++;
6552  return - i - 2; // return new postion
6553 }
6554 
6555 /****************************************************************************
6556  * Handle replacement of strings
6557  ****************************************************************************/
6558 
6559 #define PC_MALLOC 256 /* Bytes for pointers */
6560 #define PS_MALLOC 512 /* Bytes for data */
6561 
6562 static int insert_pointer_name(POINTER_ARRAY* pa, char* name)
6563 {
6564  uint32_t i,length,old_count;
6565  unsigned char *new_pos;
6566  const char **new_array;
6567 
6568 
6569  if (! pa->typelib.count)
6570  {
6571  pa->typelib.type_names=(const char **)
6572  malloc(((PC_MALLOC-MALLOC_OVERHEAD)/
6573  (sizeof(char *)+sizeof(*pa->flag))*
6574  (sizeof(char *)+sizeof(*pa->flag))));
6575  pa->str= (unsigned char*) malloc(PS_MALLOC-MALLOC_OVERHEAD);
6576  pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(unsigned char*)+
6577  sizeof(*pa->flag));
6578  pa->flag= (uint8_t*) (pa->typelib.type_names+pa->max_count);
6579  pa->length=0;
6580  pa->max_length=PS_MALLOC-MALLOC_OVERHEAD;
6581  pa->array_allocs=1;
6582  }
6583  length=(uint32_t) strlen(name)+1;
6584  if (pa->length+length >= pa->max_length)
6585  {
6586  new_pos= (unsigned char*)realloc((unsigned char*)pa->str, (size_t)(pa->max_length+PS_MALLOC));
6587  if (new_pos != pa->str)
6588  {
6589  ptrdiff_t diff= PTR_BYTE_DIFF(new_pos,pa->str);
6590  for (i=0; i < pa->typelib.count; i++)
6591  pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff,
6592  char*);
6593  pa->str=new_pos;
6594  }
6595  pa->max_length+=PS_MALLOC;
6596  }
6597  if (pa->typelib.count >= pa->max_count-1)
6598  {
6599  size_t len;
6600  pa->array_allocs++;
6601  len=(PC_MALLOC*pa->array_allocs - MALLOC_OVERHEAD);
6602  new_array= (const char **)realloc((unsigned char*) pa->typelib.type_names,
6603  len/
6604  (sizeof(unsigned char*)+sizeof(*pa->flag))*
6605  (sizeof(unsigned char*)+sizeof(*pa->flag)));
6606  pa->typelib.type_names=new_array;
6607  old_count=pa->max_count;
6608  pa->max_count=len/(sizeof(unsigned char*) + sizeof(*pa->flag));
6609  pa->flag= (uint8_t*) (pa->typelib.type_names+pa->max_count);
6610  memcpy(pa->flag, pa->typelib.type_names+old_count,
6611  old_count*sizeof(*pa->flag));
6612  }
6613  pa->flag[pa->typelib.count]=0; /* Reset flag */
6614  pa->typelib.type_names[pa->typelib.count++]= (char*) pa->str+pa->length;
6615  pa->typelib.type_names[pa->typelib.count]= NULL; /* Put end-mark */
6616  strcpy((char*) pa->str+pa->length,name);
6617  pa->length+=length;
6618  return(0);
6619 } /* insert_pointer_name */
6620 
6621 int POINTER_ARRAY::insert(char* name)
6622 {
6623  return insert_pointer_name(this, name);
6624 }
6625 
6626 
6627 /* Functions that uses replace and replace_regex */
6628 
6629 /* Append the string to ds, with optional replace */
6630 void replace_append_mem(string& ds, const char *val, int len)
6631 {
6632  char *v= strdup(val);
6633 
6634  if (glob_replace_regex && !glob_replace_regex->multi_reg_replace(v))
6635  {
6636  v= glob_replace_regex->buf_;
6637  len= strlen(v);
6638  }
6639  if (glob_replace)
6640  {
6641  /* Normal replace */
6642  replace_strings_append(glob_replace, ds, v, len);
6643  }
6644  else
6645  ds.append(v, len);
6646 }
6647 
6648 
6649 /* Append zero-terminated string to ds, with optional replace */
6650 void replace_append(string *ds, const char *val)
6651 {
6652  replace_append_mem(*ds, val, strlen(val));
6653 }
6654 
6655 /* Append uint32_t to ds, with optional replace */
6656 void replace_append_uint(string& ds, uint32_t val)
6657 {
6658  ostringstream buff;
6659  buff << val;
6660  replace_append_mem(ds, buff.str().c_str(), buff.str().size());
6661 
6662 }
6663 
6664 
6665 
6666 /*
6667  Build a list of pointer to each line in ds_input, sort
6668  the list and use the sorted list to append the strings
6669  sorted to the output ds
6670 
6671  SYNOPSIS
6672  dynstr_append_sorted
6673  ds - string where the sorted output will be appended
6674  ds_input - string to be sorted
6675 
6676 */
6677 
6678 
6679 void append_sorted(string& ds, const string& ds_input)
6680 {
6681  priority_queue<string, vector<string>, greater<string> > lines;
6682 
6683  if (ds_input.empty())
6684  return; /* No input */
6685 
6686  unsigned long eol_pos= ds_input.find_first_of('\n', 0);
6687  if (eol_pos == string::npos)
6688  return; // We should have at least one header here
6689 
6690  ds.append(ds_input.substr(0, eol_pos+1));
6691 
6692  unsigned long start_pos= eol_pos+1;
6693 
6694  /* Insert line(s) in array */
6695  do {
6696 
6697  eol_pos= ds_input.find_first_of('\n', start_pos);
6698  /* Find end of line */
6699  lines.push(ds_input.substr(start_pos, eol_pos-start_pos+1));
6700  start_pos= eol_pos+1;
6701 
6702  } while ( eol_pos != string::npos);
6703 
6704  /* Create new result */
6705  while (!lines.empty())
6706  {
6707  ds.append(lines.top());
6708  lines.pop();
6709  }
6710 }
6711 
6712 static void free_all_replace()
6713 {
6714  free_replace();
6715  glob_replace_regex.reset();
6716  free_replace_column();
6717 }
TODO: Rename this file - func.h is stupid.
DRIZZLED_API int tmpfile(const char *prefix)
Definition: session.cc:121