Drizzled Public API Documentation

drizzletest.cc

00001 /* - mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  * 
00004  *  Copyright (C) 2010 Vijay Samuel
00005  *  Copyright (C) 2008 MySQL
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020  */
00021 
00022 /*
00023   drizzletest
00024 
00025   Tool used for executing a .test file
00026 
00027   See the "DRIZZLE Test framework manual" for more information
00028   http://dev.mysql.com/doc/drizzletest/en/index.html
00029 
00030   Please keep the test framework tools identical in all versions!
00031 
00032   Written by:
00033   Sasha Pachev <sasha@mysql.com>
00034   Matt Wagner  <matt@mysql.com>
00035   Monty
00036   Jani
00037   Holyfoot
00038 */
00039 
00040 #define MTEST_VERSION "3.3"
00041 
00042 #include "client_priv.h"
00043 
00044 #include <queue>
00045 #include <map>
00046 #include <string>
00047 #include <sstream>
00048 #include <fstream>
00049 #include <iostream>
00050 #include <vector>
00051 #include <algorithm>
00052 #ifdef HAVE_SYS_WAIT_H
00053 #include <sys/wait.h>
00054 #endif
00055 #include <cassert>
00056 #include <sys/stat.h>
00057 #include <sys/types.h>
00058 #include <fcntl.h>
00059 #include <boost/array.hpp>
00060 #include <boost/foreach.hpp>
00061 #include <boost/program_options.hpp>
00062 #include <boost/smart_ptr.hpp>
00063 
00064 #include PCRE_HEADER
00065 
00066 #include <stdarg.h>
00067 #include <boost/unordered_map.hpp>
00068 
00069 /* Added this for string translation. */
00070 #include <drizzled/gettext.h>
00071 #include <drizzled/type/time.h>
00072 #include <drizzled/charset.h>
00073 #include <drizzled/typelib.h>
00074 #include <drizzled/configmake.h>
00075 #include <drizzled/util/find_ptr.h>
00076 
00077 #define PTR_BYTE_DIFF(A,B) (ptrdiff_t) (reinterpret_cast<const unsigned char*>(A) - reinterpret_cast<const unsigned char*>(B))
00078 
00079 #ifndef DRIZZLE_RETURN_SERVER_GONE
00080 #define DRIZZLE_RETURN_HANDSHAKE_FAILED DRIZZLE_RETURN_ERROR_CODE
00081 #endif
00082 namespace po= boost::program_options;
00083 using namespace std;
00084 using namespace drizzled;
00085 
00086 unsigned char *get_var_key(const unsigned char* var, size_t *len, bool);
00087 
00088 int get_one_option(int optid, const struct option *, char *argument);
00089 
00090 #define MAX_VAR_NAME_LENGTH    256
00091 #define MAX_COLUMNS            256
00092 #define MAX_DELIMITER_LENGTH 16
00093 /* Flags controlling send and reap */
00094 #define QUERY_SEND_FLAG  1
00095 #define QUERY_REAP_FLAG  2
00096 
00097 typedef boost::unordered_map<std::string, uint32_t> ErrorCodes;
00098 ErrorCodes global_error_names;
00099 
00100 enum {
00101   OPT_PS_PROTOCOL, OPT_SP_PROTOCOL, OPT_CURSOR_PROTOCOL, OPT_VIEW_PROTOCOL,
00102   OPT_MAX_CONNECT_RETRIES, OPT_MARK_PROGRESS, OPT_LOG_DIR, OPT_TAIL_LINES,
00103   OPT_TESTDIR
00104 };
00105 
00106 static int record= 0, opt_sleep= -1;
00107 static char *opt_pass= NULL;
00108 const char *unix_sock= NULL;
00109 static uint32_t opt_port= 0;
00110 static uint32_t opt_max_connect_retries;
00111 static bool silent= false, verbose= false;
00112 static bool opt_mark_progress= false;
00113 static bool parsing_disabled= false;
00114 static bool display_result_vertically= false,
00115   display_metadata= false, display_result_sorted= false;
00116 static bool disable_query_log= false, disable_result_log= false;
00117 static bool disable_warnings= false;
00118 static bool disable_info= true;
00119 static bool abort_on_error= true;
00120 static bool server_initialized= false;
00121 static bool is_windows= false;
00122 static bool use_drizzle_protocol= false;
00123 static char line_buffer[MAX_DELIMITER_LENGTH], *line_buffer_pos= line_buffer;
00124 static void free_all_replace();
00125 
00126 std::string opt_basedir,
00127   opt_charsets_dir,
00128   opt_db,
00129   opt_host,
00130   opt_include,
00131   opt_testdir,
00132   opt_logdir,
00133   password,
00134   opt_password,
00135   result_file_name,
00136   opt_user,
00137   opt_protocol;
00138 
00139 static uint32_t start_lineno= 0; /* Start line of current command */
00140 
00141 /* Number of lines of the result to include in failure report */
00142 static uint32_t opt_tail_lines= 0;
00143 
00144 static char delimiter[MAX_DELIMITER_LENGTH]= ";";
00145 static uint32_t delimiter_length= 1;
00146 
00147 static char TMPDIR[FN_REFLEN];
00148 
00149 /* Block stack */
00150 enum block_cmd {
00151   cmd_none,
00152   cmd_if,
00153   cmd_while
00154 };
00155 
00156 struct st_block
00157 {
00158   int             line; /* Start line of block */
00159   bool         ok;   /* Should block be executed */
00160   enum block_cmd  cmd;  /* Command owning the block */
00161 };
00162 
00163 static struct st_block block_stack[32];
00164 static struct st_block *cur_block, *block_stack_end;
00165 
00166 /* Open file stack */
00167 struct st_test_file
00168 {
00169   FILE* file;
00170   const char *file_name;
00171   uint32_t lineno; /* Current line in file */
00172 };
00173 
00174 static boost::array<st_test_file, 16> file_stack;
00175 static st_test_file* cur_file;
00176 
00177 static const CHARSET_INFO *charset_info= &my_charset_utf8_general_ci; /* Default charset */
00178 
00179 /*
00180   Timer related variables
00181   See the timer_output() definition for details
00182 */
00183 static char *timer_file = NULL;
00184 static uint64_t timer_start;
00185 static void timer_output();
00186 static uint64_t timer_now();
00187 
00188 static uint64_t progress_start= 0;
00189 
00190 vector<struct st_command*> q_lines;
00191 
00192 typedef struct {
00193   int read_lines,current_line;
00194 } parser_st;
00195 parser_st parser;
00196 
00197 typedef struct
00198 {
00199   char file[FN_REFLEN];
00200   uint32_t pos;
00201 } master_pos_st;
00202 
00203 master_pos_st master_pos;
00204 
00205 /* if set, all results are concated and compared against this file */
00206 
00207 class VAR
00208 {
00209 public:
00210   char *name;
00211   int name_len;
00212   char *str_val;
00213   int str_val_len;
00214   int int_val;
00215   int alloced_len;
00216   int int_dirty; /* do not update string if int is updated until first read */
00217   int alloced;
00218   char *env_s;
00219 };
00220 
00221 /*Perl/shell-like variable registers */
00222 boost::array<VAR, 10> var_reg;
00223 
00224 typedef boost::unordered_map<string, VAR *> var_hash_t;
00225 var_hash_t var_hash;
00226 
00227 struct st_connection
00228 {
00229   drizzle_st *drizzle;
00230   drizzle_con_st con;
00231   /* Used when creating views and sp, to avoid implicit commit */
00232   drizzle_con_st *util_con;
00233   char *name;
00234 };
00235 struct st_connection connections[128];
00236 struct st_connection* cur_con= NULL, *next_con, *connections_end;
00237 
00238 /*
00239   List of commands in drizzletest
00240   Must match the "command_names" array
00241   Add new commands before Q_UNKNOWN!
00242 */
00243 enum enum_commands {
00244   Q_CONNECTION=1,     Q_QUERY,
00245   Q_CONNECT,      Q_SLEEP, Q_REAL_SLEEP,
00246   Q_INC,        Q_DEC,
00247   Q_SOURCE,      Q_DISCONNECT,
00248   Q_LET,        Q_ECHO,
00249   Q_WHILE,      Q_END_BLOCK,
00250   Q_SYSTEM,      Q_RESULT,
00251   Q_REQUIRE,      Q_SAVE_MASTER_POS,
00252   Q_SYNC_WITH_MASTER,
00253   Q_SYNC_SLAVE_WITH_MASTER,
00254   Q_ERROR,
00255   Q_SEND,        Q_REAP,
00256   Q_DIRTY_CLOSE,      Q_REPLACE, Q_REPLACE_COLUMN,
00257   Q_PING,        Q_EVAL,
00258   Q_EVAL_RESULT,
00259   Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
00260   Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
00261   Q_WAIT_FOR_SLAVE_TO_STOP,
00262   Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
00263   Q_ENABLE_INFO, Q_DISABLE_INFO,
00264   Q_ENABLE_METADATA, Q_DISABLE_METADATA,
00265   Q_EXEC, Q_DELIMITER,
00266   Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR,
00267   Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS,
00268   Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, Q_SORTED_RESULT,
00269   Q_START_TIMER, Q_END_TIMER,
00270   Q_CHARACTER_SET,
00271   Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT,
00272   Q_IF,
00273   Q_DISABLE_PARSING, Q_ENABLE_PARSING,
00274   Q_REPLACE_REGEX, Q_REMOVE_FILE, Q_FILE_EXIST,
00275   Q_WRITE_FILE, Q_COPY_FILE, Q_PERL, Q_DIE, Q_EXIT, Q_SKIP,
00276   Q_CHMOD_FILE, Q_APPEND_FILE, Q_CAT_FILE, Q_DIFF_FILES,
00277   Q_SEND_QUIT, Q_CHANGE_USER, Q_MKDIR, Q_RMDIR,
00278 
00279   Q_UNKNOWN,             /* Unknown command.   */
00280   Q_COMMENT,             /* Comments, ignored. */
00281   Q_COMMENT_WITH_COMMAND
00282 };
00283 
00284 
00285 const char *command_names[]=
00286 {
00287   "connection",
00288   "query",
00289   "connect",
00290   "sleep",
00291   "real_sleep",
00292   "inc",
00293   "dec",
00294   "source",
00295   "disconnect",
00296   "let",
00297   "echo",
00298   "while",
00299   "end",
00300   "system",
00301   "result",
00302   "require",
00303   "save_master_pos",
00304   "sync_with_master",
00305   "sync_slave_with_master",
00306   "error",
00307   "send",
00308   "reap",
00309   "dirty_close",
00310   "replace_result",
00311   "replace_column",
00312   "ping",
00313   "eval",
00314   "eval_result",
00315   /* Enable/disable that the _query_ is logged to result file */
00316   "enable_query_log",
00317   "disable_query_log",
00318   /* Enable/disable that the _result_ from a query is logged to result file */
00319   "enable_result_log",
00320   "disable_result_log",
00321   "wait_for_slave_to_stop",
00322   "enable_warnings",
00323   "disable_warnings",
00324   "enable_info",
00325   "disable_info",
00326   "enable_metadata",
00327   "disable_metadata",
00328   "exec",
00329   "delimiter",
00330   "disable_abort_on_error",
00331   "enable_abort_on_error",
00332   "vertical_results",
00333   "horizontal_results",
00334   "query_vertical",
00335   "query_horizontal",
00336   "sorted_result",
00337   "start_timer",
00338   "end_timer",
00339   "character_set",
00340   "disable_reconnect",
00341   "enable_reconnect",
00342   "if",
00343   "disable_parsing",
00344   "enable_parsing",
00345   "replace_regex",
00346   "remove_file",
00347   "file_exists",
00348   "write_file",
00349   "copy_file",
00350   "perl",
00351   "die",
00352 
00353   /* Don't execute any more commands, compare result */
00354   "exit",
00355   "skip",
00356   "chmod",
00357   "append_file",
00358   "cat_file",
00359   "diff_files",
00360   "send_quit",
00361   "change_user",
00362   "mkdir",
00363   "rmdir",
00364 
00365   0
00366 };
00367 
00368 
00369 /*
00370   The list of error codes to --error are stored in an internal array of
00371   structs. This struct can hold numeric SQL error codes, error names or
00372   SQLSTATE codes as strings. The element next to the last active element
00373   in the list is set to type ERR_EMPTY. When an SQL statement returns an
00374   error, we use this list to check if this is an expected error.
00375 */
00376 enum match_err_type
00377 {
00378   ERR_EMPTY= 0,
00379   ERR_ERRNO,
00380   ERR_SQLSTATE
00381 };
00382 
00383 struct st_match_err
00384 {
00385   enum match_err_type type;
00386   union
00387   {
00388     uint32_t errnum;
00389     char sqlstate[DRIZZLE_MAX_SQLSTATE_SIZE+1];  /* \0 terminated string */
00390   } code;
00391 };
00392 
00393 struct st_expected_errors
00394 {
00395   struct st_match_err err[10];
00396   uint32_t count;
00397 };
00398 
00399 static st_expected_errors saved_expected_errors;
00400 
00401 class st_command
00402 {
00403 public:
00404   char *query, *query_buf,*first_argument,*last_argument,*end;
00405   int first_word_len, query_len;
00406   bool abort_on_error;
00407   st_expected_errors expected_errors;
00408   string require_file;
00409   enum_commands type;
00410 
00411   st_command()
00412     : query(NULL), query_buf(NULL), first_argument(NULL), last_argument(NULL),
00413       end(NULL), first_word_len(0), query_len(0), abort_on_error(false),
00414       require_file(""), type(Q_CONNECTION)
00415   {
00416     memset(&expected_errors, 0, sizeof(st_expected_errors));
00417   }
00418 
00419   ~st_command()
00420   {
00421     free(query_buf);
00422   }
00423 };
00424 
00425 TYPELIB command_typelib= {array_elements(command_names),"",
00426                           command_names, 0};
00427 
00428 string ds_res, ds_progress, ds_warning_messages;
00429 
00430 char builtin_echo[FN_REFLEN];
00431 
00432 void die(const char *fmt, ...)
00433   __attribute__((format(printf, 1, 2)));
00434 void abort_not_supported_test(const char *fmt, ...)
00435   __attribute__((format(printf, 1, 2)));
00436 void verbose_msg(const char *fmt, ...)
00437   __attribute__((format(printf, 1, 2)));
00438 void warning_msg(const char *fmt, ...)
00439   __attribute__((format(printf, 1, 2)));
00440 void log_msg(const char *fmt, ...)
00441   __attribute__((format(printf, 1, 2)));
00442 
00443 VAR* var_from_env(const char *, const char *);
00444 VAR* var_init(VAR* v, const char *name, int name_len, const char *val,
00445               int val_len);
00446 VAR* var_get(const char *var_name, const char** var_name_end,
00447              bool raw, bool ignore_not_existing);
00448 void eval_expr(VAR* v, const char *p, const char** p_end);
00449 bool match_delimiter(int c, const char *delim, uint32_t length);
00450 void dump_result_to_reject_file(char *buf, int size);
00451 void dump_result_to_log_file(const char *buf, int size);
00452 void dump_warning_messages();
00453 void dump_progress();
00454 
00455 void do_eval(string *query_eval, const char *query,
00456              const char *query_end, bool pass_through_escape_chars);
00457 void str_to_file(const char *fname, const char *str, int size);
00458 void str_to_file2(const char *fname, const char *str, int size, bool append);
00459 
00460 /* For replace_column */
00461 static char *replace_column[MAX_COLUMNS];
00462 static uint32_t max_replace_column= 0;
00463 void do_get_replace_column(struct st_command*);
00464 void free_replace_column();
00465 
00466 /* For replace */
00467 void do_get_replace(struct st_command *command);
00468 void free_replace();
00469 
00470 /* For replace_regex */
00471 void do_get_replace_regex(struct st_command *command);
00472 
00473 void replace_append_mem(string *ds, const char *val,
00474                         int len);
00475 void replace_append(string *ds, const char *val);
00476 void replace_append_uint(string *ds, uint32_t val);
00477 void append_sorted(string* ds, string* ds_input);
00478 
00479 void handle_error(struct st_command*,
00480                   unsigned int err_errno, const char *err_error,
00481                   const char *err_sqlstate, string *ds);
00482 void handle_no_error(struct st_command*);
00483 
00484 
00485 void do_eval(string *query_eval, const char *query,
00486              const char *query_end, bool pass_through_escape_chars)
00487 {
00488   char c, next_c;
00489   int escaped = 0;
00490   VAR *v;
00491 
00492   for (const char *p= query; (c= *p) && p < query_end; ++p)
00493   {
00494     switch(c) {
00495     case '$':
00496       if (escaped)
00497       {
00498         escaped= 0;
00499         query_eval->append(p, 1);
00500       }
00501       else
00502       {
00503         if (!(v= var_get(p, &p, 0, 0)))
00504           die("Bad variable in eval");
00505         query_eval->append(v->str_val, v->str_val_len);
00506       }
00507       break;
00508     case '\\':
00509       next_c= *(p+1);
00510       if (escaped)
00511       {
00512         escaped= 0;
00513         query_eval->append(p, 1);
00514       }
00515       else if (next_c == '\\' || next_c == '$' || next_c == '"')
00516       {
00517         /* Set escaped only if next char is \, " or $ */
00518         escaped= 1;
00519 
00520         if (pass_through_escape_chars)
00521         {
00522           /* The escape char should be added to the output string. */
00523           query_eval->append(p, 1);
00524         }
00525       }
00526       else
00527         query_eval->append(p, 1);
00528       break;
00529     default:
00530       escaped= 0;
00531       query_eval->append(p, 1);
00532       break;
00533     }
00534   }
00535   return;
00536 }
00537 
00538 
00539 /*
00540   Concatenates any number of strings, escapes any OS quote in the result then
00541   surround the whole affair in another set of quotes which is finally appended
00542   to specified string.  This function is especially useful when
00543   building strings to be executed with the system() function.
00544 
00545   @param str string which will have addtional strings appended.
00546   @param append string to be appended.
00547   @param ... Optional. Additional string(s) to be appended.
00548 
00549   @note The final argument in the list must be NULL even if no additional
00550   options are passed.
00551 */
00552 
00553 static void append_os_quoted(string *str, const char *append, ...)
00554 {
00555   const char *quote_str= "\'";
00556   const uint32_t  quote_len= 1;
00557 
00558   va_list dirty_text;
00559 
00560   str->append(quote_str, quote_len); /* Leading quote */
00561   va_start(dirty_text, append);
00562   while (append != NULL)
00563   {
00564     const char  *cur_pos= append;
00565     const char *next_pos= cur_pos;
00566 
00567     /* Search for quote in each string and replace with escaped quote */
00568     while((next_pos= strrchr(cur_pos, quote_str[0])) != NULL)
00569     {
00570       str->append(cur_pos, next_pos - cur_pos);
00571       str->append("\\", 1);
00572       str->append(quote_str, quote_len);
00573       cur_pos= next_pos + 1;
00574     }
00575     str->append(cur_pos);
00576     append= va_arg(dirty_text, char *);
00577   }
00578   va_end(dirty_text);
00579   str->append(quote_str, quote_len); /* Trailing quote */
00580 }
00581 
00582 
00583 /*
00584   Run query and dump the result to stdout in vertical format
00585 
00586   NOTE! This function should be safe to call when an error
00587   has occured and thus any further errors will be ignored(although logged)
00588 
00589   SYNOPSIS
00590   show_query
00591   drizzle - connection to use
00592   query - query to run
00593 
00594 */
00595 
00596 static void show_query(drizzle_con_st *con, const char* query)
00597 {
00598   drizzle_result_st res;
00599   drizzle_return_t ret;
00600 
00601   if (!con)
00602     return;
00603 
00604   if (drizzle_query_str(con, &res, query, &ret) == NULL ||
00605       ret != DRIZZLE_RETURN_OK)
00606   {
00607     if (ret == DRIZZLE_RETURN_ERROR_CODE)
00608     {
00609       log_msg("Error running query '%s': %d %s",
00610               query, drizzle_result_error_code(&res),
00611               drizzle_result_error(&res));
00612       drizzle_result_free(&res);
00613     }
00614     else
00615     {
00616       log_msg("Error running query '%s': %d %s",
00617               query, ret, drizzle_con_error(con));
00618     }
00619     return;
00620   }
00621 
00622   if (drizzle_result_column_count(&res) == 0 ||
00623       drizzle_result_buffer(&res) != DRIZZLE_RETURN_OK)
00624   {
00625     /* No result set returned */
00626     drizzle_result_free(&res);
00627     return;
00628   }
00629 
00630   {
00631     drizzle_row_t row;
00632     unsigned int i;
00633     unsigned int row_num= 0;
00634     unsigned int num_fields= drizzle_result_column_count(&res);
00635     drizzle_column_st *column;
00636 
00637     fprintf(stderr, "=== %s ===\n", query);
00638     while ((row= drizzle_row_next(&res)))
00639     {
00640       size_t *lengths= drizzle_row_field_sizes(&res);
00641       row_num++;
00642 
00643       fprintf(stderr, "---- %d. ----\n", row_num);
00644       drizzle_column_seek(&res, 0);
00645       for(i= 0; i < num_fields; i++)
00646       {
00647         column= drizzle_column_next(&res);
00648         fprintf(stderr, "%s\t%.*s\n",
00649                 drizzle_column_name(column),
00650                 (int)lengths[i], row[i] ? row[i] : "NULL");
00651       }
00652     }
00653     for (i= 0; i < strlen(query)+8; i++)
00654       fprintf(stderr, "=");
00655     fprintf(stderr, "\n\n");
00656   }
00657   drizzle_result_free(&res);
00658 
00659   return;
00660 }
00661 
00662 
00663 /*
00664   Show any warnings just before the error. Since the last error
00665   is added to the warning stack, only print @@warning_count-1 warnings.
00666 
00667   NOTE! This function should be safe to call when an error
00668   has occured and this any further errors will be ignored(although logged)
00669 
00670   SYNOPSIS
00671   show_warnings_before_error
00672   drizzle - connection to use
00673 
00674 */
00675 
00676 static void show_warnings_before_error(drizzle_con_st *con)
00677 {
00678   drizzle_result_st res;
00679   drizzle_return_t ret;
00680   const char* query= "SHOW WARNINGS";
00681 
00682   if (!con)
00683     return;
00684 
00685   if (drizzle_query_str(con, &res, query, &ret) == NULL ||
00686       ret != DRIZZLE_RETURN_OK)
00687   {
00688     if (ret == DRIZZLE_RETURN_ERROR_CODE)
00689     {
00690       log_msg("Error running query '%s': %d %s",
00691               query, drizzle_result_error_code(&res),
00692               drizzle_result_error(&res));
00693       drizzle_result_free(&res);
00694     }
00695     else
00696     {
00697       log_msg("Error running query '%s': %d %s",
00698               query, ret, drizzle_con_error(con));
00699     }
00700     return;
00701   }
00702 
00703   if (drizzle_result_column_count(&res) == 0 ||
00704       drizzle_result_buffer(&res) != DRIZZLE_RETURN_OK)
00705   {
00706     /* No result set returned */
00707     drizzle_result_free(&res);
00708     return;
00709   }
00710 
00711   if (drizzle_result_row_count(&res) <= 1)
00712   {
00713     /* Don't display the last row, it's "last error" */
00714   }
00715   else
00716   {
00717     drizzle_row_t row;
00718     unsigned int row_num= 0;
00719     unsigned int num_fields= drizzle_result_column_count(&res);
00720 
00721     fprintf(stderr, "\nWarnings from just before the error:\n");
00722     while ((row= drizzle_row_next(&res)))
00723     {
00724       uint32_t i;
00725       size_t *lengths= drizzle_row_field_sizes(&res);
00726 
00727       if (++row_num >= drizzle_result_row_count(&res))
00728       {
00729         /* Don't display the last row, it's "last error" */
00730         break;
00731       }
00732 
00733       for(i= 0; i < num_fields; i++)
00734       {
00735         fprintf(stderr, "%.*s ", (int)lengths[i],
00736                 row[i] ? row[i] : "NULL");
00737       }
00738       fprintf(stderr, "\n");
00739     }
00740   }
00741   drizzle_result_free(&res);
00742 
00743   return;
00744 }
00745 
00746 
00747 enum arg_type
00748 {
00749   ARG_STRING,
00750   ARG_REST
00751 };
00752 
00753 struct command_arg {
00754   const char *argname;       /* Name of argument   */
00755   enum arg_type type;        /* Type of argument   */
00756   bool required;          /* Argument required  */
00757   string *ds;        /* Storage for argument */
00758   const char *description;   /* Description of the argument */
00759 };
00760 
00761 
00762 static void check_command_args(struct st_command *command,
00763                                const char *arguments,
00764                                const struct command_arg *args,
00765                                int num_args, const char delimiter_arg)
00766 {
00767   int i;
00768   const char *ptr= arguments;
00769   const char *start;
00770 
00771   for (i= 0; i < num_args; i++)
00772   {
00773     const struct command_arg *arg= &args[i];
00774     arg->ds->clear();
00775 
00776     bool known_arg_type= true;
00777     switch (arg->type) {
00778       /* A string */
00779     case ARG_STRING:
00780       /* Skip leading spaces */
00781       while (*ptr && *ptr == ' ')
00782         ptr++;
00783       start= ptr;
00784       /* Find end of arg, terminated by "delimiter_arg" */
00785       while (*ptr && *ptr != delimiter_arg)
00786         ptr++;
00787       if (ptr > start)
00788       {
00789         do_eval(arg->ds, start, ptr, false);
00790       }
00791       else
00792       {
00793         /* Empty string */
00794         arg->ds->erase();
00795       }
00796       command->last_argument= (char*)ptr;
00797 
00798       /* Step past the delimiter */
00799       if (*ptr && *ptr == delimiter_arg)
00800         ptr++;
00801       break;
00802 
00803       /* Rest of line */
00804     case ARG_REST:
00805       start= ptr;
00806       do_eval(arg->ds, start, command->end, false);
00807       command->last_argument= command->end;
00808       break;
00809 
00810     default:
00811       known_arg_type= false;
00812       break;
00813     }
00814     assert(known_arg_type);
00815 
00816     /* Check required arg */
00817     if (arg->ds->length() == 0 && arg->required)
00818       die("Missing required argument '%s' to command '%.*s'", arg->argname,
00819           command->first_word_len, command->query);
00820 
00821   }
00822   /* Check for too many arguments passed */
00823   ptr= command->last_argument;
00824   while(ptr <= command->end)
00825   {
00826     if (*ptr && *ptr != ' ')
00827       die("Extra argument '%s' passed to '%.*s'",
00828           ptr, command->first_word_len, command->query);
00829     ptr++;
00830   }
00831   return;
00832 }
00833 
00834 
00835 static void handle_command_error(struct st_command *command, uint32_t error)
00836 {
00837   if (error != 0)
00838   {
00839     uint32_t i;
00840 
00841     if (command->abort_on_error)
00842       die("command \"%.*s\" failed with error %d",
00843           command->first_word_len, command->query, error);
00844     for (i= 0; i < command->expected_errors.count; i++)
00845     {
00846       if ((command->expected_errors.err[i].type == ERR_ERRNO) &&
00847           (command->expected_errors.err[i].code.errnum == error))
00848       {
00849         return;
00850       }
00851     }
00852     die("command \"%.*s\" failed with wrong error: %d",
00853         command->first_word_len, command->query, error);
00854   }
00855   else if (command->expected_errors.err[0].type == ERR_ERRNO &&
00856            command->expected_errors.err[0].code.errnum != 0)
00857   {
00858     /* Error code we wanted was != 0, i.e. not an expected success */
00859     die("command \"%.*s\" succeeded - should have failed with errno %d...",
00860         command->first_word_len, command->query,
00861         command->expected_errors.err[0].code.errnum);
00862   }
00863 }
00864 
00865 
00866 static void close_connections()
00867 {
00868   for (--next_con; next_con >= connections; --next_con)
00869   {
00870     if (next_con->drizzle != NULL)
00871     {
00872       drizzle_free(next_con->drizzle);
00873       next_con->drizzle= NULL;
00874     }
00875     free(next_con->name);
00876   }
00877 }
00878 
00879 
00880 static void close_files()
00881 {
00882   for (; cur_file >= file_stack.data(); cur_file--)
00883   {
00884     if (cur_file->file && cur_file->file != stdin)
00885       fclose(cur_file->file);
00886     free(const_cast<char*>(cur_file->file_name));
00887     cur_file->file_name= 0;
00888   }
00889 }
00890 
00891 static void free_used_memory()
00892 {
00893   close_connections();
00894   close_files();
00895   BOOST_FOREACH(var_hash_t::reference i, var_hash)
00896   {
00897     free(i.second->str_val);
00898     free(i.second->env_s);
00899     if (i.second->alloced)
00900       free(i.second);
00901   }
00902   var_hash.clear();
00903   BOOST_FOREACH(vector<st_command*>::reference i, q_lines)
00904     delete i;
00905   for (size_t i= 0; i < var_reg.size(); i++)
00906   {
00907     if (var_reg[i].alloced_len)
00908       free(var_reg[i].str_val);
00909   }
00910   free_all_replace();
00911   free(opt_pass);
00912 }
00913 
00914 
00915 static void cleanup_and_exit(int exit_code)
00916 {
00917   free_used_memory();
00918   internal::my_end();
00919 
00920   if (!silent) {
00921     switch (exit_code) {
00922     case 1:
00923       printf("not ok\n");
00924       break;
00925     case 0:
00926       printf("ok\n");
00927       break;
00928     case 62:
00929       printf("skipped\n");
00930       break;
00931     default:
00932       printf("unknown exit code: %d\n", exit_code);
00933       assert(0);
00934     }
00935   }
00936   exit(exit_code);
00937 }
00938 
00939 void die(const char *fmt, ...)
00940 {
00941   static int dying= 0;
00942   va_list args;
00943 
00944   /*
00945     Protect against dying twice
00946     first time 'die' is called, try to write log files
00947     second time, just exit
00948   */
00949   if (dying)
00950     cleanup_and_exit(1);
00951   dying= 1;
00952 
00953   /* Print the error message */
00954   fprintf(stderr, "drizzletest: ");
00955   if (cur_file && cur_file != file_stack.data())
00956     fprintf(stderr, "In included file \"%s\": ",
00957             cur_file->file_name);
00958   if (start_lineno > 0)
00959     fprintf(stderr, "At line %u: ", start_lineno);
00960   if (fmt)
00961   {
00962     va_start(args, fmt);
00963     vfprintf(stderr, fmt, args);
00964     va_end(args);
00965   }
00966   else
00967     fprintf(stderr, "unknown error");
00968   fprintf(stderr, "\n");
00969   fflush(stderr);
00970 
00971   /* Show results from queries just before failure */
00972   if (ds_res.length() && opt_tail_lines)
00973   {
00974     int tail_lines= opt_tail_lines;
00975     const char* show_from= ds_res.c_str() + ds_res.length() - 1;
00976     while(show_from > ds_res.c_str() && tail_lines > 0 )
00977     {
00978       show_from--;
00979       if (*show_from == '\n')
00980         tail_lines--;
00981     }
00982     fprintf(stderr, "\nThe result from queries just before the failure was:\n");
00983     if (show_from > ds_res.c_str())
00984       fprintf(stderr, "< snip >");
00985     fprintf(stderr, "%s", show_from);
00986     fflush(stderr);
00987   }
00988 
00989   /* Dump the result that has been accumulated so far to .log file */
00990   if (! result_file_name.empty() && ds_res.length())
00991     dump_result_to_log_file(ds_res.c_str(), ds_res.length());
00992 
00993   /* Dump warning messages */
00994   if (! result_file_name.empty() && ds_warning_messages.length())
00995     dump_warning_messages();
00996 
00997   /*
00998     Help debugging by displaying any warnings that might have
00999     been produced prior to the error
01000   */
01001   if (cur_con)
01002     show_warnings_before_error(&cur_con->con);
01003 
01004   cleanup_and_exit(1);
01005 }
01006 
01007 
01008 void abort_not_supported_test(const char *fmt, ...)
01009 {
01010   va_list args;
01011   struct st_test_file* err_file= cur_file;
01012 
01013 
01014   /* Print include filestack */
01015   fprintf(stderr, "The test '%s' is not supported by this installation\n",
01016           file_stack[0].file_name);
01017   fprintf(stderr, "Detected in file %s at line %d\n",
01018           err_file->file_name, err_file->lineno);
01019   while (err_file != file_stack.data())
01020   {
01021     err_file--;
01022     fprintf(stderr, "included from %s at line %d\n",
01023             err_file->file_name, err_file->lineno);
01024   }
01025 
01026   /* Print error message */
01027   va_start(args, fmt);
01028   if (fmt)
01029   {
01030     fprintf(stderr, "reason: ");
01031     vfprintf(stderr, fmt, args);
01032     fprintf(stderr, "\n");
01033     fflush(stderr);
01034   }
01035   va_end(args);
01036 
01037   cleanup_and_exit(62);
01038 }
01039 
01040 
01041 void verbose_msg(const char *fmt, ...)
01042 {
01043   va_list args;
01044 
01045   if (!verbose)
01046     return;
01047 
01048   va_start(args, fmt);
01049   fprintf(stderr, "drizzletest: ");
01050   if (cur_file && cur_file != file_stack.data())
01051     fprintf(stderr, "In included file \"%s\": ",
01052             cur_file->file_name);
01053   if (start_lineno != 0)
01054     fprintf(stderr, "At line %u: ", start_lineno);
01055   vfprintf(stderr, fmt, args);
01056   fprintf(stderr, "\n");
01057   va_end(args);
01058 }
01059 
01060 
01061 void warning_msg(const char *fmt, ...)
01062 {
01063   va_list args;
01064   char buff[512];
01065   size_t len;
01066 
01067 
01068   va_start(args, fmt);
01069   ds_warning_messages.append("drizzletest: ");
01070   if (start_lineno != 0)
01071   {
01072     ds_warning_messages.append("Warning detected ");
01073     if (cur_file && cur_file != file_stack.data())
01074     {
01075       len= snprintf(buff, sizeof(buff), "in included file %s ",
01076                     cur_file->file_name);
01077       ds_warning_messages.append(buff, len);
01078     }
01079     len= snprintf(buff, sizeof(buff), "at line %d: ",
01080                   start_lineno);
01081     ds_warning_messages.append(buff, len);
01082   }
01083 
01084   len= vsnprintf(buff, sizeof(buff), fmt, args);
01085   ds_warning_messages.append(buff, len);
01086 
01087   ds_warning_messages.append("\n");
01088   va_end(args);
01089 
01090   return;
01091 }
01092 
01093 
01094 void log_msg(const char *fmt, ...)
01095 {
01096   va_list args;
01097   char buff[1024];
01098   size_t len;
01099 
01100 
01101   va_start(args, fmt);
01102   len= vsnprintf(buff, sizeof(buff)-1, fmt, args);
01103   va_end(args);
01104 
01105   ds_res.append(buff, len);
01106   ds_res.append("\n");
01107 
01108   return;
01109 }
01110 
01111 
01112 /*
01113   Read a file and append it to ds
01114 
01115   SYNOPSIS
01116   cat_file
01117   ds - pointer to dynamic string where to add the files content
01118   filename - name of the file to read
01119 
01120 */
01121 
01122 static void cat_file(string* ds, const char* filename)
01123 {
01124   int fd;
01125   uint32_t len;
01126   char buff[512];
01127 
01128   if ((fd= internal::my_open(filename, O_RDONLY, MYF(0))) < 0)
01129     die("Failed to open file '%s'", filename);
01130   while((len= internal::my_read(fd, (unsigned char*)&buff,
01131                       sizeof(buff), MYF(0))) > 0)
01132   {
01133     char *p= buff, *start= buff;
01134     while (p < buff+len)
01135     {
01136       /* Convert cr/lf to lf */
01137       if (*p == '\r' && *(p+1) && *(p+1)== '\n')
01138       {
01139         /* Add fake newline instead of cr and output the line */
01140         *p= '\n';
01141         p++; /* Step past the "fake" newline */
01142         ds->append(start, p-start);
01143         p++; /* Step past the "fake" newline */
01144         start= p;
01145       }
01146       else
01147         p++;
01148     }
01149     /* Output any chars that might be left */
01150     ds->append(start, p-start);
01151   }
01152   internal::my_close(fd, MYF(0));
01153 }
01154 
01155 
01156 /*
01157   Run the specified command with popen
01158 
01159   SYNOPSIS
01160   run_command
01161   cmd - command to execute(should be properly quoted
01162   result - pointer to string where to store the result
01163 
01164 */
01165 
01166 static int run_command(const char * cmd, string * result)
01167 {
01168   assert(result!=NULL);
01169   char buf[512]= {0};
01170   FILE *res_file;
01171   int error;
01172 
01173   if (!(res_file= popen(cmd, "r")))
01174     die("popen(\"%s\", \"r\") failed", cmd);
01175 
01176   while (fgets(buf, sizeof(buf), res_file))
01177   {
01178     /* Save the output of this command in the supplied string */
01179     result->append(buf);
01180   }
01181 
01182   error= pclose(res_file);
01183   return WEXITSTATUS(error);
01184 }
01185 
01186 
01187 /*
01188   Run the specified tool with variable number of arguments
01189 
01190   SYNOPSIS
01191   run_tool
01192   tool_path - the name of the tool to run
01193   result - pointer to dynamic string where to store the result
01194   ... - variable number of arguments that will be properly
01195   quoted and appended after the tool's name
01196 
01197 */
01198 
01199 static int run_tool(const char *tool_path, string * result, ...)
01200 {
01201   int ret;
01202   const char* arg;
01203   va_list args;
01204   string ds_cmdline;
01205 
01206 
01207   append_os_quoted(&ds_cmdline, tool_path, NULL);
01208   ds_cmdline.append(" ");
01209 
01210   va_start(args, result);
01211 
01212   while ((arg= va_arg(args, char *)))
01213   {
01214     /* Options should be os quoted */
01215     if (strncmp(arg, "--", 2) == 0)
01216       append_os_quoted(&ds_cmdline, arg, NULL);
01217     else
01218       ds_cmdline.append(arg);
01219     ds_cmdline.append(" ");
01220   }
01221 
01222   va_end(args);
01223 
01224   ret= run_command(ds_cmdline.c_str(), result);
01225   return(ret);
01226 }
01227 
01228 
01229 /*
01230   Show the diff of two files using the systems builtin diff
01231   command. If no such diff command exist, just dump the content
01232   of the two files and inform about how to get "diff"
01233 
01234   SYNOPSIS
01235   show_diff
01236   ds - pointer to dynamic string where to add the diff(may be NULL)
01237   filename1 - name of first file
01238   filename2 - name of second file
01239 
01240 */
01241 
01242 static void show_diff(string* ds,
01243                       const char* filename1, const char* filename2)
01244 {
01245 
01246   string ds_tmp;
01247 
01248   /* First try with unified diff */
01249   if (run_tool("diff",
01250                &ds_tmp, /* Get output from diff in ds_tmp */
01251                "-u",
01252                filename1,
01253                filename2,
01254                "2>&1",
01255                NULL) > 1) /* Most "diff" tools return >1 if error */
01256   {
01257 
01258     /* Fallback to context diff with "diff -c" */
01259     if (run_tool("diff",
01260                  &ds_tmp, /* Get output from diff in ds_tmp */
01261                  "-c",
01262                  filename1,
01263                  filename2,
01264                  "2>&1",
01265                  NULL) > 1) /* Most "diff" tools return >1 if error */
01266     {
01267       /*
01268         Fallback to dump both files to result file and inform
01269         about installing "diff"
01270       */
01271       ds_tmp.clear();
01272 
01273       ds_tmp.append(
01274                     "\n"
01275                     "The two files differ but it was not possible to execute 'diff' in\n"
01276                     "order to show only the difference, tried both 'diff -u' or 'diff -c'.\n"
01277                     "Instead the whole content of the two files was shown for you to diff manually. ;)\n\n"
01278                     "To get a better report you should install 'diff' on your system, which you\n"
01279                     "for example can get from http://www.gnu.org/software/diffutils/diffutils.html\n"
01280                     "\n");
01281 
01282       ds_tmp.append(" --- ");
01283       ds_tmp.append(filename1);
01284       ds_tmp.append(" >>>\n");
01285       cat_file(&ds_tmp, filename1);
01286       ds_tmp.append("<<<\n --- ");
01287       ds_tmp.append(filename1);
01288       ds_tmp.append(" >>>\n");
01289       cat_file(&ds_tmp, filename2);
01290       ds_tmp.append("<<<<\n");
01291     }
01292   }
01293 
01294   if (ds)
01295   {
01296     /* Add the diff to output */
01297     ds->append(ds_tmp.c_str(), ds_tmp.length());
01298   }
01299   else
01300   {
01301     /* Print diff directly to stdout */
01302     fprintf(stderr, "%s\n", ds_tmp.c_str());
01303   }
01304 
01305 }
01306 
01307 
01308 enum compare_files_result_enum {
01309   RESULT_OK= 0,
01310   RESULT_CONTENT_MISMATCH= 1,
01311   RESULT_LENGTH_MISMATCH= 2
01312 };
01313 
01314 /*
01315   Compare two files, given a fd to the first file and
01316   name of the second file
01317 
01318   SYNOPSIS
01319   compare_files2
01320   fd - Open file descriptor of the first file
01321   filename2 - Name of second file
01322 
01323   RETURN VALUES
01324   According to the values in "compare_files_result_enum"
01325 
01326 */
01327 
01328 static int compare_files2(int fd, const char* filename2)
01329 {
01330   int error= RESULT_OK;
01331   int fd2;
01332   uint32_t len, len2;
01333   char buff[512], buff2[512];
01334   const char *fname= filename2;
01335   string tmpfile;
01336 
01337   if ((fd2= internal::my_open(fname, O_RDONLY, MYF(0))) < 0)
01338   {
01339     internal::my_close(fd, MYF(0));
01340     if (! opt_testdir.empty())
01341     {
01342       tmpfile= opt_testdir;
01343       if (tmpfile[tmpfile.length()] != '/')
01344         tmpfile.append("/");
01345       tmpfile.append(filename2);
01346       fname= tmpfile.c_str();
01347     }
01348     if ((fd2= internal::my_open(fname, O_RDONLY, MYF(0))) < 0)
01349     {
01350       internal::my_close(fd, MYF(0));
01351     
01352       die("Failed to open second file: '%s'", fname);
01353     }
01354   }
01355   while((len= internal::my_read(fd, (unsigned char*)&buff,
01356                       sizeof(buff), MYF(0))) > 0)
01357   {
01358     if ((len2= internal::my_read(fd2, (unsigned char*)&buff2,
01359                        sizeof(buff2), MYF(0))) < len)
01360     {
01361       /* File 2 was smaller */
01362       error= RESULT_LENGTH_MISMATCH;
01363       break;
01364     }
01365     if (len2 > len)
01366     {
01367       /* File 1 was smaller */
01368       error= RESULT_LENGTH_MISMATCH;
01369       break;
01370     }
01371     if ((memcmp(buff, buff2, len)))
01372     {
01373       /* Content of this part differed */
01374       error= RESULT_CONTENT_MISMATCH;
01375       break;
01376     }
01377   }
01378   if (!error && internal::my_read(fd2, (unsigned char*)&buff2,
01379                         sizeof(buff2), MYF(0)) > 0)
01380   {
01381     /* File 1 was smaller */
01382     error= RESULT_LENGTH_MISMATCH;
01383   }
01384 
01385   internal::my_close(fd2, MYF(0));
01386 
01387   return error;
01388 }
01389 
01390 
01391 /*
01392   Compare two files, given their filenames
01393 
01394   SYNOPSIS
01395   compare_files
01396   filename1 - Name of first file
01397   filename2 - Name of second file
01398 
01399   RETURN VALUES
01400   See 'compare_files2'
01401 
01402 */
01403 
01404 static int compare_files(const char* filename1, const char* filename2)
01405 {
01406   int fd;
01407   int error;
01408 
01409   if ((fd= internal::my_open(filename1, O_RDONLY, MYF(0))) < 0)
01410     die("Failed to open first file: '%s'", filename1);
01411 
01412   error= compare_files2(fd, filename2);
01413 
01414   internal::my_close(fd, MYF(0));
01415 
01416   return error;
01417 }
01418 
01419 
01420 /*
01421   Compare content of the string in ds to content of file fname
01422 
01423   SYNOPSIS
01424   string_cmp
01425   ds - Dynamic string containing the string o be compared
01426   fname - Name of file to compare with
01427 
01428   RETURN VALUES
01429   See 'compare_files2'
01430 */
01431 
01432 static int string_cmp(string* ds, const char *fname)
01433 {
01434   int error;
01435   int fd;
01436   char temp_file_path[FN_REFLEN];
01437 
01438   if ((fd= internal::create_temp_file(temp_file_path, TMPDIR,
01439                             "tmp", MYF(MY_WME))) < 0)
01440     die("Failed to create temporary file for ds");
01441 
01442   /* Write ds to temporary file and set file pos to beginning*/
01443   if (internal::my_write(fd, (unsigned char *) ds->c_str(), ds->length(),
01444                MYF(MY_FNABP | MY_WME)) ||
01445       lseek(fd, 0, SEEK_SET) == MY_FILEPOS_ERROR)
01446   {
01447     internal::my_close(fd, MYF(0));
01448     /* Remove the temporary file */
01449     internal::my_delete(temp_file_path, MYF(0));
01450     die("Failed to write file '%s'", temp_file_path);
01451   }
01452 
01453   error= compare_files2(fd, fname);
01454 
01455   internal::my_close(fd, MYF(0));
01456   /* Remove the temporary file */
01457   internal::my_delete(temp_file_path, MYF(0));
01458 
01459   return(error);
01460 }
01461 
01462 
01463 /*
01464   Check the content of ds against result file
01465 
01466   SYNOPSIS
01467   check_result
01468   ds - content to be checked
01469 
01470   RETURN VALUES
01471   error - the function will not return
01472 
01473 */
01474 
01475 static void check_result(string* ds)
01476 {
01477   const char* mess= "Result content mismatch\n";
01478 
01479 
01480   assert(result_file_name.c_str());
01481 
01482   if (access(result_file_name.c_str(), F_OK) != 0)
01483     die("The specified result file does not exist: '%s'", result_file_name.c_str());
01484 
01485   switch (string_cmp(ds, result_file_name.c_str())) {
01486   case RESULT_OK:
01487     break; /* ok */
01488   case RESULT_LENGTH_MISMATCH:
01489     mess= "Result length mismatch\n";
01490     /* Fallthrough */
01491   case RESULT_CONTENT_MISMATCH:
01492   {
01493     /*
01494       Result mismatched, dump results to .reject file
01495       and then show the diff
01496     */
01497     char reject_file[FN_REFLEN];
01498     size_t reject_length;
01499     internal::dirname_part(reject_file, result_file_name.c_str(), &reject_length);
01500 
01501     if (access(reject_file, W_OK) == 0)
01502     {
01503       /* Result file directory is writable, save reject file there */
01504       internal::fn_format(reject_file, result_file_name.c_str(), NULL,
01505                 ".reject", MY_REPLACE_EXT);
01506     }
01507     else
01508     {
01509       /* Put reject file in opt_logdir */
01510       internal::fn_format(reject_file, result_file_name.c_str(), opt_logdir.c_str(),
01511                 ".reject", MY_REPLACE_DIR | MY_REPLACE_EXT);
01512     }
01513     str_to_file(reject_file, ds->c_str(), ds->length());
01514 
01515     ds->erase(); /* Don't create a .log file */
01516 
01517     show_diff(NULL, result_file_name.c_str(), reject_file);
01518     die("%s",mess);
01519     break;
01520   }
01521   default: /* impossible */
01522     die("Unknown error code from dyn_string_cmp()");
01523   }
01524 
01525   return;
01526 }
01527 
01528 
01529 /*
01530   Check the content of ds against a require file
01531   If match fails, abort the test with special error code
01532   indicating that test is not supported
01533 
01534   SYNOPSIS
01535   check_require
01536   ds - content to be checked
01537   fname - name of file to check against
01538 
01539   RETURN VALUES
01540   error - the function will not return
01541 
01542 */
01543 
01544 static void check_require(string* ds, const string &fname)
01545 {
01546 
01547 
01548   if (string_cmp(ds, fname.c_str()))
01549   {
01550     char reason[FN_REFLEN];
01551     internal::fn_format(reason, fname.c_str(), "", "", MY_REPLACE_EXT | MY_REPLACE_DIR);
01552     abort_not_supported_test("Test requires: '%s'", reason);
01553   }
01554   return;
01555 }
01556 
01557 
01558 /*
01559   Remove surrounding chars from string
01560 
01561   Return 1 if first character is found but not last
01562 */
01563 static int strip_surrounding(char* str, char c1, char c2)
01564 {
01565   char* ptr= str;
01566 
01567   /* Check if the first non space character is c1 */
01568   while(*ptr && my_isspace(charset_info, *ptr))
01569     ptr++;
01570   if (*ptr == c1)
01571   {
01572     /* Replace it with a space */
01573     *ptr= ' ';
01574 
01575     /* Last non space charecter should be c2 */
01576     ptr= strchr(str, '\0')-1;
01577     while(*ptr && my_isspace(charset_info, *ptr))
01578       ptr--;
01579     if (*ptr == c2)
01580     {
01581       /* Replace it with \0 */
01582       *ptr= 0;
01583     }
01584     else
01585     {
01586       /* Mismatch detected */
01587       return 1;
01588     }
01589   }
01590   return 0;
01591 }
01592 
01593 
01594 static void strip_parentheses(struct st_command *command)
01595 {
01596   if (strip_surrounding(command->first_argument, '(', ')'))
01597     die("%.*s - argument list started with '%c' must be ended with '%c'",
01598         command->first_word_len, command->query, '(', ')');
01599 }
01600 
01601 
01602 
01603 VAR *var_init(VAR *v, const char *name, int name_len, const char *val,
01604               int val_len)
01605 {
01606   if (!name_len && name)
01607     name_len = strlen(name);
01608   if (!val_len && val)
01609     val_len = strlen(val) ;
01610   VAR *tmp_var = v ? v : (VAR*)malloc(sizeof(*tmp_var) + name_len+1);
01611 
01612   tmp_var->name = name ? (char*)&tmp_var[1] : 0;
01613   tmp_var->alloced = (v == 0);
01614 
01615   int val_alloc_len = val_len + 16; /* room to grow */
01616   tmp_var->str_val = (char*)malloc(val_alloc_len+1);
01617 
01618   memcpy(tmp_var->name, name, name_len);
01619   if (val)
01620   {
01621     memcpy(tmp_var->str_val, val, val_len);
01622     tmp_var->str_val[val_len]= 0;
01623   }
01624   tmp_var->name_len = name_len;
01625   tmp_var->str_val_len = val_len;
01626   tmp_var->alloced_len = val_alloc_len;
01627   tmp_var->int_val = val ? atoi(val) : 0;
01628   tmp_var->int_dirty = false;
01629   tmp_var->env_s = 0;
01630   return tmp_var;
01631 }
01632 
01633 VAR* var_from_env(const char *name, const char *def_val)
01634 {
01635   const char *tmp= getenv(name);
01636   if (!tmp)
01637     tmp = def_val;
01638   return var_hash[name] = var_init(0, name, strlen(name), tmp, strlen(tmp));
01639 }
01640 
01641 VAR* var_get(const char *var_name, const char **var_name_end, bool raw,
01642              bool ignore_not_existing)
01643 {
01644   int digit;
01645   VAR *v;
01646   if (*var_name != '$')
01647     goto err;
01648   digit = *++var_name - '0';
01649   if (digit < 0 || digit >= 10)
01650   {
01651     const char *save_var_name = var_name, *end;
01652     uint32_t length;
01653     end = (var_name_end) ? *var_name_end : 0;
01654     while (my_isvar(charset_info,*var_name) && var_name != end)
01655       var_name++;
01656     if (var_name == save_var_name)
01657     {
01658       if (ignore_not_existing)
01659         return(0);
01660       die("Empty variable");
01661     }
01662     length= (uint32_t) (var_name - save_var_name);
01663     if (length >= MAX_VAR_NAME_LENGTH)
01664       die("Too long variable name: %s", save_var_name);
01665 
01666     string save_var_name_str(save_var_name, length);
01667     if (var_hash_t::mapped_type* ptr= find_ptr(var_hash, save_var_name_str))
01668       v= *ptr;
01669     else
01670     {
01671       char buff[MAX_VAR_NAME_LENGTH+1];
01672       strncpy(buff, save_var_name, length);
01673       buff[length]= '\0';
01674       v= var_from_env(buff, "");
01675     }
01676     var_name--;  /* Point at last character */
01677   }
01678   else
01679     v = &var_reg[digit];
01680 
01681   if (!raw && v->int_dirty)
01682   {
01683     sprintf(v->str_val, "%d", v->int_val);
01684     v->int_dirty = 0;
01685     v->str_val_len = strlen(v->str_val);
01686   }
01687   if (var_name_end)
01688     *var_name_end = var_name  ;
01689   return(v);
01690 err:
01691   if (var_name_end)
01692     *var_name_end = 0;
01693   die("Unsupported variable name: %s", var_name);
01694   return(0);
01695 }
01696 
01697 
01698 static VAR *var_obtain(const char *name, int len)
01699 {
01700   string var_name(name, len);
01701   if (var_hash_t::mapped_type* ptr= find_ptr(var_hash, var_name))
01702     return *ptr;
01703   return var_hash[var_name] = var_init(0, name, len, "", 0);
01704 }
01705 
01706 
01707 /*
01708   - if variable starts with a $ it is regarded as a local test varable
01709   - if not it is treated as a environment variable, and the corresponding
01710   environment variable will be updated
01711 */
01712 
01713 static void var_set(const char *var_name, const char *var_name_end,
01714                     const char *var_val, const char *var_val_end)
01715 {
01716   int digit, env_var= 0;
01717   VAR *v;
01718 
01719   if (*var_name != '$')
01720     env_var= 1;
01721   else
01722     var_name++;
01723 
01724   digit= *var_name - '0';
01725   if (!(digit < 10 && digit >= 0))
01726   {
01727     v= var_obtain(var_name, (uint32_t) (var_name_end - var_name));
01728   }
01729   else
01730     v= &var_reg[digit];
01731 
01732   eval_expr(v, var_val, (const char**) &var_val_end);
01733 
01734   if (env_var)
01735   {
01736     char buf[1024], *old_env_s= v->env_s;
01737     if (v->int_dirty)
01738     {
01739       sprintf(v->str_val, "%d", v->int_val);
01740       v->int_dirty= 0;
01741       v->str_val_len= strlen(v->str_val);
01742     }
01743     snprintf(buf, sizeof(buf), "%.*s=%.*s",
01744              v->name_len, v->name,
01745              v->str_val_len, v->str_val);
01746     if (!(v->env_s= strdup(buf)))
01747       die("Out of memory");
01748     putenv(v->env_s);
01749     free(old_env_s);
01750   }
01751   return;
01752 }
01753 
01754 
01755 static void var_set_string(const char* name, const char* value)
01756 {
01757   var_set(name, name + strlen(name), value, value + strlen(value));
01758 }
01759 
01760 
01761 static void var_set_int(const char* name, int value)
01762 {
01763   char buf[21];
01764   snprintf(buf, sizeof(buf), "%d", value);
01765   var_set_string(name, buf);
01766 }
01767 
01768 
01769 /*
01770   Store an integer (typically the returncode of the last SQL)
01771   statement in the drizzletest builtin variable $drizzleclient_errno
01772 */
01773 
01774 static void var_set_errno(int sql_errno)
01775 {
01776   var_set_int("$drizzleclient_errno", sql_errno);
01777 }
01778 
01779 
01780 /*
01781   Update $drizzleclient_get_server_version variable with version
01782   of the currently connected server
01783 */
01784 
01785 static void var_set_drizzleclient_get_server_version(drizzle_con_st *con)
01786 {
01787   var_set_int("$drizzle_con_server_version", drizzle_con_server_version_number(con));
01788 }
01789 
01790 
01791 /*
01792   Set variable from the result of a query
01793 
01794   SYNOPSIS
01795   var_query_set()
01796   var          variable to set from query
01797   query       start of query string to execute
01798   query_end   end of the query string to execute
01799 
01800 
01801   DESCRIPTION
01802   let @<var_name> = `<query>`
01803 
01804   Execute the query and assign the first row of result to var as
01805   a tab separated strings
01806 
01807   Also assign each column of the result set to
01808   variable "$<var_name>_<column_name>"
01809   Thus the tab separated output can be read from $<var_name> and
01810   and each individual column can be read as $<var_name>_<col_name>
01811 
01812 */
01813 
01814 static void var_query_set(VAR *var, const char *query, const char** query_end)
01815 {
01816   const char *end = (char*)((query_end && *query_end) ?
01817                             *query_end : query + strlen(query));
01818   drizzle_result_st res;
01819   drizzle_return_t ret;
01820   drizzle_row_t row;
01821   drizzle_con_st *con= &cur_con->con;
01822   string ds_query;
01823 
01824 
01825   while (end > query && *end != '`')
01826     --end;
01827   if (query == end)
01828     die("Syntax error in query, missing '`'");
01829   ++query;
01830 
01831   /* Eval the query, thus replacing all environment variables */
01832   do_eval(&ds_query, query, end, false);
01833 
01834   if (drizzle_query(con, &res, ds_query.c_str(), ds_query.length(),
01835                     &ret) == NULL ||
01836       ret != DRIZZLE_RETURN_OK)
01837   {
01838     if (ret == DRIZZLE_RETURN_ERROR_CODE)
01839     {
01840       die("Error running query '%s': %d %s", ds_query.c_str(),
01841           drizzle_result_error_code(&res), drizzle_result_error(&res));
01842       drizzle_result_free(&res);
01843     }
01844     else
01845     {
01846       die("Error running query '%s': %d %s", ds_query.c_str(), ret,
01847           drizzle_con_error(con));
01848     }
01849   }
01850   if (drizzle_result_column_count(&res) == 0 ||
01851       drizzle_result_buffer(&res) != DRIZZLE_RETURN_OK)
01852     die("Query '%s' didn't return a result set", ds_query.c_str());
01853 
01854   if ((row= drizzle_row_next(&res)) && row[0])
01855   {
01856     /*
01857       Concatenate all fields in the first row with tab in between
01858       and assign that string to the $variable
01859     */
01860     string result;
01861     uint32_t i;
01862     size_t *lengths;
01863 
01864     lengths= drizzle_row_field_sizes(&res);
01865     for (i= 0; i < drizzle_result_column_count(&res); i++)
01866     {
01867       if (row[i])
01868       {
01869         /* Add column to tab separated string */
01870         result.append(row[i], lengths[i]);
01871       }
01872       result.append("\t", 1);
01873     }
01874     end= result.c_str() + result.length()-1;
01875     eval_expr(var, result.c_str(), (const char**) &end);
01876   }
01877   else
01878     eval_expr(var, "", 0);
01879 
01880   drizzle_result_free(&res);
01881   return;
01882 }
01883 
01884 
01885 /*
01886   Set variable from the result of a field in a query
01887 
01888   This function is useful when checking for a certain value
01889   in the output from a query that can't be restricted to only
01890   return some values. A very good example of that is most SHOW
01891   commands.
01892 
01893   SYNOPSIS
01894   var_set_query_get_value()
01895 
01896   DESCRIPTION
01897   let $variable= query_get_value(<query to run>,<column name>,<row no>);
01898 
01899   <query to run> -    The query that should be sent to the server
01900   <column name> -     Name of the column that holds the field be compared
01901   against the expected value
01902   <row no> -          Number of the row that holds the field to be
01903   compared against the expected value
01904 
01905 */
01906 
01907 static void var_set_query_get_value(struct st_command *command, VAR *var)
01908 {
01909   long row_no;
01910   int col_no= -1;
01911   drizzle_result_st res;
01912   drizzle_return_t ret;
01913   drizzle_con_st *con= &cur_con->con;
01914 
01915   string ds_query;
01916   string ds_col;
01917   string ds_row;
01918   const struct command_arg query_get_value_args[] = {
01919     {"query", ARG_STRING, true, &ds_query, "Query to run"},
01920     {"column name", ARG_STRING, true, &ds_col, "Name of column"},
01921     {"row number", ARG_STRING, true, &ds_row, "Number for row"}
01922   };
01923 
01924 
01925 
01926   strip_parentheses(command);
01927   check_command_args(command, command->first_argument, query_get_value_args,
01928                      sizeof(query_get_value_args)/sizeof(struct command_arg),
01929                      ',');
01930 
01931   /* Convert row number to int */
01932   row_no= atoi(ds_row.c_str());
01933   
01934   istringstream buff(ds_row);
01935   if ((buff >> row_no).fail())
01936     die("Invalid row number: '%s'", ds_row.c_str());
01937 
01938   /* Remove any surrounding "'s from the query - if there is any */
01939   // (Don't get me started on this)
01940   char * unstripped_query= strdup(ds_query.c_str());
01941   if (strip_surrounding(unstripped_query, '"', '"'))
01942     die("Mismatched \"'s around query '%s'", ds_query.c_str());
01943   ds_query.clear();
01944   ds_query.append(unstripped_query);
01945 
01946   /* Run the query */
01947   if (drizzle_query(con, &res, ds_query.c_str(), ds_query.length(),
01948                     &ret) == NULL ||
01949       ret != DRIZZLE_RETURN_OK)
01950   {
01951     if (ret == DRIZZLE_RETURN_ERROR_CODE)
01952     {
01953       die("Error running query '%s': %d %s", ds_query.c_str(),
01954           drizzle_result_error_code(&res), drizzle_result_error(&res));
01955       drizzle_result_free(&res);
01956     }
01957     else
01958     {
01959       die("Error running query '%s': %d %s", ds_query.c_str(), ret,
01960           drizzle_con_error(con));
01961     }
01962   }
01963   if (drizzle_result_column_count(&res) == 0 ||
01964       drizzle_result_buffer(&res) != DRIZZLE_RETURN_OK)
01965     die("Query '%s' didn't return a result set", ds_query.c_str());
01966 
01967   {
01968     /* Find column number from the given column name */
01969     uint32_t i;
01970     uint32_t num_fields= drizzle_result_column_count(&res);
01971     drizzle_column_st *column;
01972 
01973     for (i= 0; i < num_fields; i++)
01974     {
01975       column= drizzle_column_next(&res);
01976       if (strcmp(drizzle_column_name(column), ds_col.c_str()) == 0 &&
01977           strlen(drizzle_column_name(column)) == ds_col.length())
01978       {
01979         col_no= i;
01980         break;
01981       }
01982     }
01983     if (col_no == -1)
01984     {
01985       drizzle_result_free(&res);
01986       die("Could not find column '%s' in the result of '%s'",
01987           ds_col.c_str(), ds_query.c_str());
01988     }
01989   }
01990 
01991   {
01992     /* Get the value */
01993     drizzle_row_t row;
01994     long rows= 0;
01995     const char* value= "No such row";
01996 
01997     while ((row= drizzle_row_next(&res)))
01998     {
01999       if (++rows == row_no)
02000       {
02001 
02002         /* Found the row to get */
02003         if (row[col_no])
02004           value= row[col_no];
02005         else
02006           value= "NULL";
02007 
02008         break;
02009       }
02010     }
02011     eval_expr(var, value, 0);
02012   }
02013   drizzle_result_free(&res);
02014 }
02015 
02016 
02017 static void var_copy(VAR *dest, VAR *src)
02018 {
02019   dest->int_val= src->int_val;
02020   dest->int_dirty= src->int_dirty;
02021 
02022   /* Alloc/realloc data for str_val in dest */
02023   if (dest->alloced_len < src->alloced_len)
02024   {
02025     char *tmpptr= (char *)realloc(dest->str_val, src->alloced_len);
02026     if (tmpptr == NULL)
02027       die("Out of memory");
02028     dest->str_val= tmpptr;
02029   }
02030   else
02031     dest->alloced_len= src->alloced_len;
02032 
02033   /* Copy str_val data to dest */
02034   dest->str_val_len= src->str_val_len;
02035   if (src->str_val_len)
02036     memcpy(dest->str_val, src->str_val, src->str_val_len);
02037 }
02038 
02039 
02040 void eval_expr(VAR *v, const char *p, const char **p_end)
02041 {
02042   if (*p == '$')
02043   {
02044     VAR *vp= var_get(p, p_end, 0, 0);
02045     if (vp)
02046       var_copy(v, vp);
02047     return;
02048   }
02049 
02050   if (*p == '`')
02051   {
02052     var_query_set(v, p, p_end);
02053     return;
02054   }
02055 
02056   {
02057     /* Check if this is a "let $var= query_get_value()" */
02058     const char* get_value_str= "query_get_value";
02059     const size_t len= strlen(get_value_str);
02060     if (strncmp(p, get_value_str, len)==0)
02061     {
02062       st_command command;
02063       command.query= (char*)p;
02064       command.first_word_len= len;
02065       command.first_argument= command.query + len;
02066       command.end= (char*)*p_end;
02067       var_set_query_get_value(&command, v);
02068       return;
02069     }
02070   }
02071 
02072   {
02073     int new_val_len = (p_end && *p_end) ?
02074       (int) (*p_end - p) : (int) strlen(p);
02075     if (new_val_len + 1 >= v->alloced_len)
02076     {
02077       static int MIN_VAR_ALLOC= 32;
02078       v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ?
02079         MIN_VAR_ALLOC : new_val_len + 1;
02080       char *tmpptr= (char *)realloc(v->str_val, v->alloced_len+1);
02081       if (tmpptr == NULL)
02082         die("Out of memory");
02083       v->str_val= tmpptr;
02084     }
02085     v->str_val_len = new_val_len;
02086     memcpy(v->str_val, p, new_val_len);
02087     v->str_val[new_val_len] = 0;
02088     v->int_val=atoi(p);
02089     v->int_dirty=0;
02090   }
02091   return;
02092 }
02093 
02094 
02095 static int open_file(const char *name)
02096 {
02097   char buff[FN_REFLEN];
02098 
02099   if (!internal::test_if_hard_path(name))
02100   {
02101     snprintf(buff, sizeof(buff), "%s%s",opt_basedir.c_str(),name);
02102     name=buff;
02103   }
02104   internal::fn_format(buff, name, "", "", MY_UNPACK_FILENAME);
02105 
02106   cur_file++;
02107   if (cur_file == &*file_stack.end())
02108     die("Source directives are nesting too deep");
02109   if (!(cur_file->file= fopen(buff, "r")))
02110   {
02111     cur_file--;
02112     die("Could not open '%s' for reading", buff);
02113   }
02114   if (!(cur_file->file_name= strdup(buff)))
02115     die("Out of memory");
02116   cur_file->lineno=1;
02117   return(0);
02118 }
02119 
02120 
02121 /*
02122   Source and execute the given file
02123 
02124   SYNOPSIS
02125   do_source()
02126   query  called command
02127 
02128   DESCRIPTION
02129   source <file_name>
02130 
02131   Open the file <file_name> and execute it
02132 
02133 */
02134 
02135 static void do_source(struct st_command *command)
02136 {
02137   string ds_filename;
02138   const struct command_arg source_args[] = {
02139     { "filename", ARG_STRING, true, &ds_filename, "File to source" }
02140   };
02141 
02142 
02143   check_command_args(command, command->first_argument, source_args,
02144                      sizeof(source_args)/sizeof(struct command_arg),
02145                      ' ');
02146 
02147   /*
02148     If this file has already been sourced, don't source it again.
02149     It's already available in the q_lines cache.
02150   */
02151   if (parser.current_line < (parser.read_lines - 1))
02152     ; /* Do nothing */
02153   else
02154   {
02155     if (! opt_testdir.empty())
02156     {
02157       string testdir(opt_testdir);
02158       if (testdir[testdir.length()] != '/')
02159         testdir.append("/");
02160       testdir.append(ds_filename);
02161       ds_filename.swap(testdir);
02162     }
02163     open_file(ds_filename.c_str());
02164   }
02165 
02166   return;
02167 }
02168 
02169 
02170 static void init_builtin_echo()
02171 {
02172   builtin_echo[0]= 0;
02173 }
02174 
02175 
02176 /*
02177   Replace a substring
02178 
02179   SYNOPSIS
02180   replace
02181   ds_str      The string to search and perform the replace in
02182   search_str  The string to search for
02183   search_len  Length of the string to search for
02184   replace_str The string to replace with
02185   replace_len Length of the string to replace with
02186 
02187   RETURN
02188   0 String replaced
02189   1 Could not find search_str in str
02190 */
02191 
02192 static int replace(string *ds_str,
02193                    const char *search_str, uint32_t search_len,
02194                    const char *replace_str, uint32_t replace_len)
02195 {
02196   string ds_tmp;
02197   const char *start= strstr(ds_str->c_str(), search_str);
02198   if (!start)
02199     return 1;
02200   ds_tmp.append(ds_str->c_str(), start - ds_str->c_str());
02201   ds_tmp.append(replace_str, replace_len);
02202   ds_tmp.append(start + search_len);
02203   *ds_str= ds_tmp;
02204   return 0;
02205 }
02206 
02207 
02208 /*
02209   Execute given command.
02210 
02211   SYNOPSIS
02212   do_exec()
02213   query  called command
02214 
02215   DESCRIPTION
02216   exec <command>
02217 
02218   Execute the text between exec and end of line in a subprocess.
02219   The error code returned from the subprocess is checked against the
02220   expected error array, previously set with the --error command.
02221   It can thus be used to execute a command that shall fail.
02222 
02223   NOTE
02224   Although drizzletest is executed from cygwin shell, the command will be
02225   executed in "cmd.exe". Thus commands like "rm" etc can NOT be used, use
02226   drizzletest commmand(s) like "remove_file" for that
02227 */
02228 
02229 static void do_exec(struct st_command *command)
02230 {
02231   int error;
02232   char buf[512];
02233   FILE *res_file;
02234   char *cmd= command->first_argument;
02235   string ds_cmd;
02236 
02237   /* Skip leading space */
02238   while (*cmd && my_isspace(charset_info, *cmd))
02239     cmd++;
02240   if (!*cmd)
02241     die("Missing argument in exec");
02242   command->last_argument= command->end;
02243 
02244   /* Eval the command, thus replacing all environment variables */
02245   do_eval(&ds_cmd, cmd, command->end, !is_windows);
02246 
02247   /* Check if echo should be replaced with "builtin" echo */
02248   if (builtin_echo[0] && strncmp(cmd, "echo", 4) == 0)
02249   {
02250     /* Replace echo with our "builtin" echo */
02251     replace(&ds_cmd, "echo", 4, builtin_echo, strlen(builtin_echo));
02252   }
02253 
02254   if (!(res_file= popen(ds_cmd.c_str(), "r")) && command->abort_on_error)
02255   {
02256     die("popen(\"%s\", \"r\") failed", command->first_argument);
02257   }
02258 
02259   while (fgets(buf, sizeof(buf), res_file))
02260   {
02261     if (disable_result_log)
02262     {
02263       buf[strlen(buf)-1]=0;
02264     }
02265     else
02266     {
02267       replace_append(&ds_res, buf);
02268     }
02269   }
02270   error= pclose(res_file);
02271   if (error > 0)
02272   {
02273     uint32_t status= WEXITSTATUS(error), i;
02274     bool ok= 0;
02275 
02276     if (command->abort_on_error)
02277     {
02278       log_msg("exec of '%s' failed, error: %d, status: %d, errno: %d",
02279               ds_cmd.c_str(), error, status, errno);
02280       die("command \"%s\" failed", command->first_argument);
02281     }
02282 
02283     for (i= 0; i < command->expected_errors.count; i++)
02284     {
02285       if ((command->expected_errors.err[i].type == ERR_ERRNO) &&
02286           (command->expected_errors.err[i].code.errnum == status))
02287       {
02288         ok= 1;
02289       }
02290     }
02291     if (!ok)
02292     {
02293       die("command \"%s\" failed with wrong error: %d",
02294           command->first_argument, status);
02295     }
02296   }
02297   else if (command->expected_errors.err[0].type == ERR_ERRNO &&
02298            command->expected_errors.err[0].code.errnum != 0)
02299   {
02300     /* Error code we wanted was != 0, i.e. not an expected success */
02301     log_msg("exec of '%s failed, error: %d, errno: %d",
02302             ds_cmd.c_str(), error, errno);
02303     die("command \"%s\" succeeded - should have failed with errno %d...",
02304         command->first_argument, command->expected_errors.err[0].code.errnum);
02305   }
02306 
02307   return;
02308 }
02309 
02310 enum enum_operator
02311 {
02312   DO_DEC,
02313   DO_INC
02314 };
02315 
02316 
02317 /*
02318   Decrease or increase the value of a variable
02319 
02320   SYNOPSIS
02321   do_modify_var()
02322   query  called command
02323   operator    operation to perform on the var
02324 
02325   DESCRIPTION
02326   dec $var_name
02327   inc $var_name
02328 
02329 */
02330 
02331 static int do_modify_var(struct st_command *command,
02332                          enum enum_operator op)
02333 {
02334   const char *p= command->first_argument;
02335   VAR* v;
02336   if (!*p)
02337     die("Missing argument to %.*s", command->first_word_len, command->query);
02338   if (*p != '$')
02339     die("The argument to %.*s must be a variable (start with $)",
02340         command->first_word_len, command->query);
02341   v= var_get(p, &p, 1, 0);
02342   switch (op) {
02343   case DO_DEC:
02344     v->int_val--;
02345     break;
02346   case DO_INC:
02347     v->int_val++;
02348     break;
02349   default:
02350     die("Invalid operator to do_modify_var");
02351     break;
02352   }
02353   v->int_dirty= 1;
02354   command->last_argument= (char*)++p;
02355   return 0;
02356 }
02357 
02358 
02359 /*
02360   SYNOPSIS
02361   do_system
02362   command  called command
02363 
02364   DESCRIPTION
02365   system <command>
02366 
02367   Eval the query to expand any $variables in the command.
02368   Execute the command with the "system" command.
02369 
02370 */
02371 
02372 static void do_system(struct st_command *command)
02373 {
02374   string ds_cmd;
02375 
02376 
02377   if (strlen(command->first_argument) == 0)
02378     die("Missing arguments to system, nothing to do!");
02379 
02380   /* Eval the system command, thus replacing all environment variables */
02381   do_eval(&ds_cmd, command->first_argument, command->end, !is_windows);
02382 
02383   if (system(ds_cmd.c_str()))
02384   {
02385     if (command->abort_on_error)
02386       die("system command '%s' failed", command->first_argument);
02387 
02388     /* If ! abort_on_error, log message and continue */
02389     ds_res.append("system command '");
02390     replace_append(&ds_res, command->first_argument);
02391     ds_res.append("' failed\n");
02392   }
02393 
02394   command->last_argument= command->end;
02395   return;
02396 }
02397 
02398 
02399 /*
02400   SYNOPSIS
02401   do_remove_file
02402   command  called command
02403 
02404   DESCRIPTION
02405   remove_file <file_name>
02406   Remove the file <file_name>
02407 */
02408 
02409 static void do_remove_file(struct st_command *command)
02410 {
02411   int error;
02412   string ds_filename;
02413   const struct command_arg rm_args[] = {
02414     { "filename", ARG_STRING, true, &ds_filename, "File to delete" }
02415   };
02416 
02417 
02418   check_command_args(command, command->first_argument,
02419                      rm_args, sizeof(rm_args)/sizeof(struct command_arg),
02420                      ' ');
02421 
02422   error= internal::my_delete(ds_filename.c_str(), MYF(0)) != 0;
02423   handle_command_error(command, error);
02424 }
02425 
02426 
02427 /*
02428   SYNOPSIS
02429   do_copy_file
02430   command  command handle
02431 
02432   DESCRIPTION
02433   copy_file <from_file> <to_file>
02434   Copy <from_file> to <to_file>
02435 
02436   NOTE! Will fail if <to_file> exists
02437 */
02438 
02439 static void do_copy_file(struct st_command *command)
02440 {
02441   int error;
02442   string ds_from_file;
02443   string ds_to_file;
02444   const struct command_arg copy_file_args[] = {
02445     { "from_file", ARG_STRING, true, &ds_from_file, "Filename to copy from" },
02446     { "to_file", ARG_STRING, true, &ds_to_file, "Filename to copy to" }
02447   };
02448 
02449 
02450   check_command_args(command, command->first_argument,
02451                      copy_file_args,
02452                      sizeof(copy_file_args)/sizeof(struct command_arg),
02453                      ' ');
02454 
02455   error= (internal::my_copy(ds_from_file.c_str(), ds_to_file.c_str(),
02456                   MYF(MY_DONT_OVERWRITE_FILE)) != 0);
02457   handle_command_error(command, error);
02458 }
02459 
02460 
02461 /*
02462   SYNOPSIS
02463   do_chmod_file
02464   command  command handle
02465 
02466   DESCRIPTION
02467   chmod <octal> <file_name>
02468   Change file permission of <file_name>
02469 
02470 */
02471 
02472 static void do_chmod_file(struct st_command *command)
02473 {
02474   long mode= 0;
02475   string ds_mode;
02476   string ds_file;
02477   const struct command_arg chmod_file_args[] = {
02478     { "mode", ARG_STRING, true, &ds_mode, "Mode of file(octal) ex. 0660"},
02479     { "filename", ARG_STRING, true, &ds_file, "Filename of file to modify" }
02480   };
02481 
02482 
02483   check_command_args(command, command->first_argument,
02484                      chmod_file_args,
02485                      sizeof(chmod_file_args)/sizeof(struct command_arg),
02486                      ' ');
02487 
02488   /* Parse what mode to set */
02489   istringstream buff(ds_mode);
02490   if (ds_mode.length() != 4 ||
02491       (buff >> oct >> mode).fail())
02492     die("You must write a 4 digit octal number for mode");
02493 
02494   handle_command_error(command, chmod(ds_file.c_str(), mode));
02495 }
02496 
02497 
02498 /*
02499   SYNOPSIS
02500   do_file_exists
02501   command  called command
02502 
02503   DESCRIPTION
02504   fiile_exist <file_name>
02505   Check if file <file_name> exists
02506 */
02507 
02508 static void do_file_exist(struct st_command *command)
02509 {
02510   int error;
02511   string ds_filename;
02512   const struct command_arg file_exist_args[] = {
02513     { "filename", ARG_STRING, true, &ds_filename, "File to check if it exist" }
02514   };
02515 
02516 
02517   check_command_args(command, command->first_argument,
02518                      file_exist_args,
02519                      sizeof(file_exist_args)/sizeof(struct command_arg),
02520                      ' ');
02521 
02522   error= (access(ds_filename.c_str(), F_OK) != 0);
02523   handle_command_error(command, error);
02524 }
02525 
02526 
02527 /*
02528   SYNOPSIS
02529   do_mkdir
02530   command  called command
02531 
02532   DESCRIPTION
02533   mkdir <dir_name>
02534   Create the directory <dir_name>
02535 */
02536 
02537 static void do_mkdir(struct st_command *command)
02538 {
02539   string ds_dirname;
02540   int error;
02541   const struct command_arg mkdir_args[] = {
02542     {"dirname", ARG_STRING, true, &ds_dirname, "Directory to create"}
02543   };
02544 
02545 
02546   check_command_args(command, command->first_argument,
02547                      mkdir_args, sizeof(mkdir_args)/sizeof(struct command_arg),
02548                      ' ');
02549 
02550   error= mkdir(ds_dirname.c_str(), (0777 & internal::my_umask_dir)) != 0;
02551   handle_command_error(command, error);
02552 }
02553 
02554 /*
02555   SYNOPSIS
02556   do_rmdir
02557   command  called command
02558 
02559   DESCRIPTION
02560   rmdir <dir_name>
02561   Remove the empty directory <dir_name>
02562 */
02563 
02564 static void do_rmdir(struct st_command *command)
02565 {
02566   int error;
02567   string ds_dirname;
02568   const struct command_arg rmdir_args[] = {
02569     {"dirname", ARG_STRING, true, &ds_dirname, "Directory to remove"}
02570   };
02571 
02572 
02573   check_command_args(command, command->first_argument,
02574                      rmdir_args, sizeof(rmdir_args)/sizeof(struct command_arg),
02575                      ' ');
02576 
02577   error= rmdir(ds_dirname.c_str()) != 0;
02578   handle_command_error(command, error);
02579 }
02580 
02581 
02582 /*
02583   Read characters from line buffer or file. This is needed to allow
02584   my_ungetc() to buffer MAX_DELIMITER_LENGTH characters for a file
02585 
02586   NOTE:
02587   This works as long as one doesn't change files (with 'source file_name')
02588   when there is things pushed into the buffer.  This should however not
02589   happen for any tests in the test suite.
02590 */
02591 
02592 static int my_getc(FILE *file)
02593 {
02594   if (line_buffer_pos == line_buffer)
02595     return fgetc(file);
02596   return *--line_buffer_pos;
02597 }
02598 
02599 
02600 static void my_ungetc(int c)
02601 {
02602   *line_buffer_pos++= (char) c;
02603 }
02604 
02605 
02606 static void read_until_delimiter(string *ds,
02607                                  string *ds_delimiter)
02608 {
02609   char c;
02610 
02611   if (ds_delimiter->length() > MAX_DELIMITER_LENGTH)
02612     die("Max delimiter length(%d) exceeded", MAX_DELIMITER_LENGTH);
02613 
02614   /* Read from file until delimiter is found */
02615   while (1)
02616   {
02617     c= my_getc(cur_file->file);
02618 
02619     if (c == '\n')
02620     {
02621       cur_file->lineno++;
02622 
02623       /* Skip newline from the same line as the command */
02624       if (start_lineno == (cur_file->lineno - 1))
02625         continue;
02626     }
02627     else if (start_lineno == cur_file->lineno)
02628     {
02629       /*
02630         No characters except \n are allowed on
02631         the same line as the command
02632       */
02633       die("Trailing characters found after command");
02634     }
02635 
02636     if (feof(cur_file->file))
02637       die("End of file encountered before '%s' delimiter was found",
02638           ds_delimiter->c_str());
02639 
02640     if (match_delimiter(c, ds_delimiter->c_str(), ds_delimiter->length()))
02641       break;
02642 
02643     ds->push_back(c);
02644   }
02645   return;
02646 }
02647 
02648 
02649 static void do_write_file_command(struct st_command *command, bool append)
02650 {
02651   string ds_content;
02652   string ds_filename;
02653   string ds_delimiter;
02654   const struct command_arg write_file_args[] = {
02655     { "filename", ARG_STRING, true, &ds_filename, "File to write to" },
02656     { "delimiter", ARG_STRING, false, &ds_delimiter, "Delimiter to read until" }
02657   };
02658 
02659 
02660   check_command_args(command,
02661                      command->first_argument,
02662                      write_file_args,
02663                      sizeof(write_file_args)/sizeof(struct command_arg),
02664                      ' ');
02665 
02666   /* If no delimiter was provided, use EOF */
02667   if (ds_delimiter.length() == 0)
02668     ds_delimiter.append("EOF");
02669 
02670   if (!append && access(ds_filename.c_str(), F_OK) == 0)
02671   {
02672     /* The file should not be overwritten */
02673     die("File already exist: '%s'", ds_filename.c_str());
02674   }
02675 
02676   read_until_delimiter(&ds_content, &ds_delimiter);
02677   str_to_file2(ds_filename.c_str(), ds_content.c_str(),
02678                ds_content.length(), append);
02679   return;
02680 }
02681 
02682 
02683 /*
02684   SYNOPSIS
02685   do_write_file
02686   command  called command
02687 
02688   DESCRIPTION
02689   write_file <file_name> [<delimiter>];
02690   <what to write line 1>
02691   <...>
02692   < what to write line n>
02693   EOF
02694 
02695   --write_file <file_name>;
02696   <what to write line 1>
02697   <...>
02698   < what to write line n>
02699   EOF
02700 
02701   Write everything between the "write_file" command and 'delimiter'
02702   to "file_name"
02703 
02704   NOTE! Will fail if <file_name> exists
02705 
02706   Default <delimiter> is EOF
02707 
02708 */
02709 
02710 static void do_write_file(struct st_command *command)
02711 {
02712   do_write_file_command(command, false);
02713 }
02714 
02715 
02716 /*
02717   SYNOPSIS
02718   do_append_file
02719   command  called command
02720 
02721   DESCRIPTION
02722   append_file <file_name> [<delimiter>];
02723   <what to write line 1>
02724   <...>
02725   < what to write line n>
02726   EOF
02727 
02728   --append_file <file_name>;
02729   <what to write line 1>
02730   <...>
02731   < what to write line n>
02732   EOF
02733 
02734   Append everything between the "append_file" command
02735   and 'delimiter' to "file_name"
02736 
02737   Default <delimiter> is EOF
02738 
02739 */
02740 
02741 static void do_append_file(struct st_command *command)
02742 {
02743   do_write_file_command(command, true);
02744 }
02745 
02746 
02747 /*
02748   SYNOPSIS
02749   do_cat_file
02750   command  called command
02751 
02752   DESCRIPTION
02753   cat_file <file_name>;
02754 
02755   Print the given file to result log
02756 
02757 */
02758 
02759 static void do_cat_file(struct st_command *command)
02760 {
02761   static string ds_filename;
02762   const struct command_arg cat_file_args[] = {
02763     { "filename", ARG_STRING, true, &ds_filename, "File to read from" }
02764   };
02765 
02766 
02767   check_command_args(command,
02768                      command->first_argument,
02769                      cat_file_args,
02770                      sizeof(cat_file_args)/sizeof(struct command_arg),
02771                      ' ');
02772 
02773   cat_file(&ds_res, ds_filename.c_str());
02774 
02775   return;
02776 }
02777 
02778 
02779 /*
02780   SYNOPSIS
02781   do_diff_files
02782   command  called command
02783 
02784   DESCRIPTION
02785   diff_files <file1> <file2>;
02786 
02787   Fails if the two files differ.
02788 
02789 */
02790 
02791 static void do_diff_files(struct st_command *command)
02792 {
02793   int error= 0;
02794   string ds_filename;
02795   string ds_filename2;
02796   const struct command_arg diff_file_args[] = {
02797     { "file1", ARG_STRING, true, &ds_filename, "First file to diff" },
02798     { "file2", ARG_STRING, true, &ds_filename2, "Second file to diff" }
02799   };
02800 
02801 
02802   check_command_args(command,
02803                      command->first_argument,
02804                      diff_file_args,
02805                      sizeof(diff_file_args)/sizeof(struct command_arg),
02806                      ' ');
02807 
02808   if ((error= compare_files(ds_filename.c_str(), ds_filename2.c_str())))
02809   {
02810     /* Compare of the two files failed, append them to output
02811        so the failure can be analyzed
02812     */
02813     show_diff(&ds_res, ds_filename.c_str(), ds_filename2.c_str());
02814   }
02815 
02816   handle_command_error(command, error);
02817 }
02818 
02819 
02820 static struct st_connection * find_connection_by_name(const char *name)
02821 {
02822   struct st_connection *con;
02823   for (con= connections; con < next_con; con++)
02824   {
02825     if (!strcmp(con->name, name))
02826     {
02827       return con;
02828     }
02829   }
02830   return 0; /* Connection not found */
02831 }
02832 
02833 
02834 /*
02835   SYNOPSIS
02836   do_send_quit
02837   command  called command
02838 
02839   DESCRIPTION
02840   Sends a simple quit command to the server for the named connection.
02841 
02842 */
02843 
02844 static void do_send_quit(struct st_command *command)
02845 {
02846   char *p= command->first_argument, *name;
02847   struct st_connection *con;
02848   drizzle_result_st result;
02849   drizzle_return_t ret;
02850 
02851   if (!*p)
02852     die("Missing connection name in send_quit");
02853   name= p;
02854   while (*p && !my_isspace(charset_info,*p))
02855     p++;
02856 
02857   if (*p)
02858     *p++= 0;
02859   command->last_argument= p;
02860 
02861   if (!(con= find_connection_by_name(name)))
02862     die("connection '%s' not found in connection pool", name);
02863 
02864   if (drizzle_quit(&con->con,&result, &ret))
02865     drizzle_result_free(&result);
02866 }
02867 
02868 
02869 /*
02870   SYNOPSIS
02871   do_change_user
02872   command       called command
02873 
02874   DESCRIPTION
02875   change_user [<user>], [<passwd>], [<db>]
02876   <user> - user to change to
02877   <passwd> - user password
02878   <db> - default database
02879 
02880   Changes the user and causes the database specified by db to become
02881   the default (current) database for the the current connection.
02882 
02883 */
02884 
02885 static void do_change_user(struct st_command *)
02886 {
02887   assert(0);
02888 }
02889 
02890 /*
02891   SYNOPSIS
02892   do_perl
02893   command  command handle
02894 
02895   DESCRIPTION
02896   perl [<delimiter>];
02897   <perlscript line 1>
02898   <...>
02899   <perlscript line n>
02900   EOF
02901 
02902   Execute everything after "perl" until <delimiter> as perl.
02903   Useful for doing more advanced things
02904   but still being able to execute it on all platforms.
02905 
02906   Default <delimiter> is EOF
02907 */
02908 
02909 static void do_perl(struct st_command *command)
02910 {
02911   int error;
02912   int fd;
02913   FILE *res_file;
02914   char buf[FN_REFLEN];
02915   char temp_file_path[FN_REFLEN];
02916   string ds_script;
02917   string ds_delimiter;
02918   const struct command_arg perl_args[] = {
02919     { "delimiter", ARG_STRING, false, &ds_delimiter, "Delimiter to read until" }
02920   };
02921 
02922 
02923   check_command_args(command,
02924                      command->first_argument,
02925                      perl_args,
02926                      sizeof(perl_args)/sizeof(struct command_arg),
02927                      ' ');
02928 
02929   /* If no delimiter was provided, use EOF */
02930   if (ds_delimiter.length() == 0)
02931     ds_delimiter.append("EOF");
02932 
02933   read_until_delimiter(&ds_script, &ds_delimiter);
02934 
02935   /* Create temporary file name */
02936   if ((fd= internal::create_temp_file(temp_file_path, getenv("MYSQLTEST_VARDIR"),
02937                             "tmp", MYF(MY_WME))) < 0)
02938     die("Failed to create temporary file for perl command");
02939   internal::my_close(fd, MYF(0));
02940 
02941   str_to_file(temp_file_path, ds_script.c_str(), ds_script.length());
02942 
02943   /* Format the "perl <filename>" command */
02944   snprintf(buf, sizeof(buf), "perl %s", temp_file_path);
02945 
02946   if (!(res_file= popen(buf, "r")) && command->abort_on_error)
02947     die("popen(\"%s\", \"r\") failed", buf);
02948 
02949   while (fgets(buf, sizeof(buf), res_file))
02950   {
02951     if (disable_result_log)
02952       buf[strlen(buf)-1]=0;
02953     else
02954       replace_append(&ds_res, buf);
02955   }
02956   error= pclose(res_file);
02957 
02958   /* Remove the temporary file */
02959   internal::my_delete(temp_file_path, MYF(0));
02960 
02961   handle_command_error(command, WEXITSTATUS(error));
02962 }
02963 
02964 
02965 /*
02966   Print the content between echo and <delimiter> to result file.
02967   Evaluate all variables in the string before printing, allow
02968   for variable names to be escaped using        \
02969 
02970   SYNOPSIS
02971   do_echo()
02972   command  called command
02973 
02974   DESCRIPTION
02975   echo text
02976   Print the text after echo until end of command to result file
02977 
02978   echo $<var_name>
02979   Print the content of the variable <var_name> to result file
02980 
02981   echo Some text $<var_name>
02982   Print "Some text" plus the content of the variable <var_name> to
02983   result file
02984 
02985   echo Some text \$<var_name>
02986   Print "Some text" plus $<var_name> to result file
02987 */
02988 
02989 static int do_echo(struct st_command *command)
02990 {
02991   string ds_echo;
02992 
02993 
02994   do_eval(&ds_echo, command->first_argument, command->end, false);
02995   ds_res.append(ds_echo.c_str(), ds_echo.length());
02996   ds_res.append("\n");
02997   command->last_argument= command->end;
02998   return(0);
02999 }
03000 
03001 
03002 static void
03003 do_wait_for_slave_to_stop(struct st_command *)
03004 {
03005   static int SLAVE_POLL_INTERVAL= 300000;
03006   drizzle_con_st *con= &cur_con->con;
03007   for (;;)
03008   {
03009     drizzle_result_st res;
03010     drizzle_return_t ret;
03011     drizzle_row_t row;
03012     int done;
03013 
03014     if (drizzle_query_str(con,&res,"show status like 'Slave_running'",
03015                           &ret) == NULL || ret != DRIZZLE_RETURN_OK)
03016     {
03017       if (ret == DRIZZLE_RETURN_ERROR_CODE)
03018       {
03019         die("Query failed while probing slave for stop: %s",
03020             drizzle_result_error(&res));
03021         drizzle_result_free(&res);
03022       }
03023       else
03024       {
03025         die("Query failed while probing slave for stop: %s",
03026             drizzle_con_error(con));
03027       }
03028     }
03029 
03030     if (drizzle_result_column_count(&res) == 0 ||
03031         drizzle_result_buffer(&res) != DRIZZLE_RETURN_OK)
03032     {
03033       die("Query failed while probing slave for stop: %s",
03034           drizzle_con_error(con));
03035     }
03036 
03037     if (!(row=drizzle_row_next(&res)) || !row[1])
03038     {
03039       drizzle_result_free(&res);
03040       die("Strange result from query while probing slave for stop");
03041     }
03042     done = !strcmp(row[1],"OFF");
03043     drizzle_result_free(&res);
03044     if (done)
03045       break;
03046     usleep(SLAVE_POLL_INTERVAL);
03047   }
03048   return;
03049 }
03050 
03051 
03052 static void do_sync_with_master2(long offset)
03053 {
03054   drizzle_result_st res;
03055   drizzle_return_t ret;
03056   drizzle_row_t row;
03057   drizzle_con_st *con= &cur_con->con;
03058   char query_buf[FN_REFLEN+128];
03059   int tries= 0;
03060 
03061   if (!master_pos.file[0])
03062     die("Calling 'sync_with_master' without calling 'save_master_pos'");
03063 
03064   snprintf(query_buf, sizeof(query_buf), "select master_pos_wait('%s', %ld)", master_pos.file,
03065           master_pos.pos + offset);
03066 
03067 wait_for_position:
03068 
03069   if (drizzle_query_str(con, &res, query_buf, &ret) == NULL ||
03070       ret != DRIZZLE_RETURN_OK)
03071   {
03072     if (ret == DRIZZLE_RETURN_ERROR_CODE)
03073     {
03074       die("failed in '%s': %d: %s", query_buf, drizzle_result_error_code(&res),
03075            drizzle_result_error(&res));
03076       drizzle_result_free(&res);
03077     }
03078     else
03079       die("failed in '%s': %d: %s", query_buf, ret, drizzle_con_error(con));
03080   }
03081 
03082   if (drizzle_result_column_count(&res) == 0 ||
03083       drizzle_result_buffer(&res) != DRIZZLE_RETURN_OK)
03084     die("drizzle_result_buffer() returned NULL for '%s'", query_buf);
03085 
03086   if (!(row= drizzle_row_next(&res)))
03087   {
03088     drizzle_result_free(&res);
03089     die("empty result in %s", query_buf);
03090   }
03091   if (!row[0])
03092   {
03093     /*
03094       It may be that the slave SQL thread has not started yet, though START
03095       SLAVE has been issued ?
03096     */
03097     drizzle_result_free(&res);
03098     if (tries++ == 30)
03099     {
03100       show_query(con, "SHOW MASTER STATUS");
03101       show_query(con, "SHOW SLAVE STATUS");
03102       die("could not sync with master ('%s' returned NULL)", query_buf);
03103     }
03104     sleep(1); /* So at most we will wait 30 seconds and make 31 tries */
03105     goto wait_for_position;
03106   }
03107   drizzle_result_free(&res);
03108   return;
03109 }
03110 
03111 
03112 static void do_sync_with_master(struct st_command *command)
03113 {
03114   long offset= 0;
03115   char *p= command->first_argument;
03116   const char *offset_start= p;
03117   if (*offset_start)
03118   {
03119     for (; my_isdigit(charset_info, *p); p++)
03120       offset = offset * 10 + *p - '0';
03121 
03122     if(*p && !my_isspace(charset_info, *p))
03123       die("Invalid integer argument \"%s\"", offset_start);
03124     command->last_argument= p;
03125   }
03126   do_sync_with_master2(offset);
03127   return;
03128 }
03129 
03130 
03131 /*
03132   when ndb binlog is on, this call will wait until last updated epoch
03133   (locally in the drizzled) has been received into the binlog
03134 */
03135 static int do_save_master_pos()
03136 {
03137   drizzle_result_st res;
03138   drizzle_return_t ret;
03139   drizzle_row_t row;
03140   drizzle_con_st *con= &cur_con->con;
03141   const char *query;
03142 
03143 
03144   if (drizzle_query_str(con, &res, query= "show master status", &ret) == NULL ||
03145       ret != DRIZZLE_RETURN_OK)
03146   {
03147     if (ret == DRIZZLE_RETURN_ERROR_CODE)
03148     {
03149       die("failed in '%s': %d: %s", query, drizzle_result_error_code(&res),
03150            drizzle_result_error(&res));
03151       drizzle_result_free(&res);
03152     }
03153     else
03154       die("failed in '%s': %d: %s", query, ret, drizzle_con_error(con));
03155   }
03156 
03157   if (drizzle_result_column_count(&res) == 0 ||
03158       drizzle_result_buffer(&res) != DRIZZLE_RETURN_OK)
03159     die("drizzleclient_store_result() retuned NULL for '%s'", query);
03160   if (!(row = drizzle_row_next(&res)))
03161     die("empty result in show master status");
03162   strncpy(master_pos.file, row[0], sizeof(master_pos.file)-1);
03163   master_pos.pos = strtoul(row[1], (char**) 0, 10);
03164   drizzle_result_free(&res);
03165   return(0);
03166 }
03167 
03168 
03169 /*
03170   Assign the variable <var_name> with <var_val>
03171 
03172   SYNOPSIS
03173   do_let()
03174   query  called command
03175 
03176   DESCRIPTION
03177   let $<var_name>=<var_val><delimiter>
03178 
03179   <var_name>  - is the string string found between the $ and =
03180   <var_val>   - is the content between the = and <delimiter>, it may span
03181   multiple line and contain any characters except <delimiter>
03182   <delimiter> - is a string containing of one or more chars, default is ;
03183 
03184   RETURN VALUES
03185   Program will die if error detected
03186 */
03187 
03188 static void do_let(struct st_command *command)
03189 {
03190   char *p= command->first_argument;
03191   char *var_name, *var_name_end;
03192   string let_rhs_expr;
03193 
03194 
03195   /* Find <var_name> */
03196   if (!*p)
03197     die("Missing arguments to let");
03198   var_name= p;
03199   while (*p && (*p != '=') && !my_isspace(charset_info,*p))
03200     p++;
03201   var_name_end= p;
03202   if (var_name == var_name_end ||
03203       (var_name+1 == var_name_end && *var_name == '$'))
03204     die("Missing variable name in let");
03205   while (my_isspace(charset_info,*p))
03206     p++;
03207   if (*p++ != '=')
03208     die("Missing assignment operator in let");
03209 
03210   /* Find start of <var_val> */
03211   while (*p && my_isspace(charset_info,*p))
03212     p++;
03213 
03214   do_eval(&let_rhs_expr, p, command->end, false);
03215 
03216   command->last_argument= command->end;
03217   /* Assign var_val to var_name */
03218   var_set(var_name, var_name_end, let_rhs_expr.c_str(),
03219           (let_rhs_expr.c_str() + let_rhs_expr.length()));
03220   return;
03221 }
03222 
03223 
03224 /*
03225   Sleep the number of specified seconds
03226 
03227   SYNOPSIS
03228   do_sleep()
03229   q         called command
03230   real_sleep   use the value from opt_sleep as number of seconds to sleep
03231   if real_sleep is false
03232 
03233   DESCRIPTION
03234   sleep <seconds>
03235   real_sleep <seconds>
03236 
03237   The difference between the sleep and real_sleep commands is that sleep
03238   uses the delay from the --sleep command-line option if there is one.
03239   (If the --sleep option is not given, the sleep command uses the delay
03240   specified by its argument.) The real_sleep command always uses the
03241   delay specified by its argument.  The logic is that sometimes delays are
03242   cpu-dependent, and --sleep can be used to set this delay.  real_sleep is
03243   used for cpu-independent delays.
03244 */
03245 
03246 static int do_sleep(struct st_command *command, bool real_sleep)
03247 {
03248   bool error= false;
03249   char *p= command->first_argument;
03250   char *sleep_start, *sleep_end= command->end;
03251   double sleep_val= 0;
03252 
03253   while (my_isspace(charset_info, *p))
03254     p++;
03255   if (!*p)
03256     die("Missing argument to %.*s", command->first_word_len, command->query);
03257   sleep_start= p;
03258   /* Check that arg starts with a digit, not handled by internal::my_strtod */
03259   if (!my_isdigit(charset_info, *sleep_start))
03260     die("Invalid argument to %.*s \"%s\"", command->first_word_len,
03261         command->query,command->first_argument);
03262   string buff_str(sleep_start, sleep_end-sleep_start);
03263   istringstream buff(buff_str);
03264   error= (buff >> sleep_val).fail();
03265   if (error)
03266     die("Invalid argument to %.*s \"%s\"", command->first_word_len,
03267         command->query, command->first_argument);
03268 
03269   /* Fixed sleep time selected by --sleep option */
03270   if (opt_sleep >= 0 && !real_sleep)
03271     sleep_val= opt_sleep;
03272 
03273   if (sleep_val)
03274     usleep(sleep_val * 1000000);
03275   command->last_argument= sleep_end;
03276   return 0;
03277 }
03278 
03279 
03280 static void do_get_file_name(st_command *command, string &dest)
03281 {
03282   char *p= command->first_argument;
03283   if (!*p)
03284     die("Missing file name argument");
03285   char *name= p;
03286   while (*p && !my_isspace(charset_info,*p))
03287     p++;
03288   if (*p)
03289     *p++= 0;
03290   command->last_argument= p;
03291   if (! opt_testdir.empty())
03292   {
03293     dest= opt_testdir;
03294     if (dest[dest.length()] != '/')
03295       dest.append("/");
03296   }
03297   dest.append(name);
03298 }
03299 
03300 
03301 static void do_set_charset(struct st_command *command)
03302 {
03303   char *charset_name= command->first_argument;
03304   char *p;
03305 
03306   if (!charset_name || !*charset_name)
03307     die("Missing charset name in 'character_set'");
03308   /* Remove end space */
03309   p= charset_name;
03310   while (*p && !my_isspace(charset_info,*p))
03311     p++;
03312   if(*p)
03313     *p++= 0;
03314   command->last_argument= p;
03315   charset_info= get_charset_by_csname(charset_name, MY_CS_PRIMARY);
03316   if (!charset_info)
03317     abort_not_supported_test("Test requires charset '%s'", charset_name);
03318 }
03319 
03320 static void fill_global_error_names()
03321 {
03322   drizzle_result_st res;
03323   drizzle_return_t ret;
03324   drizzle_row_t row;
03325   drizzle_con_st *con= &cur_con->con;
03326 
03327   global_error_names.clear();
03328 
03329   const std::string ds_query("select error_name, error_code "
03330                              "from data_dictionary.errors");
03331   if (drizzle_query_str(con, &res, ds_query.c_str(), &ret) == NULL ||
03332       ret != DRIZZLE_RETURN_OK)
03333   {
03334     if (ret == DRIZZLE_RETURN_ERROR_CODE)
03335     {
03336       die("Error running query '%s': %d %s", ds_query.c_str(),
03337           drizzle_result_error_code(&res), drizzle_result_error(&res));
03338       drizzle_result_free(&res);
03339     }
03340     else
03341     {
03342       die("Error running query '%s': %d %s", ds_query.c_str(), ret,
03343           drizzle_con_error(con));
03344     }
03345   }
03346   if (drizzle_result_column_count(&res) == 0 ||
03347       drizzle_result_buffer(&res) != DRIZZLE_RETURN_OK)
03348   {
03349     drizzle_result_free(&res);
03350     die("Query '%s' didn't return a result set", ds_query.c_str());
03351   }
03352 
03353   while ((row= drizzle_row_next(&res)) && row[0])
03354   {
03355     /*
03356       Concatenate all fields in the first row with tab in between
03357       and assign that string to the $variable
03358     */
03359     size_t *lengths= drizzle_row_field_sizes(&res);
03360     try
03361     {
03362       global_error_names[string(row[0], lengths[0])] = boost::lexical_cast<uint32_t>(string(row[1], lengths[1]));
03363     }
03364     catch (boost::bad_lexical_cast &ex)
03365     {
03366       drizzle_result_free(&res);
03367       die("Invalid error_code from Drizzle: %s", ex.what());
03368     }
03369 
03370   }
03371 
03372   drizzle_result_free(&res);
03373 }
03374 
03375 static uint32_t get_errcode_from_name(const char *error_name, const char *error_end)
03376 {
03377   string error_name_s(error_name, error_end);
03378 
03379   if (ErrorCodes::mapped_type* ptr= find_ptr(global_error_names, error_name_s))
03380     return *ptr;
03381 
03382   die("Unknown SQL error name '%s'", error_name_s.c_str());
03383   return 0;
03384 }
03385 
03386 static void do_get_errcodes(struct st_command *command)
03387 {
03388   struct st_match_err *to= saved_expected_errors.err;
03389   char *p= command->first_argument;
03390   uint32_t count= 0;
03391 
03392 
03393 
03394   if (!*p)
03395     die("Missing argument(s) to 'error'");
03396 
03397   do
03398   {
03399     char *end;
03400 
03401     /* Skip leading spaces */
03402     while (*p && *p == ' ')
03403       p++;
03404 
03405     /* Find end */
03406     end= p;
03407     while (*end && *end != ',' && *end != ' ')
03408       end++;
03409 
03410     if (*p == 'S')
03411     {
03412       char *to_ptr= to->code.sqlstate;
03413 
03414       /*
03415         SQLSTATE string
03416         - Must be DRIZZLE_MAX_SQLSTATE_SIZE long
03417         - May contain only digits[0-9] and _uppercase_ letters
03418       */
03419       p++; /* Step past the S */
03420       if ((end - p) != DRIZZLE_MAX_SQLSTATE_SIZE)
03421         die("The sqlstate must be exactly %d chars long", DRIZZLE_MAX_SQLSTATE_SIZE);
03422 
03423       /* Check sqlstate string validity */
03424       while (*p && p < end)
03425       {
03426         if (my_isdigit(charset_info, *p) || my_isupper(charset_info, *p))
03427           *to_ptr++= *p++;
03428         else
03429           die("The sqlstate may only consist of digits[0-9] "   \
03430               "and _uppercase_ letters");
03431       }
03432 
03433       *to_ptr= 0;
03434       to->type= ERR_SQLSTATE;
03435     }
03436     else if (*p == 's')
03437     {
03438       die("The sqlstate definition must start with an uppercase S");
03439     }
03440     else if (*p == 'E')
03441     {
03442       /* Error name string */
03443 
03444       to->code.errnum= get_errcode_from_name(p, end);
03445       to->type= ERR_ERRNO;
03446     }
03447     else if (*p == 'e')
03448     {
03449       die("The error name definition must start with an uppercase E");
03450     }
03451     else if (*p == 'H')
03452     {
03453       /* Error name string */
03454 
03455       to->code.errnum= get_errcode_from_name(p, end);
03456       to->type= ERR_ERRNO;
03457     }
03458     else
03459     {
03460       die ("You must either use the SQLSTATE or built in drizzle error label, numbers are not accepted");
03461     }
03462     to++;
03463     count++;
03464 
03465     if (count >= (sizeof(saved_expected_errors.err) /
03466                   sizeof(struct st_match_err)))
03467       die("Too many errorcodes specified");
03468 
03469     /* Set pointer to the end of the last error code */
03470     p= end;
03471 
03472     /* Find next ',' */
03473     while (*p && *p != ',')
03474       p++;
03475 
03476     if (*p)
03477       p++; /* Step past ',' */
03478 
03479   } while (*p);
03480 
03481   command->last_argument= p;
03482   to->type= ERR_EMPTY;                        /* End of data */
03483 
03484   saved_expected_errors.count= count;
03485   return;
03486 }
03487 
03488 
03489 /*
03490   Get a string;  Return ptr to end of string
03491   Strings may be surrounded by " or '
03492 
03493   If string is a '$variable', return the value of the variable.
03494 */
03495 
03496 static char *get_string(char **to_ptr, char **from_ptr,
03497                         struct st_command *command)
03498 {
03499   char c, sep;
03500   char *to= *to_ptr, *from= *from_ptr, *start=to;
03501 
03502 
03503   /* Find separator */
03504   if (*from == '"' || *from == '\'')
03505     sep= *from++;
03506   else
03507     sep=' ';        /* Separated with space */
03508 
03509   for ( ; (c=*from) ; from++)
03510   {
03511     if (c == '\\' && from[1])
03512     {          /* Escaped character */
03513       /* We can't translate \0 -> ASCII 0 as replace can't handle ASCII 0 */
03514       switch (*++from) {
03515       case 'n':
03516         *to++= '\n';
03517         break;
03518       case 't':
03519         *to++= '\t';
03520         break;
03521       case 'r':
03522         *to++ = '\r';
03523         break;
03524       case 'b':
03525         *to++ = '\b';
03526         break;
03527       case 'Z':        /* ^Z must be escaped on Win32 */
03528         *to++='\032';
03529         break;
03530       default:
03531         *to++ = *from;
03532         break;
03533       }
03534     }
03535     else if (c == sep)
03536     {
03537       if (c == ' ' || c != *++from)
03538         break;        /* Found end of string */
03539       *to++=c;        /* Copy duplicated separator */
03540     }
03541     else
03542       *to++=c;
03543   }
03544   if (*from != ' ' && *from)
03545     die("Wrong string argument in %s", command->query);
03546 
03547   while (my_isspace(charset_info,*from))  /* Point to next string */
03548     from++;
03549 
03550   *to =0;        /* End of string marker */
03551   *to_ptr= to+1;      /* Store pointer to end */
03552   *from_ptr= from;
03553 
03554   /* Check if this was a variable */
03555   if (*start == '$')
03556   {
03557     const char *end= to;
03558     VAR *var=var_get(start, &end, 0, 1);
03559     if (var && to == (char*) end+1)
03560       return(var->str_val);  /* return found variable value */
03561   }
03562   return(start);
03563 }
03564 
03565 
03566 static void set_reconnect(drizzle_con_st *con, int val)
03567 {
03568   (void) con;
03569   (void) val;
03570 /* XXX
03571   bool reconnect= val;
03572 
03573   drizzleclient_options(drizzle, DRIZZLE_OPT_RECONNECT, (char *)&reconnect);
03574 */
03575 }
03576 
03577 
03578 static int select_connection_name(const char *name)
03579 {
03580   if (!(cur_con= find_connection_by_name(name)))
03581     die("connection '%s' not found in connection pool", name);
03582 
03583   /* Update $drizzleclient_get_server_version to that of current connection */
03584   var_set_drizzleclient_get_server_version(&cur_con->con);
03585 
03586   return(0);
03587 }
03588 
03589 
03590 static int select_connection(struct st_command *command)
03591 {
03592   char *name;
03593   char *p= command->first_argument;
03594 
03595 
03596   if (!*p)
03597     die("Missing connection name in connect");
03598   name= p;
03599   while (*p && !my_isspace(charset_info,*p))
03600     p++;
03601   if (*p)
03602     *p++= 0;
03603   command->last_argument= p;
03604   return(select_connection_name(name));
03605 }
03606 
03607 
03608 static void do_close_connection(struct st_command *command)
03609 {
03610   char *p= command->first_argument, *name;
03611   struct st_connection *con;
03612 
03613   if (!*p)
03614     die("Missing connection name in disconnect");
03615   name= p;
03616   while (*p && !my_isspace(charset_info,*p))
03617     p++;
03618 
03619   if (*p)
03620     *p++= 0;
03621   command->last_argument= p;
03622 
03623   if (!(con= find_connection_by_name(name)))
03624     die("connection '%s' not found in connection pool", name);
03625 
03626   if (con->drizzle != NULL)
03627   {
03628     drizzle_free(con->drizzle);
03629     con->drizzle= NULL;
03630   }
03631   free(con->name);
03632 
03633   /*
03634     When the connection is closed set name to "-closed_connection-"
03635     to make it possible to reuse the connection name.
03636   */
03637   if (!(con->name = strdup("-closed_connection-")))
03638     die("Out of memory");
03639 
03640   return;
03641 }
03642 
03643 
03644 /*
03645   Connect to a server doing several retries if needed.
03646 
03647   SYNOPSIS
03648   safe_connect()
03649   con               - connection structure to be used
03650   host, user, pass, - connection parameters
03651   db, port, sock
03652 
03653   NOTE
03654 
03655   Sometimes in a test the client starts before
03656   the server - to solve the problem, we try again
03657   after some sleep if connection fails the first
03658   time
03659 
03660   This function will try to connect to the given server
03661   "opt_max_connect_retries" times and sleep "connection_retry_sleep"
03662   seconds between attempts before finally giving up.
03663   This helps in situation when the client starts
03664   before the server (which happens sometimes).
03665   It will only ignore connection errors during these retries.
03666 
03667 */
03668 
03669 static void safe_connect(drizzle_con_st *con, const char *name,
03670                          const string host, const string user, const char *pass,
03671                          const string db, uint32_t port)
03672 {
03673   uint32_t failed_attempts= 0;
03674   static uint32_t connection_retry_sleep= 100000; /* Microseconds */
03675   drizzle_return_t ret;
03676 
03677   drizzle_con_set_tcp(con, host.c_str(), port);
03678   drizzle_con_set_auth(con, user.c_str(), pass);
03679   drizzle_con_set_db(con, db.c_str());
03680   while((ret= drizzle_con_connect(con)) != DRIZZLE_RETURN_OK)
03681   {
03682     /*
03683       Connect failed
03684 
03685       Only allow retry if this was an error indicating the server
03686       could not be contacted. Error code differs depending
03687       on protocol/connection type
03688     */
03689 
03690     if ((ret == DRIZZLE_RETURN_GETADDRINFO ||
03691          ret == DRIZZLE_RETURN_COULD_NOT_CONNECT) &&
03692         failed_attempts < opt_max_connect_retries)
03693     {
03694       verbose_msg("Connect attempt %d/%d failed: %d: %s", failed_attempts,
03695                   opt_max_connect_retries, ret, drizzle_con_error(con));
03696       usleep(connection_retry_sleep);
03697     }
03698     else
03699     {
03700       if (failed_attempts > 0)
03701         die("Could not open connection '%s' after %d attempts: %d %s", name,
03702             failed_attempts, ret, drizzle_con_error(con));
03703       else
03704         die("Could not open connection '%s': %d %s", name, ret,
03705             drizzle_con_error(con));
03706     }
03707     failed_attempts++;
03708   }
03709   return;
03710 }
03711 
03712 
03713 /*
03714   Connect to a server and handle connection errors in case they occur.
03715 
03716   SYNOPSIS
03717   connect_n_handle_errors()
03718   q                 - context of connect "query" (command)
03719   con               - connection structure to be used
03720   host, user, pass, - connection parameters
03721   db, port, sock
03722 
03723   DESCRIPTION
03724   This function will try to establish a connection to server and handle
03725   possible errors in the same manner as if "connect" was usual SQL-statement
03726   (If error is expected it will ignore it once it occurs and log the
03727   "statement" to the query log).
03728   Unlike safe_connect() it won't do several attempts.
03729 
03730   RETURN VALUES
03731   1 - Connected
03732   0 - Not connected
03733 
03734 */
03735 
03736 static int connect_n_handle_errors(struct st_command *command,
03737                                    drizzle_con_st *con, const char* host,
03738                                    const char* user, const char* pass,
03739                                    const char* db, int port, const char* sock)
03740 {
03741   drizzle_return_t ret;
03742 
03743   /* Only log if an error is expected */
03744   if (!command->abort_on_error &&
03745       !disable_query_log)
03746   {
03747     /*
03748       Log the connect to result log
03749     */
03750     ds_res.append("connect(");
03751     replace_append(&ds_res, host);
03752     ds_res.append(",");
03753     replace_append(&ds_res, user);
03754     ds_res.append(",");
03755     replace_append(&ds_res, pass);
03756     ds_res.append(",");
03757     if (db)
03758       replace_append(&ds_res, db);
03759     ds_res.append(",");
03760     replace_append_uint(&ds_res, port);
03761     ds_res.append(",");
03762     if (sock)
03763       replace_append(&ds_res, sock);
03764     ds_res.append(")");
03765     ds_res.append(delimiter);
03766     ds_res.append("\n");
03767   }
03768   drizzle_con_set_tcp(con, host, port);
03769   drizzle_con_set_auth(con, user, pass);
03770   drizzle_con_set_db(con, db);
03771   if ((ret= drizzle_con_connect(con)) != DRIZZLE_RETURN_OK)
03772   {
03773     if (ret == DRIZZLE_RETURN_HANDSHAKE_FAILED)
03774     {
03775       var_set_errno(drizzle_con_error_code(con));
03776       handle_error(command, drizzle_con_error_code(con), drizzle_con_error(con),
03777                    drizzle_con_sqlstate(con), &ds_res);
03778     }
03779     else
03780     {
03781       var_set_errno(ret);
03782       handle_error(command, ret, drizzle_con_error(con), "", &ds_res);
03783     }
03784 
03785     return 0; /* Not connected */
03786   }
03787 
03788   var_set_errno(0);
03789   handle_no_error(command);
03790   return 1; /* Connected */
03791 }
03792 
03793 
03794 /*
03795   Open a new connection to DRIZZLE Server with the parameters
03796   specified. Make the new connection the current connection.
03797 
03798   SYNOPSIS
03799   do_connect()
03800   q         called command
03801 
03802   DESCRIPTION
03803   connect(<name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]]);
03804   connect <name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]];
03805 
03806   <name> - name of the new connection
03807   <host> - hostname of server
03808   <user> - user to connect as
03809   <pass> - password used when connecting
03810   <db>   - initial db when connected
03811   <port> - server port
03812   <sock> - server socket
03813   <opts> - options to use for the connection
03814   * SSL - use SSL if available
03815   * COMPRESS - use compression if available
03816 
03817   */
03818 
03819 static void do_connect(struct st_command *command)
03820 {
03821   uint32_t con_port= opt_port;
03822   const char *con_options;
03823   bool con_ssl= 0;
03824   struct st_connection* con_slot;
03825 
03826   string ds_connection_name;
03827   string ds_host;
03828   string ds_user;
03829   string ds_password;
03830   string ds_database;
03831   string ds_port;
03832   string ds_sock;
03833   string ds_options;
03834   const struct command_arg connect_args[] = {
03835     { "connection name", ARG_STRING, true, &ds_connection_name, "Name of the connection" },
03836     { "host", ARG_STRING, true, &ds_host, "Host to connect to" },
03837     { "user", ARG_STRING, false, &ds_user, "User to connect as" },
03838     { "passsword", ARG_STRING, false, &ds_password, "Password used when connecting" },
03839     { "database", ARG_STRING, false, &ds_database, "Database to select after connect" },
03840     { "port", ARG_STRING, false, &ds_port, "Port to connect to" },
03841     { "socket", ARG_STRING, false, &ds_sock, "Socket to connect with" },
03842     { "options", ARG_STRING, false, &ds_options, "Options to use while connecting" }
03843   };
03844 
03845 
03846   strip_parentheses(command);
03847   check_command_args(command, command->first_argument, connect_args,
03848                      sizeof(connect_args)/sizeof(struct command_arg),
03849                      ',');
03850 
03851   /* Port */
03852   if (ds_port.length())
03853   {
03854     con_port= atoi(ds_port.c_str());
03855     if (con_port == 0)
03856       die("Illegal argument for port: '%s'", ds_port.c_str());
03857   }
03858 
03859   /* Sock */
03860   if (!ds_sock.empty())
03861   {
03862     /*
03863       If the socket is specified just as a name without path
03864       append tmpdir in front
03865     */
03866     if (*ds_sock.c_str() != FN_LIBCHAR)
03867     {
03868       char buff[FN_REFLEN];
03869       internal::fn_format(buff, ds_sock.c_str(), TMPDIR, "", 0);
03870       ds_sock.clear();
03871       ds_sock.append(buff);
03872     }
03873   }
03874 
03875   /* Options */
03876   con_options= ds_options.c_str();
03877   while (*con_options)
03878   {
03879     const char* end;
03880     /* Step past any spaces in beginning of option*/
03881     while (*con_options && my_isspace(charset_info, *con_options))
03882       con_options++;
03883     /* Find end of this option */
03884     end= con_options;
03885     while (*end && !my_isspace(charset_info, *end))
03886       end++;
03887     if (!strncmp(con_options, "SSL", 3))
03888       con_ssl= 1;
03889     else
03890       die("Illegal option to connect: %.*s",
03891           (int) (end - con_options), con_options);
03892     /* Process next option */
03893     con_options= end;
03894   }
03895 
03896   if (find_connection_by_name(ds_connection_name.c_str()))
03897     die("Connection %s already exists", ds_connection_name.c_str());
03898 
03899   if (next_con != connections_end)
03900   {
03901     con_slot= next_con;
03902   }
03903   else
03904   {
03905     if (!(con_slot= find_connection_by_name("-closed_connection-")))
03906       die("Connection limit exhausted, you can have max %d connections",
03907           (int) (sizeof(connections)/sizeof(struct st_connection)));
03908   }
03909 
03910   if ((con_slot->drizzle= drizzle_create(NULL)) == NULL)
03911     die("Failed on drizzle_create()");
03912   if (!drizzle_con_create(con_slot->drizzle, &con_slot->con))
03913     die("Failed on drizzle_con_create()");
03914   drizzle_con_add_options(&con_slot->con, use_drizzle_protocol ? DRIZZLE_CON_EXPERIMENTAL : DRIZZLE_CON_MYSQL);
03915 
03916   /* Use default db name */
03917   if (ds_database.length() == 0)
03918     ds_database.append(opt_db);
03919 
03920   /* Special database to allow one to connect without a database name */
03921   if (ds_database.length() && !strcmp(ds_database.c_str(),"*NO-ONE*"))
03922     ds_database.clear();
03923 
03924   if (connect_n_handle_errors(command, &con_slot->con,
03925                               ds_host.c_str(),ds_user.c_str(),
03926                               ds_password.c_str(), ds_database.c_str(),
03927                               con_port, ds_sock.c_str()))
03928   {
03929     if (!(con_slot->name= strdup(ds_connection_name.c_str())))
03930       die("Out of memory");
03931     cur_con= con_slot;
03932 
03933     if (con_slot == next_con)
03934       next_con++; /* if we used the next_con slot, advance the pointer */
03935   }
03936 
03937   /* Update $drizzleclient_get_server_version to that of current connection */
03938   var_set_drizzleclient_get_server_version(&cur_con->con);
03939 
03940   return;
03941 }
03942 
03943 
03944 static int do_done(struct st_command *command)
03945 {
03946   /* Check if empty block stack */
03947   if (cur_block == block_stack)
03948   {
03949     if (*command->query != '}')
03950       die("Stray 'end' command - end of block before beginning");
03951     die("Stray '}' - end of block before beginning");
03952   }
03953 
03954   /* Test if inner block has been executed */
03955   if (cur_block->ok && cur_block->cmd == cmd_while)
03956   {
03957     /* Pop block from stack, re-execute outer block */
03958     cur_block--;
03959     parser.current_line = cur_block->line;
03960   }
03961   else
03962   {
03963     /* Pop block from stack, goto next line */
03964     cur_block--;
03965     parser.current_line++;
03966   }
03967   return 0;
03968 }
03969 
03970 
03971 /*
03972   Process start of a "if" or "while" statement
03973 
03974   SYNOPSIS
03975   do_block()
03976   cmd        Type of block
03977   q         called command
03978 
03979   DESCRIPTION
03980   if ([!]<expr>)
03981   {
03982   <block statements>
03983   }
03984 
03985   while ([!]<expr>)
03986   {
03987   <block statements>
03988   }
03989 
03990   Evaluates the <expr> and if it evaluates to
03991   greater than zero executes the following code block.
03992   A '!' can be used before the <expr> to indicate it should
03993   be executed if it evaluates to zero.
03994 
03995 */
03996 
03997 static void do_block(enum block_cmd cmd, struct st_command* command)
03998 {
03999   char *p= command->first_argument;
04000   const char *expr_start, *expr_end;
04001   const char *cmd_name= (cmd == cmd_while ? "while" : "if");
04002   bool not_expr= false;
04003 
04004   /* Check stack overflow */
04005   if (cur_block == block_stack_end)
04006     die("Nesting too deeply");
04007 
04008   /* Set way to find outer block again, increase line counter */
04009   cur_block->line= parser.current_line++;
04010 
04011   /* If this block is ignored */
04012   if (!cur_block->ok)
04013   {
04014     /* Inner block should be ignored too */
04015     cur_block++;
04016     cur_block->cmd= cmd;
04017     cur_block->ok= false;
04018     return;
04019   }
04020 
04021   /* Parse and evaluate test expression */
04022   expr_start= strchr(p, '(');
04023   if (!expr_start++)
04024     die("missing '(' in %s", cmd_name);
04025 
04026   /* Check for !<expr> */
04027   if (*expr_start == '!')
04028   {
04029     not_expr= true;
04030     expr_start++; /* Step past the '!' */
04031   }
04032   /* Find ending ')' */
04033   expr_end= strrchr(expr_start, ')');
04034   if (!expr_end)
04035     die("missing ')' in %s", cmd_name);
04036   p= (char*)expr_end+1;
04037 
04038   while (*p && my_isspace(charset_info, *p))
04039     p++;
04040   if (*p && *p != '{')
04041     die("Missing '{' after %s. Found \"%s\"", cmd_name, p);
04042 
04043   VAR v;
04044   var_init(&v,0,0,0,0);
04045   eval_expr(&v, expr_start, &expr_end);
04046 
04047   /* Define inner block */
04048   cur_block++;
04049   cur_block->cmd= cmd;
04050   cur_block->ok= (v.int_val ? true : false);
04051 
04052   if (not_expr)
04053     cur_block->ok = !cur_block->ok;
04054 
04055   free(v.str_val);
04056   free(v.env_s);
04057 
04058   return;
04059 }
04060 
04061 
04062 static void do_delimiter(struct st_command* command)
04063 {
04064   char* p= command->first_argument;
04065 
04066   while (*p && my_isspace(charset_info, *p))
04067     p++;
04068 
04069   if (!(*p))
04070     die("Can't set empty delimiter");
04071 
04072   strncpy(delimiter, p, sizeof(delimiter) - 1);
04073   delimiter_length= strlen(delimiter);
04074 
04075   command->last_argument= p + delimiter_length;
04076   return;
04077 }
04078 
04079 
04080 bool match_delimiter(int c, const char *delim, uint32_t length)
04081 {
04082   uint32_t i;
04083   char tmp[MAX_DELIMITER_LENGTH];
04084 
04085   if (c != *delim)
04086     return 0;
04087 
04088   for (i= 1; i < length &&
04089          (c= my_getc(cur_file->file)) == *(delim + i);
04090        i++)
04091     tmp[i]= c;
04092 
04093   if (i == length)
04094     return 1;          /* Found delimiter */
04095 
04096   /* didn't find delimiter, push back things that we read */
04097   my_ungetc(c);
04098   while (i > 1)
04099     my_ungetc(tmp[--i]);
04100   return 0;
04101 }
04102 
04103 
04104 static bool end_of_query(int c)
04105 {
04106   return match_delimiter(c, delimiter, delimiter_length);
04107 }
04108 
04109 
04110 /*
04111   Read one "line" from the file
04112 
04113   SYNOPSIS
04114   read_line
04115   buf     buffer for the read line
04116   size    size of the buffer i.e max size to read
04117 
04118   DESCRIPTION
04119   This function actually reads several lines and adds them to the
04120   buffer buf. It continues to read until it finds what it believes
04121   is a complete query.
04122 
04123   Normally that means it will read lines until it reaches the
04124   "delimiter" that marks end of query. Default delimiter is ';'
04125   The function should be smart enough not to detect delimiter's
04126   found inside strings surrounded with '"' and '\'' escaped strings.
04127 
04128   If the first line in a query starts with '#' or '-' this line is treated
04129   as a comment. A comment is always terminated when end of line '\n' is
04130   reached.
04131 
04132 */
04133 
04134 
04135 static int my_strnncoll_simple(const CHARSET_INFO * const  cs, const unsigned char *s, size_t slen,
04136                                const unsigned char *t, size_t tlen,
04137                                bool t_is_prefix)
04138 {
04139   size_t len = ( slen > tlen ) ? tlen : slen;
04140   unsigned char *map= cs->sort_order;
04141   if (t_is_prefix && slen > tlen)
04142     slen=tlen;
04143   while (len--)
04144   {
04145     if (map[*s++] != map[*t++])
04146       return ((int) map[s[-1]] - (int) map[t[-1]]);
04147   }
04148   /*
04149     We can't use (slen - tlen) here as the result may be outside of the
04150     precision of a signed int
04151   */
04152   return slen > tlen ? 1 : slen < tlen ? -1 : 0 ;
04153 }
04154 
04155 static int read_line(char *buf, int size)
04156 {
04157   char c, last_quote= 0;
04158   char *p= buf, *buf_end= buf + size - 1;
04159   int skip_char= 0;
04160   enum {R_NORMAL, R_Q, R_SLASH_IN_Q,
04161         R_COMMENT, R_LINE_START} state= R_LINE_START;
04162 
04163 
04164   start_lineno= cur_file->lineno;
04165   for (; p < buf_end ;)
04166   {
04167     skip_char= 0;
04168     c= my_getc(cur_file->file);
04169     if (feof(cur_file->file))
04170     {
04171   found_eof:
04172       if (cur_file->file != stdin)
04173       {
04174         fclose(cur_file->file);
04175         cur_file->file= 0;
04176       }
04177       free((unsigned char*) cur_file->file_name);
04178       cur_file->file_name= 0;
04179       if (cur_file == file_stack.data())
04180       {
04181         /* We're back at the first file, check if
04182            all { have matching }
04183         */
04184         if (cur_block != block_stack)
04185           die("Missing end of block");
04186 
04187         *p= 0;
04188         return(1);
04189       }
04190       cur_file--;
04191       start_lineno= cur_file->lineno;
04192       continue;
04193     }
04194 
04195     if (c == '\n')
04196     {
04197       /* Line counting is independent of state */
04198       cur_file->lineno++;
04199 
04200       /* Convert cr/lf to lf */
04201       if (p != buf && *(p-1) == '\r')
04202         p--;
04203     }
04204 
04205     switch(state) {
04206     case R_NORMAL:
04207       if (end_of_query(c))
04208       {
04209         *p= 0;
04210         return(0);
04211       }
04212       else if ((c == '{' &&
04213                 (!my_strnncoll_simple(charset_info, (const unsigned char*) "while", 5,
04214                                       (unsigned char*) buf, min((ptrdiff_t)5, p - buf), 0) ||
04215                  !my_strnncoll_simple(charset_info, (const unsigned char*) "if", 2,
04216                                       (unsigned char*) buf, min((ptrdiff_t)2, p - buf), 0))))
04217       {
04218         /* Only if and while commands can be terminated by { */
04219         *p++= c;
04220         *p= 0;
04221         return(0);
04222       }
04223       else if (c == '\'' || c == '"' || c == '`')
04224       {
04225         last_quote= c;
04226         state= R_Q;
04227       }
04228       break;
04229 
04230     case R_COMMENT:
04231       if (c == '\n')
04232       {
04233         /* Comments are terminated by newline */
04234         *p= 0;
04235         return(0);
04236       }
04237       break;
04238 
04239     case R_LINE_START:
04240       if (c == '#' || c == '-')
04241       {
04242         /* A # or - in the first position of the line - this is a comment */
04243         state = R_COMMENT;
04244       }
04245       else if (my_isspace(charset_info, c))
04246       {
04247         /* Skip all space at begining of line */
04248         if (c == '\n')
04249         {
04250           /* Query hasn't started yet */
04251           start_lineno= cur_file->lineno;
04252         }
04253         skip_char= 1;
04254       }
04255       else if (end_of_query(c))
04256       {
04257         *p= 0;
04258         return(0);
04259       }
04260       else if (c == '}')
04261       {
04262         /* A "}" need to be by itself in the begining of a line to terminate */
04263         *p++= c;
04264         *p= 0;
04265         return(0);
04266       }
04267       else if (c == '\'' || c == '"' || c == '`')
04268       {
04269         last_quote= c;
04270         state= R_Q;
04271       }
04272       else
04273         state= R_NORMAL;
04274       break;
04275 
04276     case R_Q:
04277       if (c == last_quote)
04278         state= R_NORMAL;
04279       else if (c == '\\')
04280         state= R_SLASH_IN_Q;
04281       break;
04282 
04283     case R_SLASH_IN_Q:
04284       state= R_Q;
04285       break;
04286 
04287     }
04288 
04289     if (!skip_char)
04290     {
04291       /* Could be a multibyte character */
04292       /* This code is based on the code in "sql_load.cc" */
04293       int charlen = my_mbcharlen(charset_info, c);
04294       /* We give up if multibyte character is started but not */
04295       /* completed before we pass buf_end */
04296       if ((charlen > 1) && (p + charlen) <= buf_end)
04297       {
04298         int i;
04299         char* mb_start = p;
04300 
04301         *p++ = c;
04302 
04303         for (i= 1; i < charlen; i++)
04304         {
04305           if (feof(cur_file->file))
04306             goto found_eof;
04307           c= my_getc(cur_file->file);
04308           *p++ = c;
04309         }
04310         if (! my_ismbchar(charset_info, mb_start, p))
04311         {
04312           /* It was not a multiline char, push back the characters */
04313           /* We leave first 'c', i.e. pretend it was a normal char */
04314           while (p > mb_start)
04315             my_ungetc(*--p);
04316         }
04317       }
04318       else
04319         *p++= c;
04320     }
04321   }
04322   die("The input buffer is too small for this query.x\n"        \
04323       "check your query or increase MAX_QUERY and recompile");
04324   return(0);
04325 }
04326 
04327 
04328 /*
04329   Convert the read query to result format version 1
04330 
04331   That is: After newline, all spaces need to be skipped
04332   unless the previous char was a quote
04333 
04334   This is due to an old bug that has now been fixed, but the
04335   version 1 output format is preserved by using this function
04336 
04337 */
04338 
04339 static void convert_to_format_v1(char* query)
04340 {
04341   int last_c_was_quote= 0;
04342   char *p= query, *to= query;
04343   char *end= strchr(query, '\0');
04344   char last_c;
04345 
04346   while (p <= end)
04347   {
04348     if (*p == '\n' && !last_c_was_quote)
04349     {
04350       *to++ = *p++; /* Save the newline */
04351 
04352       /* Skip any spaces on next line */
04353       while (*p && my_isspace(charset_info, *p))
04354         p++;
04355 
04356       last_c_was_quote= 0;
04357     }
04358     else if (*p == '\'' || *p == '"' || *p == '`')
04359     {
04360       last_c= *p;
04361       *to++ = *p++;
04362 
04363       /* Copy anything until the next quote of same type */
04364       while (*p && *p != last_c)
04365         *to++ = *p++;
04366 
04367       *to++ = *p++;
04368 
04369       last_c_was_quote= 1;
04370     }
04371     else
04372     {
04373       *to++ = *p++;
04374       last_c_was_quote= 0;
04375     }
04376   }
04377 }
04378 
04379 
04380 /*
04381   Check a command that is about to be sent (or should have been
04382   sent if parsing was enabled) to DRIZZLE server for
04383   suspicious things and generate warnings.
04384 */
04385 
04386 static void scan_command_for_warnings(struct st_command *command)
04387 {
04388   const char *ptr= command->query;
04389 
04390   while(*ptr)
04391   {
04392     /*
04393       Look for query's that lines that start with a -- comment
04394       and has a drizzletest command
04395     */
04396     if (ptr[0] == '\n' &&
04397         ptr[1] && ptr[1] == '-' &&
04398         ptr[2] && ptr[2] == '-' &&
04399         ptr[3])
04400     {
04401       uint32_t type;
04402       char save;
04403       char *end, *start= (char*)ptr+3;
04404       /* Skip leading spaces */
04405       while (*start && my_isspace(charset_info, *start))
04406         start++;
04407       end= start;
04408       /* Find end of command(next space) */
04409       while (*end && !my_isspace(charset_info, *end))
04410         end++;
04411       save= *end;
04412       *end= 0;
04413       type= command_typelib.find_type(start, 1+2);
04414       if (type)
04415         warning_msg("Embedded drizzletest command '--%s' detected in "
04416                     "query '%s' was this intentional? ",
04417                     start, command->query);
04418       *end= save;
04419     }
04420 
04421     ptr++;
04422   }
04423   return;
04424 }
04425 
04426 /*
04427   Check for unexpected "junk" after the end of query
04428   This is normally caused by missing delimiters or when
04429   switching between different delimiters
04430 */
04431 
04432 static void check_eol_junk_line(const char *line)
04433 {
04434   const char *p= line;
04435 
04436   /* Check for extra delimiter */
04437   if (*p && !strncmp(p, delimiter, delimiter_length))
04438     die("Extra delimiter \"%s\" found", delimiter);
04439 
04440   /* Allow trailing # comment */
04441   if (*p && *p != '#')
04442   {
04443     if (*p == '\n')
04444       die("Missing delimiter");
04445     die("End of line junk detected: \"%s\"", p);
04446   }
04447   return;
04448 }
04449 
04450 static void check_eol_junk(const char *eol)
04451 {
04452   const char *p= eol;
04453 
04454   /* Skip past all spacing chars and comments */
04455   while (*p && (my_isspace(charset_info, *p) || *p == '#' || *p == '\n'))
04456   {
04457     /* Skip past comments started with # and ended with newline */
04458     if (*p && *p == '#')
04459     {
04460       p++;
04461       while (*p && *p != '\n')
04462         p++;
04463     }
04464 
04465     /* Check this line */
04466     if (*p && *p == '\n')
04467       check_eol_junk_line(p);
04468 
04469     if (*p)
04470       p++;
04471   }
04472 
04473   check_eol_junk_line(p);
04474 
04475   return;
04476 }
04477 
04478 /*
04479   Create a command from a set of lines
04480 
04481   SYNOPSIS
04482   read_command()
04483   command_ptr pointer where to return the new query
04484 
04485   DESCRIPTION
04486   Converts lines returned by read_line into a command, this involves
04487   parsing the first word in the read line to find the command type.
04488 
04489   A -- comment may contain a valid query as the first word after the
04490   comment start. Thus it's always checked to see if that is the case.
04491   The advantage with this approach is to be able to execute commands
04492   terminated by new line '\n' regardless how many "delimiter" it contain.
04493 */
04494 
04495 #define MAX_QUERY (768*1024*2) /* 256K -- a test in sp-big is >128K */
04496 static char read_command_buf[MAX_QUERY];
04497 
04498 static int read_command(struct st_command** command_ptr)
04499 {
04500   char *p= read_command_buf;
04501   struct st_command* command;
04502 
04503 
04504   if (parser.current_line < parser.read_lines)
04505   {
04506     *command_ptr= q_lines[parser.current_line];
04507     return(0);
04508   }
04509   if (!(*command_ptr= command= new st_command))
04510     die("command construction failed");
04511   q_lines.push_back(command);
04512   command->type= Q_UNKNOWN;
04513 
04514   read_command_buf[0]= 0;
04515   if (read_line(read_command_buf, sizeof(read_command_buf)))
04516   {
04517     check_eol_junk(read_command_buf);
04518     return(1);
04519   }
04520 
04521   convert_to_format_v1(read_command_buf);
04522 
04523   if (*p == '#')
04524   {
04525     command->type= Q_COMMENT;
04526   }
04527   else if (p[0] == '-' && p[1] == '-')
04528   {
04529     command->type= Q_COMMENT_WITH_COMMAND;
04530     p+= 2; /* Skip past -- */
04531   }
04532 
04533   /* Skip leading spaces */
04534   while (*p && my_isspace(charset_info, *p))
04535     p++;
04536 
04537   if (!(command->query_buf= command->query= strdup(p)))
04538     die("Out of memory");
04539 
04540   /* Calculate first word length(the command), terminated by space or ( */
04541   p= command->query;
04542   while (*p && !my_isspace(charset_info, *p) && *p != '(')
04543     p++;
04544   command->first_word_len= (uint32_t) (p - command->query);
04545 
04546   /* Skip spaces between command and first argument */
04547   while (*p && my_isspace(charset_info, *p))
04548     p++;
04549   command->first_argument= p;
04550 
04551   command->end= strchr(command->query, '\0');
04552   command->query_len= (command->end - command->query);
04553   parser.read_lines++;
04554   return(0);
04555 }
04556 
04557 /*
04558   Write the content of str into file
04559 
04560   SYNOPSIS
04561   str_to_file2
04562   fname - name of file to truncate/create and write to
04563   str - content to write to file
04564   size - size of content witten to file
04565   append - append to file instead of overwriting old file
04566 */
04567 
04568 void str_to_file2(const char *fname, const char *str, int size, bool append)
04569 {
04570   int fd;
04571   char buff[FN_REFLEN];
04572   int flags= O_WRONLY | O_CREAT;
04573   if (!internal::test_if_hard_path(fname))
04574   {
04575     snprintf(buff, sizeof(buff), "%s%s",opt_basedir.c_str(),fname);
04576     fname= buff;
04577   }
04578   internal::fn_format(buff, fname, "", "", MY_UNPACK_FILENAME);
04579 
04580   if (!append)
04581     flags|= O_TRUNC;
04582   if ((fd= internal::my_open(buff, flags,
04583                    MYF(MY_WME | MY_FFNF))) < 0)
04584     die("Could not open '%s' for writing: errno = %d", buff, errno);
04585   if (append && lseek(fd, 0, SEEK_END) == MY_FILEPOS_ERROR)
04586     die("Could not find end of file '%s': errno = %d", buff, errno);
04587   if (internal::my_write(fd, (unsigned char*)str, size, MYF(MY_WME|MY_FNABP)))
04588     die("write failed");
04589   internal::my_close(fd, MYF(0));
04590 }
04591 
04592 /*
04593   Write the content of str into file
04594 
04595   SYNOPSIS
04596   str_to_file
04597   fname - name of file to truncate/create and write to
04598   str - content to write to file
04599   size - size of content witten to file
04600 */
04601 
04602 void str_to_file(const char *fname, const char *str, int size)
04603 {
04604   str_to_file2(fname, str, size, false);
04605 }
04606 
04607 
04608 void dump_result_to_log_file(const char *buf, int size)
04609 {
04610   char log_file[FN_REFLEN];
04611   str_to_file(internal::fn_format(log_file, result_file_name.c_str(), opt_logdir.c_str(), ".log",
04612                         ! opt_logdir.empty() ? MY_REPLACE_DIR | MY_REPLACE_EXT :
04613                         MY_REPLACE_EXT),
04614               buf, size);
04615   fprintf(stderr, "\nMore results from queries before failure can be found in %s\n",
04616           log_file);
04617 }
04618 
04619 void dump_progress()
04620 {
04621   char progress_file[FN_REFLEN];
04622   str_to_file(internal::fn_format(progress_file, result_file_name.c_str(),
04623                         opt_logdir.c_str(), ".progress",
04624                         ! opt_logdir.empty() ? MY_REPLACE_DIR | MY_REPLACE_EXT :
04625                         MY_REPLACE_EXT),
04626               ds_progress.c_str(), ds_progress.length());
04627 }
04628 
04629 void dump_warning_messages()
04630 {
04631   char warn_file[FN_REFLEN];
04632 
04633   str_to_file(internal::fn_format(warn_file, result_file_name.c_str(), opt_logdir.c_str(), ".warnings",
04634                         ! opt_logdir.empty() ? MY_REPLACE_DIR | MY_REPLACE_EXT :
04635                         MY_REPLACE_EXT),
04636               ds_warning_messages.c_str(), ds_warning_messages.length());
04637 }
04638 
04639 
04640 /*
04641   Append the result for one field to the dynamic string ds
04642 */
04643 
04644 static void append_field(string *ds, uint32_t col_idx, drizzle_column_st *column,
04645                          const char* val, uint64_t len, bool is_null)
04646 {
04647   if (col_idx < max_replace_column && replace_column[col_idx])
04648   {
04649     val= replace_column[col_idx];
04650     len= strlen(val);
04651   }
04652   else if (is_null)
04653   {
04654     val= "NULL";
04655     len= 4;
04656   }
04657 
04658   if (!display_result_vertically)
04659   {
04660     if (col_idx)
04661       ds->append("\t");
04662     replace_append_mem(ds, val, (int)len);
04663   }
04664   else
04665   {
04666     ds->append(drizzle_column_name(column));
04667     ds->append("\t");
04668     replace_append_mem(ds, val, (int)len);
04669     ds->append("\n");
04670   }
04671 }
04672 
04673 
04674 /*
04675   Append all results to the dynamic string separated with '\t'
04676   Values may be converted with 'replace_column'
04677 */
04678 
04679 static void append_result(string *ds, drizzle_result_st *res)
04680 {
04681   drizzle_row_t row;
04682   uint32_t num_fields= drizzle_result_column_count(res);
04683   drizzle_column_st *column;
04684   size_t *lengths;
04685 
04686   while ((row = drizzle_row_next(res)))
04687   {
04688     uint32_t i;
04689     lengths = drizzle_row_field_sizes(res);
04690     drizzle_column_seek(res, 0);
04691     for (i = 0; i < num_fields; i++)
04692     {
04693       column= drizzle_column_next(res);
04694       if (row[i] && (drizzle_column_type(column) == DRIZZLE_COLUMN_TYPE_TINY))
04695       {
04696         if (boost::lexical_cast<uint32_t>(row[i]))
04697         {
04698           if ((drizzle_column_flags(column) & DRIZZLE_COLUMN_FLAGS_UNSIGNED))
04699           {
04700             append_field(ds, i, column, "YES", 3, false);
04701           }
04702           else
04703           {
04704             append_field(ds, i, column, "TRUE", 4, false);
04705           }
04706         }
04707         else
04708         {
04709           if ((drizzle_column_flags(column) & DRIZZLE_COLUMN_FLAGS_UNSIGNED))
04710           {
04711             append_field(ds, i, column, "NO", 2, false);
04712           }
04713           else
04714           {
04715             append_field(ds, i, column, "FALSE", 5, false);
04716           }
04717         }
04718       }
04719       else
04720       {
04721         append_field(ds, i, column,
04722                      (const char*)row[i], lengths[i], !row[i]);
04723       }
04724     }
04725     if (!display_result_vertically)
04726       ds->append("\n");
04727 
04728   }
04729 }
04730 
04731 
04732 /*
04733   Append metadata for fields to output
04734 */
04735 
04736 static void append_metadata(string *ds, drizzle_result_st *res)
04737 {
04738   drizzle_column_st *column;
04739   ds->append("Catalog\tDatabase\tTable\tTable_alias\tColumn\t"
04740              "Column_alias\tType\tLength\tMax length\tIs_null\t"
04741              "Flags\tDecimals\tCharsetnr\n");
04742 
04743   drizzle_column_seek(res, 0);
04744   while ((column= drizzle_column_next(res)))
04745   {
04746     ds->append(drizzle_column_catalog(column),
04747                strlen(drizzle_column_catalog(column)));
04748     ds->append("\t", 1);
04749     ds->append(drizzle_column_db(column), strlen(drizzle_column_db(column)));
04750     ds->append("\t", 1);
04751     ds->append(drizzle_column_orig_table(column),
04752                strlen(drizzle_column_orig_table(column)));
04753     ds->append("\t", 1);
04754     ds->append(drizzle_column_table(column),
04755                strlen(drizzle_column_table(column)));
04756     ds->append("\t", 1);
04757     ds->append(drizzle_column_orig_name(column),
04758                strlen(drizzle_column_orig_name(column)));
04759     ds->append("\t", 1);
04760     ds->append(drizzle_column_name(column),
04761                strlen(drizzle_column_name(column)));
04762     ds->append("\t", 1);
04763     replace_append_uint(ds, drizzle_column_type_drizzle(column));
04764     ds->append("\t", 1);
04765     replace_append_uint(ds, drizzle_column_size(column));
04766     ds->append("\t", 1);
04767     if (drizzle_column_type(column) == DRIZZLE_COLUMN_TYPE_TINY)
04768     {
04769       replace_append_uint(ds, 1);
04770     }
04771     else
04772     {
04773       replace_append_uint(ds, drizzle_column_max_size(column));
04774     }
04775     ds->append("\t", 1);
04776     ds->append((char*) ((drizzle_column_flags(column) & DRIZZLE_COLUMN_FLAGS_NOT_NULL) ? "N" : "Y"), 1);
04777     ds->append("\t", 1);
04778     replace_append_uint(ds, drizzle_column_flags(column));
04779     ds->append("\t", 1);
04780     replace_append_uint(ds, drizzle_column_decimals(column));
04781     ds->append("\t", 1);
04782     replace_append_uint(ds, drizzle_column_charset(column));
04783     ds->append("\n", 1);
04784   }
04785 }
04786 
04787 
04788 /*
04789   Append affected row count and other info to output
04790 */
04791 
04792 static void append_info(string *ds, uint64_t affected_rows,
04793                         const char *info)
04794 {
04795   ostringstream buf;
04796   buf << "affected rows: " << affected_rows << endl;
04797   ds->append(buf.str());
04798   if (info && strcmp(info, ""))
04799   {
04800     ds->append("info: ");
04801     ds->append(info);
04802     ds->append("\n", 1);
04803   }
04804 }
04805 
04806 
04807 /*
04808   Display the table headings with the names tab separated
04809 */
04810 
04811 static void append_table_headings(string *ds, drizzle_result_st *res)
04812 {
04813   uint32_t col_idx= 0;
04814   drizzle_column_st *column;
04815   drizzle_column_seek(res, 0);
04816   while ((column= drizzle_column_next(res)))
04817   {
04818     if (col_idx)
04819       ds->append("\t", 1);
04820     replace_append(ds, drizzle_column_name(column));
04821     col_idx++;
04822   }
04823   ds->append("\n", 1);
04824 }
04825 
04826 /*
04827   Fetch warnings from server and append to ds
04828 
04829   RETURN VALUE
04830   Number of warnings appended to ds
04831 */
04832 
04833 static int append_warnings(string *ds, drizzle_con_st *con,
04834                            drizzle_result_st *res)
04835 {
04836   uint32_t count;
04837   drizzle_result_st warn_res;
04838   drizzle_return_t ret;
04839 
04840 
04841   if (!(count= drizzle_result_warning_count(res)))
04842     return(0);
04843 
04844   if (drizzle_query_str(con, &warn_res, "SHOW WARNINGS", &ret) == NULL ||
04845       ret != DRIZZLE_RETURN_OK)
04846   {
04847     if (ret == DRIZZLE_RETURN_ERROR_CODE)
04848       die("Error running query \"SHOW WARNINGS\": %s", drizzle_result_error(&warn_res));
04849     else
04850       die("Error running query \"SHOW WARNINGS\": %s", drizzle_con_error(con));
04851   }
04852 
04853   if (drizzle_result_column_count(&warn_res) == 0 ||
04854       drizzle_result_buffer(&warn_res) != DRIZZLE_RETURN_OK)
04855     die("Warning count is %u but didn't get any warnings", count);
04856 
04857   append_result(ds, &warn_res);
04858   drizzle_result_free(&warn_res);
04859 
04860   return(count);
04861 }
04862 
04863 
04864 /*
04865   Run query using DRIZZLE C API
04866 
04867   SYNOPSIS
04868   run_query_normal()
04869   drizzle  DRIZZLE handle
04870   command  current command pointer
04871   flags  flags indicating if we should SEND and/or REAP
04872   query  query string to execute
04873   query_len  length query string to execute
04874   ds    output buffer where to store result form query
04875 */
04876 
04877 static void run_query_normal(struct st_connection *cn,
04878                              struct st_command *command,
04879                              int flags, char *query, int query_len,
04880                              string *ds, string *ds_warnings)
04881 {
04882   drizzle_result_st res;
04883   drizzle_return_t ret;
04884   drizzle_con_st *con= &cn->con;
04885   int err= 0;
04886 
04887   drizzle_con_add_options(con, DRIZZLE_CON_NO_RESULT_READ);
04888 
04889   if (flags & QUERY_SEND_FLAG)
04890   {
04891     /*
04892      * Send the query
04893      */
04894 
04895     (void) drizzle_query(con, &res, query, query_len, &ret);
04896     if (ret != DRIZZLE_RETURN_OK)
04897     {
04898       if (ret == DRIZZLE_RETURN_ERROR_CODE ||
04899           ret == DRIZZLE_RETURN_HANDSHAKE_FAILED)
04900       {
04901         err= drizzle_result_error_code(&res);
04902         handle_error(command, err, drizzle_result_error(&res),
04903                      drizzle_result_sqlstate(&res), ds);
04904         if (ret == DRIZZLE_RETURN_ERROR_CODE)
04905           drizzle_result_free(&res);
04906       }
04907       else
04908       {
04909         handle_error(command, ret, drizzle_con_error(con), "", ds);
04910         err= ret;
04911       }
04912       goto end;
04913     }
04914   }
04915   if (!(flags & QUERY_REAP_FLAG))
04916     return;
04917 
04918   {
04919     /*
04920      * Read the result packet
04921      */
04922     if (drizzle_result_read(con, &res, &ret) == NULL ||
04923         ret != DRIZZLE_RETURN_OK)
04924     {
04925       if (ret == DRIZZLE_RETURN_ERROR_CODE)
04926       {
04927         handle_error(command, drizzle_result_error_code(&res),
04928                      drizzle_result_error(&res), drizzle_result_sqlstate(&res),
04929                      ds);
04930       }
04931       else
04932         handle_error(command, ret, drizzle_con_error(con), "", ds);
04933       drizzle_result_free(&res);
04934       err= ret;
04935       goto end;
04936     }
04937 
04938     /*
04939       Store the result of the query if it will return any fields
04940     */
04941     if (drizzle_result_column_count(&res) &&
04942         (ret= drizzle_result_buffer(&res)) != DRIZZLE_RETURN_OK)
04943     {
04944       if (ret == DRIZZLE_RETURN_ERROR_CODE)
04945       {
04946         handle_error(command, drizzle_result_error_code(&res),
04947                      drizzle_result_error(&res), drizzle_result_sqlstate(&res),
04948                      ds);
04949       }
04950       else
04951         handle_error(command, ret, drizzle_con_error(con), "", ds);
04952       drizzle_result_free(&res);
04953       err= ret;
04954       goto end;
04955     }
04956 
04957     if (!disable_result_log)
04958     {
04959       uint64_t affected_rows= 0;    /* Ok to be undef if 'disable_info' is set */
04960 
04961       if (drizzle_result_column_count(&res))
04962       {
04963         if (display_metadata)
04964           append_metadata(ds, &res);
04965 
04966         if (!display_result_vertically)
04967           append_table_headings(ds, &res);
04968 
04969         append_result(ds, &res);
04970       }
04971 
04972       /*
04973         Need to call drizzle_result_affected_rows() before the "new"
04974         query to find the warnings
04975       */
04976       if (!disable_info)
04977         affected_rows= drizzle_result_affected_rows(&res);
04978 
04979       /*
04980         Add all warnings to the result. We can't do this if we are in
04981         the middle of processing results from multi-statement, because
04982         this will break protocol.
04983       */
04984       if (!disable_warnings)
04985       {
04986         drizzle_con_remove_options(con, DRIZZLE_CON_NO_RESULT_READ);
04987         if (append_warnings(ds_warnings, con, &res) || ds_warnings->length())
04988         {
04989           ds->append("Warnings:\n", 10);
04990           ds->append(ds_warnings->c_str(), ds_warnings->length());
04991         }
04992       }
04993 
04994       if (!disable_info)
04995         append_info(ds, affected_rows, drizzle_result_info(&res));
04996     }
04997 
04998     drizzle_result_free(&res);
04999   }
05000 
05001   /* If we come here the query is both executed and read successfully */
05002   handle_no_error(command);
05003 
05004 end:
05005 
05006   /*
05007     We save the return code (drizzleclient_errno(drizzle)) from the last call sent
05008     to the server into the drizzletest builtin variable $drizzleclient_errno. This
05009     variable then can be used from the test case itself.
05010   */
05011   drizzle_con_remove_options(con, DRIZZLE_CON_NO_RESULT_READ);
05012   var_set_errno(err);
05013   return;
05014 }
05015 
05016 
05017 /*
05018   Handle errors which occurred during execution
05019 
05020   SYNOPSIS
05021   handle_error()
05022   q     - query context
05023   err_errno - error number
05024   err_error - error message
05025   err_sqlstate - sql state
05026   ds    - dynamic string which is used for output buffer
05027 
05028   NOTE
05029   If there is an unexpected error this function will abort drizzletest
05030   immediately.
05031 */
05032 
05033 void handle_error(struct st_command *command,
05034                   unsigned int err_errno, const char *err_error,
05035                   const char *err_sqlstate, string *ds)
05036 {
05037   uint32_t i;
05038 
05039 
05040   if (! command->require_file.empty())
05041   {
05042     /*
05043       The query after a "--require" failed. This is fine as long the server
05044       returned a valid reponse. Don't allow 2013 or 2006 to trigger an
05045       abort_not_supported_test
05046     */
05047     if (err_errno == DRIZZLE_RETURN_SERVER_GONE)
05048       die("require query '%s' failed: %d: %s", command->query,
05049           err_errno, err_error);
05050 
05051     /* Abort the run of this test, pass the failed query as reason */
05052     abort_not_supported_test("Query '%s' failed, required functionality " \
05053                              "not supported", command->query);
05054   }
05055 
05056   if (command->abort_on_error)
05057     die("query '%s' failed: %d: %s", command->query, err_errno, err_error);
05058 
05059   for (i= 0 ; (uint32_t) i < command->expected_errors.count ; i++)
05060   {
05061     if (((command->expected_errors.err[i].type == ERR_ERRNO) &&
05062          (command->expected_errors.err[i].code.errnum == err_errno)) ||
05063         ((command->expected_errors.err[i].type == ERR_SQLSTATE) &&
05064          (strncmp(command->expected_errors.err[i].code.sqlstate,
05065                   err_sqlstate, DRIZZLE_MAX_SQLSTATE_SIZE) == 0)))
05066     {
05067       if (!disable_result_log)
05068       {
05069         if (command->expected_errors.count == 1)
05070         {
05071           /* Only log error if there is one possible error */
05072           ds->append("ERROR ", 6);
05073           replace_append(ds, err_sqlstate);
05074           ds->append(": ", 2);
05075           replace_append(ds, err_error);
05076           ds->append("\n",1);
05077         }
05078         /* Don't log error if we may not get an error */
05079         else if (command->expected_errors.err[0].type == ERR_SQLSTATE ||
05080                  (command->expected_errors.err[0].type == ERR_ERRNO &&
05081                   command->expected_errors.err[0].code.errnum != 0))
05082           ds->append("Got one of the listed errors\n");
05083       }
05084       /* OK */
05085       return;
05086     }
05087   }
05088 
05089   if (!disable_result_log)
05090   {
05091     ds->append("ERROR ",6);
05092     replace_append(ds, err_sqlstate);
05093     ds->append(": ", 2);
05094     replace_append(ds, err_error);
05095     ds->append("\n", 1);
05096   }
05097 
05098   if (i)
05099   {
05100     if (command->expected_errors.err[0].type == ERR_ERRNO)
05101       die("query '%s' failed with wrong errno %d: '%s', instead of %d...",
05102           command->query, err_errno, err_error,
05103           command->expected_errors.err[0].code.errnum);
05104     else
05105       die("query '%s' failed with wrong sqlstate %s: '%s', instead of %s...",
05106           command->query, err_sqlstate, err_error,
05107           command->expected_errors.err[0].code.sqlstate);
05108   }
05109 
05110   return;
05111 }
05112 
05113 
05114 /*
05115   Handle absence of errors after execution
05116 
05117   SYNOPSIS
05118   handle_no_error()
05119   q - context of query
05120 
05121   RETURN VALUE
05122   error - function will not return
05123 */
05124 
05125 void handle_no_error(struct st_command *command)
05126 {
05127 
05128 
05129   if (command->expected_errors.err[0].type == ERR_ERRNO &&
05130       command->expected_errors.err[0].code.errnum != 0)
05131   {
05132     /* Error code we wanted was != 0, i.e. not an expected success */
05133     die("query '%s' succeeded - should have failed with errno %d...",
05134         command->query, command->expected_errors.err[0].code.errnum);
05135   }
05136   else if (command->expected_errors.err[0].type == ERR_SQLSTATE &&
05137            strcmp(command->expected_errors.err[0].code.sqlstate,"00000") != 0)
05138   {
05139     /* SQLSTATE we wanted was != "00000", i.e. not an expected success */
05140     die("query '%s' succeeded - should have failed with sqlstate %s...",
05141         command->query, command->expected_errors.err[0].code.sqlstate);
05142   }
05143 
05144   return;
05145 }
05146 
05147 
05148 /*
05149   Run query
05150 
05151   SYNPOSIS
05152   run_query()
05153   drizzle  DRIZZLE handle
05154   command  currrent command pointer
05155 
05156   flags control the phased/stages of query execution to be performed
05157   if QUERY_SEND_FLAG bit is on, the query will be sent. If QUERY_REAP_FLAG
05158   is on the result will be read - for regular query, both bits must be on
05159 */
05160 
05161 static void run_query(struct st_connection *cn,
05162                       struct st_command *command,
05163                       int flags)
05164 {
05165   string *ds= NULL;
05166   string *save_ds= NULL;
05167   string ds_result;
05168   string ds_sorted;
05169   string ds_warnings;
05170   string eval_query;
05171   char *query;
05172   int query_len;
05173 
05174 
05175   /* Scan for warning before sending to server */
05176   scan_command_for_warnings(command);
05177 
05178   /*
05179     Evaluate query if this is an eval command
05180   */
05181   if (command->type == Q_EVAL)
05182   {
05183     do_eval(&eval_query, command->query, command->end, false);
05184     query = strdup(eval_query.c_str());
05185     query_len = eval_query.length();
05186   }
05187   else
05188   {
05189     query = command->query;
05190     query_len = strlen(query);
05191   }
05192 
05193   /*
05194     When command->require_file is set the output of _this_ query
05195     should be compared with an already existing file
05196     Create a temporary dynamic string to contain the output from
05197     this query.
05198   */
05199   if (! command->require_file.empty())
05200   {
05201     ds= &ds_result;
05202   }
05203   else
05204   {
05205     ds= &ds_res;
05206   }
05207   /*
05208     Log the query into the output buffer
05209   */
05210   if (!disable_query_log && (flags & QUERY_SEND_FLAG))
05211   {
05212     replace_append_mem(ds, query, query_len);
05213     ds->append(delimiter, delimiter_length);
05214     ds->append("\n");
05215   }
05216 
05217   if (display_result_sorted)
05218   {
05219     /*
05220       Collect the query output in a separate string
05221       that can be sorted before it's added to the
05222       global result string
05223     */
05224     save_ds= ds; /* Remember original ds */
05225     ds= &ds_sorted;
05226   }
05227 
05228   /*
05229     Always run with normal C API if it's not a complete
05230     SEND + REAP
05231   */
05232   run_query_normal(cn, command, flags, query, query_len,
05233                    ds, &ds_warnings);
05234 
05235   if (display_result_sorted)
05236   {
05237     /* Sort the result set and append it to result */
05238     append_sorted(save_ds, &ds_sorted);
05239     ds= save_ds;
05240   }
05241 
05242   if (! command->require_file.empty())
05243   {
05244     /* A result file was specified for _this_ query
05245        and the output should be checked against an already
05246        existing file which has been specified using --require or --result
05247     */
05248     check_require(ds, command->require_file);
05249   }
05250 
05251   return;
05252 }
05253 
05254 
05255 /****************************************************************************/
05256 
05257 static void get_command_type(struct st_command* command)
05258 {
05259   char save;
05260   uint32_t type;
05261 
05262 
05263   if (*command->query == '}')
05264   {
05265     command->type = Q_END_BLOCK;
05266     return;
05267   }
05268 
05269   save= command->query[command->first_word_len];
05270   command->query[command->first_word_len]= 0;
05271   type= command_typelib.find_type(command->query, 1+2);
05272   command->query[command->first_word_len]= save;
05273   if (type > 0)
05274   {
05275     command->type=(enum enum_commands) type;    /* Found command */
05276 
05277     /*
05278       Look for case where "query" was explicitly specified to
05279       force command being sent to server
05280     */
05281     if (type == Q_QUERY)
05282     {
05283       /* Skip the "query" part */
05284       command->query= command->first_argument;
05285     }
05286   }
05287   else
05288   {
05289     /* No drizzletest command matched */
05290 
05291     if (command->type != Q_COMMENT_WITH_COMMAND)
05292     {
05293       /* A query that will sent to drizzled */
05294       command->type= Q_QUERY;
05295     }
05296     else
05297     {
05298       /* -- comment that didn't contain a drizzletest command */
05299       command->type= Q_COMMENT;
05300       warning_msg("Suspicious command '--%s' detected, was this intentional? " \
05301                   "Use # instead of -- to avoid this warning",
05302                   command->query);
05303 
05304       if (command->first_word_len &&
05305           strcmp(command->query + command->first_word_len - 1, delimiter) == 0)
05306       {
05307         /*
05308           Detect comment with command using extra delimiter
05309           Ex --disable_query_log;
05310           ^ Extra delimiter causing the command
05311           to be skipped
05312         */
05313         save= command->query[command->first_word_len-1];
05314         command->query[command->first_word_len-1]= 0;
05315         if (command_typelib.find_type(command->query, 1+2) > 0)
05316           die("Extra delimiter \";\" found");
05317         command->query[command->first_word_len-1]= save;
05318 
05319       }
05320     }
05321   }
05322 
05323   /* Set expected error on command */
05324   memcpy(&command->expected_errors, &saved_expected_errors,
05325          sizeof(saved_expected_errors));
05326   command->abort_on_error= (command->expected_errors.count == 0 &&
05327                             abort_on_error);
05328 
05329   return;
05330 }
05331 
05332 
05333 
05334 /*
05335   Record how many milliseconds it took to execute the test file
05336   up until the current line and save it in the dynamic string ds_progress.
05337 
05338   The ds_progress will be dumped to <test_name>.progress when
05339   test run completes
05340 
05341 */
05342 
05343 static void mark_progress(struct st_command*, int line)
05344 {
05345   uint64_t timer= timer_now();
05346   if (!progress_start)
05347     progress_start= timer;
05348   timer-= progress_start;
05349 
05350   ostringstream buf;
05351   /* Milliseconds since start */
05352   buf << timer << "\t";
05353 
05354   /* Parser line number */
05355   buf << line << "\t";
05356 
05357   /* Filename */
05358   buf << cur_file->file_name << ":";
05359 
05360   /* Line in file */
05361   buf << cur_file->lineno << endl;
05362 
05363   ds_progress.append(buf.str());
05364 
05365 }
05366 
05367 static void check_retries(uint32_t in_opt_max_connect_retries)
05368 {
05369   if (in_opt_max_connect_retries > 10000 || opt_max_connect_retries<1)
05370   {
05371     cout << N_("Error: Invalid Value for opt_max_connect_retries"); 
05372     exit(-1);
05373   }
05374   opt_max_connect_retries= in_opt_max_connect_retries;
05375 }
05376 
05377 static void check_tail_lines(uint32_t in_opt_tail_lines)
05378 {
05379   if (in_opt_tail_lines > 10000)
05380   {
05381     cout << N_("Error: Invalid Value for opt_tail_lines"); 
05382     exit(-1);
05383   }
05384   opt_tail_lines= in_opt_tail_lines;
05385 }
05386 
05387 static void check_sleep(int32_t in_opt_sleep)
05388 {
05389   if (in_opt_sleep < -1)
05390   {
05391     cout << N_("Error: Invalid Value for opt_sleep"); 
05392     exit(-1);
05393   }
05394   opt_sleep= in_opt_sleep;
05395 }
05396 
05397 int main(int argc, char **argv)
05398 {
05399 try
05400 {
05401   struct st_command *command;
05402   bool q_send_flag= 0, abort_flag= 0;
05403   uint32_t command_executed= 0, last_command_executed= 0;
05404   string save_file("");
05405   struct stat res_info;
05406 
05407   TMPDIR[0]= 0;
05408 
05409   internal::my_init();
05410 
05411   po::options_description commandline_options("Options used only in command line");
05412   commandline_options.add_options()
05413   ("help,?", "Display this help and exit.")
05414   ("mark-progress", po::value<bool>(&opt_mark_progress)->default_value(false)->zero_tokens(),
05415   "Write linenumber and elapsed time to <testname>.progress ")
05416   ("sleep,T", po::value<int32_t>(&opt_sleep)->default_value(-1)->notifier(&check_sleep),
05417   "Sleep always this many seconds on sleep commands.")
05418   ("test-file,x", po::value<string>(),
05419   "Read test from/in this file (default stdin).")
05420   ("timer-file,f", po::value<string>(),
05421   "File where the timing in micro seconds is stored.")
05422   ("tmpdir,t", po::value<string>(),
05423   "Temporary directory where sockets are put.")
05424   ("verbose,v", po::value<bool>(&verbose)->default_value(false),
05425   "Write more.")
05426   ("version,V", "Output version information and exit.")
05427   ("no-defaults", po::value<bool>()->default_value(false)->zero_tokens(),
05428   "Configuration file defaults are not used if no-defaults is set")
05429   ;
05430 
05431   po::options_description test_options("Options specific to the drizzleimport");
05432   test_options.add_options()
05433   ("basedir,b", po::value<string>(&opt_basedir)->default_value(""),
05434   "Basedir for tests.")
05435   ("character-sets-dir", po::value<string>(&opt_charsets_dir)->default_value(""),
05436   "Directory where character sets are.")
05437   ("database,D", po::value<string>(&opt_db)->default_value(""),
05438   "Database to use.")
05439   ("include,i", po::value<string>(&opt_include)->default_value(""),
05440   "Include SQL before each test case.")  
05441   ("testdir", po::value<string>(&opt_testdir)->default_value(""),
05442   "Path to use to search for test files")
05443   ("logdir", po::value<string>(&opt_logdir)->default_value(""),
05444   "Directory for log files")
05445   ("max-connect-retries", po::value<uint32_t>(&opt_max_connect_retries)->default_value(500)->notifier(&check_retries),
05446   "Max number of connection attempts when connecting to server")
05447   ("quiet,s", po::value<bool>(&silent)->default_value(false)->zero_tokens(),
05448   "Suppress all normal output.")
05449   ("record,r", "Record output of test_file into result file.")
05450   ("result-file,R", po::value<string>(&result_file_name)->default_value(""),
05451   "Read/Store result from/in this file.")
05452   ("silent,s", po::value<bool>(&silent)->default_value(false)->zero_tokens(),
05453   "Suppress all normal output. Synonym for --quiet.")
05454   ("tail-lines", po::value<uint32_t>(&opt_tail_lines)->default_value(0)->notifier(&check_tail_lines),
05455   "Number of lines of the resul to include in a failure report")
05456   ;
05457 
05458   po::options_description client_options("Options specific to the client");
05459   client_options.add_options()
05460 
05461   ("host,h", po::value<string>(&opt_host)->default_value("localhost"),
05462   "Connect to host.")
05463   ("password,P", po::value<string>(&password)->default_value("PASSWORD_SENTINEL"),
05464   "Password to use when connecting to server.")
05465   ("port,p", po::value<uint32_t>(&opt_port)->default_value(0),
05466   "Port number to use for connection or 0 for default")
05467   ("protocol", po::value<string>(&opt_protocol),
05468   "The protocol of connection (mysql or drizzle).")
05469   ("user,u", po::value<string>(&opt_user)->default_value(""),
05470   "User for login.")
05471   ;
05472 
05473   po::positional_options_description p;
05474   p.add("database", 1);
05475 
05476   po::options_description long_options("Allowed Options");
05477   long_options.add(commandline_options).add(test_options).add(client_options);
05478 
05479   std::string system_config_dir_test(SYSCONFDIR); 
05480   system_config_dir_test.append("/drizzle/drizzletest.cnf");
05481 
05482   std::string system_config_dir_client(SYSCONFDIR); 
05483   system_config_dir_client.append("/drizzle/client.cnf");
05484 
05485   std::string user_config_dir((getenv("XDG_CONFIG_HOME")? getenv("XDG_CONFIG_HOME"):"~/.config"));
05486 
05487   if (user_config_dir.compare(0, 2, "~/") == 0)
05488   {
05489     const char *homedir= getenv("HOME");
05490     if (homedir != NULL)
05491       user_config_dir.replace(0, 1, homedir);
05492   }
05493 
05494   po::variables_map vm;
05495 
05496   // Disable allow_guessing
05497   int style = po::command_line_style::default_style & ~po::command_line_style::allow_guessing;
05498 
05499   po::store(po::command_line_parser(argc, argv).options(long_options).
05500             style(style).positional(p).extra_parser(parse_password_arg).run(),
05501             vm);
05502 
05503   if (! vm["no-defaults"].as<bool>())
05504   {
05505     std::string user_config_dir_test(user_config_dir);
05506     user_config_dir_test.append("/drizzle/drizzletest.cnf"); 
05507 
05508     std::string user_config_dir_client(user_config_dir);
05509     user_config_dir_client.append("/drizzle/client.cnf");
05510 
05511     ifstream user_test_ifs(user_config_dir_test.c_str());
05512     po::store(parse_config_file(user_test_ifs, test_options), vm);
05513 
05514     ifstream user_client_ifs(user_config_dir_client.c_str());
05515     po::store(parse_config_file(user_client_ifs, client_options), vm);
05516 
05517     ifstream system_test_ifs(system_config_dir_test.c_str());
05518     store(parse_config_file(system_test_ifs, test_options), vm);
05519 
05520     ifstream system_client_ifs(system_config_dir_client.c_str());
05521     po::store(parse_config_file(system_client_ifs, client_options), vm);
05522   }
05523 
05524   po::notify(vm);
05525 
05526   /* Init expected errors */
05527   memset(&saved_expected_errors, 0, sizeof(saved_expected_errors));
05528 
05529   /* Init connections */
05530   memset(connections, 0, sizeof(connections));
05531   connections_end= connections +
05532     (sizeof(connections)/sizeof(struct st_connection)) - 1;
05533   next_con= connections + 1;
05534 
05535   /* Init file stack */
05536   memset(file_stack.data(), 0, sizeof(file_stack));
05537   cur_file= file_stack.data();
05538 
05539   /* Init block stack */
05540   memset(block_stack, 0, sizeof(block_stack));
05541   block_stack_end=
05542     block_stack + (sizeof(block_stack)/sizeof(struct st_block)) - 1;
05543   cur_block= block_stack;
05544   cur_block->ok= true; /* Outer block should always be executed */
05545   cur_block->cmd= cmd_none;
05546 
05547   var_set_string("$DRIZZLE_SERVER_VERSION", drizzle_version());
05548 
05549   memset(&master_pos, 0, sizeof(master_pos));
05550 
05551   parser.current_line= parser.read_lines= 0;
05552   memset(&var_reg, 0, sizeof(var_reg));
05553 
05554   init_builtin_echo();
05555 
05556   ds_res.reserve(65536);
05557   ds_progress.reserve(2048);
05558   ds_warning_messages.reserve(2048);
05559 
05560  
05561   if (vm.count("record"))
05562   {
05563     record = 1;
05564   }
05565 
05566   if (vm.count("test-file"))
05567   {
05568     string tmp= vm["test-file"].as<string>();
05569     char buff[FN_REFLEN];
05570     if (!internal::test_if_hard_path(tmp.c_str()))
05571     {
05572       snprintf(buff, sizeof(buff), "%s%s",opt_basedir.c_str(),tmp.c_str());
05573       tmp= buff;
05574     }
05575     internal::fn_format(buff, tmp.c_str(), "", "", MY_UNPACK_FILENAME);
05576     assert(cur_file == file_stack.data() && cur_file->file == 0);
05577     if (!(cur_file->file= fopen(buff, "r")))
05578     {
05579       fprintf(stderr, _("Could not open '%s' for reading: errno = %d"), buff, errno);
05580       return EXIT_ARGUMENT_INVALID;
05581     }
05582     if (!(cur_file->file_name= strdup(buff)))
05583     {
05584       fprintf(stderr, _("Out of memory"));
05585       return EXIT_OUT_OF_MEMORY;
05586     }
05587     cur_file->lineno= 1;
05588   }
05589 
05590   if (vm.count("timer-file"))
05591   {
05592     string tmp= vm["timer-file"].as<string>().c_str();
05593     static char buff[FN_REFLEN];
05594     if (!internal::test_if_hard_path(tmp.c_str()))
05595     {
05596       snprintf(buff, sizeof(buff), "%s%s",opt_basedir.c_str(),tmp.c_str());
05597       tmp= buff;
05598     }
05599     internal::fn_format(buff, tmp.c_str(), "", "", MY_UNPACK_FILENAME);
05600     timer_file= buff;
05601     unlink(timer_file);       /* Ignore error, may not exist */
05602   }
05603 
05604   if (vm.count("protocol"))
05605   {
05606     std::transform(opt_protocol.begin(), opt_protocol.end(),
05607       opt_protocol.begin(), ::tolower);
05608 
05609     if (not opt_protocol.compare("mysql"))
05610       use_drizzle_protocol=false;
05611     else if (not opt_protocol.compare("drizzle"))
05612       use_drizzle_protocol=true;
05613     else
05614     {
05615       cout << _("Error: Unknown protocol") << " '" << opt_protocol << "'" << endl;
05616       exit(-1);
05617     }
05618   }
05619 
05620   if (vm.count("port"))
05621   {
05622     /* If the port number is > 65535 it is not a valid port
05623        This also helps with potential data loss casting unsigned long to a
05624        uint32_t. */
05625     if (opt_port > 65535)
05626     {
05627       fprintf(stderr, _("Value supplied for port is not valid.\n"));
05628       exit(EXIT_ARGUMENT_INVALID);
05629     }
05630   }
05631 
05632   if( vm.count("password") )
05633   {
05634     if (!opt_password.empty())
05635       opt_password.erase();
05636     if (password == PASSWORD_SENTINEL)
05637     {
05638       opt_password= "";
05639     }
05640     else
05641     {
05642       opt_password= password;
05643       tty_password= false;
05644     }
05645   }
05646   else
05647   {
05648       tty_password= true;
05649   }
05650 
05651   if (vm.count("tmpdir"))
05652   {
05653     strncpy(TMPDIR, vm["tmpdir"].as<string>().c_str(), sizeof(TMPDIR));
05654   }
05655 
05656   if (vm.count("version"))
05657   {
05658     printf("%s  Ver %s Distrib %s, for %s-%s (%s)\n",internal::my_progname,MTEST_VERSION,
05659     drizzle_version(),HOST_VENDOR,HOST_OS,HOST_CPU);
05660     exit(0);
05661   }
05662   
05663   if (vm.count("help"))
05664   {
05665     printf("%s  Ver %s Distrib %s, for %s-%s (%s)\n",internal::my_progname,MTEST_VERSION,
05666     drizzle_version(),HOST_VENDOR,HOST_OS,HOST_CPU);
05667     printf("MySQL AB, by Sasha, Matt, Monty & Jani\n");
05668     printf("Drizzle version modified by Brian, Jay, Monty Taylor, PatG and Stewart\n");
05669     printf("This software comes with ABSOLUTELY NO WARRANTY\n\n");
05670     printf("Runs a test against the DRIZZLE server and compares output with a results file.\n\n");
05671     printf("Usage: %s [OPTIONS] [database] < test_file\n", internal::my_progname);
05672     exit(0);
05673   }
05674 
05675   if (tty_password)
05676   {
05677     opt_pass= client_get_tty_password(NULL);          /* purify tested */
05678   }
05679 
05680   server_initialized= 1;
05681   if (cur_file == file_stack.data() && cur_file->file == 0)
05682   {
05683     cur_file->file= stdin;
05684     cur_file->file_name= strdup("<stdin>");
05685     if (cur_file->file_name == NULL)
05686       die("Out of memory");
05687     cur_file->lineno= 1;
05688   }
05689   cur_con= connections;
05690   if ((cur_con->drizzle= drizzle_create(NULL)) == NULL)
05691     die("Failed in drizzle_create()");
05692   if (!( drizzle_con_create(cur_con->drizzle, &cur_con->con)))
05693     die("Failed in drizzle_con_create()");
05694   drizzle_con_add_options(&cur_con->con, use_drizzle_protocol ? DRIZZLE_CON_EXPERIMENTAL : DRIZZLE_CON_MYSQL);
05695 
05696   if (!(cur_con->name = strdup("default")))
05697     die("Out of memory");
05698   safe_connect(&cur_con->con, cur_con->name, opt_host, opt_user, opt_pass,
05699                opt_db, opt_port);
05700 
05701   fill_global_error_names();
05702 
05703   /* Use all time until exit if no explicit 'start_timer' */
05704   timer_start= timer_now();
05705 
05706   /*
05707     Initialize $drizzleclient_errno with -1, so we can
05708     - distinguish it from valid values ( >= 0 ) and
05709     - detect if there was never a command sent to the server
05710   */
05711   var_set_errno(-1);
05712 
05713   /* Update $drizzleclient_get_server_version to that of current connection */
05714   var_set_drizzleclient_get_server_version(&cur_con->con);
05715 
05716   if (! opt_include.empty())
05717   {
05718     open_file(opt_include.c_str());
05719   }
05720 
05721   while (!read_command(&command) && !abort_flag)
05722   {
05723     int current_line_inc = 1, processed = 0;
05724     if (command->type == Q_UNKNOWN || command->type == Q_COMMENT_WITH_COMMAND)
05725       get_command_type(command);
05726 
05727     if (parsing_disabled &&
05728         command->type != Q_ENABLE_PARSING &&
05729         command->type != Q_DISABLE_PARSING)
05730     {
05731       command->type= Q_COMMENT;
05732       scan_command_for_warnings(command);
05733     }
05734 
05735     if (cur_block->ok)
05736     {
05737       command->last_argument= command->first_argument;
05738       processed = 1;
05739       switch (command->type) {
05740       case Q_CONNECT:
05741         do_connect(command);
05742         break;
05743       case Q_CONNECTION:
05744         select_connection(command);
05745         break;
05746       case Q_DISCONNECT:
05747       case Q_DIRTY_CLOSE:
05748         do_close_connection(command); break;
05749       case Q_ENABLE_QUERY_LOG:   disable_query_log=0; break;
05750       case Q_DISABLE_QUERY_LOG:  disable_query_log=1; break;
05751       case Q_ENABLE_ABORT_ON_ERROR:  abort_on_error=1; break;
05752       case Q_DISABLE_ABORT_ON_ERROR: abort_on_error=0; break;
05753       case Q_ENABLE_RESULT_LOG:  disable_result_log=0; break;
05754       case Q_DISABLE_RESULT_LOG: disable_result_log=1; break;
05755       case Q_ENABLE_WARNINGS:    disable_warnings=0; break;
05756       case Q_DISABLE_WARNINGS:   disable_warnings=1; break;
05757       case Q_ENABLE_INFO:        disable_info=0; break;
05758       case Q_DISABLE_INFO:       disable_info=1; break;
05759       case Q_ENABLE_METADATA:    display_metadata=1; break;
05760       case Q_DISABLE_METADATA:   display_metadata=0; break;
05761       case Q_SOURCE: do_source(command); break;
05762       case Q_SLEEP: do_sleep(command, 0); break;
05763       case Q_REAL_SLEEP: do_sleep(command, 1); break;
05764       case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(command); break;
05765       case Q_INC: do_modify_var(command, DO_INC); break;
05766       case Q_DEC: do_modify_var(command, DO_DEC); break;
05767       case Q_ECHO: do_echo(command); command_executed++; break;
05768       case Q_SYSTEM: do_system(command); break;
05769       case Q_REMOVE_FILE: do_remove_file(command); break;
05770       case Q_MKDIR: do_mkdir(command); break;
05771       case Q_RMDIR: do_rmdir(command); break;
05772       case Q_FILE_EXIST: do_file_exist(command); break;
05773       case Q_WRITE_FILE: do_write_file(command); break;
05774       case Q_APPEND_FILE: do_append_file(command); break;
05775       case Q_DIFF_FILES: do_diff_files(command); break;
05776       case Q_SEND_QUIT: do_send_quit(command); break;
05777       case Q_CHANGE_USER: do_change_user(command); break;
05778       case Q_CAT_FILE: do_cat_file(command); break;
05779       case Q_COPY_FILE: do_copy_file(command); break;
05780       case Q_CHMOD_FILE: do_chmod_file(command); break;
05781       case Q_PERL: do_perl(command); break;
05782       case Q_DELIMITER:
05783         do_delimiter(command);
05784         break;
05785       case Q_DISPLAY_VERTICAL_RESULTS:
05786         display_result_vertically= true;
05787         break;
05788       case Q_DISPLAY_HORIZONTAL_RESULTS:
05789         display_result_vertically= false;
05790         break;
05791       case Q_SORTED_RESULT:
05792         /*
05793           Turn on sorting of result set, will be reset after next
05794           command
05795         */
05796         display_result_sorted= true;
05797         break;
05798       case Q_LET: do_let(command); break;
05799       case Q_EVAL_RESULT:
05800         die("'eval_result' command  is deprecated");
05801       case Q_EVAL:
05802       case Q_QUERY_VERTICAL:
05803       case Q_QUERY_HORIZONTAL:
05804         if (command->query == command->query_buf)
05805         {
05806           /* Skip the first part of command, i.e query_xxx */
05807           command->query= command->first_argument;
05808           command->first_word_len= 0;
05809         }
05810         /* fall through */
05811       case Q_QUERY:
05812       case Q_REAP:
05813       {
05814         bool old_display_result_vertically= display_result_vertically;
05815         /* Default is full query, both reap and send  */
05816         int flags= QUERY_REAP_FLAG | QUERY_SEND_FLAG;
05817 
05818         if (q_send_flag)
05819         {
05820           /* Last command was an empty 'send' */
05821           flags= QUERY_SEND_FLAG;
05822           q_send_flag= 0;
05823         }
05824         else if (command->type == Q_REAP)
05825         {
05826           flags= QUERY_REAP_FLAG;
05827         }
05828 
05829         /* Check for special property for this query */
05830         display_result_vertically|= (command->type == Q_QUERY_VERTICAL);
05831 
05832         if (! save_file.empty())
05833         {
05834           command->require_file= save_file;
05835           save_file.clear();
05836         }
05837         run_query(cur_con, command, flags);
05838         command_executed++;
05839         command->last_argument= command->end;
05840 
05841         /* Restore settings */
05842         display_result_vertically= old_display_result_vertically;
05843 
05844         break;
05845       }
05846       case Q_SEND:
05847         if (!*command->first_argument)
05848         {
05849           /*
05850             This is a send without arguments, it indicates that _next_ query
05851             should be send only
05852           */
05853           q_send_flag= 1;
05854           break;
05855         }
05856 
05857         /* Remove "send" if this is first iteration */
05858         if (command->query == command->query_buf)
05859           command->query= command->first_argument;
05860 
05861         /*
05862           run_query() can execute a query partially, depending on the flags.
05863           QUERY_SEND_FLAG flag without QUERY_REAP_FLAG tells it to just send
05864           the query and read the result some time later when reap instruction
05865           is given on this connection.
05866         */
05867         run_query(cur_con, command, QUERY_SEND_FLAG);
05868         command_executed++;
05869         command->last_argument= command->end;
05870         break;
05871       case Q_REQUIRE:
05872         do_get_file_name(command, save_file);
05873         break;
05874       case Q_ERROR:
05875         do_get_errcodes(command);
05876         break;
05877       case Q_REPLACE:
05878         do_get_replace(command);
05879         break;
05880       case Q_REPLACE_REGEX:
05881         do_get_replace_regex(command);
05882         break;
05883       case Q_REPLACE_COLUMN:
05884         do_get_replace_column(command);
05885         break;
05886       case Q_SAVE_MASTER_POS: do_save_master_pos(); break;
05887       case Q_SYNC_WITH_MASTER: do_sync_with_master(command); break;
05888       case Q_SYNC_SLAVE_WITH_MASTER:
05889       {
05890         do_save_master_pos();
05891         if (*command->first_argument)
05892           select_connection(command);
05893         else
05894           select_connection_name("slave");
05895         do_sync_with_master2(0);
05896         break;
05897       }
05898       case Q_COMMENT:        /* Ignore row */
05899         command->last_argument= command->end;
05900         break;
05901       case Q_PING:
05902         {
05903           drizzle_result_st result;
05904           drizzle_return_t ret;
05905           (void) drizzle_ping(&cur_con->con, &result, &ret);
05906           if (ret == DRIZZLE_RETURN_OK || ret == DRIZZLE_RETURN_ERROR_CODE)
05907             drizzle_result_free(&result);
05908         }
05909         break;
05910       case Q_EXEC:
05911         do_exec(command);
05912         command_executed++;
05913         break;
05914       case Q_START_TIMER:
05915         /* Overwrite possible earlier start of timer */
05916         timer_start= timer_now();
05917         break;
05918       case Q_END_TIMER:
05919         /* End timer before ending drizzletest */
05920         timer_output();
05921         break;
05922       case Q_CHARACTER_SET:
05923         do_set_charset(command);
05924         break;
05925       case Q_DISABLE_RECONNECT:
05926         set_reconnect(&cur_con->con, 0);
05927         break;
05928       case Q_ENABLE_RECONNECT:
05929         set_reconnect(&cur_con->con, 1);
05930         break;
05931       case Q_DISABLE_PARSING:
05932         if (parsing_disabled == 0)
05933           parsing_disabled= 1;
05934         else
05935           die("Parsing is already disabled");
05936         break;
05937       case Q_ENABLE_PARSING:
05938         /*
05939           Ensure we don't get parsing_disabled < 0 as this would accidentally
05940           disable code we don't want to have disabled
05941         */
05942         if (parsing_disabled == 1)
05943           parsing_disabled= 0;
05944         else
05945           die("Parsing is already enabled");
05946         break;
05947       case Q_DIE:
05948         /* Abort test with error code and error message */
05949         die("%s", command->first_argument);
05950         break;
05951       case Q_EXIT:
05952         /* Stop processing any more commands */
05953         abort_flag= 1;
05954         break;
05955       case Q_SKIP:
05956         abort_not_supported_test("%s", command->first_argument);
05957         break;
05958 
05959       case Q_RESULT:
05960         die("result, deprecated command");
05961         break;
05962 
05963       default:
05964         processed= 0;
05965         break;
05966       }
05967     }
05968 
05969     if (!processed)
05970     {
05971       current_line_inc= 0;
05972       switch (command->type) {
05973       case Q_WHILE: do_block(cmd_while, command); break;
05974       case Q_IF: do_block(cmd_if, command); break;
05975       case Q_END_BLOCK: do_done(command); break;
05976       default: current_line_inc = 1; break;
05977       }
05978     }
05979     else
05980       check_eol_junk(command->last_argument);
05981 
05982     if (command->type != Q_ERROR &&
05983         command->type != Q_COMMENT)
05984     {
05985       /*
05986         As soon as any non "error" command or comment has been executed,
05987         the array with expected errors should be cleared
05988       */
05989       memset(&saved_expected_errors, 0, sizeof(saved_expected_errors));
05990     }
05991 
05992     if (command_executed != last_command_executed)
05993     {
05994       /*
05995         As soon as any command has been executed,
05996         the replace structures should be cleared
05997       */
05998       free_all_replace();
05999 
06000       /* Also reset "sorted_result" */
06001       display_result_sorted= false;
06002     }
06003     last_command_executed= command_executed;
06004 
06005     parser.current_line += current_line_inc;
06006     if ( opt_mark_progress )
06007       mark_progress(command, parser.current_line);
06008   }
06009 
06010   start_lineno= 0;
06011 
06012   if (parsing_disabled)
06013     die("Test ended with parsing disabled");
06014 
06015   /*
06016     The whole test has been executed _sucessfully_.
06017     Time to compare result or save it to record file.
06018     The entire output from test is now kept in ds_res.
06019   */
06020   if (ds_res.length())
06021   {
06022     if (! result_file_name.empty())
06023     {
06024       /* A result file has been specified */
06025 
06026       if (record)
06027       {
06028         /* Recording - dump the output from test to result file */
06029         str_to_file(result_file_name.c_str(), ds_res.c_str(), ds_res.length());
06030       }
06031       else
06032       {
06033         /* Check that the output from test is equal to result file
06034            - detect missing result file
06035            - detect zero size result file
06036         */
06037         check_result(&ds_res);
06038       }
06039     }
06040     else
06041     {
06042       /* No result_file_name specified to compare with, print to stdout */
06043       printf("%s", ds_res.c_str());
06044     }
06045   }
06046   else
06047   {
06048     die("The test didn't produce any output");
06049   }
06050 
06051   if (!command_executed &&
06052       ! result_file_name.empty() && !stat(result_file_name.c_str(), &res_info))
06053   {
06054     /*
06055       my_stat() successful on result file. Check if we have not run a
06056       single query, but we do have a result file that contains data.
06057       Note that we don't care, if my_stat() fails. For example, for a
06058       non-existing or non-readable file, we assume it's fine to have
06059       no query output from the test file, e.g. regarded as no error.
06060     */
06061     die("No queries executed but result file found!");
06062   }
06063 
06064   if ( opt_mark_progress && ! result_file_name.empty() )
06065     dump_progress();
06066 
06067   /* Dump warning messages */
06068   if (! result_file_name.empty() && ds_warning_messages.length())
06069     dump_warning_messages();
06070 
06071   timer_output();
06072   /* Yes, if we got this far the test has suceeded! Sakila smiles */
06073   cleanup_and_exit(0);
06074 }
06075 
06076   catch(exception &err)
06077   {
06078     cerr<<err.what()<<endl;
06079   }
06080 
06081   return 0; /* Keep compiler happy too */
06082 }
06083 
06084 
06085 /*
06086   A primitive timer that give results in milliseconds if the
06087   --timer-file=<filename> is given. The timer result is written
06088   to that file when the result is available. To not confuse
06089   mysql-test-run with an old obsolete result, we remove the file
06090   before executing any commands. The time we measure is
06091 
06092   - If no explicit 'start_timer' or 'end_timer' is given in the
06093   test case, the timer measure how long we execute in drizzletest.
06094 
06095   - If only 'start_timer' is given we measure how long we execute
06096   from that point until we terminate drizzletest.
06097 
06098   - If only 'end_timer' is given we measure how long we execute
06099   from that we enter drizzletest to the 'end_timer' is command is
06100   executed.
06101 
06102   - If both 'start_timer' and 'end_timer' are given we measure
06103   the time between executing the two commands.
06104 */
06105 
06106 void timer_output()
06107 {
06108   if (timer_file)
06109   {
06110     ostringstream buf;
06111     uint64_t timer= timer_now() - timer_start;
06112     buf << timer;
06113     str_to_file(timer_file,buf.str().c_str(), buf.str().size() );
06114     /* Timer has been written to the file, don't use it anymore */
06115     timer_file= 0;
06116   }
06117 }
06118 
06119 
06120 uint64_t timer_now()
06121 {
06122 #if defined(HAVE_GETHRTIME)
06123   return gethrtime()/1000/1000;
06124 #else
06125   uint64_t newtime;
06126   struct timeval t;
06127   /*
06128     The following loop is here because gettimeofday may fail on some systems
06129   */
06130   while (gettimeofday(&t, NULL) != 0)
06131   {}
06132   newtime= (uint64_t)t.tv_sec * 1000000 + t.tv_usec;
06133   return newtime/1000;
06134 #endif  /* defined(HAVE_GETHRTIME) */
06135 }
06136 
06137 
06138 /*
06139   Get arguments for replace_columns. The syntax is:
06140   replace-column column_number to_string [column_number to_string ...]
06141   Where each argument may be quoted with ' or "
06142   A argument may also be a variable, in which case the value of the
06143   variable is replaced.
06144 */
06145 
06146 void do_get_replace_column(struct st_command *command)
06147 {
06148   char *from= command->first_argument;
06149   char *buff, *start;
06150 
06151 
06152   free_replace_column();
06153   if (!*from)
06154     die("Missing argument in %s", command->query);
06155 
06156   /* Allocate a buffer for results */
06157   start= buff= (char *)malloc(strlen(from)+1);
06158   while (*from)
06159   {
06160     uint32_t column_number;
06161 
06162     char *to= get_string(&buff, &from, command);
06163     if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS)
06164       die("Wrong column number to replace_column in '%s'", command->query);
06165     if (!*from)
06166       die("Wrong number of arguments to replace_column in '%s'", command->query);
06167     to= get_string(&buff, &from, command);
06168     free(replace_column[column_number-1]);
06169     replace_column[column_number-1]= strdup(to);
06170     if (replace_column[column_number-1] == NULL)
06171       die("Out of memory");
06172     set_if_bigger(max_replace_column, column_number);
06173   }
06174   free(start);
06175   command->last_argument= command->end;
06176 }
06177 
06178 
06179 void free_replace_column()
06180 {
06181   for (uint32_t i= 0 ; i < max_replace_column; i++)
06182   {
06183     free(replace_column[i]);
06184     replace_column[i]= 0;
06185   }
06186   max_replace_column= 0;
06187 }
06188 
06189 
06190 /****************************************************************************/
06191 /*
06192   Replace functions
06193 */
06194 
06195 /* Definitions for replace result */
06196 
06197 class POINTER_ARRAY
06198 {    /* when using array-strings */
06199 public:
06200   ~POINTER_ARRAY();
06201   int insert(char* name);
06202 
06203   POINTER_ARRAY()
06204   {
06205     memset(this, 0, sizeof(*this));
06206   }
06207 
06208   TYPELIB typelib;        /* Pointer to strings */
06209   unsigned char *str;          /* Strings is here */
06210   uint8_t* flag;          /* Flag about each var. */
06211   uint32_t array_allocs;
06212   uint32_t max_count;
06213   uint32_t length;
06214   uint32_t max_length;
06215 };
06216 
06217 struct st_replace;
06218 struct st_replace *init_replace(const char **from, const char **to, uint32_t count,
06219                                 char *word_end_chars);
06220 
06221 void replace_strings_append(struct st_replace *rep, string* ds,
06222                             const char *from, int len);
06223 
06224 st_replace *glob_replace= NULL;
06225 // boost::scoped_ptr<st_replace> glob_replace;
06226 
06227 /*
06228   Get arguments for replace. The syntax is:
06229   replace from to [from to ...]
06230   Where each argument may be quoted with ' or "
06231   A argument may also be a variable, in which case the value of the
06232   variable is replaced.
06233 */
06234 
06235 POINTER_ARRAY::~POINTER_ARRAY()
06236 {
06237   if (!typelib.count)
06238     return;
06239   typelib.count= 0;
06240   free((char*) typelib.type_names);
06241   typelib.type_names=0;
06242   free(str);
06243 }
06244 
06245 void do_get_replace(st_command *command)
06246 {
06247   char *from= command->first_argument;
06248   if (!*from)
06249     die("Missing argument in %s", command->query);
06250   free_replace();
06251   POINTER_ARRAY to_array, from_array;
06252   char* start= (char*)malloc(strlen(from) + 1);
06253   char* buff= start;
06254   while (*from)
06255   {
06256     char *to= get_string(&buff, &from, command);
06257     if (!*from)
06258       die("Wrong number of arguments to replace_result in '%s'", command->query);
06259     from_array.insert(to);
06260     to= get_string(&buff, &from, command);
06261     to_array.insert(to);
06262   }
06263   char word_end_chars[256];
06264   char* pos= word_end_chars;
06265   for (int i= 1; i < 256; i++)
06266   {
06267     if (my_isspace(charset_info, i))
06268       *pos++= i;
06269   }
06270   *pos=0;          /* End pointer */
06271   if (!(glob_replace= init_replace(from_array.typelib.type_names,
06272                                    to_array.typelib.type_names,
06273                                    from_array.typelib.count,
06274                                    word_end_chars)))
06275     die("Can't initialize replace from '%s'", command->query);
06276   free(start);
06277   command->last_argument= command->end;
06278   return;
06279 }
06280 
06281 
06282 void free_replace()
06283 {
06284   free(glob_replace);
06285   glob_replace=0;
06286 }
06287 
06288 
06289 typedef struct st_replace {
06290   bool found;
06291   struct st_replace *next[256];
06292 } REPLACE;
06293 
06294 typedef struct st_replace_found {
06295   bool found;
06296   char *replace_string;
06297   uint32_t to_offset;
06298   int from_offset;
06299 } REPLACE_STRING;
06300 
06301 
06302 void replace_strings_append(REPLACE *rep, string* ds,
06303                             const char *str, int len)
06304 {
06305   REPLACE *rep_pos;
06306   REPLACE_STRING *rep_str;
06307   const char *start, *from;
06308 
06309 
06310   start= from= str;
06311   rep_pos=rep+1;
06312   for (;;)
06313   {
06314     /* Loop through states */
06315     while (!rep_pos->found)
06316       rep_pos= rep_pos->next[(unsigned char) *from++];
06317 
06318     /* Does this state contain a string to be replaced */
06319     if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string)
06320     {
06321       /* No match found */
06322       ds->append(start, from - start - 1);
06323       return;
06324     }
06325 
06326     /* Append part of original string before replace string */
06327     ds->append(start, (from - rep_str->to_offset) - start);
06328 
06329     /* Append replace string */
06330     ds->append(rep_str->replace_string,
06331                strlen(rep_str->replace_string));
06332 
06333     if (!*(from-=rep_str->from_offset) && rep_pos->found != 2)
06334       return;
06335 
06336     assert(from <= str+len);
06337     start= from;
06338     rep_pos=rep;
06339   }
06340 }
06341 
06342 
06343 /*
06344   Regex replace  functions
06345 */
06346 
06347 
06348 /* Stores regex substitutions */
06349 
06350 struct st_regex
06351 {
06352   char* pattern; /* Pattern to be replaced */
06353   char* replace; /* String or expression to replace the pattern with */
06354   int icase; /* true if the match is case insensitive */
06355   int global; /* true if the match should be global -- 
06356                  i.e. repeat the matching until the end of the string */
06357 };
06358 
06359 class st_replace_regex
06360 {
06361 public:
06362   st_replace_regex(char* expr);
06363   int multi_reg_replace(char* val);
06364 
06365   /*
06366     Temporary storage areas for substitutions. To reduce unnessary copying
06367     and memory freeing/allocation, we pre-allocate two buffers, and alternate
06368     their use, one for input/one for output, the roles changing on the next
06369     st_regex substition. At the end of substitutions  buf points to the
06370     one containing the final result.
06371   */
06372   typedef vector<st_regex> regex_arr_t;
06373 
06374   char* buf_;
06375   char* even_buf;
06376   char* odd_buf;
06377   int even_buf_len;
06378   int odd_buf_len;
06379   boost::array<char, 8 << 10> buf0_;
06380   boost::array<char, 8 << 10> buf1_;
06381   regex_arr_t regex_arr;
06382 };
06383 
06384 boost::scoped_ptr<st_replace_regex> glob_replace_regex;
06385 
06386 int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace,
06387                 char *string, int icase, int global);
06388 
06389 
06390 
06391 /*
06392   Finds the next (non-escaped) '/' in the expression.
06393   (If the character '/' is needed, it can be escaped using '\'.)
06394 */
06395 
06396 #define PARSE_REGEX_ARG                         \
06397   while (p < expr_end)                          \
06398   {                                             \
06399     char c= *p;                                 \
06400     if (c == '/')                               \
06401     {                                           \
06402       if (last_c == '\\')                       \
06403       {                                         \
06404         buf_p[-1]= '/';                         \
06405       }                                         \
06406       else                                      \
06407       {                                         \
06408         *buf_p++ = 0;                           \
06409         break;                                  \
06410       }                                         \
06411     }                                           \
06412     else                                        \
06413       *buf_p++ = c;                             \
06414                                                 \
06415     last_c= c;                                  \
06416     p++;                                        \
06417   }                                             \
06418                                                 \
06419 /*
06420   Initializes the regular substitution expression to be used in the
06421   result output of test.
06422 
06423   Returns: st_replace_regex struct with pairs of substitutions
06424 */
06425 
06426 st_replace_regex::st_replace_regex(char* expr)
06427 {
06428   uint32_t expr_len= strlen(expr);
06429   char last_c = 0;
06430   st_regex reg;
06431 
06432   char* buf= new char[expr_len];
06433   char* expr_end= expr + expr_len;
06434   char* p= expr;
06435   char* buf_p= buf;
06436 
06437   /* for each regexp substitution statement */
06438   while (p < expr_end)
06439   {
06440     memset(&reg, 0, sizeof(reg));
06441     /* find the start of the statement */
06442     while (p < expr_end)
06443     {
06444       if (*p == '/')
06445         break;
06446       p++;
06447     }
06448 
06449     if (p == expr_end || ++p == expr_end)
06450     {
06451       if (!regex_arr.empty())
06452         break;
06453       else
06454         goto err;
06455     }
06456     /* we found the start */
06457     reg.pattern= buf_p;
06458 
06459     /* Find first argument -- pattern string to be removed */
06460     PARSE_REGEX_ARG
06461 
06462       if (p == expr_end || ++p == expr_end)
06463         goto err;
06464 
06465     /* buf_p now points to the replacement pattern terminated with \0 */
06466     reg.replace= buf_p;
06467 
06468     /* Find second argument -- replace string to replace pattern */
06469     PARSE_REGEX_ARG
06470 
06471       if (p == expr_end)
06472         goto err;
06473 
06474     /* skip the ending '/' in the statement */
06475     p++;
06476 
06477     /* Check if we should do matching case insensitive */
06478     if (p < expr_end && *p == 'i')
06479     {
06480       p++;
06481       reg.icase= 1;
06482     }
06483 
06484     /* Check if we should do matching globally */
06485     if (p < expr_end && *p == 'g')
06486     {
06487       p++;
06488       reg.global= 1;
06489     }
06490     regex_arr.push_back(reg);
06491   }
06492   odd_buf_len= even_buf_len= buf0_.size();
06493   even_buf= buf0_.data();
06494   odd_buf= buf1_.data();
06495   buf_= even_buf;
06496 
06497   return;
06498 
06499 err:
06500   die("Error parsing replace_regex \"%s\"", expr);
06501 }
06502 
06503 /*
06504   Execute all substitutions on val.
06505 
06506   Returns: true if substituition was made, false otherwise
06507   Side-effect: Sets r->buf to be the buffer with all substitutions done.
06508 
06509   IN:
06510   struct st_replace_regex* r
06511   char* val
06512   Out:
06513   struct st_replace_regex* r
06514   r->buf points at the resulting buffer
06515   r->even_buf and r->odd_buf might have been reallocated
06516   r->even_buf_len and r->odd_buf_len might have been changed
06517 
06518   TODO:  at some point figure out if there is a way to do everything
06519   in one pass
06520 */
06521 
06522 int st_replace_regex::multi_reg_replace(char* val)
06523 {
06524   char* in_buf= val;
06525   char* out_buf= even_buf;
06526   int* buf_len_p= &even_buf_len;
06527   buf_= 0;
06528 
06529   /* For each substitution, do the replace */
06530   BOOST_FOREACH(regex_arr_t::const_reference i, regex_arr)
06531   {
06532     char* save_out_buf= out_buf;
06533     if (!reg_replace(&out_buf, buf_len_p, i.pattern, i.replace,
06534                      in_buf, i.icase, i.global))
06535     {
06536       /* if the buffer has been reallocated, make adjustements */
06537       if (save_out_buf != out_buf)
06538       {
06539         if (save_out_buf == even_buf)
06540           even_buf= out_buf;
06541         else
06542           odd_buf= out_buf;
06543       }
06544       buf_= out_buf;
06545       if (in_buf == val)
06546         in_buf= odd_buf;
06547       std::swap(in_buf, out_buf);
06548       buf_len_p= (out_buf == even_buf) ? &even_buf_len : &odd_buf_len;
06549     }
06550   }
06551   return buf_ == 0;
06552 }
06553 
06554 /*
06555   Parse the regular expression to be used in all result files
06556   from now on.
06557 
06558   The syntax is --replace_regex /from/to/i /from/to/i ...
06559   i means case-insensitive match. If omitted, the match is
06560   case-sensitive
06561 
06562 */
06563 void do_get_replace_regex(struct st_command *command)
06564 {
06565   char *expr= command->first_argument;
06566   glob_replace_regex.reset(new st_replace_regex(expr));
06567   command->last_argument= command->end;
06568 }
06569 
06570 /*
06571   Performs a regex substitution
06572 
06573   IN:
06574 
06575   buf_p - result buffer pointer. Will change if reallocated
06576   buf_len_p - result buffer length. Will change if the buffer is reallocated
06577   pattern - regexp pattern to match
06578   replace - replacement expression
06579   string - the string to perform substituions in
06580   icase - flag, if set to 1 the match is case insensitive
06581 */
06582 int reg_replace(char** buf_p, int* buf_len_p, char *pattern,
06583                 char *replace, char *in_string, int icase, int global)
06584 {
06585   const char *error= NULL;
06586   int erroffset;
06587   int ovector[3];
06588   pcre *re= pcre_compile(pattern,
06589                          icase ? PCRE_CASELESS | PCRE_MULTILINE : PCRE_MULTILINE,
06590                          &error, &erroffset, NULL);
06591   if (re == NULL)
06592     return 1;
06593 
06594   if (! global)
06595   {
06596 
06597     int rc= pcre_exec(re, NULL, in_string, (int)strlen(in_string),
06598                       0, 0, ovector, 3);
06599     if (rc < 0)
06600     {
06601       pcre_free(re);
06602       return 1;
06603     }
06604 
06605     char *substring_to_replace= in_string + ovector[0];
06606     int substring_length= ovector[1] - ovector[0];
06607     *buf_len_p= strlen(in_string) - substring_length + strlen(replace);
06608     char * new_buf = (char *)malloc(*buf_len_p+1);
06609     if (new_buf == NULL)
06610     {
06611       pcre_free(re);
06612       return 1;
06613     }
06614 
06615     memset(new_buf, 0, *buf_len_p+1);
06616     strncpy(new_buf, in_string, substring_to_replace-in_string);
06617     strncpy(new_buf+(substring_to_replace-in_string), replace, strlen(replace));
06618     strncpy(new_buf+(substring_to_replace-in_string)+strlen(replace),
06619             substring_to_replace + substring_length,
06620             strlen(in_string)
06621               - substring_length
06622               - (substring_to_replace-in_string));
06623     *buf_p= new_buf;
06624 
06625     pcre_free(re);
06626     return 0;
06627   }
06628   else
06629   {
06630     /* Repeatedly replace the string with the matched regex */
06631     string subject(in_string);
06632     size_t replace_length= strlen(replace);
06633     size_t length_of_replacement= strlen(replace);
06634     size_t current_position= 0;
06635     int rc= 0;
06636 
06637     while (true) 
06638     {
06639       rc= pcre_exec(re, NULL, subject.c_str(), subject.length(), 
06640                     current_position, 0, ovector, 3);
06641       if (rc < 0)
06642       {
06643         break;
06644       }
06645 
06646       current_position= static_cast<size_t>(ovector[0]);
06647       replace_length= static_cast<size_t>(ovector[1] - ovector[0]);
06648       subject.replace(current_position, replace_length, replace, length_of_replacement);
06649       current_position= current_position + length_of_replacement;
06650     }
06651 
06652     char *new_buf = (char *) malloc(subject.length() + 1);
06653     if (new_buf == NULL)
06654     {
06655       pcre_free(re);
06656       return 1;
06657     }
06658     memset(new_buf, 0, subject.length() + 1);
06659     strncpy(new_buf, subject.c_str(), subject.length());
06660     *buf_len_p= subject.length() + 1;
06661     *buf_p= new_buf;
06662           
06663     pcre_free(re);
06664     return 0;
06665   }
06666 }
06667 
06668 
06669 #ifndef WORD_BIT
06670 #define WORD_BIT (8*sizeof(uint32_t))
06671 #endif
06672 
06673 #define SET_MALLOC_HUNC 64
06674 #define LAST_CHAR_CODE 259
06675 
06676 class REP_SET
06677 {
06678 public:
06679   void internal_set_bit(uint32_t bit);
06680   void internal_clear_bit(uint32_t bit);
06681   void or_bits(const REP_SET *from);
06682   void copy_bits(const REP_SET *from);
06683   int cmp_bits(const REP_SET *set2) const;
06684   int get_next_bit(uint32_t lastpos) const;
06685 
06686   uint32_t  *bits;        /* Pointer to used sets */
06687   short next[LAST_CHAR_CODE];    /* Pointer to next sets */
06688   uint32_t  found_len;      /* Best match to date */
06689   int  found_offset;
06690   uint32_t  table_offset;
06691   uint32_t  size_of_bits;      /* For convinience */
06692 };
06693 
06694 class REP_SETS
06695 {
06696 public:
06697   int find_set(const REP_SET *find);
06698   void free_last_set();
06699   void free_sets();
06700   void make_sets_invisible();
06701 
06702   uint32_t    count;      /* Number of sets */
06703   uint32_t    extra;      /* Extra sets in buffer */
06704   uint32_t    invisible;    /* Sets not shown */
06705   uint32_t    size_of_bits;
06706   REP_SET  *set,*set_buffer;
06707   uint32_t    *bit_buffer;
06708 };
06709 
06710 struct FOUND_SET 
06711 {
06712   uint32_t table_offset;
06713   int found_offset;
06714 };
06715 
06716 struct FOLLOWS
06717 {
06718   int chr;
06719   uint32_t table_offset;
06720   uint32_t len;
06721 };
06722 
06723 int init_sets(REP_SETS *sets, uint32_t states);
06724 REP_SET *make_new_set(REP_SETS *sets);
06725 int find_found(FOUND_SET *found_set, uint32_t table_offset, int found_offset);
06726 
06727 static uint32_t found_sets= 0;
06728 
06729 static uint32_t replace_len(const char *str)
06730 {
06731   uint32_t len=0;
06732   while (*str)
06733   {
06734     if (str[0] == '\\' && str[1])
06735       str++;
06736     str++;
06737     len++;
06738   }
06739   return len;
06740 }
06741 
06742 /* Return 1 if regexp starts with \b or ends with \b*/
06743 
06744 static bool start_at_word(const char *pos)
06745 {
06746   return ((!memcmp(pos, "\\b",2) && pos[2]) || !memcmp(pos, "\\^", 2));
06747 }
06748 
06749 static bool end_of_word(const char *pos)
06750 {
06751   const char *end= strchr(pos, '\0');
06752   return (end > pos+2 && !memcmp(end-2, "\\b", 2)) || (end >= pos+2 && !memcmp(end-2, "\\$",2));
06753 }
06754 
06755 /* Init a replace structure for further calls */
06756 
06757 REPLACE *init_replace(const char **from, const char **to, uint32_t count, char *word_end_chars)
06758 {
06759   const int SPACE_CHAR= 256;
06760   const int START_OF_LINE= 257;
06761   const int END_OF_LINE= 258;
06762 
06763   uint32_t i,j,states,set_nr,len,result_len,max_length,found_end,bits_set,bit_nr;
06764   int used_sets,chr,default_state;
06765   char used_chars[LAST_CHAR_CODE],is_word_end[256];
06766   char *to_pos, **to_array;
06767 
06768   /* Count number of states */
06769   for (i=result_len=max_length=0 , states=2 ; i < count ; i++)
06770   {
06771     len=replace_len(from[i]);
06772     if (!len)
06773     {
06774       errno=EINVAL;
06775       return(0);
06776     }
06777     states+=len+1;
06778     result_len+=(uint32_t) strlen(to[i])+1;
06779     if (len > max_length)
06780       max_length=len;
06781   }
06782   memset(is_word_end, 0, sizeof(is_word_end));
06783   for (i=0 ; word_end_chars[i] ; i++)
06784     is_word_end[(unsigned char) word_end_chars[i]]=1;
06785 
06786   REP_SETS sets;
06787   REP_SET *set,*start_states,*word_states,*new_set;
06788   REPLACE_STRING *rep_str;
06789   if (init_sets(&sets, states))
06790     return 0;
06791   found_sets=0;
06792   vector<FOUND_SET> found_set(max_length * count);
06793   make_new_set(&sets);      /* Set starting set */
06794   sets.make_sets_invisible();      /* Hide previus sets */
06795   used_sets=-1;
06796   word_states=make_new_set(&sets);    /* Start of new word */
06797   start_states=make_new_set(&sets);    /* This is first state */
06798   vector<FOLLOWS> follow(states + 2);
06799   FOLLOWS *follow_ptr= &follow[1];
06800   /* Init follow_ptr[] */
06801   for (i=0, states=1; i < count; i++)
06802   {
06803     if (from[i][0] == '\\' && from[i][1] == '^')
06804     {
06805       start_states->internal_set_bit(states + 1);
06806       if (!from[i][2])
06807       {
06808         start_states->table_offset=i;
06809         start_states->found_offset=1;
06810       }
06811     }
06812     else if (from[i][0] == '\\' && from[i][1] == '$')
06813     {
06814       start_states->internal_set_bit(states);
06815       word_states->internal_set_bit(states);
06816       if (!from[i][2] && start_states->table_offset == UINT32_MAX)
06817       {
06818         start_states->table_offset=i;
06819         start_states->found_offset=0;
06820       }
06821     }
06822     else
06823     {
06824       word_states->internal_set_bit(states);
06825       if (from[i][0] == '\\' && (from[i][1] == 'b' && from[i][2]))
06826         start_states->internal_set_bit(states + 1);
06827       else
06828         start_states->internal_set_bit(states);
06829     }
06830     const char *pos;
06831     for (pos= from[i], len=0; *pos ; pos++)
06832     {
06833       if (*pos == '\\' && *(pos+1))
06834       {
06835         pos++;
06836         switch (*pos) {
06837         case 'b':
06838           follow_ptr->chr = SPACE_CHAR;
06839           break;
06840         case '^':
06841           follow_ptr->chr = START_OF_LINE;
06842           break;
06843         case '$':
06844           follow_ptr->chr = END_OF_LINE;
06845           break;
06846         case 'r':
06847           follow_ptr->chr = '\r';
06848           break;
06849         case 't':
06850           follow_ptr->chr = '\t';
06851           break;
06852         case 'v':
06853           follow_ptr->chr = '\v';
06854           break;
06855         default:
06856           follow_ptr->chr = (unsigned char) *pos;
06857           break;
06858         }
06859       }
06860       else
06861         follow_ptr->chr= (unsigned char) *pos;
06862       follow_ptr->table_offset=i;
06863       follow_ptr->len= ++len;
06864       follow_ptr++;
06865     }
06866     follow_ptr->chr=0;
06867     follow_ptr->table_offset=i;
06868     follow_ptr->len=len;
06869     follow_ptr++;
06870     states+=(uint32_t) len+1;
06871   }
06872 
06873 
06874   for (set_nr=0; set_nr < sets.count ; set_nr++)
06875   {
06876     set=sets.set+set_nr;
06877     default_state= 0;        /* Start from beginning */
06878 
06879     /* If end of found-string not found or start-set with current set */
06880 
06881     for (i= UINT32_MAX; (i= set->get_next_bit(i)) ;)
06882     {
06883       if (!follow[i].chr && !default_state)
06884         default_state= find_found(&found_set.front(), set->table_offset, set->found_offset+1);
06885     }
06886     sets.set[used_sets].copy_bits(set);    /* Save set for changes */
06887     if (!default_state)
06888       sets.set[used_sets].or_bits(sets.set);  /* Can restart from start */
06889 
06890     /* Find all chars that follows current sets */
06891     memset(used_chars, 0, sizeof(used_chars));
06892     for (i= UINT32_MAX; (i= sets.set[used_sets].get_next_bit(i)) ;)
06893     {
06894       used_chars[follow[i].chr]=1;
06895       if ((follow[i].chr == SPACE_CHAR && !follow[i+1].chr &&
06896            follow[i].len > 1) || follow[i].chr == END_OF_LINE)
06897         used_chars[0]=1;
06898     }
06899 
06900     /* Mark word_chars used if \b is in state */
06901     if (used_chars[SPACE_CHAR])
06902       for (const char *pos= word_end_chars ; *pos ; pos++)
06903         used_chars[(int) (unsigned char) *pos] = 1;
06904 
06905     /* Handle other used characters */
06906     for (chr= 0 ; chr < 256 ; chr++)
06907     {
06908       if (! used_chars[chr])
06909         set->next[chr]= chr ? default_state : -1;
06910       else
06911       {
06912         new_set=make_new_set(&sets);
06913         set=sets.set+set_nr;      /* if realloc */
06914         new_set->table_offset=set->table_offset;
06915         new_set->found_len=set->found_len;
06916         new_set->found_offset=set->found_offset+1;
06917         found_end=0;
06918 
06919         for (i= UINT32_MAX ; (i= sets.set[used_sets].get_next_bit(i)) ; )
06920         {
06921           if (!follow[i].chr || follow[i].chr == chr ||
06922               (follow[i].chr == SPACE_CHAR &&
06923                (is_word_end[chr] ||
06924                 (!chr && follow[i].len > 1 && ! follow[i+1].chr))) ||
06925               (follow[i].chr == END_OF_LINE && ! chr))
06926           {
06927             if ((! chr || (follow[i].chr && !follow[i+1].chr)) &&
06928                 follow[i].len > found_end)
06929               found_end=follow[i].len;
06930             if (chr && follow[i].chr)
06931               new_set->internal_set_bit(i + 1);    /* To next set */
06932             else
06933               new_set->internal_set_bit(i);
06934           }
06935         }
06936         if (found_end)
06937         {
06938           new_set->found_len=0;      /* Set for testing if first */
06939           bits_set=0;
06940           for (i= UINT32_MAX; (i= new_set->get_next_bit(i)) ;)
06941           {
06942             if ((follow[i].chr == SPACE_CHAR ||
06943                  follow[i].chr == END_OF_LINE) && ! chr)
06944               bit_nr=i+1;
06945             else
06946               bit_nr=i;
06947             if (follow[bit_nr-1].len < found_end ||
06948                 (new_set->found_len &&
06949                  (chr == 0 || !follow[bit_nr].chr)))
06950               new_set->internal_clear_bit(i);
06951             else
06952             {
06953               if (chr == 0 || !follow[bit_nr].chr)
06954               {          /* best match  */
06955                 new_set->table_offset=follow[bit_nr].table_offset;
06956                 if (chr || (follow[i].chr == SPACE_CHAR ||
06957                             follow[i].chr == END_OF_LINE))
06958                   new_set->found_offset=found_end;  /* New match */
06959                 new_set->found_len=found_end;
06960               }
06961               bits_set++;
06962             }
06963           }
06964           if (bits_set == 1)
06965           {
06966             set->next[chr] = find_found(&found_set.front(), new_set->table_offset, new_set->found_offset);
06967             sets.free_last_set();
06968           }
06969           else
06970             set->next[chr] = sets.find_set(new_set);
06971         }
06972         else
06973           set->next[chr] = sets.find_set(new_set);
06974       }
06975     }
06976   }
06977 
06978   /* Alloc replace structure for the replace-state-machine */
06979 
06980   REPLACE *replace= (REPLACE*)malloc(sizeof(REPLACE) * (sets.count)
06981     + sizeof(REPLACE_STRING) * (found_sets + 1) + sizeof(char*) * count + result_len);
06982   if (replace)
06983   {
06984     memset(replace, 0, sizeof(REPLACE)*(sets.count)+
06985                        sizeof(REPLACE_STRING)*(found_sets+1)+
06986                        sizeof(char *)*count+result_len);
06987     rep_str=(REPLACE_STRING*) (replace+sets.count);
06988     to_array= (char **) (rep_str+found_sets+1);
06989     to_pos=(char *) (to_array+count);
06990     for (i=0 ; i < count ; i++)
06991     {
06992       to_array[i]=to_pos;
06993       to_pos=strcpy(to_pos,to[i])+strlen(to[i])+1;
06994     }
06995     rep_str[0].found=1;
06996     rep_str[0].replace_string=0;
06997     for (i=1 ; i <= found_sets ; i++)
06998     {
06999       const char *pos= from[found_set[i-1].table_offset];
07000       rep_str[i].found= !memcmp(pos, "\\^", 3) ? 2 : 1;
07001       rep_str[i].replace_string= to_array[found_set[i-1].table_offset];
07002       rep_str[i].to_offset= found_set[i-1].found_offset-start_at_word(pos);
07003       rep_str[i].from_offset= found_set[i-1].found_offset-replace_len(pos) + end_of_word(pos);
07004     }
07005     for (i=0 ; i < sets.count ; i++)
07006     {
07007       for (j=0 ; j < 256 ; j++)
07008         if (sets.set[i].next[j] >= 0)
07009           replace[i].next[j]=replace+sets.set[i].next[j];
07010         else
07011           replace[i].next[j]=(REPLACE*) (rep_str+(-sets.set[i].next[j]-1));
07012     }
07013   }
07014   sets.free_sets();
07015   return replace;
07016 }
07017 
07018 
07019 int init_sets(REP_SETS *sets,uint32_t states)
07020 {
07021   memset(sets, 0, sizeof(*sets));
07022   sets->size_of_bits=((states+7)/8);
07023   if (!(sets->set_buffer=(REP_SET*) malloc(sizeof(REP_SET)*SET_MALLOC_HUNC)))
07024     return 1;
07025   if (!(sets->bit_buffer=(uint*) malloc(sizeof(uint32_t)*sets->size_of_bits*
07026                                         SET_MALLOC_HUNC)))
07027   {
07028     free(sets->set);
07029     return 1;
07030   }
07031   return 0;
07032 }
07033 
07034 /* Make help sets invisible for nicer codeing */
07035 
07036 void REP_SETS::make_sets_invisible()
07037 {
07038   invisible= count;
07039   set += count;
07040   count= 0;
07041 }
07042 
07043 REP_SET *make_new_set(REP_SETS *sets)
07044 {
07045   uint32_t i,count,*bit_buffer;
07046   REP_SET *set;
07047   if (sets->extra)
07048   {
07049     sets->extra--;
07050     set=sets->set+ sets->count++;
07051     memset(set->bits, 0, sizeof(uint32_t)*sets->size_of_bits);
07052     memset(&set->next[0], 0, sizeof(set->next[0])*LAST_CHAR_CODE);
07053     set->found_offset=0;
07054     set->found_len=0;
07055     set->table_offset= UINT32_MAX;
07056     set->size_of_bits=sets->size_of_bits;
07057     return set;
07058   }
07059   count=sets->count+sets->invisible+SET_MALLOC_HUNC;
07060   if (!(set=(REP_SET*) realloc((unsigned char*) sets->set_buffer,
07061                                   sizeof(REP_SET)*count)))
07062     return 0;
07063   sets->set_buffer=set;
07064   sets->set=set+sets->invisible;
07065   if (!(bit_buffer=(uint*) realloc((unsigned char*) sets->bit_buffer,
07066                                    (sizeof(uint32_t)*sets->size_of_bits)*count)))
07067     return 0;
07068   sets->bit_buffer=bit_buffer;
07069   for (i=0 ; i < count ; i++)
07070   {
07071     sets->set_buffer[i].bits=bit_buffer;
07072     bit_buffer+=sets->size_of_bits;
07073   }
07074   sets->extra=SET_MALLOC_HUNC;
07075   return make_new_set(sets);
07076 }
07077 
07078 void REP_SETS::free_last_set()
07079 {
07080   count--;
07081   extra++;
07082 }
07083 
07084 void REP_SETS::free_sets()
07085 {
07086   free(set_buffer);
07087   free(bit_buffer);
07088 }
07089 
07090 void REP_SET::internal_set_bit(uint32_t bit)
07091 {
07092   bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT);
07093 }
07094 
07095 void REP_SET::internal_clear_bit(uint32_t bit)
07096 {
07097   bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT));
07098 }
07099 
07100 
07101 void REP_SET::or_bits(const REP_SET *from)
07102 {
07103   for (uint32_t i= 0 ; i < size_of_bits; i++)
07104     bits[i]|=from->bits[i];
07105 }
07106 
07107 void REP_SET::copy_bits(const REP_SET *from)
07108 {
07109   memcpy(bits, from->bits, sizeof(uint32_t) * size_of_bits);
07110 }
07111 
07112 int REP_SET::cmp_bits(const REP_SET *set2) const
07113 {
07114   return memcmp(bits, set2->bits, sizeof(uint32_t) * size_of_bits);
07115 }
07116 
07117 /* Get next set bit from set. */
07118 
07119 int REP_SET::get_next_bit(uint32_t lastpos) const
07120 {
07121   uint32_t *start= bits + ((lastpos+1) / WORD_BIT);
07122   uint32_t *end= bits + size_of_bits;
07123   uint32_t bits0= start[0] & ~((1 << ((lastpos+1) % WORD_BIT)) -1);
07124 
07125   while (!bits0 && ++start < end)
07126     bits0= start[0];
07127   if (!bits0)
07128     return 0;
07129   uint32_t pos= (start - bits) * WORD_BIT;
07130   while (!(bits0 & 1))
07131   {
07132     bits0 >>=1;
07133     pos++;
07134   }
07135   return pos;
07136 }
07137 
07138 /* find if there is a same set in sets. If there is, use it and
07139    free given set, else put in given set in sets and return its
07140    position */
07141 
07142 int REP_SETS::find_set(const REP_SET *find)
07143 {
07144   uint32_t i= 0;
07145   for (; i < count - 1; i++)
07146   {
07147     if (!set[i].cmp_bits(find))
07148     {
07149       free_last_set();
07150       return i;
07151     }
07152   }
07153   return i;        /* return new postion */
07154 }
07155 
07156 /* find if there is a found_set with same table_offset & found_offset
07157    If there is return offset to it, else add new offset and return pos.
07158    Pos returned is -offset-2 in found_set_structure because it is
07159    saved in set->next and set->next[] >= 0 points to next set and
07160    set->next[] == -1 is reserved for end without replaces.
07161 */
07162 
07163 int find_found(FOUND_SET *found_set, uint32_t table_offset, int found_offset)
07164 {
07165   uint32_t i= 0;
07166   for (; i < found_sets; i++)
07167   {
07168     if (found_set[i].table_offset == table_offset &&
07169         found_set[i].found_offset == found_offset)
07170       return - i - 2;
07171   }
07172   found_set[i].table_offset= table_offset;
07173   found_set[i].found_offset= found_offset;
07174   found_sets++;
07175   return - i - 2; // return new postion
07176 }
07177 
07178 /****************************************************************************
07179  * Handle replacement of strings
07180  ****************************************************************************/
07181 
07182 #define PC_MALLOC    256  /* Bytes for pointers */
07183 #define PS_MALLOC    512  /* Bytes for data */
07184 
07185 static int insert_pointer_name(POINTER_ARRAY* pa, char* name)
07186 {
07187   uint32_t i,length,old_count;
07188   unsigned char *new_pos;
07189   const char **new_array;
07190 
07191 
07192   if (! pa->typelib.count)
07193   {
07194     if (!(pa->typelib.type_names=(const char **)
07195           malloc(((PC_MALLOC-MALLOC_OVERHEAD)/
07196                      (sizeof(char *)+sizeof(*pa->flag))*
07197                      (sizeof(char *)+sizeof(*pa->flag))))))
07198       return(-1);
07199     if (!(pa->str= (unsigned char*) malloc(PS_MALLOC-MALLOC_OVERHEAD)))
07200     {
07201       free((char*) pa->typelib.type_names);
07202       return (-1);
07203     }
07204     pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(unsigned char*)+
07205                                                sizeof(*pa->flag));
07206     pa->flag= (uint8_t*) (pa->typelib.type_names+pa->max_count);
07207     pa->length=0;
07208     pa->max_length=PS_MALLOC-MALLOC_OVERHEAD;
07209     pa->array_allocs=1;
07210   }
07211   length=(uint32_t) strlen(name)+1;
07212   if (pa->length+length >= pa->max_length)
07213   {
07214     if (!(new_pos= (unsigned char*)realloc((unsigned char*)pa->str,
07215                                            (size_t)(pa->max_length+PS_MALLOC))))
07216       return(1);
07217     if (new_pos != pa->str)
07218     {
07219       ptrdiff_t diff= PTR_BYTE_DIFF(new_pos,pa->str);
07220       for (i=0 ; i < pa->typelib.count ; i++)
07221         pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff,
07222                                               char*);
07223       pa->str=new_pos;
07224     }
07225     pa->max_length+=PS_MALLOC;
07226   }
07227   if (pa->typelib.count >= pa->max_count-1)
07228   {
07229     size_t len;
07230     pa->array_allocs++;
07231     len=(PC_MALLOC*pa->array_allocs - MALLOC_OVERHEAD);
07232     if (!(new_array=
07233          (const char **)realloc((unsigned char*) pa->typelib.type_names,
07234                                  len/
07235                                   (sizeof(unsigned char*)+sizeof(*pa->flag))*
07236                                   (sizeof(unsigned char*)+sizeof(*pa->flag)))))
07237       return(1);
07238     pa->typelib.type_names=new_array;
07239     old_count=pa->max_count;
07240     pa->max_count=len/(sizeof(unsigned char*) + sizeof(*pa->flag));
07241     pa->flag= (uint8_t*) (pa->typelib.type_names+pa->max_count);
07242     memcpy(pa->flag, pa->typelib.type_names+old_count,
07243            old_count*sizeof(*pa->flag));
07244   }
07245   pa->flag[pa->typelib.count]=0;      /* Reset flag */
07246   pa->typelib.type_names[pa->typelib.count++]= (char*) pa->str+pa->length;
07247   pa->typelib.type_names[pa->typelib.count]= NULL;  /* Put end-mark */
07248   strcpy((char*) pa->str+pa->length,name);
07249   pa->length+=length;
07250   return(0);
07251 } /* insert_pointer_name */
07252 
07253 int POINTER_ARRAY::insert(char* name)
07254 {
07255   return insert_pointer_name(this, name);
07256 }
07257 
07258 
07259 /* Functions that uses replace and replace_regex */
07260 
07261 /* Append the string to ds, with optional replace */
07262 void replace_append_mem(string *ds, const char *val, int len)
07263 {
07264   char *v= strdup(val);
07265 
07266   if (glob_replace_regex && !glob_replace_regex->multi_reg_replace(v))
07267   {
07268     v= glob_replace_regex->buf_;
07269     len= strlen(v);
07270   }
07271   if (glob_replace)
07272   {
07273     /* Normal replace */
07274     replace_strings_append(glob_replace, ds, v, len);
07275   }
07276   else
07277     ds->append(v, len);
07278 }
07279 
07280 
07281 /* Append zero-terminated string to ds, with optional replace */
07282 void replace_append(string *ds, const char *val)
07283 {
07284   replace_append_mem(ds, val, strlen(val));
07285 }
07286 
07287 /* Append uint32_t to ds, with optional replace */
07288 void replace_append_uint(string *ds, uint32_t val)
07289 {
07290   ostringstream buff;
07291   buff << val;
07292   replace_append_mem(ds, buff.str().c_str(), buff.str().size());
07293 
07294 }
07295 
07296 
07297 
07298 /*
07299   Build a list of pointer to each line in ds_input, sort
07300   the list and use the sorted list to append the strings
07301   sorted to the output ds
07302 
07303   SYNOPSIS
07304   dynstr_append_sorted
07305   ds - string where the sorted output will be appended
07306   ds_input - string to be sorted
07307 
07308 */
07309 
07310 
07311 void append_sorted(string* ds, string *ds_input)
07312 {
07313   priority_queue<string, vector<string>, greater<string> > lines;
07314 
07315   if (ds_input->empty())
07316     return;  /* No input */
07317 
07318   unsigned long eol_pos= 0;
07319 
07320   eol_pos= ds_input->find_first_of('\n', 0);
07321   if (eol_pos == string::npos)
07322     return; // We should have at least one header here
07323 
07324   ds->append(ds_input->substr(0, eol_pos+1));
07325 
07326   unsigned long start_pos= eol_pos+1;
07327 
07328   /* Insert line(s) in array */
07329   do {
07330 
07331     eol_pos= ds_input->find_first_of('\n', start_pos);
07332     /* Find end of line */
07333     lines.push(ds_input->substr(start_pos, eol_pos-start_pos+1));
07334     start_pos= eol_pos+1;
07335 
07336   } while ( eol_pos != string::npos);
07337 
07338   /* Create new result */
07339   while (!lines.empty()) {
07340     ds->append(lines.top());
07341     lines.pop();
07342   }
07343 
07344   return;
07345 }
07346 
07347 static void free_all_replace()
07348 {
07349   free_replace();
07350   glob_replace_regex.reset();
07351   free_replace_column();
07352 }