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